From d7284c7deffa0d6f170027c85bae3f64197381f7 Mon Sep 17 00:00:00 2001 From: JenkinsRobo Date: Wed, 25 May 2022 11:06:04 +0000 Subject: [PATCH] Update to v12.0.0 --- .gitignore | 29 + .nuget/packages/AjaxPro.2.2.9.17.2.nupkg | Bin 84731 -> 0 bytes .../packages/AjaxPro.2.2.9.17.2.symbols.nupkg | Bin 250192 -> 0 bytes .nuget/packages/AjaxPro.2.21.12.22.2.nupkg | Bin 0 -> 52565 bytes .../AjaxPro.2.21.12.22.2.symbols.nupkg | Bin 0 -> 291061 bytes ...it.CloudComputing.SharpBox.1.1.0.452.nupkg | Bin 83021 -> 0 bytes ...Computing.SharpBox.1.1.0.452.symbols.nupkg | Bin 354345 -> 0 bytes ...imit.CloudComputing.SharpBox.1.2.0.1.nupkg | Bin 0 -> 176651 bytes .../Microsoft.Graph.Core.1.20.1.4.nupkg | Bin 56062 -> 0 bytes ...icrosoft.Graph.Core.1.20.1.4.symbols.nupkg | Bin 209975 -> 0 bytes .../packages/Microsoft.Graph.Core.1.6.0.nupkg | Bin 0 -> 121103 bytes .../Microsoft.OneDriveSDK.2.0.7.5.nupkg | Bin 51507 -> 0 bytes ...icrosoft.OneDriveSDK.2.0.7.5.symbols.nupkg | Bin 295932 -> 0 bytes .../Microsoft.OneDriveSDK.2.1.0.nupkg | Bin 0 -> 102468 bytes .nuget/packages/Openstack.net.1.8.0.nupkg | Bin 0 -> 738345 bytes .nuget/packages/Rackspace.1.0.0.nupkg | Bin 0 -> 52116 bytes .nuget/packages/twitterizer.2.4.2.2.nupkg | Bin 98550 -> 0 bytes .../twitterizer.2.4.2.2.symbols.nupkg | Bin 557146 -> 0 bytes 3rd-Party.txt | 58 +- README.md | 2 +- build/Build.bat | 2 +- build/BuildAndDeploy.bat | 10 +- build/ManageServices.bat | 8 + build/config/TeamLabSvc.exe.config | 149 +- .../deb/Files/Tools/default-onlyoffice-ssl.sh | 4 +- .../deb/Files/Tools/default-onlyoffice.sh | 3 +- .../onlyoffice-communityserver-services.conf | 18 +- .../deb/debian/monoserveApiSystem.service | 2 +- .../debian/onlyoffice-communityserver.install | 1 + .../deb/debian/onlyofficeAutoCleanUp.service | 20 + .../debian/onlyofficeMailAggregator.service | 16 +- .../deb/debian/onlyofficeMailCleaner.service | 15 +- .../deb/debian/onlyofficeMailImap.service | 20 + .../deb/debian/onlyofficeMailWatchdog.service | 16 +- .../deb/debian/onlyofficeWebDav.service | 20 + build/install/deb/debian/postinst | 34 +- build/install/deb/debian/rules | 9 + build/install/deb/debian/templates | 7 +- .../rpm/Files/Tools/default-onlyoffice-ssl.sh | 4 +- .../rpm/Files/Tools/default-onlyoffice.sh | 3 +- .../onlyoffice-communityserver-services.conf | 18 +- .../systemd/onlyofficeAutoCleanUp.service | 20 + .../systemd/onlyofficeMailAggregator.service | 15 +- .../systemd/onlyofficeMailCleaner.service | 15 +- .../Files/systemd/onlyofficeMailImap.service | 19 + .../systemd/onlyofficeMailWatchdog.service | 16 +- .../Files/systemd/onlyofficeWebDav.service | 20 + build/install/rpm/common.spec | 27 +- .../rpm/onlyoffice-communityserver.spec | 4 +- build/msbuild/ASC.Web.Core.BuildTask.dll | Bin 23552 -> 27648 bytes build/msbuild/build.proj | 21 +- build/msbuild/deploy.proj | 189 +- build/sql/onlyoffice.data.sql | 212 +- build/sql/onlyoffice.sql | 27 +- build/sql/onlyoffice.upgradev110.sql | 1 - build/sql/onlyoffice.upgradev115.sql | 1 - build/sql/onlyoffice.upgradev120.sql | 275 + .../ASC.ActiveDirectory.csproj | 9 +- .../ComplexOperations/LdapOperation.cs | 2 +- .../LdapSaveSyncOperation.cs | 4 +- common/ASC.ActiveDirectory/LdapUserManager.cs | 14 +- common/ASC.Common/ASC.Common.csproj | 37 +- common/ASC.Common/Caching/AscCache.cs | 2 +- common/ASC.Common/Caching/RedisCache.cs | 205 +- common/ASC.Common/Data/DbManager.cs | 1 + .../Geolocation/GeolocationHelper.cs | 2 + common/ASC.Common/Logging/Log.cs | 337 +- .../ASC.Common/Logging/SelfCleaningTarget.cs | 141 - common/ASC.Common/Notify/Context.cs | 3 +- .../Notify/Engine/DispatchEngine.cs | 2 + .../ASC.Common/Notify/Engine/NotifyEngine.cs | 2 + .../ASC.Common/Notify/Engine/NotifyRequest.cs | 3 +- .../Notify/Model/ISubscriptionProvider.cs | 5 +- common/ASC.Common/Radicale/CalDavCalendar.cs | 96 + .../ASC.Common/Radicale/CardDavAddressbook.cs | 175 + common/ASC.Common/Radicale/CardDavItem.cs | 56 + common/ASC.Common/Radicale/Core/DbRadicale.cs | 39 + .../Radicale/Core/RadicaleClient.cs | 120 + .../Radicale/Core/RadicaleException.cs | 15 + common/ASC.Common/Radicale/DavRequest.cs | 18 + common/ASC.Common/Radicale/DavResponse.cs | 28 + common/ASC.Common/Radicale/IRadicaleEntity.cs | 18 + common/ASC.Common/Radicale/RadicaleEntity.cs | 60 + .../Threading/Workers/WorkerQueue.cs | 1 + .../ASC.Common/Utils/HttpRequestExtensions.cs | 5 + common/ASC.Common/Utils/StringUtils.cs | 44 + .../TimeZoneConverter/TimeZoneConverter.cs | 2 + .../Web/AbstractHttpAsyncHandler.cs | 4 +- common/ASC.Common/Web/MimeMapping.cs | 1 + common/ASC.Core.Common/ASC.Core.Common.csproj | 22 +- common/ASC.Core.Common/Audit/BaseEvent.cs | 70 + .../ASC.Core.Common/BaseCommonLinkUtility.cs | 4 +- .../ASC.Core.Common/Billing/BillingClient.cs | 67 +- .../ASC.Core.Common/Billing/CouponManager.cs | 321 - .../ASC.Core.Common/Billing/ITariffService.cs | 2 + .../Billing/License/License.cs | 27 +- .../Billing/License/LicenseReader.cs | 116 +- common/ASC.Core.Common/Billing/PaymentInfo.cs | 2 + .../ASC.Core.Common/Billing/TariffService.cs | 97 +- .../Caching/CachedAzService.cs | 8 +- .../Caching/CachedSubscriptionService.cs | 4 +- .../Caching/CachedTenantService.cs | 11 +- .../Caching/CachedUserService.cs | 62 +- .../ASC.Core.Common/Configuration/Consumer.cs | 9 +- .../Context/Impl/PaymentManager.cs | 12 + .../Context/Impl/TenantManager.cs | 1 + .../Context/Impl/UserManager.cs | 147 +- .../Context/SecurityContext.cs | 129 +- .../Contracts/HealthCheck/HealthCheckSvc.cs | 95 + common/ASC.Core.Common/Core/AzRecord.cs | 48 + common/ASC.Core.Common/Core/IUserService.cs | 2 + .../Core/SubscriptionMethod.cs | 34 + common/ASC.Core.Common/Core/UserGroupRef.cs | 36 + .../Data/DbLoginEventsManager.cs | 188 + common/ASC.Core.Common/Data/DbQuotaService.cs | 8 +- .../ASC.Core.Common/Data/DbSettingsManager.cs | 24 +- .../ASC.Core.Common/Data/DbTenantService.cs | 27 +- common/ASC.Core.Common/Data/DbUserService.cs | 83 +- common/ASC.Core.Common/HostedSolution.cs | 2 +- common/ASC.Core.Common/Logging/LogManager.cs | 33 + .../Messaging}/MessageAction.cs | 35 +- .../ASC.Core.Common/Notify/EmailSenderSink.cs | 5 +- .../ASC.Core.Common/Notify/MentionProvider.cs | 33 + .../Notify/ReplyToTagProvider.cs | 82 - .../Notify/Senders/AWSSender.cs | 2 - .../Notify/Senders/SmtpSender.cs | 68 +- .../Notify/Signalr/MailNotificationState.cs | 29 + .../Notify/Signalr/SignalrServiceClient.cs | 3 +- .../Notify/Telegram/Dao/CachedTelegramDao.cs | 2 +- .../Notify/Telegram/Dao/TelegramDao.cs | 2 +- .../Notify/Telegram/Dao/TelegramUser.cs | 2 +- .../Security/Authentication/CookieStorage.cs | 47 +- .../Tenants/TenantDomainValidator.cs | 2 +- common/ASC.Core.Common/Tenants/TenantQuota.cs | 71 +- common/ASC.Data.Backup/ASC.Data.Backup.csproj | 12 +- common/ASC.Data.Backup/IDataOperator.cs | 1 + .../Service/BackupCleanerTempFileService.cs | 73 + .../Service/BackupServiceLauncher.cs | 8 + .../ASC.Data.Backup/Service/BackupWorker.cs | 14 +- .../Storage/DocumentsBackupStorage.cs | 4 +- .../ASC.Data.Backup/Tasks/BackupPortalTask.cs | 142 +- .../Tasks/Modules/FilesModuleSpecifics.cs | 5 +- .../Tasks/Modules/MailModuleSpecifics.cs | 6 +- .../ASC.Data.Backup/Tasks/PortalTaskBase.cs | 95 +- .../Tasks/RestorePortalTask.cs | 66 +- common/ASC.Data.Backup/ZipOperator.cs | 7 + .../ASC.Data.Encryption.csproj | 1 + .../ASC.Data.Storage.Encryption.csproj | 2 +- .../ASC.Data.Storage/ASC.Data.Storage.csproj | 8 +- common/ASC.Data.Storage/BaseStorage.cs | 1 - .../CommonChunkedUploadSession.cs | 35 +- .../CommonChunkedUploadSessionHolder.cs | 13 +- .../Configuration/StorageSettings.cs | 4 +- .../DiscStorage/DiscDataStore.cs | 5 - .../Encryption/EncryptionFactory.cs | 2 +- .../GoogleCloud/GoogleCloudStorage.cs | 14 +- common/ASC.Data.Storage/IDataStore.cs | 1 - .../RackspaceCloud/RackspaceCloudStorage.cs | 5 - common/ASC.Data.Storage/S3/S3Storage.cs | 207 +- .../Selectel/SelectelStorage.cs | 5 - common/ASC.Data.Storage/StaticUploader.cs | 4 +- common/ASC.Data.Storage/StorageUploader.cs | 2 +- common/ASC.IPSecurity/IPSecurity.cs | 46 + .../ASC.Notify.Textile.csproj | 4 + common/ASC.Notify.Textile/MarkDownStyler.cs | 95 + .../NotifyTemplateResource.az-Latn-AZ.resx | 242 + .../ASC.Resource.Data.csproj | 17 +- .../ASC.Resource.Data/JsonManager.cs | 213 +- .../ASC.Resource.Data/ResourceData.cs | 4 +- common/Tests/ASC.BenchmarkTest/App.config | 15 +- .../Tests/ASC.BenchmarkTest/DbManagerTest.cs | 19 +- common/Tests/ASC.BenchmarkTest/Program.cs | 19 +- licenses/3rd-Party.license | 58 +- .../javascript plugins/linkify-html.license | 19 + licenses/npm packages/axios.license | 19 + licenses/npm packages/form-data.license | 19 + licenses/npm packages/get-byte.license | 121 + licenses/npm packages/help.license | 21 + licenses/npm packages/ipaddr.license | 19 + licenses/npm packages/to-byte-array.license | 8 + licenses/npm packages/webdav-server.license | 24 + licenses/nuget packages/AjaxMin.license | 53 - licenses/nuget packages/DotNetZip.license | 88 - .../FolkerKinzel.VCards.license | 21 + ...Microsoft.IdentityModel.Tokens.Jwt.license | 21 + licenses/nuget packages/NUglify.license | 41 + module/ASC.Api/ASC.Api.CRM/ASC.Api.CRM.csproj | 4 +- module/ASC.Api/ASC.Api.CRM/CRMApi.Cases.cs | 119 +- .../ASC.Api/ASC.Api.CRM/CRMApi.ContactInfo.cs | 97 +- module/ASC.Api/ASC.Api.CRM/CRMApi.Contacts.cs | 406 +- .../ASC.Api.CRM/CRMApi.CurrencyRates.cs | 62 +- .../ASC.Api.CRM/CRMApi.CustomFields.cs | 113 +- module/ASC.Api/ASC.Api.CRM/CRMApi.Deals.cs | 192 +- module/ASC.Api/ASC.Api.CRM/CRMApi.Invoices.cs | 312 +- module/ASC.Api/ASC.Api.CRM/CRMApi.ListItem.cs | 283 +- .../ASC.Api.CRM/CRMApi.RelationshipEvent.cs | 88 +- module/ASC.Api/ASC.Api.CRM/CRMApi.Reports.cs | 24 +- module/ASC.Api/ASC.Api.CRM/CRMApi.Tag.cs | 106 +- .../ASC.Api.CRM/CRMApi.TaskTemplate.cs | 110 +- module/ASC.Api/ASC.Api.CRM/CRMApi.Tasks.cs | 84 +- module/ASC.Api/ASC.Api.CRM/CRMApi.Utils.cs | 178 +- module/ASC.Api/ASC.Api.CRM/CRMApi.Voip.cs | 212 +- .../Wrappers/ContactInfoWrapper.cs | 29 +- .../ASC.Api.Calendar/ASC.Api.Calendar.csproj | 10 +- .../Attachments/AttachmentEngine.cs | 170 + .../Attachments/SecurityAdapter.cs | 139 + .../Attachments/SecurityAdapterProvider.cs | 56 + .../BusinessObjects/DataProvider.cs | 34 +- .../ASC.Api/ASC.Api.Calendar/CalendarApi.cs | 3677 +++---- .../ASC.Api.Calendar/CalendarBootstrap.cs | 35 + .../Notification/CalendarNotifySource.cs | 31 + .../CalendarPatternResource.Designer.cs | 30 +- .../CalendarPatternResource.az-Latn-AZ.resx | 64 +- .../Notification/CalendarPatternResource.resx | 18 +- .../Notification/calendar_patterns.xml | 4 +- .../CalendarApiResource.az-Latn-AZ.resx | 17 +- .../ASC.Api.Calendar/Wrappers/EventWrapper.cs | 12 +- .../iCalParser/DDayICalParser.cs | 20 +- .../ASC.Api.Client/ASC.Api.Client.csproj | 2 +- .../ASC.Api.Community.csproj | 5 + .../ASC.Api.Community/Blogs/BlogApi.cs | 124 +- .../Bookmarks/BookmarkApi.cs | 129 +- .../ASC.Api/ASC.Api.Community/CommunityApi.cs | 4 +- .../ASC.Api.Community/CommunityApiCommon.cs | 22 +- .../ASC.Api.Community/Events/EventApi.cs | 116 +- .../ASC.Api.Community/Forums/ForumApi.cs | 94 +- .../ASC.Api/ASC.Api.Community/Wiki/WikiApi.cs | 115 +- .../ASC.Api.Documents.csproj | 4 +- .../ASC.Api/ASC.Api.Documents/DocumentsApi.cs | 926 +- .../ASC.Api.Documents/FileEntryWrapper.cs | 12 + .../ASC.Api.Documents/FolderWrapper.cs | 6 + module/ASC.Api/ASC.Api.Feed/FeedApi.cs | 29 + module/ASC.Api/ASC.Api.Feed/FeedWrapper.cs | 3 + .../ASC.Api/ASC.Api.Mail/MailApi.Accounts.cs | 177 +- module/ASC.Api/ASC.Api.Mail/MailApi.Alerts.cs | 12 +- .../ASC.Api.Mail/MailApi.Attachments.cs | 41 +- .../ASC.Api/ASC.Api.Mail/MailApi.Autoreply.cs | 21 +- .../ASC.Api/ASC.Api.Mail/MailApi.Contacts.cs | 74 +- .../ASC.Api.Mail/MailApi.Conversations.cs | 214 +- .../ASC.Api/ASC.Api.Mail/MailApi.Filters.cs | 54 +- .../ASC.Api/ASC.Api.Mail/MailApi.Folders.cs | 73 +- .../ASC.Api.Mail/MailApi.HelpCenter.cs | 8 +- module/ASC.Api/ASC.Api.Mail/MailApi.Images.cs | 28 +- .../ASC.Api.Mail/MailApi.MailService.cs | 51 + .../ASC.Api/ASC.Api.Mail/MailApi.Messages.cs | 338 +- .../ASC.Api/ASC.Api.Mail/MailApi.Settings.cs | 66 +- .../ASC.Api/ASC.Api.Mail/MailApi.Signature.cs | 17 +- module/ASC.Api/ASC.Api.Mail/MailApi.Tags.cs | 68 +- module/ASC.Api/ASC.Api.Mail/MailApi.Tests.cs | 45 +- module/ASC.Api/ASC.Api.Mail/MailApi.cs | 95 +- .../MailServerApi.MailGroup.cs | 56 +- .../MailServerApi.Mailbox.cs | 100 +- .../MailServerApi.Notification.cs | 17 +- .../MailServerApi.Server.cs | 20 +- .../MailServerApi.WebDomain.cs | 54 +- .../ASC.Api.Migration.csproj | 75 + .../ASC.Api/ASC.Api.Migration/MigrationApi.cs | 240 + .../ASC.Api.Migration/MigrationStatus.cs | 30 + .../ASC.Api.Migration/OngoingMigration.cs | 41 + .../Properties/AssemblyInfo.cs | 36 + module/ASC.Api/ASC.Api.Portal/PortalApi.cs | 433 +- .../ASC.Api.Projects/ASC.Api.Projects.csproj | 4 +- .../ASC.Api.Projects/ProjectApi.Comments.cs | 51 +- .../ASC.Api.Projects/ProjectApi.Messages.cs | 119 +- .../ASC.Api.Projects/ProjectApi.Milestones.cs | 74 +- .../ASC.Api.Projects/ProjectApi.Projects.cs | 441 +- .../ASC.Api.Projects/ProjectApi.Reports.cs | 130 +- .../ASC.Api.Projects/ProjectApi.Tags.cs | 27 +- .../ASC.Api.Projects/ProjectApi.Tasks.cs | 286 +- .../ASC.Api.Projects/ProjectApi.TimeSpend.cs | 86 +- module/ASC.Api/ASC.Api.Projects/ProjectApi.cs | 66 +- .../ASC.Api.Security/ASC.Api.Security.csproj | 12 + .../ASC.Api.Security/AuditEventWrapper.cs | 68 +- .../ASC.Api.Security/AuditReportCreator.cs | 89 + .../ASC.Api.Security/LoginEventWrapper.cs | 31 +- module/ASC.Api/ASC.Api.Security/ModelTypes.cs | 13 + .../ASC.Api/ASC.Api.Security/SecurityApi.cs | 382 +- .../ASC.Api.Settings/ASC.Api.Settings.csproj | 5 +- .../ASC.Api/ASC.Api.Settings/BuildVersion.cs | 6 +- .../ASC.Api.Settings/LdapSettingsApi.cs | 83 +- .../ASC.Api.Settings/RadicaleSettingsAPI.cs | 163 + .../ASC.Api/ASC.Api.Settings/SettingsApi.cs | 608 +- .../ASC.Api.Settings/Smtp/SmtpOperation.cs | 2 +- .../ASC.Api.Settings/SmtpSettingsApi.cs | 37 +- .../ASC.Api.Settings/SsoSettingsV2Api.cs | 42 +- module/ASC.Api/ASC.Api/ASC.Api.Core.csproj | 6 +- module/ASC.Api/ASC.Api/ApiServer.cs | 2 +- .../ASC.Api/Batch/ApiBatchHttpHandler.cs | 23 +- .../ASC.Api/Impl/ApiArgumentBuilder.cs | 13 +- module/ASC.Api/ASC.Api/Impl/ApiHttpHandler.cs | 24 +- .../ASC.Api/Impl/ApiHttpHandlerBase.cs | 23 +- .../ASC.Api/ASC.Api/Impl/ApiRouteHandler.cs | 2 +- .../Impl/Responders/ContentResponder.cs | 7 +- .../ASC.Api/Interfaces/ApiHttpAsyncHandler.cs | 26 + .../ASC.Api/Interfaces/IApiHttpHandler.cs | 27 - module/ASC.Api/ASC.Api/Utils/Binder.cs | 14 +- module/ASC.Api/ASC.Employee/EmployeeApi.cs | 462 +- module/ASC.Api/ASC.Employee/GroupsApi.cs | 107 +- .../ASC.Api/ASC.Specific/ASC.Specific.csproj | 4 +- .../AuthenticationEntryPoint.cs | 145 +- .../CapabilitiesApi/CapabilitiesData.cs | 3 + .../CapabilitiesApi/CapabilitiesEntryPoint.cs | 57 +- module/ASC.AuditTrail/ASC.AuditTrail.csproj | 24 +- module/ASC.AuditTrail/AuditActionsMapper.cs | 2746 ----- module/ASC.AuditTrail/AuditEvent.cs | 80 +- .../ASC.AuditTrail/AuditEventsRepository.cs | 245 +- module/ASC.AuditTrail/AuditReportCreator.cs | 67 - .../AuditReportResource.Designer.cs | 209 +- .../AuditReportResource.az-Latn-AZ.resx | 1423 +++ .../ASC.AuditTrail/AuditReportResource.resx | 132 +- module/ASC.AuditTrail/BaseEvent.cs | 92 - module/ASC.AuditTrail/LoginEvent.cs | 50 +- .../ASC.AuditTrail/LoginEventsRepository.cs | 164 +- .../Mappers/AuditActionMapper.cs | 71 +- .../ASC.AuditTrail/Mappers/CrmActionMapper.cs | 2008 +--- .../Mappers/DocumentsActionMapper.cs | 491 +- .../Mappers/IModuleActionMapper.cs | 13 + .../Mappers/IProductActionMapper.cs | 12 + .../Mappers/LoginActionMapper.cs | 70 +- module/ASC.AuditTrail/Mappers/MessageMaps.cs | 261 +- .../Mappers/OthersActionMapper.cs | 40 +- .../Mappers/PeopleActionMapper.cs | 390 +- .../Mappers/ProjectsActionMapper.cs | 764 +- .../Mappers/SettingsActionMapper.cs | 545 +- module/ASC.AuditTrail/Types/ActionType.cs | 26 + module/ASC.AuditTrail/Types/EntryType.cs | 31 + module/ASC.AuditTrail/Types/ModuleType.cs | 33 + module/ASC.AuditTrail/Types/ProductType.cs | 14 + .../ASC.Data.Reassigns.csproj | 2 +- .../ReassignProgressItem.cs | 4 +- .../ASC.Data.Reassigns/RemoveProgressItem.cs | 4 +- .../ASC.ElasticSearch.csproj | 6 +- .../ASC.ElasticSearch/Core/SearchSettings.cs | 7 +- .../ASC.ElasticSearch/Core/WrapperWithDoc.cs | 10 +- .../ASC.ElasticSearch/Engine/BaseIndexer.cs | 62 +- .../Engine/FactoryIndexer.cs | 42 +- .../ASC.Feed.Aggregator.csproj | 4 +- .../FeedAggregatorService.cs | 5 +- .../Modules/Community/BlogsModule.cs | 2 +- .../Modules/Community/EventsModule.cs | 4 +- .../Modules/Community/ForumPostsModule.cs | 4 +- .../Modules/Community/ForumTopicsModule.cs | 4 +- .../Modules/ModulesHelper.cs | 10 + .../Modules/People/BirthdaysModule.cs | 129 + .../Modules/People/NewEmployeeModule.cs | 115 + .../Modules/Projects/DiscussionsModule.cs | 2 +- module/ASC.Feed/ASC.Feed.csproj | 2 +- module/ASC.Feed/Constants.cs | 96 +- .../Data/FeedAggregateDataProvider.cs | 15 +- module/ASC.Feed/Feed.cs | 3 + module/ASC.Feed/FeedAction.cs | 3 +- .../ASC.Files.AutoCleanUp.csproj | 69 + module/ASC.Files.AutoCleanUp/ConfigSection.cs | 36 + module/ASC.Files.AutoCleanUp/Launcher.cs | 57 + .../Properties/AssemblyInfo.cs | 35 + .../TenantUserSettings.cs | 29 + module/ASC.Files.AutoCleanUp/Worker.cs | 191 + module/ASC.Files.AutoCleanUp/app.config | 7 + .../ASC.Files.Thirdparty.csproj | 16 +- module/ASC.Files.Thirdparty/Box/BoxFileDao.cs | 15 +- .../CachedProviderAccountDao.cs | 3 + .../Dropbox/DropboxDaoBase.cs | 2 +- .../Dropbox/DropboxFileDao.cs | 9 + .../Dropbox/DropboxProviderInfo.cs | 20 +- .../GoogleDrive/GoogleDriveFileDao.cs | 9 + .../OneDrive/OneDriveFileDao.cs | 9 + .../OneDrive/OneDriveProviderInfo.cs | 34 +- .../ProviderAccountDao.cs | 26 +- .../ProviderDao/ProviderFileDao.cs | 18 + .../ProviderDao/ProviderFolderDao.cs | 3 +- .../SharePoint/SharePointFileDao.cs | 9 + .../Sharpbox/SharpBoxFileDao.cs | 9 + module/ASC.Files.Thirdparty/app.config | 100 - module/ASC.Files.ThumbnailBuilder/Builder.cs | 52 +- .../ConfigSection.cs | 8 +- .../ASC.HealthCheck/ASC.HealthCheck.csproj | 3 + .../ASC.HealthCheck/Models/Service.cs | 16 - .../ASC.HealthCheck/Models/ServiceEnum.cs | 1 - .../Resources/HealthCheckResource.Designer.cs | 9 - .../HealthCheckResource.az-Latn-AZ.resx | 319 + .../Resources/HealthCheckResource.cs.resx | 3 - .../Resources/HealthCheckResource.de.resx | 3 - .../Resources/HealthCheckResource.es.resx | 3 - .../Resources/HealthCheckResource.fi.resx | 3 - .../Resources/HealthCheckResource.fr.resx | 3 - .../Resources/HealthCheckResource.it.resx | 3 - .../Resources/HealthCheckResource.nl.resx | 3 - .../Resources/HealthCheckResource.pt-BR.resx | 3 - .../Resources/HealthCheckResource.resx | 3 - .../Resources/HealthCheckResource.sk.resx | 3 - .../ASC.Mail.Autoreply.csproj | 118 - .../AddressParsers/AddressParser.cs | 76 - .../AddressParsers/CommentAddressParser.cs | 98 - .../AddressParsers/CommunityAddressParser.cs | 49 - .../AddressParsers/FileAddressParser.cs | 44 - .../AddressParsers/IAddressParser.cs | 24 - .../AddressParsers/ProjectAddressParser.cs | 62 - .../ASC.Mail.Autoreply/ApiRequest.cs | 78 - .../ASC.Mail.Autoreply/ApiService.cs | 248 - .../ASC.Mail.Autoreply/AutoreplyService.cs | 287 - .../AutoreplyServiceController.cs | 53 - .../ASC.Mail.Autoreply/Configuration.cs | 141 - .../ASC.Mail.Autoreply/CooldownInspector.cs | 126 - .../ParameterResolvers/BlogTagsResolver.cs | 41 - .../ParameterResolvers/ContentResolver.cs | 54 - .../ParameterResolvers/IParameterResolver.cs | 26 - .../TaskDeadlineResolver.cs | 37 - .../TaskMilestoneResolver.cs | 36 - .../TaskPriorityResolver.cs | 36 - .../TaskResponsiblesResolver.cs | 87 - .../ParameterResolvers/TitleResolver.cs | 44 - .../Properties/AssemblyInfo.cs | 39 - .../Utility/Html/Html2TextConverter.cs | 90 - .../Utility/Html/HtmlSanitizer.cs | 105 - .../Utility/Html/Text2HtmlConverter.cs | 43 - .../Utility/StringDistance.cs | 72 - .../Utility/StringExtensions.cs | 53 - .../ASC.Mail.Core/Net/ABNF/ABFN.cs | 150 - .../ASC.Mail.Core/Net/ABNF/ABFN_Group.cs | 101 - .../Net/ABNF/ABNF_Alternation.cs | 111 - .../ASC.Mail.Core/Net/ABNF/ABNF_BinVal.cs | 29 - .../ASC.Mail.Core/Net/ABNF/ABNF_CharVal.cs | 184 - .../Net/ABNF/ABNF_Concatenation.cs | 111 - .../ASC.Mail.Core/Net/ABNF/ABNF_DecVal.cs | 206 - .../ASC.Mail.Core/Net/ABNF/ABNF_Element.cs | 24 - .../ASC.Mail.Core/Net/ABNF/ABNF_HexVal.cs | 29 - .../ASC.Mail.Core/Net/ABNF/ABNF_Option.cs | 101 - .../ASC.Mail.Core/Net/ABNF/ABNF_ProseVal.cs | 189 - .../ASC.Mail.Core/Net/ABNF/ABNF_Repetition.cs | 204 - .../ASC.Mail.Core/Net/ABNF/ABNF_Rule.cs | 161 - .../ASC.Mail.Core/Net/ABNF/ABNF_RuleName.cs | 164 - .../ASC.Mail.Core/Net/ASC.Mail.csproj | 843 -- .../Net/AUTH/AUTH_SASL_ServerMechanism.cs | 74 - .../AUTH/AUTH_SASL_ServerMechanism_CramMd5.cs | 257 - .../AUTH_SASL_ServerMechanism_DigestMd5.cs | 227 - .../AUTH/AUTH_SASL_ServerMechanism_Login.cs | 188 - .../AUTH/AUTH_SASL_ServerMechanism_Plain.cs | 190 - .../Net/AUTH/AUTH_e_Authenticate.cs | 100 - .../ASC.Mail.Core/Net/AUTH/AUTH_e_UserInfo.cs | 89 - .../ASC.Mail.Core/Net/AUTH/AuthHelper.cs | 204 - .../ASC.Mail.Core/Net/AUTH/AuthType.cs | 47 - .../ASC.Mail.Core/Net/AUTH/Auth_HttpDigest.cs | 684 -- .../Net/AUTH/Auth_HttpDigest_NonceManager.cs | 236 - .../ASC.Mail.Core/Net/AsyncOP.cs | 44 - .../ASC.Mail.Core/Net/BalanceMode.cs | 35 - .../ASC.Mail.Core/Net/BindInfoProtocol.cs | 35 - .../ASC.Mail.Core/Net/CircleCollection.cs | 214 - .../Net/DNS/Client/DNS_Client.cs | 808 -- .../Net/DNS/Client/DNS_ClientException.cs | 62 - .../ASC.Mail.Core/Net/DNS/Client/DNS_rr_A.cs | 87 - .../Net/DNS/Client/DNS_rr_AAAA.cs | 85 - .../Net/DNS/Client/DNS_rr_CNAME.cs | 88 - .../Net/DNS/Client/DNS_rr_HINFO.cs | 107 - .../ASC.Mail.Core/Net/DNS/Client/DNS_rr_MX.cs | 154 - .../Net/DNS/Client/DNS_rr_NAPTR.cs | 177 - .../ASC.Mail.Core/Net/DNS/Client/DNS_rr_NS.cs | 90 - .../Net/DNS/Client/DNS_rr_PTR.cs | 88 - .../Net/DNS/Client/DNS_rr_SOA.cs | 244 - .../Net/DNS/Client/DNS_rr_SRV.cs | 129 - .../Net/DNS/Client/DNS_rr_TXT.cs | 84 - .../Net/DNS/Client/DNS_rr_base.cs | 67 - .../ASC.Mail.Core/Net/DNS/Client/DnsCache.cs | 221 - .../Net/DNS/Client/DnsServerResponse.cs | 367 - .../ASC.Mail.Core/Net/DNS/Client/QTYPE.cs | 108 - .../ASC.Mail.Core/Net/DNS/Client/RCODE.cs | 56 - .../ASC.Mail.Core/Net/DNS/Client/_OPCODE.cs | 40 - .../ASC.Mail.Core/Net/Data/DbFile.cs | 823 -- .../ASC.Mail.Core/Net/Data/LDB_DataColumn.cs | 221 - .../Net/Data/LDB_DataColumnCollection.cs | 208 - .../ASC.Mail.Core/Net/Data/LDB_DataType.cs | 50 - .../ASC.Mail.Core/Net/Data/LDB_Record.cs | 554 - .../ASC.Mail.Core/Net/Data/_DataPage.cs | 416 - .../ASC.Mail.Core/Net/Data/_ldb_Utils.cs | 103 - .../Net/Data/lsDB_FixedLengthRecord.cs | 235 - .../Net/Data/lsDB_FixedLengthTable.cs | 738 -- .../ASC.Mail.Core/Net/EncodingTools.cs | 661 -- .../ASC.Mail.Core/Net/EventArgs.cs | 63 - .../ASC.Mail.Core/Net/ExceptionEventArgs.cs | 68 - .../Net/FTP/Client/FTP_Client.cs | 1808 ---- .../Net/FTP/Client/FTP_ClientException.cs | 104 - .../ASC.Mail.Core/Net/FTP/FTP_ListItem.cs | 114 - .../ASC.Mail.Core/Net/FTP/FTP_TransferMode.cs | 35 - .../Net/FTP/Server/AuthUser_EventArgs.cs | 115 - .../Net/FTP/Server/FTP_Server.cs | 349 - .../Net/FTP/Server/FTP_Session.cs | 1271 --- .../Net/FTP/Server/FileSysEntry_EventArgs.cs | 118 - .../Net/HTTP/Server/HTTP_Server.cs | 85 - .../Net/HTTP/Server/HTTP_Session.cs | 284 - .../ASC.Mail.Core/Net/HostEndPoint.cs | 191 - .../ASC.Mail.Core/Net/ICMP/Icmp.cs | 336 - .../ASC.Mail.Core/Net/IMAP/Client/IMAP_Acl.cs | 88 - .../Net/IMAP/Client/IMAP_Client.cs | 2910 ------ .../Net/IMAP/Client/IMAP_ClientException.cs | 83 - .../Net/IMAP/Client/IMAP_FetchItem.cs | 317 - .../Net/IMAP/Client/IMAP_FetchItem_Flags.cs | 71 - .../Net/IMAP/Client/IMAP_Namespace.cs | 67 - .../Net/IMAP/Client/IMAP_NamespacesInfo.cs | 185 - .../Net/IMAP/Client/IMAP_Quota.cs | 100 - .../ASC.Mail.Core/Net/IMAP/IMAP_ACL_Flags.cs | 70 - .../ASC.Mail.Core/Net/IMAP/IMAP_BODY.cs | 513 - .../Net/IMAP/IMAP_BODY_Entity.cs | 314 - .../ASC.Mail.Core/Net/IMAP/IMAP_Envelope.cs | 650 -- .../Net/IMAP/IMAP_Flags_SetType.cs | 38 - .../Net/IMAP/IMAP_MessageFlags.cs | 63 - .../Net/IMAP/IMAP_SequenceSet.cs | 281 - .../Net/IMAP/Server/AuthUser_EventArgs.cs | 71 - .../Net/IMAP/Server/Folder_EventArgs.cs | 81 - .../Net/IMAP/Server/IMAP_Folder.cs | 69 - .../Net/IMAP/Server/IMAP_Folders.cs | 232 - .../Net/IMAP/Server/IMAP_Message.cs | 148 - .../Net/IMAP/Server/IMAP_MessageCollection.cs | 179 - .../Net/IMAP/Server/IMAP_MessageItems_enum.cs | 50 - .../Net/IMAP/Server/IMAP_SearchGroup.cs | 265 - .../Net/IMAP/Server/IMAP_SearchKey.cs | 1324 --- .../Net/IMAP/Server/IMAP_SearchMatcher.cs | 105 - .../Net/IMAP/Server/IMAP_SelectedFolder.cs | 249 - .../Net/IMAP/Server/IMAP_Server.cs | 782 -- .../Net/IMAP/Server/IMAP_Session.cs | 4775 --------- .../Net/IMAP/Server/IMAP_Utils.cs | 359 - .../Net/IMAP/Server/IMAP_eArgs_DELETEACL.cs | 89 - .../Net/IMAP/Server/IMAP_eArgs_GETACL.cs | 95 - .../IMAP/Server/IMAP_eArgs_GetMessagesInfo.cs | 70 - .../Net/IMAP/Server/IMAP_eArgs_GetQuota.cs | 74 - .../Net/IMAP/Server/IMAP_eArgs_GetUserACL.cs | 100 - .../IMAP/Server/IMAP_eArgs_MessageItems.cs | 277 - .../Net/IMAP/Server/IMAP_eArgs_SETACL.cs | 115 - .../Net/IMAP/Server/IMAP_eArgs_Search.cs | 85 - .../Net/IMAP/Server/Message_EventArgs.cs | 121 - .../Server/SharedRootFolders_EventArgs.cs | 65 - .../Net/IMAP/Server/_FetchHelper.cs | 320 - .../ASC.Mail.Core/Net/IO/Base64Stream.cs | 698 -- .../Net/IO/DataSizeExceededException.cs | 30 - .../Net/IO/IncompleteDataException.cs | 47 - .../ASC.Mail.Core/Net/IO/LineReader.cs | 387 - .../Net/IO/LineSizeExceededException.cs | 30 - .../ASC.Mail.Core/Net/IO/MultiStream.cs | 306 - .../ASC.Mail.Core/Net/IO/PartialStream.cs | 300 - .../Net/IO/QuotedPrintableStream.cs | 347 - .../ASC.Mail.Core/Net/IO/ReadLineEventArgs.cs | 269 - .../Net/IO/ReadWriteControlledStream.cs | 207 - .../Net/IO/SizeExceededAction.cs | 36 - .../ASC.Mail.Core/Net/IO/SmartStream.cs | 3447 ------- .../ASC.Mail.Core/Net/IPBindInfo.cs | 245 - .../ASC.Mail.Core/Net/JunkingStream.cs | 128 - .../ASC.Mail.Core/Net/Log/LogEntry.cs | 217 - .../ASC.Mail.Core/Net/Log/LogEntryType.cs | 45 - .../ASC.Mail.Core/Net/Log/Logger.cs | 250 - .../Net/Log/WriteLogEventArgs.cs | 68 - .../ASC.Mail.Core/Net/LumiSoft.Net | 5910 ----------- .../Net/MIME/MIME_DispositionTypes .cs | 41 - .../Net/MIME/MIME_EncodedWordEncoding.cs | 35 - .../Net/MIME/MIME_Encoding_EncodedWord.cs | 434 - .../ASC.Mail.Core/Net/MIME/MIME_Entity.cs | 972 -- .../Net/MIME/MIME_EntityCollection.cs | 197 - .../ASC.Mail.Core/Net/MIME/MIME_MediaTypes.cs | 231 - .../ASC.Mail.Core/Net/MIME/MIME_Message.cs | 194 - .../ASC.Mail.Core/Net/MIME/MIME_Reader.cs | 943 -- .../Net/MIME/MIME_TransferEncodings.cs | 62 - .../ASC.Mail.Core/Net/MIME/MIME_Utils.cs | 546 - .../ASC.Mail.Core/Net/MIME/MIME_b.cs | 141 - .../Net/MIME/MIME_b_Application.cs | 79 - .../ASC.Mail.Core/Net/MIME/MIME_b_Audio.cs | 75 - .../ASC.Mail.Core/Net/MIME/MIME_b_Image.cs | 78 - .../ASC.Mail.Core/Net/MIME/MIME_b_Message.cs | 74 - .../Net/MIME/MIME_b_MessageRfc822.cs | 140 - .../Net/MIME/MIME_b_Multipart.cs | 616 -- .../Net/MIME/MIME_b_MultipartAlternative.cs | 94 - .../Net/MIME/MIME_b_MultipartDigest.cs | 113 - .../Net/MIME/MIME_b_MultipartEncrypted.cs | 88 - .../Net/MIME/MIME_b_MultipartFormData.cs | 88 - .../Net/MIME/MIME_b_MultipartMixed.cs | 92 - .../Net/MIME/MIME_b_MultipartParallel.cs | 92 - .../Net/MIME/MIME_b_MultipartRelated.cs | 92 - .../Net/MIME/MIME_b_MultipartReport.cs | 93 - .../Net/MIME/MIME_b_MultipartSigned.cs | 104 - .../ASC.Mail.Core/Net/MIME/MIME_b_Provider.cs | 153 - .../Net/MIME/MIME_b_SinglepartBase.cs | 348 - .../ASC.Mail.Core/Net/MIME/MIME_b_Text.cs | 148 - .../ASC.Mail.Core/Net/MIME/MIME_b_Video.cs | 78 - .../ASC.Mail.Core/Net/MIME/MIME_h.cs | 70 - .../Net/MIME/MIME_h_Collection.cs | 614 -- .../Net/MIME/MIME_h_ContentDisposition.cs | 341 - .../Net/MIME/MIME_h_ContentType.cs | 327 - .../Net/MIME/MIME_h_Parameter.cs | 95 - .../Net/MIME/MIME_h_ParameterCollection.cs | 487 - .../ASC.Mail.Core/Net/MIME/MIME_h_Provider.cs | 144 - .../ASC.Mail.Core/Net/MIME/MIME_h_Unparsed.cs | 135 - .../Net/MIME/MIME_h_Unstructured.cs | 177 - .../Net/MIME/O/MIME_MultipartBody.cs | 281 - .../ASC.Mail.Core/Net/Mail/Mail_Message.cs | 2293 ----- .../ASC.Mail.Core/Net/Mail/Mail_Utils.cs | 84 - .../Net/Mail/Mail_h_AddressList.cs | 215 - .../Mail_h_DispositionNotificationOptions.cs | 86 - .../ASC.Mail.Core/Net/Mail/Mail_h_Mailbox.cs | 181 - .../Net/Mail/Mail_h_MailboxList.cs | 223 - .../ASC.Mail.Core/Net/Mail/Mail_h_Received.cs | 517 - .../Net/Mail/Mail_h_ReturnPath.cs | 150 - .../ASC.Mail.Core/Net/Mail/Mail_t_Address.cs | 43 - .../Net/Mail/Mail_t_AddressList.cs | 303 - .../ASC.Mail.Core/Net/Mail/Mail_t_Group.cs | 126 - .../ASC.Mail.Core/Net/Mail/Mail_t_Mailbox.cs | 127 - .../Net/Mail/Mail_t_MailboxList.cs | 206 - .../ASC.Mail.Core/Net/Mail/Mail_t_TcpInfo.cs | 107 - .../Net/Multilang/CMLangConvertCharset.cs | 26 - .../Multilang/CMLangConvertCharsetClass.cs | 43 - .../Net/Multilang/CMLangString.cs | 26 - .../Net/Multilang/CMLangStringClass.cs | 85 - .../Net/Multilang/CMultiLanguage.cs | 26 - .../Net/Multilang/CMultiLanguageClass.cs | 237 - .../Net/Multilang/IEnumCodePage.cs | 37 - .../Net/Multilang/IEnumRfc1766.cs | 37 - .../Net/Multilang/IEnumScript.cs | 37 - .../Net/Multilang/IMLangCodePages.cs | 37 - .../Net/Multilang/IMLangConvertCharset.cs | 43 - .../Net/Multilang/IMLangFontLink.cs | 47 - .../Net/Multilang/IMLangFontLink2.cs | 55 - .../Net/Multilang/IMLangLineBreakConsole.cs | 35 - .../Net/Multilang/IMLangString.cs | 37 - .../Net/Multilang/IMLangStringAStr.cs | 57 - .../Net/Multilang/IMLangStringBufA.cs | 39 - .../Net/Multilang/IMLangStringBufW.cs | 39 - .../Net/Multilang/IMLangStringWStr.cs | 57 - .../Net/Multilang/IMultiLanguage.cs | 59 - .../Net/Multilang/IMultiLanguage2.cs | 96 - .../Net/Multilang/IMultiLanguage3.cs | 112 - .../Net/Multilang/ISequentialStream.cs | 33 - .../ASC.Mail.Core/Net/Multilang/IStream.cs | 55 - .../ASC.Mail.Core/Net/Multilang/_FILETIME.cs | 30 - .../Net/Multilang/_LARGE_INTEGER.cs | 29 - .../Net/Multilang/_RemotableHandle.cs | 30 - .../Net/Multilang/_ULARGE_INTEGER.cs | 29 - .../Net/Multilang/__MIDL_IWinTypes_0009.cs | 32 - .../Net/Multilang/tagDetectEncodingInfo.cs | 34 - .../Net/Multilang/tagMIMECONTF.cs | 39 - .../Net/Multilang/tagMIMECPINFO.cs | 44 - .../Net/Multilang/tagMIMECSETINFO.cs | 32 - .../ASC.Mail.Core/Net/Multilang/tagMLCPF.cs | 54 - .../Net/Multilang/tagMLDETECTCP.cs | 43 - .../Net/Multilang/tagMLSTR_FLAGS.cs | 28 - .../Net/Multilang/tagRFC1766INFO.cs | 33 - .../Net/Multilang/tagSCRIPFONTINFO.cs | 31 - .../Net/Multilang/tagSCRIPTINFO.cs | 36 - .../ASC.Mail.Core/Net/Multilang/tagSTATSTG.cs | 40 - .../Net/Multilang/tagUNICODERANGE.cs | 30 - .../Net/NNTP/Client/NNTP_Client.cs | 221 - .../ASC.Mail.Core/Net/Net_Core.cs | 1364 --- .../ASC.Mail.Core/Net/Net_Utils.cs | 259 - .../Net/POP3/Client/POP3_Client.cs | 1205 --- .../Net/POP3/Client/POP3_ClientException.cs | 83 - .../Net/POP3/Client/POP3_ClientMessage.cs | 380 - .../Client/POP3_ClientMessageCollection.cs | 206 - .../Net/POP3/POP3_ExtendedCapabilities.cs | 78 - .../Net/POP3/Server/AuthUser_EventArgs.cs | 134 - .../POP3/Server/GetMessagesInfo_EventArgs.cs | 86 - .../Net/POP3/Server/POP3_Message.cs | 91 - .../Net/POP3/Server/POP3_MessageCollection.cs | 193 - .../Net/POP3/Server/POP3_Message_EventArgs.cs | 142 - .../Net/POP3/Server/POP3_Server.cs | 354 - .../Net/POP3/Server/POP3_Session.cs | 1712 ---- .../Server/POP3_eArgs_GetMessageStream.cs | 147 - .../ASC.Mail.Core/Net/ParseException.cs | 41 - .../ASC.Mail.Core/Net/PortRange.cs | 88 - .../Net/Properties/AssemblyInfo.cs | 55 - .../Net/RTP/Debug/wfrm_RTP_Debug.cs | 1252 --- .../Net/RTP/RTCP_CompoundPacket.cs | 223 - .../ASC.Mail.Core/Net/RTP/RTCP_Packet.cs | 194 - .../ASC.Mail.Core/Net/RTP/RTCP_PacketType.cs | 54 - .../ASC.Mail.Core/Net/RTP/RTCP_Packet_APP.cs | 232 - .../ASC.Mail.Core/Net/RTP/RTCP_Packet_BYE.cs | 252 - .../ASC.Mail.Core/Net/RTP/RTCP_Packet_RR.cs | 268 - .../Net/RTP/RTCP_Packet_ReportBlock.cs | 268 - .../ASC.Mail.Core/Net/RTP/RTCP_Packet_SDES.cs | 197 - .../Net/RTP/RTCP_Packet_SDES_Chunk.cs | 453 - .../ASC.Mail.Core/Net/RTP/RTCP_Packet_SR.cs | 354 - .../Net/RTP/RTCP_Packet_Unknown.cs | 34 - .../Net/RTP/RTCP_Report_Receiver.cs | 122 - .../Net/RTP/RTCP_Report_Sender.cs | 98 - .../ASC.Mail.Core/Net/RTP/RTP_Address.cs | 240 - .../ASC.Mail.Core/Net/RTP/RTP_Clock.cs | 98 - .../Net/RTP/RTP_MultimediaSession.cs | 344 - .../ASC.Mail.Core/Net/RTP/RTP_Packet.cs | 404 - .../Net/RTP/RTP_PacketEventArgs.cs | 67 - .../ASC.Mail.Core/Net/RTP/RTP_Participant.cs | 208 - .../Net/RTP/RTP_ParticipantEventArgs.cs | 68 - .../Net/RTP/RTP_Participant_Local.cs | 248 - .../Net/RTP/RTP_Participant_Remote.cs | 230 - .../ASC.Mail.Core/Net/RTP/RTP_PayloadTypes.cs | 145 - .../Net/RTP/RTP_ReceiveStream.cs | 703 -- .../Net/RTP/RTP_ReceiveStreamEventArgs.cs | 68 - .../ASC.Mail.Core/Net/RTP/RTP_SendStream.cs | 397 - .../Net/RTP/RTP_SendStreamEventArgs.cs | 67 - .../ASC.Mail.Core/Net/RTP/RTP_Session.cs | 2143 ---- .../ASC.Mail.Core/Net/RTP/RTP_Source.cs | 440 - .../Net/RTP/RTP_SourceEventArgs.cs | 68 - .../ASC.Mail.Core/Net/RTP/RTP_SourceState.cs | 40 - .../ASC.Mail.Core/Net/RTP/RTP_Source_Local.cs | 239 - .../Net/RTP/RTP_Source_Remote.cs | 254 - .../ASC.Mail.Core/Net/RTP/RTP_Utils.cs | 100 - .../ASC.Mail.Core/Net/SDP/SDP_Attribute.cs | 134 - .../Net/SDP/SDP_ConnectionData.cs | 144 - .../ASC.Mail.Core/Net/SDP/SDP_Media.cs | 128 - .../Net/SDP/SDP_MediaDescription.cs | 206 - .../ASC.Mail.Core/Net/SDP/SDP_Message.cs | 357 - .../ASC.Mail.Core/Net/SDP/SDP_Time.cs | 129 - .../Net/SIP/Message/SIP_HeaderField.cs | 91 - .../SIP/Message/SIP_HeaderFieldCollection.cs | 679 -- .../SIP/Message/SIP_MVGroupHFCollection.cs | 209 - .../Net/SIP/Message/SIP_Message.cs | 1566 --- .../Net/SIP/Message/SIP_MultiValueHF.cs | 174 - .../Net/SIP/Message/SIP_OptionTags.cs | 153 - .../Net/SIP/Message/SIP_Parameter.cs | 90 - .../SIP/Message/SIP_ParameterCollection.cs | 176 - .../Net/SIP/Message/SIP_ParseException.cs | 41 - .../SIP/Message/SIP_SVGroupHFCollection.cs | 184 - .../Net/SIP/Message/SIP_SingleValueHF.cs | 108 - .../Net/SIP/Message/SIP_WarningCodes.cs | 92 - .../Net/SIP/Message/SIP_t_ACValue.cs | 205 - .../Net/SIP/Message/SIP_t_AcceptRange.cs | 283 - .../Net/SIP/Message/SIP_t_AddressParam.cs | 137 - .../Net/SIP/Message/SIP_t_AlertParam.cs | 129 - .../SIP/Message/SIP_t_AuthenticationInfo.cs | 267 - .../Net/SIP/Message/SIP_t_CSeq.cs | 180 - .../Net/SIP/Message/SIP_t_CallID.cs | 130 - .../Net/SIP/Message/SIP_t_Challenge.cs | 160 - .../Net/SIP/Message/SIP_t_ContactParam.cs | 256 - .../Net/SIP/Message/SIP_t_ContentCoding.cs | 124 - .../SIP/Message/SIP_t_ContentDisposition.cs | 187 - .../Net/SIP/Message/SIP_t_Credentials.cs | 163 - .../Net/SIP/Message/SIP_t_Directive.cs | 319 - .../Net/SIP/Message/SIP_t_Encoding.cs | 181 - .../Net/SIP/Message/SIP_t_ErrorUri.cs | 129 - .../Net/SIP/Message/SIP_t_Event.cs | 189 - .../Net/SIP/Message/SIP_t_EventType.cs | 111 - .../Net/SIP/Message/SIP_t_From.cs | 173 - .../Net/SIP/Message/SIP_t_HiEntry.cs | 176 - .../Net/SIP/Message/SIP_t_IdentityInfo.cs | 205 - .../Net/SIP/Message/SIP_t_Info.cs | 145 - .../Net/SIP/Message/SIP_t_Join.cs | 233 - .../Net/SIP/Message/SIP_t_Language.cs | 175 - .../Net/SIP/Message/SIP_t_LanguageTag.cs | 140 - .../Net/SIP/Message/SIP_t_Method.cs | 126 - .../Net/SIP/Message/SIP_t_MinSE.cs | 166 - .../Net/SIP/Message/SIP_t_NameAddress.cs | 269 - .../Net/SIP/Message/SIP_t_OptionTag.cs | 118 - .../Net/SIP/Message/SIP_t_RAck.cs | 224 - .../Net/SIP/Message/SIP_t_RCValue.cs | 109 - .../Net/SIP/Message/SIP_t_RValue.cs | 176 - .../Net/SIP/Message/SIP_t_ReasonValue.cs | 235 - .../Net/SIP/Message/SIP_t_ReferSub.cs | 159 - .../Net/SIP/Message/SIP_t_ReferredBy.cs | 187 - .../Net/SIP/Message/SIP_t_Replaces.cs | 241 - .../Net/SIP/Message/SIP_t_RetryAfter.cs | 200 - .../Net/SIP/Message/SIP_t_SecMechanism.cs | 298 - .../Net/SIP/Message/SIP_t_SessionExpires.cs | 212 - .../SIP/Message/SIP_t_SubscriptionState.cs | 378 - .../Net/SIP/Message/SIP_t_TargetDialog.cs | 226 - .../Net/SIP/Message/SIP_t_Timestamp.cs | 173 - .../ASC.Mail.Core/Net/SIP/Message/SIP_t_To.cs | 173 - .../Net/SIP/Message/SIP_t_Value.cs | 41 - .../Net/SIP/Message/SIP_t_ValueWithParams.cs | 129 - .../Net/SIP/Message/SIP_t_ViaParm.cs | 537 - .../Net/SIP/Message/SIP_t_WarningValue.cs | 190 - .../SIP/Proxy/SIP_AuthenticateEventArgs.cs | 67 - .../ASC.Mail.Core/Net/SIP/Proxy/SIP_B2BUA.cs | 349 - .../Net/SIP/Proxy/SIP_B2BUA_Call.cs | 298 - .../Net/SIP/Proxy/SIP_ForkingMode.cs | 42 - .../Net/SIP/Proxy/SIP_Gateway.cs | 194 - .../Net/SIP/Proxy/SIP_GatewayEventArgs.cs | 101 - .../Net/SIP/Proxy/SIP_Presence.cs | 47 - .../Net/SIP/Proxy/SIP_ProxyContext.cs | 1875 ---- .../Net/SIP/Proxy/SIP_ProxyCore.cs | 1213 --- .../Net/SIP/Proxy/SIP_ProxyMode.cs | 61 - .../Net/SIP/Proxy/SIP_ProxyTarget.cs | 87 - .../Net/SIP/Proxy/SIP_Registrar.cs | 585 -- .../Net/SIP/Proxy/SIP_Registration.cs | 247 - .../Net/SIP/Proxy/SIP_RegistrationBinding.cs | 247 - .../SIP/Proxy/SIP_RegistrationCollection.cs | 188 - .../SIP/Proxy/SIP_RegistrationEventArgs.cs | 68 - .../ASC.Mail.Core/Net/SIP/Proxy/SIP_Route.cs | 83 - .../ASC.Mail.Core/Net/SIP/SIP_Utils.cs | 275 - .../Net/SIP/SIP_ValidateRequestEventArgs.cs | 79 - .../Net/SIP/Stack/SIP_ClientTransaction.cs | 1215 --- .../ASC.Mail.Core/Net/SIP/Stack/SIP_Dialog.cs | 927 -- .../Net/SIP/Stack/SIP_DialogState.cs | 50 - .../Net/SIP/Stack/SIP_Dialog_Invite.cs | 978 -- .../Net/SIP/Stack/SIP_Dialog_Subscribe.cs | 58 - .../ASC.Mail.Core/Net/SIP/Stack/SIP_Flow.cs | 737 -- .../ASC.Mail.Core/Net/SIP/Stack/SIP_Hop.cs | 136 - .../Net/SIP/Stack/SIP_Methods.cs | 99 - .../Net/SIP/Stack/SIP_Request.cs | 302 - .../Net/SIP/Stack/SIP_RequestLine.cs | 157 - .../SIP/Stack/SIP_RequestReceivedEventArgs.cs | 121 - .../Net/SIP/Stack/SIP_RequestSender.cs | 814 -- .../Net/SIP/Stack/SIP_Response.cs | 378 - .../Net/SIP/Stack/SIP_ResponseCodes.cs | 380 - .../Stack/SIP_ResponseReceivedEventArgs.cs | 87 - .../SIP/Stack/SIP_ResponseSentEventArgs.cs | 83 - .../Net/SIP/Stack/SIP_ServerTransaction.cs | 934 -- .../ASC.Mail.Core/Net/SIP/Stack/SIP_Stack.cs | 1528 --- .../Net/SIP/Stack/SIP_StatusCodeType.cs | 55 - .../Net/SIP/Stack/SIP_StatusLine.cs | 148 - .../Net/SIP/Stack/SIP_TimerConstants.cs | 33 - .../Net/SIP/Stack/SIP_Transaction.cs | 527 - .../Net/SIP/Stack/SIP_TransactionLayer.cs | 609 -- .../Net/SIP/Stack/SIP_TransactionState.cs | 65 - .../Net/SIP/Stack/SIP_Transport.cs | 44 - .../Net/SIP/Stack/SIP_TransportException.cs | 41 - .../Net/SIP/Stack/SIP_TransportLayer.cs | 2074 ---- .../Net/SIP/Stack/SIP_UA_Registration.cs | 514 - .../Net/SIP/Stack/SIP_UA_RegistrationState.cs | 50 - .../ASC.Mail.Core/Net/SIP/UA/SIP_UA.cs | 311 - .../ASC.Mail.Core/Net/SIP/UA/SIP_UA_Call.cs | 555 - .../Net/SIP/UA/SIP_UA_CallState.cs | 70 - .../Net/SIP/UA/SIP_UA_Call_EventArgs.cs | 68 - .../Net/SMTP/Client/SMTP_Client.cs | 1987 ---- .../Net/SMTP/Client/SMTP_ClientException.cs | 104 - .../Net/SMTP/Relay/Relay_Mode.cs | 35 - .../Net/SMTP/Relay/Relay_Queue.cs | 144 - .../Net/SMTP/Relay/Relay_QueueItem.cs | 118 - .../Net/SMTP/Relay/Relay_Server.cs | 795 -- .../Net/SMTP/Relay/Relay_Session.cs | 1004 -- .../Relay/Relay_SessionCompletedEventArgs.cs | 79 - .../Net/SMTP/Relay/Relay_SmartHost.cs | 184 - .../ASC.Mail.Core/Net/SMTP/SMTP_Notify.cs | 55 - .../Net/SMTP/SMTP_ServiceExtensions.cs | 144 - .../ASC.Mail.Core/Net/SMTP/SMTP_Utils.cs | 41 - .../ASC.Mail.Core/Net/SMTP/SMTP_t_Mailbox.cs | 149 - .../Net/SMTP/Server/SMTP_MailFrom.cs | 118 - .../Net/SMTP/Server/SMTP_RcptTo.cs | 94 - .../Net/SMTP/Server/SMTP_Reply.cs | 133 - .../Net/SMTP/Server/SMTP_Server.cs | 326 - .../Net/SMTP/Server/SMTP_Session.cs | 1968 ---- .../Net/SMTP/Server/SMTP_e_Ehlo.cs | 114 - .../Net/SMTP/Server/SMTP_e_MailFrom.cs | 109 - .../Net/SMTP/Server/SMTP_e_Message.cs | 89 - .../Net/SMTP/Server/SMTP_e_MessageStored.cs | 110 - .../Net/SMTP/Server/SMTP_e_RcptTo.cs | 109 - .../Net/SMTP/Server/SMTP_e_Started.cs | 94 - .../Net/SMTP/Server/old/AuthUser_EventArgs.cs | 118 - .../Server/old/GetMessageStoreStream_eArgs.cs | 73 - .../old/MessageStoringCompleted_eArgs.cs | 96 - .../Net/SMTP/Server/old/SMTP_Server.cs | 408 - .../Net/SMTP/Server/old/SMTP_Session.cs | 1700 ---- .../Net/SMTP/Server/old/SmtpServerReply.cs | 111 - .../old/ValidateMailboxSize_EventArgs.cs | 90 - .../Server/old/ValidateRecipient_EventArgs.cs | 103 - .../Server/old/ValidateSender_EventArgs.cs | 93 - .../Net/SNTP/Client/SNTP_Client.cs | 84 - .../Net/STUN/Client/STUN_Client.cs | 370 - .../Net/STUN/Client/STUN_NetType.cs | 72 - .../Net/STUN/Client/STUN_Result.cs | 73 - .../Net/STUN/Message/STUN_Message.cs | 729 -- .../Net/STUN/Message/STUN_MessageType.cs | 55 - .../Net/STUN/Message/STUN_t_ChangeRequest.cs | 76 - .../Net/STUN/Message/STUN_t_ErrorCode.cs | 65 - .../Net/ServersCore/Error_EventArgs.cs | 83 - .../Net/ServersCore/SocketBufferedWriter.cs | 114 - .../Net/ServersCore/ValidateIP_EventArgs.cs | 108 - .../Net/ServersCore/commonDelegates.cs | 38 - .../ASC.Mail.Core/Net/SslMode.cs | 40 - .../ASC.Mail.Core/Net/StringReader.cs | 513 - .../ASC.Mail.Core/Net/TCP/TCP_Client.cs | 1041 -- .../ASC.Mail.Core/Net/TCP/TCP_Server.cs | 925 -- .../Net/TCP/TCP_ServerSession.cs | 567 -- .../Net/TCP/TCP_ServerSessionEventArgs.cs | 73 - .../ASC.Mail.Core/Net/TCP/TCP_Session.cs | 111 - .../Net/TCP/TCP_SessionCollection.cs | 154 - .../ASC.Mail.Core/Net/TextUtils.cs | 395 - .../ASC.Mail.Core/Net/TimerEx.cs | 58 - .../Net/UDP/UDP_PacketEventArgs.cs | 128 - .../ASC.Mail.Core/Net/UDP/UDP_ProcessMode.cs | 35 - .../ASC.Mail.Core/Net/UDP/UDP_Server.cs | 958 -- .../ASC.Mail.Core/Net/URI/AbsoluteUri.cs | 138 - .../ASC.Mail.Core/Net/URI/SIP_Uri.cs | 976 -- .../ASC.Mail.Core/Net/URI/TEL_Uri.cs | 53 - .../ASC.Mail.Core/Net/URI/UriSchemes.cs | 74 - .../ASC.Mail.Core/Net/WellKnownPorts.cs | 99 - .../Net/Workaround/Definitions.cs | 29 - .../ASC.Mail.Core/Net/_AsyncResultState.cs | 163 - .../ASC.Mail.Core/Net/_Debug/BitDebuger.cs | 80 - .../Net/_Obsolete/Log_EventArgs.cs | 70 - .../Net/_Obsolete/MIME/Address.cs | 68 - .../Net/_Obsolete/MIME/AddressList.cs | 293 - .../_Obsolete/MIME/ContentDisposition_enum.cs | 52 - .../MIME/ContentTransferEncoding_enum.cs | 79 - .../Net/_Obsolete/MIME/GroupAddress.cs | 134 - .../Net/_Obsolete/MIME/HeaderField.cs | 127 - .../_Obsolete/MIME/HeaderFieldCollection.cs | 351 - .../_Obsolete/MIME/HeaderFieldParameter.cs | 74 - .../MIME/HeaderFieldParameterCollection.cs | 183 - .../Net/_Obsolete/MIME/MailboxAddress.cs | 274 - .../MIME/MailboxAddressCollection.cs | 209 - .../Net/_Obsolete/MIME/MediaType_enum.cs | 147 - .../ASC.Mail.Core/Net/_Obsolete/MIME/Mime.cs | 544 - .../Net/_Obsolete/MIME/MimeEntity.cs | 1769 ---- .../_Obsolete/MIME/MimeEntityCollection.cs | 180 - .../Net/_Obsolete/MIME/MimeUtils.cs | 1143 --- .../_Obsolete/MIME/ParametizedHeaderField.cs | 152 - .../Net/_Obsolete/ReadException.cs | 98 - .../Net/_Obsolete/ReadLine_EventArgs.cs | 191 - .../Net/_Obsolete/ReadToStream_EventArgs.cs | 167 - .../Net/_Obsolete/SIP_RequestSender.cs | 115 - .../ASC.Mail.Core/Net/_Obsolete/SIP_Target.cs | 129 - .../Net/_Obsolete/SaslAuthTypes.cs | 55 - .../Net/_Obsolete/SmtpClientEx.cs | 2553 ----- .../Net/_Obsolete/SocketCallBackResult.cs | 56 - .../ASC.Mail.Core/Net/_Obsolete/SocketEx.cs | 2293 ----- .../Net/_Obsolete/SocketLogEntry.cs | 78 - .../Net/_Obsolete/SocketLogEntryType.cs | 40 - .../Net/_Obsolete/SocketLogger.cs | 276 - .../Net/_Obsolete/SocketServer.cs | 528 - .../Net/_Obsolete/SocketServerSession.cs | 254 - .../Net/_Obsolete/StreamHelper.cs | 2702 ----- .../Net/_Obsolete/StreamLineReader.cs | 171 - .../Net/_Obsolete/WriteStream_EventArgs.cs | 96 - .../Net/_Obsolete/Write_EventArgs.cs | 62 - .../Net/vCard/DeliveryAddress.cs | 317 - .../Net/vCard/DeliveryAddressCollection.cs | 151 - .../Net/vCard/DeliveryAddressType_enum.cs | 65 - .../ASC.Mail.Core/Net/vCard/EmailAddress.cs | 158 - .../Net/vCard/EmailAddressCollection.cs | 130 - .../Net/vCard/EmailAddressType_enum.cs | 45 - .../ASC.Mail.Core/Net/vCard/Item.cs | 231 - .../ASC.Mail.Core/Net/vCard/ItemCollection.cs | 224 - .../ASC.Mail.Core/Net/vCard/Name.cs | 158 - .../ASC.Mail.Core/Net/vCard/PhoneNumber.cs | 246 - .../Net/vCard/PhoneNumberCollection.cs | 116 - .../Net/vCard/PhoneNumberType_enum.cs | 107 - .../ASC.Mail.Core/Net/vCard/vCard.cs | 596 -- .../ASC.Mail.Server/ASC.Mail.Server.csproj | 4 +- module/ASC.Mail/ASC.Mail/ASC.Mail.csproj | 27 +- .../Authorization/AuthorizationTracker.cs | 43 - .../ASC.Mail/ASC.Mail/Core/Dao/AccountDao.cs | 18 +- .../ASC.Mail/Core/Dao/ContactCardDao.cs | 2 +- .../ASC.Mail/Core/Dao/CrmContactDao.cs | 2 +- .../ASC.Mail/ASC.Mail/Core/Dao/FolderDao.cs | 2 +- module/ASC.Mail/ASC.Mail/Core/Dao/MailDao.cs | 17 +- .../ASC.Mail/ASC.Mail/Core/Dao/MailInfoDao.cs | 7 +- .../ASC.Mail/Core/Dao/UserFolderDao.cs | 2 +- .../Core/DbSchema/Tables/MailTable.cs | 4 +- .../ASC.Mail/Core/Engine/AccountEngine.cs | 6 +- .../ASC.Mail/Core/Engine/AttachmentEngine.cs | 2 +- .../ASC.Mail/Core/Engine/CalendarEngine.cs | 16 +- .../ASC.Mail/Core/Engine/ContactEngine.cs | 8 +- .../ASC.Mail/Core/Engine/DocumentsEngine.cs | 2 +- .../ASC.Mail/Core/Engine/DraftEngine.cs | 125 +- .../ASC.Mail/Core/Engine/MailGarbageEngine.cs | 2 +- .../ASC.Mail/Core/Engine/MessageEngine.cs | 28 +- .../Engine/Operations/ApplyFilterOperation.cs | 2 +- .../Operations/ApplyFiltersOperation.cs | 2 +- .../Engine/Operations/Base/MailOperation.cs | 2 +- .../MailCheckMailserverDomainsDnsOperation.cs | 4 +- .../MailDownloadAllAttachmentsOperation.cs | 24 +- .../MailRecalculateFoldersOperation.cs | 2 +- .../Operations/MailRemoveMailboxOperation.cs | 2 +- .../MailRemoveMailserverDomainOperation.cs | 4 +- .../MailRemoveMailserverMailboxOperation.cs | 4 +- .../MailRemoveUserFolderOperation.cs | 2 +- .../ASC.Mail/Core/Entities/Account.cs | 1 + .../Core/Entities/CashedMailUserAction.cs | 26 + .../Core/Entities/CashedTenantUserMailBox.cs | 13 + .../ASC.Mail/ASC.Mail/Core/Entities/Mail.cs | 1 + .../ASC.Mail/Core/Entities/MailInfo.cs | 1 + .../ASC.Mail/Data/Contracts/AccountInfo.cs | 3 + .../Data/Contracts/MailAccountData.cs | 3 + .../ASC.Mail/Data/Contracts/MailDraftData.cs | 1 + .../Data/Contracts/MailMessageData.cs | 3 + module/ASC.Mail/ASC.Mail/Defines.cs | 2 +- .../Exceptions/LimitMessageException.cs | 19 +- .../Extensions/DataContractsExtensions.cs | 3 +- .../ASC.Mail/Extensions/DateTimeExtensions.cs | 10 + .../ASC.Mail/Extensions/MailBoxExtensions.cs | 6 +- .../Extensions/MailDraftExtensions.cs | 30 +- .../MailCoreResource.az-Latn-AZ.resx | 73 + module/ASC.Mail/ASC.Mail/Utils/ApiHelper.cs | 36 +- module/ASC.Mail/ASC.Mail/Utils/MailUtil.cs | 19 - module/ASC.Mail/ASC.Mail/Utils/Parser.cs | 52 +- ...C.Mail.Aggregator.CollectionService.csproj | 8 +- .../AggregatorService.cs | 4 +- .../App.config | 8 + .../Program.Options.cs | 8 +- .../Program.cs | 7 +- .../Queue/QueueManager.cs | 8 +- .../mail.agg.nlog.config | 13 +- .../ASC.Mail.EmlDownloader.csproj | 16 +- .../ASC.Mail.EmlDownloader/App.config | 8 + .../ASC.Mail.EmlDownloader/Program.Options.cs | 9 - .../ASC.Mail.EmlDownloader/Program.cs | 112 +- .../Services/ASC.Mail.Reloader/Program.cs | 14 +- .../ASC.Mail.StorageCleaner.Service.csproj | 9 +- .../ASC.Mail.StorageCleaner/App.config | 8 + .../mail.cln.nlog.config | 11 +- .../ASC.Mail.Watchdog.Service.csproj | 9 +- .../ASC.Mail.Watchdog.Service/App.config | 8 + .../Program.Options.cs | 6 - .../mail.dog.nlog.config | 11 +- .../ASC.MessagingSystem.csproj | 11 +- .../DbSender/DbMessageSender.cs | 16 +- .../DbSender/MessagesRepository.cs | 146 +- module/ASC.MessagingSystem/EventMessage.cs | 102 +- module/ASC.MessagingSystem/IMessageSender.cs | 2 +- module/ASC.MessagingSystem/MessageFactory.cs | 55 +- .../ASC.MessagingSystem/MessageInitiator.cs | 1 + module/ASC.MessagingSystem/MessageService.cs | 68 +- module/ASC.MessagingSystem/MessageSettings.cs | 127 + module/ASC.MessagingSystem/MessageTarget.cs | 54 +- module/ASC.MessagingSystem/MessageUserData.cs | 64 +- module/ASC.Migration/ASC.Migration.csproj | 166 + module/ASC.Migration/App.config | 6 + .../ASC.Migration/Core/AbstractMigration.cs | 65 + module/ASC.Migration/Core/IMigration.cs | 29 + module/ASC.Migration/Core/ImportableEntity.cs | 20 + module/ASC.Migration/Core/MigrationLogger.cs | 61 + .../Core/Models/Api/ApiMigratorAttribute.cs | 24 + .../Core/Models/Api/ImportableApiEntity.cs | 13 + .../Core/Models/Api/MigratingApiCalendar.cs | 18 + .../Core/Models/Api/MigratingApiContacts.cs | 16 + .../Core/Models/Api/MigratingApiFiles.cs | 20 + .../Core/Models/Api/MigratingApiGroup.cs | 23 + .../Core/Models/Api/MigratingApiMail.cs | 18 + .../Core/Models/Api/MigratingApiUser.cs | 35 + .../Core/Models/Api/MigrationApiInfo.cs | 23 + .../Core/Models/Api/MigrationCore.cs | 41 + .../Api/MigrationLogApiContentResponce.cs | 41 + .../Core/Models/Api/MigratorMeta.cs | 17 + .../Core/Models/IMigrationInfo.cs | 11 + .../Core/Models/MigratingCalendar.cs | 25 + .../Core/Models/MigratingContacts.cs | 23 + .../Core/Models/MigratingFiles.cs | 27 + .../Core/Models/MigratingGroup.cs | 28 + .../Core/Models/MigratingMail.cs | 22 + .../Core/Models/MigratingUser.cs | 44 + .../Core/Models/MigrationInfo.cs | 56 + .../Core/Models/MigrationModules.cs | 19 + .../GoogleWorkspaceMigration.cs | 234 + .../Models/GWSMigratingCalendar.cs | 50 + .../Models/GWSMigratingContacts.cs | 118 + .../Models/GWSMigratingFiles.cs | 410 + .../Models/GWSMigratingGroups.cs | 106 + .../Models/GWSMigratingMail.cs | 59 + .../Models/GWSMigratingUser.cs | 261 + .../Models/GWSMigrationInfo.cs | 8 + .../Models/Parse/GWSContact.cs | 14 + .../Models/Parse/GWSDriveFileInfo.cs | 70 + .../GoogleWorkspace/Models/Parse/GWSMail.cs | 12 + .../Models/Parse/GWSProfile.cs | 63 + .../GoogleWorkspace/Models/Parse/GWSTasks.cs | 67 + .../Models/NCMigratingCalendar.cs | 59 + .../Models/NCMigratingContacts.cs | 123 + .../Models/NCMigratingFiles.cs | 233 + .../Models/NCMigratingGroups.cs | 77 + .../Models/NCMigratingMail.cs | 28 + .../Models/NCMigratingUser.cs | 179 + .../Models/NCMigrationInfo.cs | 9 + .../Models/Parse/NCAddressbooks.cs | 16 + .../Models/Parse/NCCalendars.cs | 17 + .../Models/Parse/NCContact.cs | 14 + .../Models/Parse/NCGroup.cs | 14 + .../Models/Parse/NCStorages.cs | 25 + .../NextcloudWorkspace/Models/Parse/NCUser.cs | 22 + .../NextcloudWorkspaceMigration.cs | 511 + .../OwnCloud/Models/OCMigratingCalendar.cs | 59 + .../OwnCloud/Models/OCMigratingContacts.cs | 122 + .../OwnCloud/Models/OCMigratingFiles.cs | 232 + .../OwnCloud/Models/OCMigratingGroups.cs | 77 + .../OwnCloud/Models/OCMigratingMail.cs | 27 + .../OwnCloud/Models/OCMigratingUser.cs | 154 + .../OwnCloud/Models/OCMigrationinfo.cs | 8 + .../OwnCloud/Models/Parse/OCAddressbooks.cs | 16 + .../OwnCloud/Models/Parse/OCCalendars.cs | 17 + .../OwnCloud/Models/Parse/OCContact.cs | 14 + .../OwnCloud/Models/Parse/OCGroup.cs | 14 + .../OwnCloud/Models/Parse/OCStorages.cs | 25 + .../OwnCloud/Models/Parse/OCUser.cs | 19 + .../OwnCloud/OwnCloudMigration.cs | 488 + .../ASC.Migration/Properties/AssemblyInfo.cs | 35 + .../Resources/MigrationResource.Designer.cs | 315 + .../Resources/MigrationResource.resx | 204 + .../ASC.Notify/ASC.Notify/ASC.Notify.csproj | 6 +- module/ASC.Radicale/ASC.Radicale.csproj | 2 +- module/ASC.Radicale/Launcher.cs | 87 +- .../ASC.Radicale/RadicaleCfgSectionHandler.cs | 6 + module/ASC.Radicale/radicale.rights | 6 +- module/ASC.Socket.IO.Svc/Launcher.cs | 14 +- module/ASC.Socket.IO/app/hubs/counters.js | 47 +- module/ASC.SsoAuth.Svc/ASC.SsoAuth.Svc.csproj | 6 +- module/ASC.SsoAuth.Svc/Launcher.cs | 11 + module/ASC.SsoAuth/app/middleware/saml.js | 5 + module/ASC.SsoAuth/app/routes.js | 2 + .../ASC.TeamLabSvc/ASC.TeamLabSvc.csproj | 5 - .../ASC.TelegramService.csproj | 7 +- module/ASC.TelegramService/Core/Core.cs | 2 +- .../Core/TenantTgClient.cs | 3 + module/ASC.TelegramService/TelegramHandler.cs | 79 +- .../ASC.FederatedLogin.csproj | 16 +- .../ASC.Thrdparty/ASC.FederatedLogin/Login.cs | 10 +- .../LoginProviders/AppleIdLoginProvider.cs | 157 + .../LoginProviders/BaseLoginProvider.cs | 10 + .../LoginProviders/BitlyLoginProvider.cs | 53 +- .../LoginProviders/EncryptionLoginProvider.cs | 16 +- .../LoginProviders/GosUslugiLoginProvider.cs | 10 +- .../LoginProviders/ILoginProvider.cs | 4 + .../LoginProviders/MicrosoftLoginProvider.cs | 72 + .../LoginProviders/OpenIdLoginProvider.cs | 171 - .../LoginProviders/ProviderManager.cs | 11 +- .../LoginProviders/TwitterLoginProvider.cs | 98 +- .../LoginProviders/YahooLoginProvider.cs | 51 - .../Profile/LoginProfile.cs | 15 +- .../Profile/LoginProfileExtensions.cs | 10 +- .../ASC.FederatedLogin/ProviderConstants.cs | 3 +- .../ASC.Thrdparty/ASC.Thrdparty.csproj | 17 +- .../ASC.Thrdparty/InMemoryTokenManager.cs | 192 - .../ASC.Thrdparty/Twitter/TwitterConsumer.cs | 149 - .../Twitter/TwitterDataProvider.cs | 117 +- .../ASC.Thumbnails.Svc.csproj | 6 +- module/ASC.Thumbnails.Svc/Launcher.cs | 10 + module/ASC.Thumbnails/app/isLife.js | 20 + module/ASC.Thumbnails/app/thumb.js | 32 + module/ASC.Thumbnails/index.js | 2 + module/ASC.Thumbnails/package.json | 2 + module/ASC.Thumbnails/yarn.lock | 10 + .../ASC.UrlShortener.Svc.csproj | 8 +- module/ASC.UrlShortener.Svc/Launcher.cs | 10 + module/ASC.UrlShortener/app/app.js | 7 +- module/ASC.UrlShortener/index.js | 1 + module/ASC.VoipService/ASC.VoipService.csproj | 4 +- module/ASC.VoipService/Twilio/TwilioPhone.cs | 2 +- .../ASC.VoipService/Twilio/TwilioProvider.cs | 4 +- .../Twilio/TwilioResponseHelper.cs | 32 +- module/ASC.WebDav.Svc/ASC.WebDav.Svc.csproj | 56 + module/ASC.WebDav.Svc/ConfigHandler.cs | 36 + module/ASC.WebDav.Svc/Launcher.cs | 99 + .../ASC.WebDav.Svc/Properties/AssemblyInfo.cs | 35 + module/ASC.WebDav.Svc/app.config | 5 + module/ASC.WebDav/helper/helper.js | 138 + module/ASC.WebDav/helper/localLockManager.js | 26 + module/ASC.WebDav/helper/logger.js | 59 + module/ASC.WebDav/helper/propertyParser.js | 63 + .../helper/renamingDuplicateElements.js | 109 + module/ASC.WebDav/helper/writable.js | 108 + module/ASC.WebDav/manager/customFileSystem.js | 296 + module/ASC.WebDav/package-lock.json | 1116 ++ module/ASC.WebDav/package.json | 27 + .../patches/webdav-server+2.6.2.patch | 13 + .../resource/customVirtualResource.js | 582 ++ module/ASC.WebDav/resource/simpleStruct.js | 153 + module/ASC.WebDav/server/config.js | 84 + module/ASC.WebDav/server/requestAPI.js | 464 + module/ASC.WebDav/server/webDavServer.js | 99 + .../customHTTPBasicAuthentication.js | 51 + module/ASC.WebDav/user/customSimpleUser.js | 29 + module/ASC.WebDav/user/customUserLayout.js | 53 + module/ASC.WebDav/user/customUserManager.js | 55 + module/ASC.WebDav/yarn.lock | 1317 +++ redistributable/AjaxPro/AjaxPro.2.license | 35 +- redistributable/AjaxPro/AjaxPro.csproj | 10 +- redistributable/AjaxPro/AssemblyInfo.cs | 17 +- .../AjaxSettingsSectionHandler.cs | 29 + .../AjaxPro/Handler/AjaxProcHelper.cs | 49 +- .../Handler/AjaxProcessors/IFrameProcessor.cs | 4 +- .../AjaxProcessors/XmlHttpRequestProcessor.cs | 18 +- .../Handler/ConverterJavaScriptHandler.cs | 4 +- .../Handler/EmbeddedJavaScriptHandler.cs | 16 +- .../AjaxPro/Handler/TypeJavaScriptHandler.cs | 2 +- .../JSON/Converters/DataTableConverter.cs | 5 +- .../JSON/Converters/HashtableConverter.cs | 13 +- .../JSON/Converters/HtmlControlConverter.cs | 6 +- .../AjaxPro/JSON/JavaScriptDeserializer.cs | 58 +- .../AjaxPro/Services/AuthenticationService.cs | 89 - .../AjaxPro/Services/CartService.cs | 61 - .../AjaxPro/Services/ChatService.cs | 71 - .../AjaxPro/Services/ProfileService.cs | 91 - .../AjaxPro/Utilities/AjaxSettings.cs | 25 + redistributable/AjaxPro/Utilities/Constant.cs | 2 +- .../AjaxPro/Utilities/HashHelper.cs | 67 + .../AjaxPro/Utilities/MD5Helper.cs | 67 - redistributable/AjaxPro/Utilities/Utility.cs | 18 +- redistributable/AjaxPro/converter.js | 178 - redistributable/AjaxPro/core.js | 388 +- redistributable/AjaxPro/jquery-1.3.1.js | 4241 -------- redistributable/AjaxPro/web.config | 40 +- .../AppLimit.CloudComputing.SharpBox.csproj | 39 + .../CloudStorage.cs | 1251 +-- .../CloudStorageAsyncFunctions.cs | 418 +- .../CloudStorageComfortFunctions.cs | 169 +- .../CloudStorageLimits.cs | 84 +- .../CloudStorageSyncFramework.cs | 498 +- .../Common/AsyncObjectRequest.cs | 54 +- .../Common/AsyncResultEx.cs | 98 +- .../Common/Cache/CachedDictionary.cs | 211 + .../Common/Cache/CachedDictionaryBase.cs | 262 +- .../Common/Extensions/SharpBoxExtensions.cs | 63 + .../Common/IO/PathHelper.cs | 175 +- .../Common/IO/StreamHelper.cs | 255 + .../Common/Net/HttpException.cs | 41 +- .../Common/Net/HttpUtility.cs | 2428 ++--- .../Common/Net/HttpUtilityEx.cs | 316 +- .../Common/Net/Json/JsonDateTimeConverter.cs | 130 +- .../Common/Net/Json/JsonHelper.cs | 143 +- .../Common/Net/MimeMapping.cs | 1830 ++-- .../Common/Net/Web/Dav/DavService.cs | 321 +- .../Common/Net/Web/Ftp/FtpService.cs | 270 +- .../Common/Net/Web/Http/HttpService.cs | 60 +- .../Net/Web/WebRequestExecutedEventArgs.cs | 62 +- .../Net/Web/WebRequestExecutingEventArgs.cs | 30 +- .../Common/Net/Web/WebRequestManager.cs | 276 +- .../Net/Web/WebRequestManagerNullProxy.cs | 46 +- .../Common/Net/Web/WebRequestMethods.cs | 298 +- .../Web/WebRequestMultipartFormDataSupport.cs | 198 +- .../Common/Net/Web/WebRequestService.cs | 1044 +- .../Common/Net/Web/WebRequestStream.cs | 260 +- .../Common/Net/Web/WebRequestStreamHelper.cs | 46 +- .../Common/Net/Web/WebResponseStream.cs | 166 +- .../Net/oAuth/Context/OAuthConsumerContext.cs | 18 + .../Net/oAuth/Context/OAuthServiceContext.cs | 18 + .../Common/Net/oAuth/Impl/OAuthBase.cs | 736 +- .../Net/oAuth/Impl/OAuthStreamParser.cs | 77 +- .../Net/oAuth/Impl/OAuthUrlGenerator.cs | 168 +- .../Common/Net/oAuth/OAuthService.cs | 207 +- .../Common/Net/oAuth/Token/OAuthToken.cs | 30 + .../Common/Net/oAuth20/OAuth20Token.cs | 170 +- .../Exceptions/ErrorMessages.Designer.cs | 360 +- .../Exceptions/ErrorMessages.resx | 316 +- .../Exceptions/SharpBoxErrorCodes.cs | 196 +- .../Exceptions/SharpBoxException.cs | 186 +- .../ICloudDirectoryEntry.cs | 143 +- .../ICloudFileDataTransfer.cs | 372 +- .../ICloudFileSystemEntry.cs | 114 +- .../ICloudStorageAccessToken.cs | 18 +- .../ICloudStorageAsyncInterface.cs | 126 +- .../ICloudStorageConfiguration.cs | 58 +- .../ICloudStorageProvider.cs | 41 + .../ICloudStoragePublicAPI.cs | 150 + .../IResumableUploadSession.cs | 56 +- .../Resources/loader.gif | Bin .../StorageProvider/API/GenericHelper.cs | 28 + .../API/GenericStorageProviderFactory.cs | 290 +- .../API/IStorageProviderService.cs | 353 +- .../API/IStorageProviderSession.cs | 50 +- .../BaseObjects/BaseDirectoryEntry.cs | 338 +- .../BaseObjects/BaseFileEntry.cs | 230 +- .../BaseObjects/BaseFileEntryDataTransfer.cs | 323 +- .../BaseFileEntryDownloadStream.cs | 190 +- .../BaseObjects/ResumableUploadSession.cs | 148 +- .../BoxNet/BoxNetConfiguration.cs | 62 +- .../BoxNet/BoxNetStorageProvider.cs | 24 +- .../Logic/BoxNetStorageProviderService.cs | 13 + .../StorageProvider/CIFS/CIFSConfiguration.cs | 98 +- .../CIFS/CIFSStorageProvider.cs | 22 +- .../CIFS/Logic/CIFSStorageProviderService.cs | 582 +- .../CIFS/Logic/CIFSStorageProviderSession.cs | 46 +- .../StorageProvider/CachedServiceWrapper.cs | 332 +- .../DropBox/DropBoxAccountInfo.cs | 117 +- .../DropBox/DropBoxBaseTokenInformation.cs | 20 + .../DropBox/DropBoxConfiguration.cs | 246 +- .../DropBox/DropBoxQuotaInfo.cs | 88 +- .../DropBox/DropBoxRequestToken.cs | 56 +- .../DropBox/DropBoxResourceIDHelpers.cs | 30 + .../DropBox/DropBoxStorageProvider.cs | 78 +- .../DropBox/DropBoxStorageProviderTools.cs | 445 +- .../StorageProvider/DropBox/DropBoxToken.cs | 85 +- .../DropBox/Logic/DropBoxRequestParser.cs | 483 +- .../Logic/DropBoxStorageProviderService.cs | 1439 ++- .../Logic/DropBoxStorageProviderSession.cs | 59 +- .../StorageProvider/Ftp/FtpConfiguration.cs | 98 +- .../StorageProvider/Ftp/FtpStorageProvider.cs | 22 +- .../Ftp/Logic/FtpStorageProviderService.cs | 704 +- .../Ftp/Logic/FtpStorageProviderSession.cs | 46 +- .../GenericCurrentCredentials.cs | 54 +- .../GenericNetworkCredentials.cs | 88 +- .../StorageProvider/GenericStorageProvider.cs | 608 +- .../GenericStorageProviderService.cs | 570 +- .../GoogleDocsAuthorizationHelper.cs | 148 +- .../GoogleDocs/GoogleDocsConfiguration.cs | 122 +- .../GoogleDocs/GoogleDocsConstants.cs | 100 +- .../GoogleDocs/GoogleDocsRequestToken.cs | 26 +- .../GoogleDocs/GoogleDocsResourceHelper.cs | 151 +- .../GoogleDocs/GoogleDocsStorageProvider.cs | 100 +- .../GoogleDocs/GoogleDocsToken.cs | 27 + .../Logic/GoogleDocsStorageProviderService.cs | 1296 +-- .../Logic/GoogleDocsStorageProviderSession.cs | 46 +- .../GoogleDocs/Logic/GoogleDocsXmlParser.cs | 282 +- .../SkyDriveAuthorizationHelper.cs | 268 +- .../SkyDrive/Logic/SkyDriveStorageProvider.cs | 28 +- .../Logic/SkyDriveStorageProviderService.cs | 710 +- .../Logic/SkyDriveStorageProviderSession.cs | 38 +- .../SkyDrive/SkyDriveConfiguration.cs | 42 +- .../SkyDrive/SkyDriveConstants.cs | 31 + .../SkyDrive/SkyDriveHelpers.cs | 63 + .../SkyDrive/SkyDriveJsonParser.cs | 332 +- .../SkyDrive/SkyDriveRequestHelper.cs | 328 +- .../WebDav/Logic/WebDavRequestParser.cs | 264 +- .../Logic/WebDavStorageProviderService.cs | 1126 +- .../Logic/WebDavStorageProviderSession.cs | 46 +- .../WebDav/WebDavConfiguration.cs | 191 + .../WebDav/WebDavStorageProvider.cs | 34 +- .../SyncFramework/DirectoryDiff.cs | 416 +- .../SyncFramework/DirectoryDiffResultItem.cs | 78 +- .../IpGeolocationConverter/App.config | 6 + .../IpGeolocationConverter.csproj | 47 + .../IpGeolocationConverter/Program.cs | 210 + .../Properties/AssemblyInfo.cs | 35 + .../Microsoft.Graph.Core/CoreConstants.cs | 2 - .../Microsoft.Graph.Core/Exceptions/Error.cs | 2 +- .../Exceptions/ErrorResponse.cs | 2 +- .../Microsoft.Graph.Core.csproj | 161 +- .../Models/AsyncOperationStatus.cs | 1 - .../Models/BatchRequestStep.cs | 1 - .../Models/ReferenceRequestBody.cs | 1 - .../Properties/AssemblyInfo.cs | 32 - .../Requests/BaseRequest.cs | 1 - .../Requests/Content/BatchResponseContent.cs | 2 +- .../Requests/IBatchRequestBuilder.cs | 4 +- .../Requests/Upload/UploadSliceRequest.cs | 1 - .../Serialization/EnumConverter.cs | 8 +- .../AppLimit.CloudComputing.SharpBox.csproj | 237 - .../AppLimit.CloudComputing.SharpBox.snk | Bin 596 -> 0 bytes .../Common/Cache/CachedDictionary.cs | 100 - .../Common/Extensions/SharpBoxExtensions.cs | 63 - .../Common/IO/StreamHelper.cs | 255 - .../Net/oAuth/Context/OAuthConsumerContext.cs | 19 - .../Net/oAuth/Context/OAuthServiceContext.cs | 20 - .../Common/Net/oAuth/Token/OAuthToken.cs | 32 - .../ICloudStorageProvider.cs | 41 - .../ICloudStoragePublicAPI.cs | 150 - .../Properties/Resources.Designer.cs | 70 - .../StorageProvider/API/GenericHelper.cs | 30 - .../Logic/BoxNetStorageProviderService.cs | 14 - .../DropBox/DropBoxBaseTokenInformation.cs | 22 - .../DropBox/DropBoxResourceIDHelpers.cs | 32 - .../GoogleDocs/GoogleDocsToken.cs | 28 - .../SkyDrive/SkyDriveConstants.cs | 32 - .../SkyDrive/SkyDriveHelpers.cs | 64 - .../WebDav/WebDavConfiguration.cs | 191 - .../UI/Controler/FileSizeFormat.cs | 88 - .../UI/Controler/ListViewColumnSorter.cs | 100 - .../UI/Presentation/LoginControl.Designer.cs | 89 - .../UI/Presentation/LoginControl.cs | 52 - .../UI/Presentation/LoginControl.resx | 273 - .../UI/Presentation/Sandbox.Designer.cs | 152 - .../UI/Presentation/Sandbox.cs | 271 - .../UI/Presentation/Sandbox.resx | 354 - redistributable/ShrapBox/Src/AssemblyInfo.cs | 23 - .../ShrapBox/Src/GenerateAssemblyInfo.msbuild | 14 - .../ShrapBox/Src/SharpBoxPackNuget.cmd | 13 - .../Attributes/AuthorizedCommandAttribute.cs | 46 - .../Attributes/RateLimitedAttribute.cs | 45 - .../Twitterizer2/Core/AccessLevel.cs | 67 - .../Twitterizer2/Core/CommandPerformer.cs | 54 - .../Twitterizer2/Core/ConversionUtility.cs | 102 - .../Twitterizer2/Core/ICommand.cs | 64 - .../Twitterizer2/Core/ITwitterObject.cs | 49 - .../Twitterizer2/Core/NamespaceDoc.cs | 44 - .../Twitterizer2/Core/OptionalProperties.cs | 73 - .../Twitterizer2/Core/OptionalProperties.xml | 100 - .../Twitterizer2/Core/RateLimit.cs | 64 - .../Twitterizer2/Core/RequestResult.cs | 97 - .../Twitterizer2/Core/SerializationHelper.cs | 118 - .../Twitterizer2/Core/TwitterCollection.cs | 59 - .../Twitterizer2/Core/TwitterCommand.cs | 439 - .../Core/TwitterCursorPagedIdCollection.cs | 87 - .../Twitterizer2/Core/TwitterDictionary.cs | 62 - .../Twitterizer2/Core/TwitterIdCollection.cs | 113 - .../Twitterizer2/Core/TwitterImage.cs | 112 - .../Twitterizer2/Core/TwitterObject.cs | 73 - .../Core/TwitterizerDateConverter.cs | 87 - .../Exceptions/CommandValidationException.cs | 168 - .../Exceptions/TwitterErrorDetails.cs | 71 - .../Exceptions/TwitterizerException.cs | 226 - .../Twitterizer2/Twitterizer2/Information.cs | 55 - .../Methods/Account/RateLimitStatusCommand.cs | 64 - .../Methods/Account/TwitterAccount.cs | 155 - .../Methods/Account/TwitterRateLimitStatus.cs | 112 - .../UpdateProfileBackgroundImageCommand.cs | 96 - .../UpdateProfileBackgroundImageOptions.cs | 67 - .../Account/UpdateProfileColorsCommand.cs | 139 - .../Account/UpdateProfileColorsOptions.cs | 120 - .../Methods/Account/UpdateProfileCommand.cs | 73 - .../Account/UpdateProfileImageCommand.cs | 84 - .../Methods/Account/UpdateProfileOptions.cs | 70 - .../Account/VerifyCredentialsCommand.cs | 73 - .../Account/VerifyCredentialsOptions.cs | 60 - .../Methods/Block/BlockingCommand.cs | 74 - .../Methods/Block/BlockingIdsCommand.cs | 61 - .../Methods/Block/BlockingOptions.cs | 48 - .../Methods/Block/CreateBlockCommand.cs | 96 - .../Methods/Block/DestroyBlockCommand.cs | 96 - .../Methods/Block/ExistsBlockCommand.cs | 96 - .../Methods/Block/TwitterBlock.cs | 261 - .../DeleteDirectMessageCommand.cs | 86 - .../DirectMessage/DirectMessagesCommand.cs | 93 - .../DirectMessage/DirectMessagesOptions.cs | 85 - .../DirectMessagesSentCommand.cs | 93 - .../DirectMessagesSentOptions.cs | 84 - .../DirectMessage/SendDirectMessageCommand.cs | 142 - .../DirectMessage/ShowDirectMessageCommand.cs | 71 - .../DirectMessage/TwitterDirectMessage.cs | 294 - .../TwitterDirectMessageCollection.cs | 49 - .../Favorites/CreateFavoriteCommand.cs | 77 - .../Favorites/DeleteFavoriteCommand.cs | 79 - .../Methods/Favorites/ListFavoritesCommand.cs | 97 - .../Methods/Favorites/ListFavoritesOptions.cs | 75 - .../Methods/Favorites/TwitterFavorite.cs | 130 - .../Friendship/CreateFriendshipCommand.cs | 134 - .../Friendship/CreateFriendshipOptions.cs | 52 - .../Friendship/DeleteFriendshipCommand.cs | 106 - .../Methods/Friendship/FollowersIdsCommand.cs | 88 - .../Methods/Friendship/FriendsIdsCommand.cs | 85 - .../Friendship/IncomingFriendshipsCommand.cs | 69 - .../Friendship/IncomingFriendshipsOptions.cs | 48 - .../Methods/Friendship/NoRetweetIDsCommand.cs | 60 - .../Friendship/OutgoingFriendshipsCommand.cs | 72 - .../Friendship/OutgoingFriendshipsOptions.cs | 48 - .../Friendship/ShowFriendshipCommand.cs | 139 - .../Methods/Friendship/TwitterFriendship.cs | 559 - .../Methods/Friendship/TwitterRelationship.cs | 102 - .../Friendship/TwitterRelationshipUser.cs | 123 - .../Friendship/UpdateFriendshipCommand.cs | 137 - .../Friendship/UpdateFriendshipOptions.cs | 67 - .../Methods/Friendship/UserIdCollection.cs | 96 - .../Methods/Friendship/UsersIdsOptions.cs | 64 - .../Twitterizer2/Methods/Geo/Coordinate.cs | 151 - .../Methods/Geo/ReverseGeocodeCommand.cs | 104 - .../Methods/Geo/TwitterBoundingBox.cs | 65 - .../Twitterizer2/Methods/Geo/TwitterGeo.cs | 90 - .../Twitterizer2/Methods/Geo/TwitterPlace.cs | 186 - .../Methods/Geo/TwitterPlaceCollection.cs | 121 - .../Methods/Geo/TwitterPlaceLookupOptions.cs | 57 - .../Methods/List/AddListMemberCommand.cs | 98 - .../List/CheckListMembershipCommand.cs | 89 - .../Methods/List/CreateListCommand.cs | 108 - .../List/CreateListMembershipCommand.cs | 40 - .../Methods/List/DeleteListCommand.cs | 90 - .../Methods/List/DestroyListSubscriber.cs | 35 - .../Methods/List/GetListCommand.cs | 108 - .../Methods/List/GetListMembersCommand.cs | 92 - .../Methods/List/GetListMembersOptions.cs | 47 - .../List/GetListSubscriptionsCommand.cs | 81 - .../List/GetListSubscriptionsOptions.cs | 47 - .../Methods/List/GetListsCommand.cs | 86 - .../Methods/List/GetListsOptions.cs | 47 - .../Methods/List/ListMembershipsCommand.cs | 129 - .../Methods/List/ListMembershipsOptions.cs | 54 - .../Methods/List/ListStatusesCommand.cs | 111 - .../Methods/List/ListStatusesOptions.cs | 81 - .../Methods/List/RemoveListMemberCommand.cs | 98 - .../Twitterizer2/Methods/List/TwitterList.cs | 588 -- .../Methods/List/TwitterListCollection.cs | 81 - .../Methods/List/UpdateListCommand.cs | 111 - .../Methods/List/UpdateListOptions.cs | 63 - .../Notification/NotificationFollowCommand.cs | 94 - .../Notification/NotificationLeaveCommand.cs | 94 - .../Notification/TwitterNotification.cs | 141 - .../SavedSearches/CreateSavedSearchCommand.cs | 89 - .../SavedSearches/DeleteSavedSearchCommand.cs | 80 - .../SavedSearches/SavedSearchesCommand.cs | 71 - .../SavedSearches/TwitterSavedSearch.cs | 167 - .../TwitterSavedSearchCollection.cs | 49 - .../Methods/Search/SearchCommand.cs | 147 - .../Methods/Search/SearchOptions.cs | 135 - .../Methods/Search/TwitterSearch.cs | 91 - .../Methods/Search/TwitterSearchResult.cs | 160 - .../Search/TwitterSearchResultCollection.cs | 139 - .../Methods/Spam/ReportSpamCommand.cs | 87 - .../Twitterizer2/Methods/Spam/TwitterSpam.cs | 91 - .../Timeline/FriendsTimelineCommand.cs | 66 - .../Methods/Timeline/HomeTimelineCommand.cs | 75 - .../Methods/Timeline/MentionsCommand.cs | 75 - .../Methods/Timeline/PagedTimelineCommand.cs | 91 - .../Methods/Timeline/PublicTimelineCommand.cs | 69 - .../Methods/Timeline/RetweetedByMeCommand.cs | 90 - .../Methods/Timeline/RetweetedToMeCommand.cs | 88 - .../Methods/Timeline/TimelineOptions.cs | 121 - .../Methods/Timeline/TwitterTimeline.cs | 283 - .../Methods/Timeline/UserTimelineCommand.cs | 85 - .../Methods/Timeline/UserTimelineOptions.cs | 54 - .../Methods/Trends/AvailableTrendsCommand.cs | 82 - .../Methods/Trends/AvailableTrendsOptions.cs | 56 - .../Methods/Trends/DailyTrendsCommand.cs | 86 - .../Methods/Trends/LocalTrendsOptions.cs | 51 - .../Methods/Trends/TrendsCommand.cs | 84 - .../Methods/Trends/TrendsOptions.cs | 50 - .../Methods/Trends/TwitterTrend.cs | 236 - .../Methods/Trends/TwitterTrendCollection.cs | 184 - .../Methods/Trends/TwitterTrendDictionary.cs | 160 - .../Methods/Trends/TwitterTrendLocation.cs | 92 - .../Trends/TwitterTrendLocationCollection.cs | 164 - .../Trends/TwitterTrendLocationPlaceType.cs | 64 - .../Methods/Trends/WeeklyTrendsCommand.cs | 86 - .../Methods/Tweets/DeleteStatusCommand.cs | 81 - .../Methods/Tweets/Entities/TwitterEntity.cs | 66 - .../Entities/TwitterEntityCollection.cs | 513 - .../Tweets/Entities/TwitterHashTagEntity.cs | 51 - .../Tweets/Entities/TwitterMediaEntity.cs | 191 - .../Tweets/Entities/TwitterMentionEntity.cs | 63 - .../Tweets/Entities/TwitterUrlEntity.cs | 63 - .../Methods/Tweets/RelatedResultsCommand.cs | 83 - .../Methods/Tweets/RetweetCommand.cs | 88 - .../Methods/Tweets/RetweetsCommand.cs | 102 - .../Methods/Tweets/RetweetsOfMeCommand.cs | 89 - .../Methods/Tweets/RetweetsOfMeOptions.cs | 71 - .../Methods/Tweets/RetweetsOptions.cs | 51 - .../Methods/Tweets/ShowStatusCommand.cs | 80 - .../Methods/Tweets/StatusUpdateOptions.cs | 82 - .../Methods/Tweets/TwitterRelatedTweets.cs | 79 - .../Tweets/TwitterRelatedTweetsCollection.cs | 48 - .../Methods/Tweets/TwitterStatus.cs | 590 -- .../Methods/Tweets/TwitterStatus.xml | 23 - .../Methods/Tweets/TwitterStatusCollection.cs | 57 - .../Methods/Tweets/UpdateStatusCommand.cs | 112 - .../Methods/Tweets/UpdateWithmediaCommand.cs | 122 - .../Methods/TwitterResultTypeEnum.cs | 57 - .../Methods/User/FollowersCommand.cs | 98 - .../Methods/User/FollowersOptions.cs | 62 - .../Methods/User/FriendsCommand.cs | 86 - .../Methods/User/FriendsOptions.cs | 63 - .../Methods/User/LookupUsersCommand.cs | 93 - .../Methods/User/LookupUsersOptions.cs | 37 - .../Methods/User/RetweetedByCommand.cs | 95 - .../Methods/User/RetweetedByIdsCommand.cs | 95 - .../Methods/User/RetweetedByIdsOptions.cs | 65 - .../Methods/User/RetweetedByOptions.cs | 65 - .../Methods/User/ShowUserCommand.cs | 94 - .../User/SuggestedUserCategoriesCommand.cs | 67 - .../Methods/User/SuggestedUsersCommand.cs | 76 - .../Twitterizer2/Methods/User/TwitterUser.cs | 552 - .../Twitterizer2/Methods/User/TwitterUser.xml | 298 - .../Methods/User/TwitterUserCategory.cs | 142 - .../Methods/User/TwitterUserCollection.cs | 90 - .../Methods/User/UserSearchCommand.cs | 96 - .../Methods/User/UserSearchOptions.cs | 57 - .../Twitterizer2/OAuth/OAuthTokenResponse.cs | 77 - .../Twitterizer2/OAuth/OAuthTokens.cs | 109 - .../Twitterizer2/OAuth/OAuthTokens.xml | 24 - .../Twitterizer2/OAuth/OAuthUtility.cs | 396 - .../Twitterizer2/OAuth/OAuthUtility.xml | 69 - .../Twitterizer2/OAuth/WebRequestBuilder.cs | 661 -- .../Twitterizer2/OAuth/XAuthUtility.cs | 116 - .../Twitterizer2/Properties/AssemblyInfo.cs | 73 - .../Twitterizer2/TwitterResponse.cs | 63 - .../Twitterizer2/Twitterizer2/Twitterizer2.cd | 197 - .../Twitterizer2/Twitterizer2.csproj | 304 - .../Twitterizer2/Twitterizer2.nuspec | 24 - .../Twitterizer2/Twitterizer2.snk | Bin 596 -> 0 bytes .../Twitterizer2/Twitterizer2Inheritance.cd | 97 - .../Twitterizer2/Twitterizer2/packages.config | 4 - .../OneDriveSdk/Microsoft.OneDrive.Sdk.csproj | 93 +- .../src/OneDriveSdk/Models/Generated/Audio.cs | 1 - .../ChunkedUploadSessionDescriptor.cs | 2 - .../OneDriveSdk/Models/Generated/Deleted.cs | 2 - .../src/OneDriveSdk/Models/Generated/Drive.cs | 2 - .../src/OneDriveSdk/Models/Generated/File.cs | 2 - .../Models/Generated/FileSystemInfo.cs | 1 - .../OneDriveSdk/Models/Generated/Folder.cs | 1 - .../OneDriveSdk/Models/Generated/Hashes.cs | 2 - .../OneDriveSdk/Models/Generated/Identity.cs | 2 - .../Models/Generated/IdentitySet.cs | 2 - .../src/OneDriveSdk/Models/Generated/Image.cs | 1 - .../Models/Generated/ItemCopyRequestBody.cs | 5 - .../Generated/ItemCreateLinkRequestBody.cs | 5 - .../Generated/ItemCreateSessionRequestBody.cs | 5 - .../Models/Generated/ItemInviteRequestBody.cs | 5 - .../Models/Generated/ItemReference.cs | 2 - .../OneDriveSdk/Models/Generated/Location.cs | 2 - .../Models/Generated/OpenWithApp.cs | 2 - .../Models/Generated/OpenWithSet.cs | 2 - .../Models/Generated/Permission.cs | 2 - .../src/OneDriveSdk/Models/Generated/Photo.cs | 1 - .../src/OneDriveSdk/Models/Generated/Quota.cs | 1 - .../OneDriveSdk/Models/Generated/Recipient.cs | 2 - .../Models/Generated/SearchResult.cs | 2 - .../src/OneDriveSdk/Models/Generated/Share.cs | 2 - .../OneDriveSdk/Models/Generated/Shared.cs | 2 - .../Models/Generated/SharingInvitation.cs | 2 - .../Models/Generated/SharingLink.cs | 2 - .../Models/Generated/SpecialFolder.cs | 2 - .../Models/Generated/ThumbnailSet.cs | 2 - .../Models/Generated/UploadSession.cs | 1 - .../src/OneDriveSdk/Models/Generated/Video.cs | 1 - .../OneDriveSdk/Properties/AssemblyInfo.cs | 35 - .../Generated/DriveItemsCollectionPage.cs | 2 - .../DriveItemsCollectionRequestBuilder.cs | 1 - .../Generated/DriveItemsCollectionResponse.cs | 2 - .../DriveRecentCollectionResponse.cs | 2 - .../Requests/Generated/DriveRecentRequest.cs | 3 - .../Generated/DriveRecentRequestBuilder.cs | 2 - .../Requests/Generated/DriveRequestBuilder.cs | 2 - .../Generated/DriveSharedCollectionPage.cs | 2 - .../DriveSharedCollectionRequestBuilder.cs | 1 - .../DriveSharedCollectionResponse.cs | 2 - .../Generated/DriveSpecialCollectionPage.cs | 2 - .../DriveSpecialCollectionRequestBuilder.cs | 1 - .../DriveSpecialCollectionResponse.cs | 2 - .../Generated/IDriveItemsCollectionPage.cs | 2 - .../Generated/IDriveItemsCollectionRequest.cs | 3 - .../IDriveItemsCollectionRequestBuilder.cs | 1 - .../Requests/Generated/IDriveRecentRequest.cs | 4 - .../Generated/IDriveRecentRequestBuilder.cs | 2 - .../Requests/Generated/IDriveRequest.cs | 3 - .../Generated/IDriveRequestBuilder.cs | 2 - .../Generated/IDriveSharedCollectionPage.cs | 2 - .../IDriveSharedCollectionRequest.cs | 3 - .../IDriveSharedCollectionRequestBuilder.cs | 1 - .../Generated/IDriveSpecialCollectionPage.cs | 2 - .../IDriveSpecialCollectionRequest.cs | 3 - .../IDriveSpecialCollectionRequestBuilder.cs | 1 - .../Generated/IItemChildrenCollectionPage.cs | 2 - .../IItemChildrenCollectionRequest.cs | 3 - .../IItemChildrenCollectionRequestBuilder.cs | 1 - .../Generated/IItemContentRequestBuilder.cs | 4 - .../Requests/Generated/IItemCopyRequest.cs | 4 - .../Generated/IItemCopyRequestBuilder.cs | 2 - .../Generated/IItemCreateLinkRequest.cs | 4 - .../IItemCreateLinkRequestBuilder.cs | 2 - .../Generated/IItemCreateSessionRequest.cs | 4 - .../IItemCreateSessionRequestBuilder.cs | 2 - .../Requests/Generated/IItemDeltaRequest.cs | 4 - .../Generated/IItemDeltaRequestBuilder.cs | 2 - .../Requests/Generated/IItemInviteRequest.cs | 4 - .../Generated/IItemInviteRequestBuilder.cs | 2 - .../IItemPermissionsCollectionPage.cs | 2 - .../IItemPermissionsCollectionRequest.cs | 3 - ...ItemPermissionsCollectionRequestBuilder.cs | 1 - .../Requests/Generated/IItemRequest.cs | 3 - .../Requests/Generated/IItemRequestBuilder.cs | 2 - .../Requests/Generated/IItemSearchRequest.cs | 4 - .../Generated/IItemSearchRequestBuilder.cs | 2 - .../IItemThumbnailsCollectionPage.cs | 2 - .../IItemThumbnailsCollectionRequest.cs | 3 - ...IItemThumbnailsCollectionRequestBuilder.cs | 1 - .../Generated/IItemVersionsCollectionPage.cs | 2 - .../IItemVersionsCollectionRequest.cs | 3 - .../IItemVersionsCollectionRequestBuilder.cs | 1 - .../IOneDriveDrivesCollectionPage.cs | 2 - .../IOneDriveDrivesCollectionRequest.cs | 3 - ...IOneDriveDrivesCollectionRequestBuilder.cs | 1 - .../IOneDriveSharesCollectionPage.cs | 2 - .../IOneDriveSharesCollectionRequest.cs | 3 - ...IOneDriveSharesCollectionRequestBuilder.cs | 1 - .../Requests/Generated/IPermissionRequest.cs | 3 - .../Generated/IPermissionRequestBuilder.cs | 2 - .../Generated/IShareItemsCollectionPage.cs | 2 - .../Generated/IShareItemsCollectionRequest.cs | 3 - .../IShareItemsCollectionRequestBuilder.cs | 1 - .../Requests/Generated/IShareRequest.cs | 3 - .../Generated/IShareRequestBuilder.cs | 2 - .../Generated/IThumbnailContentRequest.cs | 1 - .../IThumbnailContentRequestBuilder.cs | 4 - .../Generated/IThumbnailSetRequest.cs | 3 - .../Generated/IThumbnailSetRequestBuilder.cs | 2 - .../Generated/ItemChildrenCollectionPage.cs | 2 - .../ItemChildrenCollectionRequestBuilder.cs | 1 - .../ItemChildrenCollectionResponse.cs | 2 - .../Generated/ItemContentRequestBuilder.cs | 2 - .../Generated/ItemCopyRequestBuilder.cs | 2 - .../Generated/ItemCreateLinkRequest.cs | 3 - .../Generated/ItemCreateLinkRequestBuilder.cs | 2 - .../Generated/ItemCreateSessionRequest.cs | 3 - .../ItemCreateSessionRequestBuilder.cs | 2 - .../Generated/ItemDeltaCollectionResponse.cs | 2 - .../Requests/Generated/ItemDeltaRequest.cs | 3 - .../Generated/ItemDeltaRequestBuilder.cs | 2 - .../Requests/Generated/ItemInviteRequest.cs | 3 - .../Generated/ItemInviteRequestBuilder.cs | 2 - .../ItemPermissionsCollectionPage.cs | 2 - ...ItemPermissionsCollectionRequestBuilder.cs | 1 - .../ItemPermissionsCollectionResponse.cs | 2 - .../Requests/Generated/ItemRequestBuilder.cs | 2 - .../Generated/ItemSearchCollectionResponse.cs | 2 - .../Requests/Generated/ItemSearchRequest.cs | 3 - .../Generated/ItemSearchRequestBuilder.cs | 2 - .../Generated/ItemThumbnailsCollectionPage.cs | 2 - .../ItemThumbnailsCollectionRequestBuilder.cs | 1 - .../ItemThumbnailsCollectionResponse.cs | 2 - .../Generated/ItemVersionsCollectionPage.cs | 2 - .../ItemVersionsCollectionRequestBuilder.cs | 1 - .../ItemVersionsCollectionResponse.cs | 2 - .../Generated/OneDriveDrivesCollectionPage.cs | 2 - .../OneDriveDrivesCollectionRequestBuilder.cs | 1 - .../OneDriveDrivesCollectionResponse.cs | 2 - .../Generated/OneDriveSharesCollectionPage.cs | 2 - .../OneDriveSharesCollectionRequestBuilder.cs | 1 - .../OneDriveSharesCollectionResponse.cs | 2 - .../Generated/PermissionRequestBuilder.cs | 2 - .../Generated/ShareItemsCollectionPage.cs | 2 - .../ShareItemsCollectionRequestBuilder.cs | 1 - .../Generated/ShareItemsCollectionResponse.cs | 2 - .../Requests/Generated/ShareRequestBuilder.cs | 2 - .../ThumbnailContentRequestBuilder.cs | 2 - .../Generated/ThumbnailSetRequestBuilder.cs | 2 - .../OneDriveSdk/Requests/IThumbnailRequest.cs | 3 - .../Requests/IThumbnailRequestBuilder.cs | 1 - .../Requests/IUploadChunkRequest.cs | 3 - .../OneDriveSdk/Requests/ThumbnailRequest.cs | 3 - .../Requests/ThumbnailRequestBuilder.cs | 1 - .../Test.OneDriveSdk/35MSSharedLib1024.snk | Bin 160 -> 0 bytes .../openstack.net/.paket/paket.targets | 41 + redistributable/openstack.net/CONTRIBUTING.md | 75 + .../openstack.net/GitVersionConfig.yaml | 2 + redistributable/openstack.net/LICENSE.md | 2 + redistributable/openstack.net/README.md | 61 + redistributable/openstack.net/appveyor.yml | 9 + .../openstack.net/azure-pipelines.yml | 30 + redistributable/openstack.net/build.cmd | 52 + redistributable/openstack.net/build.sh | 2 + .../openstack.net/build/build.proj | 125 + .../build/check-nuget-version-exists.ps1 | 13 + .../build/keys/OpenStackNetV1.dev.snk | Bin 0 -> 596 bytes .../build/keys/OpenStackNetV1.snk | Bin 0 -> 596 bytes .../build/keys/SDKTestingKey.snk | Bin 0 -> 596 bytes redistributable/openstack.net/myget.ps1 | 2 + .../openstack.net/paket.dependencies | 50 + redistributable/openstack.net/paket.lock | 904 ++ redistributable/openstack.net/pre-myget.ps1 | 1 + .../samples/.paket/paket.bootstrapper.exe | Bin 0 -> 28208 bytes .../openstack.net/samples/ComputeSample.cs | 74 + .../openstack.net/samples/ISample.cs | 7 + .../openstack.net/samples/NetworkingSample.cs | 79 + .../samples/OpenStack.Samples.csproj | 118 + .../openstack.net/samples/Program.cs | 60 + .../samples/Properties/AssemblyInfo.cs | 36 + .../openstack.net/samples/paket.dependencies | 3 + .../openstack.net/samples/paket.references | 1 + ...alReferenceDocumentation.Portable.shfbproj | 63 + .../AdditionalReferenceDocumentation.shfbproj | 58 + .../Content/AsynchronousServices.aml | 367 + .../Content/Authentication/Authentication.aml | 63 + .../Authentication/HPAuthentication.aml | 91 + .../OpenStackAuthentication.aml | 74 + .../RackspaceAuthentication.aml | 81 + .../Content/BreakingChangesPolicy.aml | 376 + .../src/Documentation/Content/License.aml | 223 + .../Content/MSHelpViewerRoot.aml | 18 + .../Content/UserGuide/UserGuide.aml | 751 ++ .../src/Documentation/Content/Welcome.aml | 70 + .../Documentation/Documentation.v4.0.shfbproj | 202 + .../1.3.6/Documentation.1.3.6.shfbproj | 110 + .../History/1.4/Documentation.1.4.shfbproj | 122 + .../History/1.5/Documentation.1.5.shfbproj | 122 + .../Current/Documentation.Current.shfbproj | 105 + .../src/Documentation/OpenStackSDK.content | 27 + .../openstack.net/src/Documentation/README.md | 7 + .../src/Documentation/SharedTokens.tokens | 77 + .../openstack.net/src/Local.testsettings | 29 + .../Samples/CPPCodeSamples/AssemblyInfo.cpp | 38 + .../CPPCodeSamples/CPPCodeSamples.vcxproj | 115 + .../CPPCodeSamples.vcxproj.filters | 55 + .../IdentityProviderExamples.cpp | 136 + .../ObjectStorageProviderExamples.cpp | 55 + .../QueueingServiceExamples.cpp | 82 + .../src/Samples/CPPCodeSamples/Stdafx.cpp | 5 + .../src/Samples/CPPCodeSamples/Stdafx.h | 7 + .../src/Samples/CPPCodeSamples/app.ico | Bin 0 -> 11001 bytes .../src/Samples/CPPCodeSamples/app.rc | Bin 0 -> 2558 bytes .../src/Samples/CPPCodeSamples/resource.h | 3 + .../AsynchronousExceptionsExamples.cs | 61 + .../CSharpCodeSamples.csproj | 79 + .../ContentDeliveryNetworkExample.cs | 68 + .../IdentityProviderExamples.cs | 117 + .../ObjectStorageProviderExamples.cs | 97 + .../Properties/AssemblyInfo.cs | 36 + .../QueueingServiceExamples.cs | 138 + .../src/Samples/CSharpCodeSamples/app.config | 11 + .../packages.CSharpCodeSamples.config | 5 + .../FSharpCodeSamples.fsproj | 1601 +++ .../IdentityProviderExamples.fs | 94 + .../ObjectStorageProviderExamples.fs | 29 + .../QueueingServiceExamples.fs | 121 + .../src/Samples/FSharpCodeSamples/app.config | 10 + .../FSharpCodeSamples/paket.references | 2 + .../AsynchronousExceptionsExamples.vb | 47 + .../VBCodeSamples/IdentityProviderExamples.vb | 105 + .../My Project/Application.Designer.vb | 13 + .../My Project/Application.myapp | 10 + .../VBCodeSamples/My Project/AssemblyInfo.vb | 35 + .../My Project/Resources.Designer.vb | 63 + .../VBCodeSamples/My Project}/Resources.resx | 239 +- .../My Project/Settings.Designer.vb | 73 + .../My Project/Settings.settings | 7 + .../ObjectStorageProviderExamples.vb | 41 + .../VBCodeSamples/QueueingServiceExamples.vb | 115 + .../VBCodeSamples/VBCodeSamples.vbproj | 131 + .../src/Samples/VBCodeSamples/app.config | 11 + .../packages.VBCodeSamples.config | 5 + .../src/TraceAndTestImpact.testsettings | 37 + .../AuthenticatedHttpClientFactory.cs | 30 + .../AuthenticatedMessageHandler.cs | 47 + .../Authentication/IAuthenticationProvider.cs | 37 + .../corelib/Authentication/NamespaceDoc.cs | 13 + .../src/corelib/Authentication/ServiceType.cs | 99 + .../v2/Serialization/SnapshotStatus.cs | 37 + .../v2/Serialization/VolumeStatus.cs | 67 + .../corelib/BlockStorage/v2/SnapshotStatus.cs | 7 + .../corelib/BlockStorage/v2/VolumeStatus.cs | 7 + .../ComputeOperationFailedException.cs | 26 + .../src/corelib/Compute/NamespaceDoc.cs | 17 + .../Actions/AssociateFloatingIPRequest.cs | 33 + .../v2_1/Actions/RebootServerRequest.cs | 18 + .../Compute/v2_1/Actions/RebootType.cs | 8 + .../v2_1/Actions/RescueServerRequest.cs | 24 + .../v2_1/Actions/SnapshotServerRequest.cs | 35 + .../src/corelib/Compute/v2_1/AddressType.cs | 8 + .../corelib/Compute/v2_1/ComputeService.cs | 536 + .../Compute/v2_1/ComputeServiceExtensions.cs | 500 + .../corelib/Compute/v2_1/DiskConfiguration.cs | 8 + .../src/corelib/Compute/v2_1/Flavor.cs | 49 + .../corelib/Compute/v2_1/FlavorExtensions.cs | 18 + .../corelib/Compute/v2_1/FlavorListOptions.cs | 30 + .../corelib/Compute/v2_1/FlavorReference.cs | 36 + .../src/corelib/Compute/v2_1/FlavorSummary.cs | 16 + .../src/corelib/Compute/v2_1/IPProtocol.cs | 8 + .../src/corelib/Compute/v2_1/Image.cs | 129 + .../corelib/Compute/v2_1/ImageExtensions.cs | 49 + .../corelib/Compute/v2_1/ImageListOptions.cs | 55 + .../src/corelib/Compute/v2_1/ImageMetadata.cs | 90 + .../Compute/v2_1/ImageMetadataExtensions.cs | 30 + .../corelib/Compute/v2_1/ImageReference.cs | 70 + .../src/corelib/Compute/v2_1/ImageSummary.cs | 16 + .../src/corelib/Compute/v2_1/ImageType.cs | 8 + .../src/corelib/Compute/v2_1/KeyPair.cs | 24 + .../corelib/Compute/v2_1/KeyPairDefinition.cs | 31 + .../corelib/Compute/v2_1/KeyPairExtensions.cs | 18 + .../corelib/Compute/v2_1/KeyPairRequest.cs | 27 + .../corelib/Compute/v2_1/KeyPairResponse.cs | 17 + .../corelib/Compute/v2_1/KeyPairSummary.cs | 48 + .../src/corelib/Compute/v2_1/NamespaceDoc.cs | 13 + .../v2_1/Operator/ComputeServiceExtensions.cs | 36 + .../v2_1/Operator/EvacuateServerRequest.cs | 39 + .../Compute/v2_1/Operator/NamespaceDoc.cs | 12 + .../Compute/v2_1/Operator/ServerExtensions.cs | 23 + .../Compute/v2_1/Operator/ServiceQuotas.cs | 108 + .../src/corelib/Compute/v2_1/RemoteConsole.cs | 25 + .../corelib/Compute/v2_1/RemoteConsoleType.cs | 9 + .../corelib/Compute/v2_1/SchedulerHints.cs | 73 + .../src/corelib/Compute/v2_1/SecurityGroup.cs | 68 + .../Compute/v2_1/SecurityGroupDefinition.cs | 31 + .../Compute/v2_1/SecurityGroupExtensions.cs | 42 + .../Compute/v2_1/SecurityGroupReference.cs | 64 + .../corelib/Compute/v2_1/SecurityGroupRule.cs | 87 + .../v2_1/SecurityGroupRuleDefinition.cs | 56 + .../Compute/v2_1/Serialization/AddressType.cs | 22 + .../Compute/v2_1/Serialization/ComputeApi.cs | 2261 ++++ .../v2_1/Serialization/DiskConfiguration.cs | 22 + .../v2_1/Serialization/FlavorCollection.cs | 35 + .../v2_1/Serialization/ImageCollection.cs | 35 + .../Compute/v2_1/Serialization/ImageType.cs | 27 + .../v2_1/Serialization/KeyPairCollection.cs | 25 + .../v2_1/Serialization/KeyPairConverter.cs | 50 + .../Compute/v2_1/Serialization/RebootType.cs | 22 + .../v2_1/Serialization/RemoteConsoleType.cs | 37 + .../Serialization/SecurityGroupCollection.cs | 23 + .../Serialization/ServerActionCollection.cs | 25 + .../Serialization/ServerAddressCollection.cs | 18 + .../Serialization/ServerBlockDeviceType.cs | 27 + .../v2_1/Serialization/ServerCollection.cs | 35 + .../ServerCreateDefinitionConverter.cs | 41 + .../v2_1/Serialization/ServerEventStatus.cs | 18 + .../Serialization/ServerGroupCollection.cs | 23 + .../v2_1/Serialization/ServerStatus.cs | 103 + .../Serialization/ServerVolumeCollection.cs | 25 + .../v2_1/Serialization/VolumeCollection.cs | 25 + .../Serialization/VolumeSnapshotCollection.cs | 24 + .../src/corelib/Compute/v2_1/Server.cs | 287 + .../src/corelib/Compute/v2_1/ServerAction.cs | 67 + .../Compute/v2_1/ServerActionExtensions.cs | 18 + .../Compute/v2_1/ServerActionSummary.cs | 66 + .../src/corelib/Compute/v2_1/ServerAddress.cs | 41 + .../Compute/v2_1/ServerBlockDeviceMapping.cs | 62 + .../Compute/v2_1/ServerBlockDeviceType.cs | 8 + .../Compute/v2_1/ServerCreateDefinition.cs | 172 + .../corelib/Compute/v2_1/ServerEventStatus.cs | 8 + .../corelib/Compute/v2_1/ServerExtensions.cs | 206 + .../src/corelib/Compute/v2_1/ServerGroup.cs | 61 + .../Compute/v2_1/ServerGroupDefinition.cs | 30 + .../Compute/v2_1/ServerGroupExtensions.cs | 18 + .../corelib/Compute/v2_1/ServerListOptions.cs | 64 + .../corelib/Compute/v2_1/ServerMetadata.cs | 89 + .../Compute/v2_1/ServerMetadataExtensions.cs | 30 + .../Compute/v2_1/ServerNetworkDefinition.cs | 28 + .../corelib/Compute/v2_1/ServerReference.cs | 226 + .../src/corelib/Compute/v2_1/ServerStatus.cs | 8 + .../src/corelib/Compute/v2_1/ServerSummary.cs | 16 + .../Compute/v2_1/ServerUpdateDefinition.cs | 43 + .../src/corelib/Compute/v2_1/ServerVolume.cs | 40 + .../Compute/v2_1/ServerVolumeDefinition.cs | 29 + .../Compute/v2_1/ServerVolumeExtensions.cs | 23 + .../Compute/v2_1/ServerVolumeReference.cs | 90 + .../src/corelib/Compute/v2_1/ServiceLimits.cs | 225 + .../src/corelib/Compute/v2_1/Volume.cs | 171 + .../corelib/Compute/v2_1/VolumeDefinition.cs | 57 + .../corelib/Compute/v2_1/VolumeExtensions.cs | 52 + .../corelib/Compute/v2_1/VolumeSnapshot.cs | 113 + .../Compute/v2_1/VolumeSnapshotDefinition.cs | 46 + .../Compute/v2_1/VolumeSnapshotExtensions.cs | 18 + .../src/corelib/Compute/v2_2/ComputeApi.cs | 19 + .../corelib/Compute/v2_2/ComputeService.cs | 44 + .../Compute/v2_2/ComputeServiceExtensions.cs | 24 + .../src/corelib/Compute/v2_2/KeyPair.cs | 12 + .../corelib/Compute/v2_2/KeyPairDefinition.cs | 20 + .../src/corelib/Compute/v2_2/KeyPairType.cs | 16 + .../src/corelib/Compute/v2_2/RemoteConsole.cs | 7 + .../corelib/Compute/v2_2/RemoteConsoleType.cs | 9 + .../v2_2/Serialization/ServerCollection.cs | 16 + .../src/corelib/Compute/v2_2/Server.cs | 7 + .../corelib/Compute/v2_2/ServerListOptions.cs | 6 + .../corelib/Compute/v2_2/ServerReference.cs | 6 + .../src/corelib/Compute/v2_2/ServerStatus.cs | 6 + .../src/corelib/Compute/v2_6/ComputeApi.cs | 51 + .../corelib/Compute/v2_6/ComputeService.cs | 48 + .../Compute/v2_6/ComputeServiceExtensions.cs | 18 + .../src/corelib/Compute/v2_6/Console.cs | 25 + .../corelib/Compute/v2_6/ConsoleProtocol.cs | 39 + .../src/corelib/Compute/v2_6/KeyPair.cs | 7 + .../corelib/Compute/v2_6/KeyPairDefinition.cs | 28 + .../src/corelib/Compute/v2_6/KeyPairType.cs | 16 + .../corelib/Compute/v2_6/RemoteConsoleType.cs | 6 + .../v2_6/Serialization/ServerCollection.cs | 16 + .../src/corelib/Compute/v2_6/Server.cs | 27 + .../corelib/Compute/v2_6/ServerListOptions.cs | 6 + .../corelib/Compute/v2_6/ServerReference.cs | 6 + .../src/corelib/Compute/v2_6/ServerStatus.cs | 6 + .../v1/ContentDeliveryNetworkService.cs | 304 + ...ContentDeliveryNetworkServiceExtensions.cs | 179 + .../ContentDeliveryNetworks/v1/Flavor.cs | 33 + .../v1/FlavorCollection.cs | 28 + .../v1/IContentDeliveryNetworkService.cs | 150 + .../v1/NamespaceDoc.cs | 13 + .../ContentDeliveryNetworks/v1/Provider.cs | 25 + .../ContentDeliveryNetworks/v1/Service.cs | 76 + .../v1/ServiceCache.cs | 48 + .../v1/ServiceCacheRule.cs | 34 + .../v1/ServiceCollection.cs | 25 + .../v1/ServiceDefinition.cs | 77 + .../v1/ServiceDomain.cs | 32 + .../v1/ServiceError.cs | 17 + .../v1/ServiceOperationFailedException.cs | 54 + .../v1/ServiceOrigin.cs | 49 + .../v1/ServiceOriginRule.cs | 34 + .../v1/ServiceProtocol.cs | 21 + .../v1/ServiceRestriction.cs | 35 + .../v1/ServiceRestrictionRule.cs | 34 + .../v1/ServiceStatus.cs | 43 + .../src/corelib/Core/AsyncCompletionOption.cs | 24 + .../src/corelib/Core/BackoffPolicy.cs | 63 + .../src/corelib/Core/Caching/ICache`1.cs | 41 + .../src/corelib/Core/Caching/NamespaceDoc.cs | 14 + .../corelib/Core/Caching/UserAccessCache.cs | 126 + .../BasicReadOnlyCollectionPage`1.cs | 55 + .../corelib/Core/Collections/NamespaceDoc.cs | 13 + .../Collections/ReadOnlyCollectionPage`1.cs | 108 + .../src/corelib/Core/Compat/Funcs.cs | 52 + .../Core/Compat/ISafeSerializationData.cs | 17 + .../Core/Compat/IStructuralComparable.cs | 72 + .../Core/Compat/IStructuralEquatable.cs | 57 + .../src/corelib/Core/Compat/Tuple.cs | 222 + .../src/corelib/Core/Compat/Tuples.cs | 1280 +++ .../src/corelib/Core/CoreTaskExtensions.cs | 712 ++ .../Core/Domain/AuthenticationRequirement.cs | 56 + .../corelib/Core/Domain/AuthenticationType.cs | 82 + .../src/corelib/Core/Domain/CloudIdentity.cs | 47 + .../Core/Domain/CloudIdentityWithProject.cs | 38 + .../src/corelib/Core/Domain/CloudNetwork.cs | 39 + .../src/corelib/Core/Domain/Container.cs | 49 + .../src/corelib/Core/Domain/ContainerCDN.cs | 111 + .../corelib/Core/Domain/ContainerObject.cs | 54 + .../Converters/IPAddressDetailsConverter.cs | 137 + .../IPAddressNoneIsNullSimpleConverter.cs | 32 + .../Converters/IPAddressSimpleConverter.cs | 30 + .../Core/Domain/Converters/NamespaceDoc.cs | 18 + .../PhysicalAddressSimpleConverter.cs | 46 + .../Converters/SimpleStringJsonConverter`1.cs | 80 + .../corelib/Core/Domain/DiskConfiguration.cs | 101 + .../src/corelib/Core/Domain/Endpoint.cs | 62 + .../corelib/Core/Domain/EndpointTemplate.cs | 64 + .../corelib/Core/Domain/EndpointTemplateId.cs | 45 + .../corelib/Core/Domain/ExtendedEndpoint.cs | 37 + .../Core/Domain/ExtensibleJsonObject.cs | 391 + .../src/corelib/Core/Domain/Flavor.cs | 35 + .../src/corelib/Core/Domain/FlavorDetails.cs | 100 + .../src/corelib/Core/Domain/FlavorId.cs | 41 + .../src/corelib/Core/Domain/HomeDocument.cs | 36 + .../src/corelib/Core/Domain/IPAddressList.cs | 48 + .../src/corelib/Core/Domain/IdentityToken.cs | 64 + .../src/corelib/Core/Domain/ImageId.cs | 41 + .../src/corelib/Core/Domain/ImageState.cs | 129 + .../src/corelib/Core/Domain/ImageType.cs | 85 + .../src/corelib/Core/Domain/Link.cs | 48 + .../Domain/Mapping/IJsonObjectMapper`1.cs | 23 + .../Core/Domain/Mapping/IObjectMapper`2.cs | 41 + .../Core/Domain/Mapping/NamespaceDoc.cs | 14 + .../src/corelib/Core/Domain/Metadata.cs | 19 + .../src/corelib/Core/Domain/NamespaceDoc.cs | 13 + .../src/corelib/Core/Domain/NetworkId.cs | 41 + .../src/corelib/Core/Domain/NewServer.cs | 51 + .../src/corelib/Core/Domain/NewUser.cs | 78 + .../src/corelib/Core/Domain/ObjectStore.cs | 23 + .../src/corelib/Core/Domain/Personality.cs | 133 + .../src/corelib/Core/Domain/PowerState.cs | 106 + .../src/corelib/Core/Domain/ProjectId.cs | 40 + .../Core/Domain/ProviderStateBase`1.cs | 29 + .../src/corelib/Core/Domain/Queues/Claim.cs | 304 + .../src/corelib/Core/Domain/Queues/ClaimId.cs | 42 + .../corelib/Core/Domain/Queues/CloudQueue.cs | 79 + .../src/corelib/Core/Domain/Queues/Message.cs | 36 + .../corelib/Core/Domain/Queues/MessageId.cs | 42 + .../Core/Domain/Queues/MessageStatistics.cs | 77 + .../corelib/Core/Domain/Queues/Message`1.cs | 75 + .../Core/Domain/Queues/MessagesEnqueued.cs | 103 + .../Core/Domain/Queues/NamespaceDoc.cs | 13 + .../Domain/Queues/QueueMessagesStatistics.cs | 118 + .../corelib/Core/Domain/Queues/QueueName.cs | 42 + .../Core/Domain/Queues/QueueStatistics.cs | 43 + .../Core/Domain/Queues/QueuedMessage.cs | 129 + .../Core/Domain/Queues/QueuedMessageList.cs | 52 + .../Core/Domain/Queues/QueuedMessageListId.cs | 44 + .../src/corelib/Core/Domain/RebootType.cs | 86 + .../src/corelib/Core/Domain/ResourceHints.cs | 298 + .../src/corelib/Core/Domain/ResourceObject.cs | 112 + .../src/corelib/Core/Domain/Role.cs | 52 + .../src/corelib/Core/Domain/Server.cs | 492 + .../corelib/Core/Domain/ServerAddresses.cs | 50 + .../src/corelib/Core/Domain/ServerBase.cs | 487 + .../src/corelib/Core/Domain/ServerId.cs | 41 + .../src/corelib/Core/Domain/ServerImage.cs | 312 + .../src/corelib/Core/Domain/ServerState.cs | 309 + .../src/corelib/Core/Domain/ServerVolume.cs | 46 + .../src/corelib/Core/Domain/ServiceCatalog.cs | 64 + .../src/corelib/Core/Domain/SimpleServer.cs | 38 + .../corelib/Core/Domain/SimpleServerImage.cs | 225 + .../src/corelib/Core/Domain/Snapshot.cs | 69 + .../src/corelib/Core/Domain/SnapshotState.cs | 121 + .../src/corelib/Core/Domain/Status.cs | 42 + .../src/corelib/Core/Domain/TaskState.cs | 461 + .../src/corelib/Core/Domain/Tenant.cs | 28 + .../src/corelib/Core/Domain/User.cs | 94 + .../src/corelib/Core/Domain/UserAccess.cs | 65 + .../src/corelib/Core/Domain/UserCredential.cs | 50 + .../src/corelib/Core/Domain/UserDetails.cs | 78 + .../corelib/Core/Domain/VirtualInterface.cs | 46 + .../Core/Domain/VirtualInterfaceAddress.cs | 48 + .../Core/Domain/VirtualMachineState.cs | 189 + .../src/corelib/Core/Domain/Volume.cs | 101 + .../src/corelib/Core/Domain/VolumeState.cs | 181 + .../src/corelib/Core/Domain/VolumeType.cs | 29 + .../Core/Exceptions/CDNNotEnabledException.cs | 60 + .../Core/Exceptions/CidrFormatException.cs | 45 + .../Core/Exceptions/ContainerNameException.cs | 46 + .../Exceptions/ContainerNotEmptyException.cs | 61 + .../ImageEnteredErrorStateException.cs | 63 + .../InvalidCloudIdentityException.cs | 37 + .../corelib/Core/Exceptions/NamespaceDoc.cs | 14 + .../Exceptions/NoDefaultRegionSetException.cs | 36 + .../Core/Exceptions/ObjectNameException.cs | 46 + .../Response/BadServiceRequestException.cs | 37 + .../Response/ItemNotFoundException.cs | 37 + .../Response/MethodNotImplementedException.cs | 37 + .../Core/Exceptions/Response/NamespaceDoc.cs | 13 + .../Exceptions/Response/ResponseException.cs | 101 + .../Response/ServiceConflictException.cs | 37 + .../Response/ServiceFaultException.cs | 37 + .../Response/ServiceLimitReachedException.cs | 37 + .../Response/ServiceUnavailableException.cs | 37 + .../Response/UserNotAuthorizedException.cs | 38 + .../ServerEnteredErrorStateException.cs | 63 + .../SnapshotEnteredErrorStateException.cs | 63 + .../Core/Exceptions/TTLLengthException.cs | 44 + .../Exceptions/UserAuthenticationException.cs | 37 + .../Exceptions/UserAuthorizationException.cs | 40 + .../VolumeEnteredErrorStateException.cs | 63 + .../src/corelib/Core/ExtensibleEnum`1.cs | 97 + .../src/corelib/Core/HttpStatusCodeParser.cs | 105 + .../src/corelib/Core/IBackoffPolicy.cs | 28 + .../src/corelib/Core/IEncodeDecodeProvider.cs | 26 + .../Core/IObjectStorageMetadataProcessor.cs | 37 + .../src/corelib/Core/IStatusParser.cs | 23 + .../corelib/Core/InternalTaskExtensions.cs | 57 + .../LegacyAuthenticationProviderHelper.cs | 49 + .../src/corelib/Core/NamespaceDoc.cs | 13 + .../TaskCompletionSourceExtensions.cs | 39 + .../TaskExtrasExtensions.cs | 37 + .../TaskFactoryExtensions_Common.cs | 16 + ...askFactoryExtensions_ContinueWhenAllAny.cs | 26 + .../TaskFactoryExtensions_Delayed.cs | 63 + .../TaskFactoryExtensions_From.cs | 25 + .../Core/Providers/IBlockStorageProvider.cs | 493 + .../Core/Providers/IComputeProvider.cs | 1765 ++++ .../Core/Providers/IIdentityProvider.cs | 330 + .../Core/Providers/IIdentityService.cs | 68 + .../Core/Providers/INetworksProvider.cs | 116 + .../Core/Providers/IObjectStorageProvider.cs | 1639 +++ .../Core/Providers/IQueueingService.cs | 555 + .../corelib/Core/Providers/NamespaceDoc.cs | 15 + .../Providers/OpenStackIdentityProvider.cs | 139 + .../Core/ReadOnlyCollectionPageExtensions.cs | 104 + .../src/corelib/Core/ResourceIdentifier`1.cs | 155 + .../src/corelib/Core/ResponseExtensions.cs | 85 + .../corelib/Core/RestWebHeaderCollection.cs | 83 + .../Synchronous/AutoScaleServiceExtensions.cs | 816 ++ .../Core/Synchronous/ClaimExtensions.cs | 87 + .../Synchronous/DatabaseServiceExtensions.cs | 1006 ++ .../Core/Synchronous/DnsServiceExtensions.cs | 964 ++ .../LoadBalancerServiceExtensions.cs | 2238 ++++ .../MonitoringServiceExtensions.cs | 2572 +++++ .../corelib/Core/Synchronous/NamespaceDoc.cs | 18 + .../Synchronous/QueueingServiceExtensions.cs | 869 ++ ...OnlyCollectionPageSynchronousExtensions.cs | 83 + .../openstack.net/src/corelib/Core/UriPart.cs | 52 + .../src/corelib/Core/UriUtility.cs | 392 + .../Core/Validators/IBlockStorageValidator.cs | 22 + .../Validators/IHttpResponseCodeValidator.cs | 23 + .../Core/Validators/INetworksValidator.cs | 21 + .../Validators/IObjectStorageValidator.cs | 29 + .../corelib/Core/Validators/NamespaceDoc.cs | 14 + .../src/corelib/Core/WebRequestExtensions.cs | 124 + .../Exceptions/IdentityRequiredException.cs | 33 + .../Exceptions/RegionRequiredException.cs | 31 + .../Exceptions/ResourceErrorException.cs | 26 + .../Exceptions/UserAuthenticationException.cs | 37 + .../Extensions/EnumerableExtensions.cs | 19 + .../src/corelib/Extensions/FlurlExtensions.cs | 135 + .../Extensions/HttpHeadersExtensions.cs | 17 + .../HttpRequestMessageExtensions.cs | 25 + .../src/corelib/Extensions/TaskExtensions.cs | 27 + .../src/corelib/Extensions/TypeExtensions.cs | 61 + .../src/corelib/Flurl/PreparedRequest.cs | 152 + .../src/corelib/Icons/openstack_net_logo.png | Bin 0 -> 3746 bytes .../openstack.net/src/corelib/Identifier.cs | 133 + .../src/corelib/Images/v2/ImageStatus.cs | 8 + .../Images/v2/Serialization/ImageStatus.cs | 27 + .../openstack.net/src/corelib/NamespaceDoc.cs | 28 + .../src/corelib/Networking/IPVersion.cs | 18 + .../src/corelib/Networking/NamespaceDoc.cs | 13 + .../corelib/Networking/v2/AllocationPool.cs | 59 + .../corelib/Networking/v2/AllowedAddress.cs | 71 + .../src/corelib/Networking/v2/HostRoute.cs | 59 + .../Networking/v2/IPAddressAssociation.cs | 100 + .../src/corelib/Networking/v2/IPProtocol.cs | 8 + .../Networking/v2/Layer3/ExternalGateway.cs | 37 + .../v2/Layer3/ExternalGatewayDefinition.cs | 38 + .../Networking/v2/Layer3/FloatingIP.cs | 100 + .../v2/Layer3/FloatingIPCreateDefinition.cs | 37 + .../v2/Layer3/FloatingIPExtensions.cs | 23 + .../v2/Layer3/FloatingIPListOptions.cs | 56 + .../Networking/v2/Layer3/FloatingIPStatus.cs | 6 + .../v2/Layer3/FloatingIPUpdateDefinition.cs | 18 + .../NetworkingService_Layer3_Extensions.cs | 230 + .../corelib/Networking/v2/Layer3/Router.cs | 117 + .../v2/Layer3/RouterCreateDefinition.cs | 24 + .../Networking/v2/Layer3/RouterExtensions.cs | 41 + .../Networking/v2/Layer3/RouterListOptions.cs | 32 + .../Networking/v2/Layer3/RouterStatus.cs | 6 + .../v2/Layer3/RouterUpdateDefinition.cs | 37 + .../Networking/v2/Layer3/SecurityGroup.cs | 43 + .../v2/Layer3/SecurityGroupListOptions.cs | 26 + .../Networking/v2/Layer3/SecurityGroupRule.cs | 82 + .../v2/Layer3/SecurityGroupRuleListOptions.cs | 26 + .../src/corelib/Networking/v2/NamespaceDoc.cs | 13 + .../src/corelib/Networking/v2/Network.cs | 65 + .../Networking/v2/NetworkDefinition.cs | 26 + .../corelib/Networking/v2/NetworkStatus.cs | 6 + .../Networking/v2/NetworkingApiBuilder.cs | 890 ++ .../Networking/v2/NetworkingService.cs | 195 + .../v2/NetworkingServiceExtensions.cs | 241 + .../Networking/v2/Operator/NamespaceDoc.cs | 12 + .../v2/Operator/NetworkDefinition.cs | 14 + .../src/corelib/Networking/v2/Port.cs | 46 + .../Networking/v2/PortCreateDefinition.cs | 69 + .../corelib/Networking/v2/PortListOptions.cs | 56 + .../src/corelib/Networking/v2/PortStatus.cs | 6 + .../Networking/v2/PortUpdateDefinition.cs | 41 + .../v2/Serialization/DHCPOptionsConverter.cs | 113 + .../v2/Serialization/FloatingIPCollection.cs | 26 + .../Networking/v2/Serialization/IPProtocol.cs | 27 + .../v2/Serialization/NetworkCollection.cs | 30 + .../NetworkDefinitionCollection.cs | 18 + .../v2/Serialization/NetworkResourceStatus.cs | 27 + .../v2/Serialization/PortCollection.cs | 30 + .../Serialization/PortDefinitionCollection.cs | 18 + .../v2/Serialization/RouterCollection.cs | 26 + .../Serialization/SecurityGroupCollection.cs | 32 + .../SecurityGroupRuleCollection.cs | 32 + .../v2/Serialization/SubnetCollection.cs | 30 + .../SubnetDefinitionCollection.cs | 18 + .../v2/Serialization/TrafficDirection.cs | 22 + .../src/corelib/Networking/v2/Subnet.cs | 34 + .../Networking/v2/SubnetCreateDefinition.cs | 49 + .../Networking/v2/SubnetUpdateDefinition.cs | 60 + .../corelib/Networking/v2/TrafficDirection.cs | 8 + .../src/corelib/OpenStack.csproj | 35 + .../openstack.net/src/corelib/OpenStackNet.cs | 302 + .../openstack.net/src/corelib/Page.cs | 23 + .../src/corelib/PageExtensions.cs | 18 + .../openstack.net/src/corelib/PageOptions.cs | 45 + .../src/corelib/Properties/AssemblyInfo.cs | 38 + .../Providers/Hp/HpIdentityProvider.cs | 199 + .../src/corelib/Providers/Hp/NamespaceDoc.cs | 14 + .../Hp/PredefinedHpIdentityEndpoints.cs | 42 + .../Rackspace/CloudAutoScaleProvider.cs | 840 ++ .../Rackspace/CloudBlockStorageProvider.cs | 511 + .../Rackspace/CloudDatabasesProvider.cs | 1222 +++ .../Providers/Rackspace/CloudDnsProvider.cs | 1369 +++ .../Rackspace/CloudFilesMetadataProcessor.cs | 90 + .../Providers/Rackspace/CloudFilesProvider.cs | 2599 +++++ .../Rackspace/CloudIdentityProvider.cs | 1173 +++ .../Rackspace/CloudLoadBalancerProvider.cs | 2671 +++++ .../Rackspace/CloudMonitoringProvider.cs | 2103 ++++ .../Rackspace/CloudNetworksProvider.cs | 231 + .../Rackspace/CloudQueuesProvider.cs | 860 ++ .../Rackspace/CloudServersProvider.cs | 1543 +++ .../Rackspace/EncodeDecodeProvider.cs | 48 + .../Exceptions/BulkDeletionException.cs | 74 + .../Exceptions/InvalidETagException.cs | 32 + .../Exceptions/InvalidVolumeSizeException.cs | 58 + .../Rackspace/Exceptions/NamespaceDoc.cs | 13 + .../Rackspace/ExtendedJsonRestServices.cs | 70 + .../Providers/Rackspace/IAutoScaleService.cs | 445 + .../Providers/Rackspace/IDatabaseService.cs | 554 + .../Providers/Rackspace/IDnsService.cs | 679 ++ .../IExtendedCloudIdentityProvider.cs | 380 + .../Rackspace/ILoadBalancerService.cs | 1417 +++ .../Providers/Rackspace/IMonitoringService.cs | 1435 +++ .../Providers/Rackspace/IProviderFactory`2.cs | 20 + .../Providers/Rackspace/IRackspaceProvider.cs | 18 + .../Providers/Rackspace/NamespaceDoc.cs | 14 + .../Rackspace/Objects/ArchiveFormat.cs | 102 + .../Rackspace/Objects/AuthDetails.cs | 17 + .../Objects/AutoScale/ActiveServer.cs | 67 + .../AutoScale/GenericLaunchConfiguration.cs | 39 + .../Objects/AutoScale/GroupConfiguration.cs | 168 + .../Rackspace/Objects/AutoScale/GroupState.cs | 142 + .../Objects/AutoScale/LaunchConfiguration.cs | 56 + .../AutoScale/LaunchConfiguration`1.cs | 73 + .../Rackspace/Objects/AutoScale/LaunchType.cs | 76 + .../Objects/AutoScale/LoadBalancerArgument.cs | 83 + .../Objects/AutoScale/NamespaceDoc.cs | 14 + .../AutoScale/NewWebhookConfiguration.cs | 55 + .../Rackspace/Objects/AutoScale/Policy.cs | 63 + .../Objects/AutoScale/PolicyConfiguration.cs | 324 + .../Rackspace/Objects/AutoScale/PolicyId.cs | 42 + .../Rackspace/Objects/AutoScale/PolicyType.cs | 100 + .../Objects/AutoScale/ScalingGroup.cs | 80 + .../AutoScale/ScalingGroupConfiguration.cs | 37 + .../AutoScale/ScalingGroupConfiguration`1.cs | 120 + .../Objects/AutoScale/ScalingGroupId.cs | 42 + .../Objects/AutoScale/ServerArgument.cs | 217 + .../AutoScale/ServerLaunchArguments.cs | 104 + .../AutoScale/ServerLaunchConfiguration.cs | 37 + .../AutoScale/ServerNetworkArgument.cs | 60 + .../AutoScale/UpdateWebhookConfiguration.cs | 79 + .../Rackspace/Objects/AutoScale/Webhook.cs | 63 + .../Objects/AutoScale/WebhookConfiguration.cs | 84 + .../Rackspace/Objects/AutoScale/WebhookId.cs | 42 + .../Objects/BulkDeletionFailedObject.cs | 51 + .../Rackspace/Objects/BulkDeletionResults.cs | 42 + .../Rackspace/Objects/Credentials.cs | 61 + .../Rackspace/Objects/Databases/Backup.cs | 165 + .../Objects/Databases/BackupConfiguration.cs | 103 + .../Rackspace/Objects/Databases/BackupId.cs | 42 + .../Objects/Databases/BackupStatus.cs | 126 + .../Rackspace/Objects/Databases/Database.cs | 42 + .../Databases/DatabaseConfiguration.cs | 105 + .../Objects/Databases/DatabaseFlavor.cs | 125 + .../Objects/Databases/DatabaseInstance.cs | 184 + .../DatabaseInstanceConfiguration.cs | 139 + .../Objects/Databases/DatabaseInstanceId.cs | 42 + .../Databases/DatabaseInstanceStatus.cs | 162 + .../Objects/Databases/DatabaseName.cs | 42 + .../Objects/Databases/DatabaseUser.cs | 24 + .../Databases/DatabaseVolumeConfiguration.cs | 83 + .../Rackspace/Objects/Databases/FlavorId.cs | 42 + .../Rackspace/Objects/Databases/FlavorRef.cs | 42 + .../Objects/Databases/NamespaceDoc.cs | 14 + .../Objects/Databases/RestorePoint.cs | 54 + .../Rackspace/Objects/Databases/RootUser.cs | 60 + .../Databases/UpdateUserConfiguration.cs | 96 + .../Objects/Databases/UserConfiguration.cs | 207 + .../Rackspace/Objects/Databases/UserName.cs | 126 + .../Rackspace/Objects/Dns/DnsChange.cs | 91 + .../Rackspace/Objects/Dns/DnsConfiguration.cs | 74 + .../Rackspace/Objects/Dns/DnsDomain.cs | 274 + .../Rackspace/Objects/Dns/DnsDomainChange.cs | 161 + .../Rackspace/Objects/Dns/DnsDomainChanges.cs | 125 + .../Objects/Dns/DnsDomainConfiguration.cs | 326 + .../Dns/DnsDomainRecordConfiguration.cs | 195 + .../Dns/DnsDomainRecordUpdateConfiguration.cs | 186 + .../Dns/DnsDomainUpdateConfiguration.cs | 130 + .../Rackspace/Objects/Dns/DnsDomains.cs | 57 + .../Providers/Rackspace/Objects/Dns/DnsJob.cs | 193 + .../Rackspace/Objects/Dns/DnsJobStatus.cs | 113 + .../Rackspace/Objects/Dns/DnsJob`1.cs | 50 + .../Rackspace/Objects/Dns/DnsNameserver.cs | 49 + .../Rackspace/Objects/Dns/DnsRateLimit.cs | 139 + .../Objects/Dns/DnsRateLimitPattern.cs | 97 + .../Rackspace/Objects/Dns/DnsRateLimitUnit.cs | 88 + .../Rackspace/Objects/Dns/DnsRateLimits.cs | 55 + .../Rackspace/Objects/Dns/DnsRecord.cs | 220 + .../Rackspace/Objects/Dns/DnsRecordType.cs | 161 + .../Rackspace/Objects/Dns/DnsRecordsList.cs | 52 + .../Rackspace/Objects/Dns/DnsServiceLimits.cs | 75 + .../Rackspace/Objects/Dns/DnsSubdomain.cs | 154 + .../Objects/Dns/DnsSubdomainConfiguration.cs | 109 + .../Objects/Dns/DnsSubdomainsList.cs | 52 + .../Objects/Dns/DnsUpdateConfiguration.cs | 74 + .../Rackspace/Objects/Dns/DomainId.cs | 42 + .../Rackspace/Objects/Dns/ExportedDomain.cs | 72 + .../Providers/Rackspace/Objects/Dns/JobId.cs | 42 + .../Rackspace/Objects/Dns/LimitType.cs | 103 + .../Rackspace/Objects/Dns/NamespaceDoc.cs | 13 + .../Rackspace/Objects/Dns/RecordId.cs | 42 + .../Rackspace/Objects/Dns/SerializedDomain.cs | 88 + .../Objects/Dns/SerializedDomainFormat.cs | 75 + .../Providers/Rackspace/Objects/Domain.cs | 42 + .../Objects/LoadBalancers/AccessType.cs | 90 + .../LoadBalancers/ConnectionHealthMonitor.cs | 43 + .../LoadBalancers/ConnectionThrottles.cs | 146 + .../LoadBalancers/CustomHealthMonitor.cs | 22 + .../Objects/LoadBalancers/HealthMonitor.cs | 160 + .../LoadBalancers/HealthMonitorType.cs | 101 + .../Objects/LoadBalancers/LoadBalancer.cs | 194 + .../LoadBalancers/LoadBalancerCluster.cs | 43 + .../LoadBalancerConfiguration.cs | 73 + .../LoadBalancerConfiguration`1.cs | 403 + .../LoadBalancers/LoadBalancerEnabledFlag.cs | 86 + .../Objects/LoadBalancers/LoadBalancerId.cs | 42 + .../LoadBalancers/LoadBalancerMetadataItem.cs | 106 + .../LoadBalancerSslConfiguration.cs | 198 + .../LoadBalancers/LoadBalancerStatistics.cs | 111 + .../LoadBalancers/LoadBalancerStatus.cs | 160 + .../LoadBalancers/LoadBalancerTimestamp.cs | 44 + .../LoadBalancers/LoadBalancerUpdate.cs | 158 + .../LoadBalancers/LoadBalancerUsage.cs | 268 + .../LoadBalancers/LoadBalancerUsageId.cs | 42 + .../LoadBalancerVirtualAddress.cs | 171 + .../LoadBalancerVirtualAddressType.cs | 101 + .../LoadBalancers/LoadBalancingAlgorithm.cs | 133 + .../LoadBalancers/LoadBalancingProtocol.cs | 87 + .../Objects/LoadBalancers/MetadataId.cs | 42 + .../Objects/LoadBalancers/NamespaceDoc.cs | 13 + .../Objects/LoadBalancers/NetworkItem.cs | 125 + .../Objects/LoadBalancers/NetworkItemId.cs | 42 + .../Rackspace/Objects/LoadBalancers/Node.cs | 66 + .../Objects/LoadBalancers/NodeCondition.cs | 108 + .../LoadBalancers/NodeConfiguration.cs | 195 + .../Rackspace/Objects/LoadBalancers/NodeId.cs | 42 + .../Objects/LoadBalancers/NodeServiceEvent.cs | 260 + .../LoadBalancers/NodeServiceEventCategory.cs | 66 + .../LoadBalancers/NodeServiceEventId.cs | 42 + .../LoadBalancers/NodeServiceEventSeverity.cs | 66 + .../LoadBalancers/NodeServiceEventType.cs | 66 + .../Objects/LoadBalancers/NodeStatus.cs | 77 + .../Objects/LoadBalancers/NodeType.cs | 89 + .../Objects/LoadBalancers/NodeUpdate.cs | 97 + .../Request/AddLoadBalancerMetadataRequest.cs | 31 + .../LoadBalancers/Request/AddNodesRequest.cs | 24 + .../Request/CreateAccessListRequest.cs | 24 + .../Request/CreateLoadBalancerRequest.cs | 22 + .../LoadBalancers/Request/NamespaceDoc.cs | 14 + ...SetLoadBalancerConnectionLoggingRequest.cs | 16 + .../SetLoadBalancerContentCachingRequest.cs | 16 + .../SetLoadBalancerErrorPageRequest.cs | 16 + .../UpdateLoadBalancerMetadataItemRequest.cs | 49 + .../Request/UpdateLoadBalancerNodeRequest.cs | 42 + .../Request/UpdateLoadBalancerRequest.cs | 18 + .../Response/GetAccessListResponse.cs | 36 + ...etLoadBalancerConnectionLoggingResponse.cs | 38 + .../GetLoadBalancerContentCachingResponse.cs | 38 + .../GetLoadBalancerErrorPageResponse.cs | 74 + .../GetLoadBalancerMetadataItemResponse.cs | 35 + .../Response/GetLoadBalancerNodeResponse.cs | 32 + .../Response/GetLoadBalancerResponse.cs | 32 + ...GetLoadBalancerSslConfigurationResponse.cs | 32 + .../Response/ListAllowedDomainsResponse.cs | 92 + .../ListLoadBalancerMetadataResponse.cs | 48 + .../Response/ListLoadBalancerNodesResponse.cs | 48 + .../ListLoadBalancerThrottlesResponse.cs | 43 + .../Response/ListLoadBalancerUsageResponse.cs | 47 + .../Response/ListLoadBalancersResponse.cs | 47 + .../ListLoadBalancingAlgorithmsResponse.cs | 72 + .../ListLoadBalancingProtocolsResponse.cs | 47 + .../Response/ListNodeServiceEventsResponse.cs | 47 + .../Response/ListVirtualAddressesResponse.cs | 36 + .../LoadBalancers/Response/NamespaceDoc.cs | 14 + .../LoadBalancers/SessionPersistence.cs | 107 + .../LoadBalancers/SessionPersistenceType.cs | 92 + .../Objects/LoadBalancers/VirtualAddressId.cs | 42 + .../LoadBalancers/WebServerHealthMonitor.cs | 184 + .../Mapping/BulkDeletionResultMapper.cs | 60 + .../Rackspace/Objects/Mapping/NamespaceDoc.cs | 15 + .../Monitoring/AccountConfiguration.cs | 116 + .../Rackspace/Objects/Monitoring/Agent.cs | 61 + .../Objects/Monitoring/AgentConnection.cs | 157 + .../Objects/Monitoring/AgentConnectionId.cs | 42 + .../Rackspace/Objects/Monitoring/AgentId.cs | 42 + .../Objects/Monitoring/AgentToken.cs | 60 + .../Monitoring/AgentTokenConfiguration.cs | 52 + .../Objects/Monitoring/AgentTokenId.cs | 42 + .../Rackspace/Objects/Monitoring/Alarm.cs | 77 + .../Objects/Monitoring/AlarmChangelog.cs | 129 + .../Objects/Monitoring/AlarmChangelogId.cs | 42 + .../Objects/Monitoring/AlarmConfiguration.cs | 167 + .../Rackspace/Objects/Monitoring/AlarmData.cs | 78 + .../Objects/Monitoring/AlarmExample.cs | 136 + .../Objects/Monitoring/AlarmExampleField.cs | 77 + .../Objects/Monitoring/AlarmExampleId.cs | 42 + .../Rackspace/Objects/Monitoring/AlarmId.cs | 42 + .../AlarmNotificationHistoryItem.cs | 176 + .../AlarmNotificationHistoryItemId.cs | 42 + .../Objects/Monitoring/AlarmState.cs | 100 + .../Objects/Monitoring/AlarmStateHistory.cs | 164 + .../Rackspace/Objects/Monitoring/Audit.cs | 289 + .../Rackspace/Objects/Monitoring/AuditId.cs | 42 + .../Objects/Monitoring/BoundAlarmExample.cs | 47 + .../Rackspace/Objects/Monitoring/Check.cs | 98 + .../Objects/Monitoring/CheckConfiguration.cs | 304 + .../Rackspace/Objects/Monitoring/CheckData.cs | 258 + .../Objects/Monitoring/CheckDetails.cs | 86 + .../Rackspace/Objects/Monitoring/CheckId.cs | 42 + .../Objects/Monitoring/CheckMetricType.cs | 100 + .../Objects/Monitoring/CheckTarget.cs | 67 + .../Objects/Monitoring/CheckTargetId.cs | 42 + .../Rackspace/Objects/Monitoring/CheckType.cs | 82 + .../Objects/Monitoring/CheckTypeId.cs | 345 + .../Objects/Monitoring/CheckTypeType.cs | 88 + .../Monitoring/ConnectionCheckDetails.cs | 55 + .../Objects/Monitoring/CpuCheckDetails.cs | 31 + .../Objects/Monitoring/CpuInformation.cs | 290 + .../Rackspace/Objects/Monitoring/DataPoint.cs | 135 + .../Monitoring/DataPointGranularity.cs | 136 + .../Objects/Monitoring/DataPointStatistic.cs | 124 + .../Monitoring/DateTimeOffsetExtensions.cs | 74 + .../Objects/Monitoring/DiskCheckDetails.cs | 68 + .../Objects/Monitoring/DiskInformation.cs | 173 + .../Objects/Monitoring/DnsCheckDetails.cs | 96 + .../Monitoring/EmailNotificationDetails.cs | 67 + .../Rackspace/Objects/Monitoring/Entity.cs | 72 + .../Objects/Monitoring/EntityConfiguration.cs | 149 + .../Rackspace/Objects/Monitoring/EntityId.cs | 42 + .../Objects/Monitoring/EntityOverview.cs | 107 + .../Monitoring/FilesystemCheckDetails.cs | 68 + .../Monitoring/FilesystemInformation.cs | 197 + .../Monitoring/FtpBannerCheckDetails.cs | 45 + .../Objects/Monitoring/GenericCheckDetails.cs | 73 + .../Monitoring/GenericNotificationDetails.cs | 73 + .../Objects/Monitoring/HostInformationType.cs | 168 + .../Objects/Monitoring/HostInformation`1.cs | 62 + .../Objects/Monitoring/HttpCheckDetails.cs | 264 + .../Monitoring/ImapBannerCheckDetails.cs | 46 + .../Monitoring/LoadAverageCheckDetails.cs | 31 + .../Objects/Monitoring/LoginInformation.cs | 96 + .../Objects/Monitoring/MemoryCheckDetails.cs | 31 + .../Objects/Monitoring/MemoryInformation.cs | 203 + .../Rackspace/Objects/Monitoring/Metric.cs | 52 + .../Objects/Monitoring/MetricName.cs | 42 + .../Objects/Monitoring/MonitoringAccount.cs | 46 + .../Objects/Monitoring/MonitoringAccountId.cs | 42 + .../Objects/Monitoring/MonitoringLimits.cs | 145 + .../Objects/Monitoring/MonitoringZone.cs | 112 + .../Objects/Monitoring/MonitoringZoneId.cs | 42 + .../Monitoring/MssqlBannerCheckDetails.cs | 46 + .../Monitoring/MysqlBannerCheckDetails.cs | 46 + .../Objects/Monitoring/NamespaceDoc.cs | 13 + .../Objects/Monitoring/NetworkCheckDetails.cs | 68 + .../Monitoring/NetworkInterfaceInformation.cs | 408 + .../Monitoring/NewAlarmConfiguration.cs | 53 + .../Monitoring/NewCheckConfiguration.cs | 88 + .../Monitoring/NewEntityConfiguration.cs | 50 + .../NewNotificationConfiguration.cs | 58 + .../NewNotificationPlanConfiguration.cs | 54 + .../Objects/Monitoring/Notification.cs | 78 + .../Objects/Monitoring/NotificationAttempt.cs | 61 + .../Monitoring/NotificationConfiguration.cs | 144 + .../Objects/Monitoring/NotificationData.cs | 61 + .../Objects/Monitoring/NotificationDetails.cs | 54 + .../Objects/Monitoring/NotificationId.cs | 42 + .../Objects/Monitoring/NotificationPlan.cs | 79 + .../NotificationPlanConfiguration.cs | 188 + .../Objects/Monitoring/NotificationPlanId.cs | 42 + .../Objects/Monitoring/NotificationResult.cs | 156 + .../Objects/Monitoring/NotificationType.cs | 65 + .../Monitoring/NotificationTypeField.cs | 77 + .../Objects/Monitoring/NotificationTypeId.cs | 90 + .../PagerDutyNotificationDetails.cs | 67 + .../Objects/Monitoring/PingCheckDetails.cs | 65 + .../Objects/Monitoring/PluginCheckDetails.cs | 121 + .../Objects/Monitoring/Pop3CheckDetails.cs | 46 + .../PostgresqlBannerCheckDetails.cs | 46 + .../Objects/Monitoring/ProcessInformation.cs | 409 + .../Monitoring/ReadOnlyCollectionPage`2.cs | 111 + .../SecureConnectionCheckDetails.cs | 52 + .../Monitoring/SmtpBannerCheckDetails.cs | 46 + .../Objects/Monitoring/SmtpCheckDetails.cs | 140 + .../Objects/Monitoring/SshCheckDetails.cs | 45 + .../Objects/Monitoring/SystemInformation.cs | 112 + .../Objects/Monitoring/TargetResolverType.cs | 88 + .../Objects/Monitoring/TcpCheckDetails.cs | 103 + .../Monitoring/TelnetBannerCheckDetails.cs | 65 + .../Monitoring/TestAlarmConfiguration.cs | 98 + .../Objects/Monitoring/TraceRoute.cs | 49 + .../Monitoring/TraceRouteConfiguration.cs | 98 + .../Objects/Monitoring/TraceRouteHop.cs | 138 + .../Objects/Monitoring/TransactionId.cs | 41 + .../Monitoring/UpdateAlarmConfiguration.cs | 44 + .../Monitoring/UpdateCheckConfiguration.cs | 68 + .../Monitoring/UpdateEntityConfiguration.cs | 47 + .../UpdateNotificationConfiguration.cs | 48 + .../UpdateNotificationPlanConfiguration.cs | 51 + .../Monitoring/WebhookNotificationDetails.cs | 67 + .../Objects/Monitoring/WebhookToken.cs | 42 + .../Rackspace/Objects/NamespaceDoc.cs | 13 + .../Rackspace/Objects/Queues/NamespaceDoc.cs | 13 + .../Queues/Request/ClaimMessagesRequest.cs | 28 + .../Objects/Queues/Request/NamespaceDoc.cs | 14 + .../ListCloudQueueMessagesResponse.cs | 52 + .../Response/ListCloudQueuesResponse.cs | 45 + .../Objects/Queues/Response/NamespaceDoc.cs | 14 + .../Objects/RackspaceCloudIdentity.cs | 52 + .../Objects/Request/AddRoleRequest.cs | 35 + .../AddServiceCatalogEndpointRequest.cs | 75 + .../Objects/Request/AddUserRequest.cs | 35 + .../Request/AttachServerVolumeRequest.cs | 86 + .../Rackspace/Objects/Request/AuthRequest.cs | 46 + .../ChangeServerAdminPasswordRequest.cs | 64 + .../Request/ConfirmServerResizeRequest.cs | 18 + .../CreateCloudBlockStorageSnapshotDetails.cs | 65 + .../CreateCloudBlockStorageSnapshotRequest.cs | 34 + .../CreateCloudBlockStorageVolumeDetails.cs | 70 + .../CreateCloudBlockStorageVolumeRequest.cs | 34 + .../Request/CreateCloudNetworkRequest.cs | 34 + .../Request/CreateCloudNetworksDetails.cs | 58 + .../Request/CreateServerImageDetails.cs | 47 + .../Request/CreateServerImageRequest.cs | 34 + .../Objects/Request/CreateServerRequest.cs | 166 + .../Request/CreateVirtualInterfaceRequest.cs | 70 + .../Rackspace/Objects/Request/NamespaceDoc.cs | 14 + .../Objects/Request/PasswordCredential.cs | 65 + .../Rackspace/Objects/Request/Personality.cs | 121 + .../Objects/Request/RescueServerRequest.cs | 18 + .../Request/RevertServerResizeRequest.cs | 18 + .../Objects/Request/ServerRebootDetails.cs | 40 + .../Objects/Request/ServerRebootRequest.cs | 34 + .../Objects/Request/ServerRebuildDetails.cs | 136 + .../Objects/Request/ServerRebuildRequest.cs | 34 + .../Objects/Request/ServerResizeDetails.cs | 67 + .../Objects/Request/ServerResizeRequest.cs | 34 + .../Objects/Request/SetPasswordRequest.cs | 52 + .../Objects/Request/UnrescueServerRequest.cs | 18 + .../Request/UpdateMetadataItemRequest.cs | 48 + .../Objects/Request/UpdateMetadataRequest.cs | 36 + .../Objects/Request/UpdateServerRequest.cs | 97 + .../Request/UpdateUserCredentialRequest.cs | 52 + .../Objects/Request/UpdateUserRequest.cs | 38 + .../Response/AuthenticationResponse.cs | 21 + .../Objects/Response/BulkDeleteResponse.cs | 32 + .../Objects/Response/CloudNetworkResponse.cs | 21 + .../Objects/Response/CreateServerResponse.cs | 20 + .../Objects/Response/ExtractArchiveError.cs | 65 + .../Response/ExtractArchiveResponse.cs | 132 + .../Objects/Response/FlavorDetailsResponse.cs | 20 + .../GetCloudBlockStorageSnapshotResponse.cs | 21 + .../GetCloudBlockStorageVolumeResponse.cs | 21 + .../GetCloudBlockStorageVolumeTypeResponse.cs | 20 + .../Objects/Response/GetEndpointResponse.cs | 52 + .../Response/GetImageDetailsResponse.cs | 20 + .../Objects/Response/ListAddressesResponse.cs | 20 + .../Response/ListCloudNetworksResponse.cs | 20 + .../Objects/Response/ListEndpointsResponse.cs | 22 + .../Response/ListFlavorDetailsResponse.cs | 20 + .../Objects/Response/ListFlavorsResponse.cs | 20 + .../Response/ListImagesDetailsResponse.cs | 20 + .../Objects/Response/ListImagesResponse.cs | 20 + .../Objects/Response/ListServersResponse.cs | 26 + .../Objects/Response/ListSnapshotResponse.cs | 20 + .../Response/ListVirtualInterfacesResponse.cs | 22 + .../Objects/Response/ListVolumeResponse.cs | 20 + .../Response/ListVolumeTypeResponse.cs | 20 + .../Objects/Response/MetaDataResponse.cs | 20 + .../Objects/Response/MetadataItemResponse.cs | 21 + .../Objects/Response/NamespaceDoc.cs | 14 + .../Objects/Response/NewUserResponse.cs | 21 + .../Response/PasswordCredentialResponse.cs | 21 + .../Objects/Response/RescueServerResponse.cs | 20 + .../Objects/Response/RoleResponse.cs | 21 + .../Objects/Response/RolesResponse.cs | 27 + .../Objects/Response/ServerDetailsResponse.cs | 22 + .../Response/ServerVolumeListResponse.cs | 21 + .../Objects/Response/ServerVolumeResponse.cs | 21 + .../Objects/Response/TenantsResponse.cs | 20 + .../Response/UserCredentialResponse.cs | 21 + .../Response/UserImpersonationResponse.cs | 43 + .../Objects/Response/UserResponse.cs | 28 + .../Objects/Response/UsersResponse.cs | 20 + .../Providers/Rackspace/ProviderBase`1.cs | 1408 +++ .../RackspaceImpersonationIdentity.cs | 21 + .../Validators/CloudBlockStorageValidator.cs | 40 + .../Validators/CloudFilesValidator.cs | 58 + .../Validators/CloudNetworksValidator.cs | 74 + .../Validators/HttpResponseCodeValidator.cs | 66 + .../Rackspace/Validators/NamespaceDoc.cs | 14 + .../Rackspace/WebRequestEventArgs.cs | 44 + .../Rackspace/WebResponseEventArgs.cs | 44 + .../Serialization/DefaultJsonConverter.cs | 76 + .../corelib/Serialization/IHaveExtraData.cs | 45 + .../src/corelib/Serialization/IPageBuilder.cs | 17 + .../src/corelib/Serialization/IPageLink.cs | 12 + .../Serialization/IQueryStringBuilder.cs | 11 + .../corelib/Serialization/IServiceResource.cs | 139 + .../Serialization/IdentifierConverter.cs | 40 + .../JsonConverterWithConstructorAttribute.cs | 63 + .../OpenStackContractResolver.cs | 67 + .../src/corelib/Serialization/Page.cs | 76 + .../src/corelib/Serialization/PageLink.cs | 38 + .../Serialization/ResourceCollection.cs | 61 + .../corelib/Serialization/ResourceStatus.cs | 9 + .../Serialization/RootWrapperConverter.cs | 75 + .../corelib/Serialization/ServiceEndpoint.cs | 329 + .../Serialization/StringEnumeration.cs | 106 + .../StringEnumerationConverter.cs | 32 + .../TimeSpanInSecondsConverter.cs | 32 + .../Serialization/TolerantEnumConverter.cs | 65 + .../src/corelib/Testing/HttpTest.cs | 62 + .../src/corelib/Testing/NamespaceDoc.cs | 12 + .../openstack.net/src/corelib/app.config | 10 + .../openstack.net/src/corelib/corelib.nuspec | 33 + .../src/corelib/paket.references | 6 + .../Security.Cryptography.dll | Bin 0 -> 95232 bytes .../Security.Cryptography.pdb | Bin 0 -> 265728 bytes .../Security.Cryptography.xml | 3318 ++++++ .../openstack.net/src/openstack.net.vsmdi | 122 + .../src/testing/integration/App.config | 26 + .../v2/BlockStorageTestDataManager.cs | 64 + .../src/testing/integration/Bootstrapper.cs | 140 + .../Compute/v2_1/ComputeServiceTests.cs | 42 + .../Compute/v2_1/ComputeTestDataManager.cs | 295 + .../integration/Compute/v2_1/FlavorTests.cs | 77 + .../integration/Compute/v2_1/ImageTests.cs | 144 + .../integration/Compute/v2_1/KeyPairTests.cs | 96 + .../v2_1/Operator/ComputeServiceTests.cs | 49 + .../Compute/v2_1/Operator/ServerTests.cs | 65 + .../Compute/v2_1/SecurityGroupTests.cs | 105 + .../Compute/v2_1/ServerGroupTests.cs | 66 + .../integration/Compute/v2_1/ServerTests.cs | 473 + .../integration/Compute/v2_1/VolumeTests.cs | 117 + .../v1/ContentDeliveryNetworkServiceTests.cs | 32 + .../v1/ServiceTests.cs | 150 + .../integration/Identity/v2/IdentityTests.cs | 32 + .../v2/Layer3/Layer3ExtensionTests.cs | 108 + .../Networking/v2/NetworkingServiceTests.cs | 321 + .../v2/NetworkingTestDataManager.cs | 218 + .../ObjectStorage/CloudFilesProviderTests.cs | 88 + .../OpenStack.IntegrationTests.csproj | 2770 +++++ .../integration/Properties/AssemblyInfo.cs | 39 + .../src/testing/integration/TestData.cs | 12 + .../integration/TestIdentityProvider.cs | 88 + .../testing/integration/XunitTraceListener.cs | 41 + .../src/testing/integration/paket.references | 14 + .../src/testing/migration/App.config | 6 + .../src/testing/migration/Program.cs | 39 + .../migration/Properties/AssemblyInfo.cs | 36 + .../src/testing/migration/migration.csproj | 118 + .../src/testing/migration/paket.references | 2 + .../src/testing/unit/AuthenticationTests.cs | 39 + .../unit/Compute/v2_1/ComputeServiceTests.cs | 88 + .../testing/unit/Compute/v2_1/FlavorTests.cs | 101 + .../testing/unit/Compute/v2_1/ImageTests.cs | 346 + .../testing/unit/Compute/v2_1/KeyPairTests.cs | 136 + .../v2_1/Operator/ComputeServiceTests.cs | 89 + .../unit/Compute/v2_1/Operator/ServerTests.cs | 42 + .../unit/Compute/v2_1/SecurityGroupTests.cs | 257 + .../unit/Compute/v2_1/ServerAddressTests.cs | 65 + .../unit/Compute/v2_1/ServerGroupTests.cs | 94 + .../testing/unit/Compute/v2_1/ServerTests.cs | 856 ++ .../testing/unit/Compute/v2_1/VolumeTests.cs | 234 + .../unit/Compute/v2_2/ConsoleTypeTests.cs | 13 + .../testing/unit/Compute/v2_6/ServerTests.cs | 33 + .../v1/ContentDeliveryNetworkServiceTests.cs | 23 + .../ContentDeliveryNetworks/v1/FlavorTests.cs | 56 + .../v1/ServiceCacheTests.cs | 32 + .../ServiceOperationFailedExceptionTests.cs | 28 + .../v1/ServiceTests.cs | 302 + .../NetworkAddressDeserializationTests.cs | 96 + .../Extensions/EnumerableExtensionsTests.cs | 15 + .../unit/Extensions/TypeExtensionsTests.cs | 41 + .../src/testing/unit/IdentifierTests.cs | 73 + .../Networking/v2/DHCPOptionConverterTests.cs | 62 + .../unit/Networking/v2/Layer3/Layer3Tests.cs | 368 + .../unit/Networking/v2/NetworkTests.cs | 133 + .../testing/unit/Networking/v2/PortTests.cs | 152 + .../testing/unit/Networking/v2/SubnetTests.cs | 139 + .../testing/unit/OpenStack.UnitTests.csproj | 2720 +++++ .../src/testing/unit/OpenStackNetTests.cs | 141 + .../testing/unit/Properties/AssemblyInfo.cs | 40 + .../Rackspace/CloudBlockStorageTests.cs | 65 + .../Rackspace/CloudNetworksValidatorTests.cs | 198 + .../Rackspace/EncodeDecodeProviderTests.cs | 54 + .../Rackspace/IdentityProviderCacheTests.cs | 55 + .../Providers/Rackspace/JsonModelTests.cs | 233 + .../Rackspace/ObjectProviderHelperTests.cs | 124 + .../Providers/Rackspace/ProviderBaseTests.cs | 310 + .../Providers/Rackspace/SerializationTests.cs | 63 + .../Serialization/EmptyEnumerableTests.cs | 43 + .../RootWrapperConverterTests.cs | 86 + .../TolerantEnumConverterTests.cs | 65 + .../openstack.net/src/testing/unit/Stubs.cs | 34 + .../src/testing/unit/TestCategories.cs | 11 + .../src/testing/unit/XunitTraceListener.cs | 41 + .../openstack.net/src/testing/unit/app.config | 20 + .../src/testing/unit/paket.references | 13 + .../.paket/paket.bootstrapper.exe | Bin 0 -> 28208 bytes redistributable/rackspace-net-sdk/LICENSE | 203 + redistributable/rackspace-net-sdk/README.md | 45 + redistributable/rackspace-net-sdk/build.cmd | 40 + redistributable/rackspace-net-sdk/build.sh | 2 + .../rackspace-net-sdk/build/Rackspace.nuspec | 33 + .../rackspace-net-sdk/build/build.proj | 100 + .../build/check-nuget-version-exists.ps1 | 13 + .../build/rackspace-logo.png | Bin 0 -> 7374 bytes .../docs/ContentLayout.content | 2 + .../docs/Rackspace.Docs.shfbproj | 185 + .../rackspace-net-sdk/docs/icons/Help.png | Bin 0 -> 4942 bytes .../Newtonsoft.Json.6.0.1/.signature.p7s | Bin 0 -> 9464 bytes .../Newtonsoft.Json.6.0.1.nupkg | Bin 0 -> 1493979 bytes .../lib/net20/Newtonsoft.Json.dll | Bin 0 -> 488448 bytes .../lib/net20/Newtonsoft.Json.xml | 9054 +++++++++++++++++ .../lib/net35/Newtonsoft.Json.dll | Bin 0 -> 425472 bytes .../lib/net35/Newtonsoft.Json.xml | 8197 +++++++++++++++ .../lib/net40/Newtonsoft.Json.dll | Bin 0 -> 481792 bytes .../lib/net40/Newtonsoft.Json.xml | 8472 +++++++++++++++ .../lib/net45/Newtonsoft.Json.dll | Bin 0 -> 491008 bytes .../lib/net45/Newtonsoft.Json.xml | 8472 +++++++++++++++ .../lib/netcore45/Newtonsoft.Json.dll | Bin 0 -> 440320 bytes .../lib/netcore45/Newtonsoft.Json.xml | 8029 +++++++++++++++ .../Newtonsoft.Json.dll | Bin 0 -> 374784 bytes .../Newtonsoft.Json.xml | 7625 ++++++++++++++ .../Newtonsoft.Json.dll | Bin 0 -> 431616 bytes .../Newtonsoft.Json.xml | 7997 +++++++++++++++ .../Newtonsoft.Json.6.0.1/tools/install.ps1 | 93 + .../System.Buffers.4.5.1/.signature.p7s | Bin 0 -> 18702 bytes .../System.Buffers.4.5.1.nupkg | Bin 0 -> 93737 bytes .../lib/net461/System.Buffers.dll | Bin 0 -> 20856 bytes .../lib/net461/System.Buffers.xml | 38 + .../lib/netcoreapp2.0/_._ | 0 .../lib/netstandard1.1/System.Buffers.dll | Bin 0 -> 20864 bytes .../lib/netstandard1.1/System.Buffers.xml | 38 + .../lib/netstandard2.0/System.Buffers.dll | Bin 0 -> 21376 bytes .../lib/netstandard2.0/System.Buffers.xml | 38 + .../lib/uap10.0.16299/_._ | 0 .../ref/net45/System.Buffers.dll | Bin 0 -> 14720 bytes .../ref/net45/System.Buffers.xml | 38 + .../ref/netcoreapp2.0/_._ | 0 .../ref/netstandard1.1/System.Buffers.dll | Bin 0 -> 14712 bytes .../ref/netstandard1.1/System.Buffers.xml | 38 + .../ref/netstandard2.0/System.Buffers.dll | Bin 0 -> 14720 bytes .../ref/netstandard2.0/System.Buffers.xml | 38 + .../ref/uap10.0.16299/_._ | 0 .../.signature.p7s | Bin 0 -> 18703 bytes .../Icon.png | Bin 0 -> 1371 bytes ...m.Diagnostics.DiagnosticSource.4.7.1.nupkg | Bin 0 -> 176397 bytes .../System.Diagnostics.DiagnosticSource.dll | Bin 0 -> 65400 bytes .../System.Diagnostics.DiagnosticSource.xml | 438 + .../System.Diagnostics.DiagnosticSource.dll | Bin 0 -> 66432 bytes .../System.Diagnostics.DiagnosticSource.xml | 438 + .../System.Diagnostics.DiagnosticSource.dll | Bin 0 -> 35712 bytes .../System.Diagnostics.DiagnosticSource.xml | 438 + .../System.Diagnostics.DiagnosticSource.dll | Bin 0 -> 51584 bytes .../System.Diagnostics.DiagnosticSource.xml | 438 + .../System.Diagnostics.DiagnosticSource.dll | Bin 0 -> 35712 bytes .../System.Diagnostics.DiagnosticSource.xml | 438 + .../System.Memory.4.5.4/.signature.p7s | Bin 0 -> 18702 bytes .../System.Memory.4.5.4.nupkg | Bin 0 -> 205467 bytes .../lib/net461/System.Memory.dll | Bin 0 -> 141184 bytes .../lib/net461/System.Memory.xml | 355 + .../System.Memory.4.5.4/lib/netcoreapp2.1/_._ | 0 .../lib/netstandard1.1/System.Memory.dll | Bin 0 -> 137088 bytes .../lib/netstandard1.1/System.Memory.xml | 355 + .../lib/netstandard2.0/System.Memory.dll | Bin 0 -> 141688 bytes .../lib/netstandard2.0/System.Memory.xml | 355 + .../System.Memory.4.5.4/ref/netcoreapp2.1/_._ | 0 .../.signature.p7s | Bin 0 -> 18702 bytes ...untime.CompilerServices.Unsafe.4.5.3.nupkg | Bin 0 -> 102160 bytes ...System.Runtime.CompilerServices.Unsafe.dll | Bin 0 -> 16768 bytes ...System.Runtime.CompilerServices.Unsafe.xml | 200 + ...System.Runtime.CompilerServices.Unsafe.dll | Bin 0 -> 16248 bytes ...System.Runtime.CompilerServices.Unsafe.xml | 200 + ...System.Runtime.CompilerServices.Unsafe.dll | Bin 0 -> 16768 bytes ...System.Runtime.CompilerServices.Unsafe.xml | 200 + ...System.Runtime.CompilerServices.Unsafe.dll | Bin 0 -> 16768 bytes ...System.Runtime.CompilerServices.Unsafe.xml | 200 + ...System.Runtime.CompilerServices.Unsafe.dll | Bin 0 -> 16248 bytes ...System.Runtime.CompilerServices.Unsafe.xml | 200 + ...System.Runtime.CompilerServices.Unsafe.dll | Bin 0 -> 16248 bytes ...System.Runtime.CompilerServices.Unsafe.xml | 200 + ...System.Runtime.CompilerServices.Unsafe.dll | Bin 0 -> 16976 bytes ...System.Runtime.CompilerServices.Unsafe.xml | 200 + .../System.ValueTuple.4.3.0/.signature.p7s | Bin 0 -> 9482 bytes .../System.ValueTuple.4.3.0.nupkg | Bin 0 -> 87411 bytes .../lib/netstandard1.0/.xml | 1299 +++ .../lib/netstandard1.0/System.ValueTuple.dll | Bin 0 -> 77672 bytes .../lib/portable-net40+sl4+win8+wp8/.xml | 1299 +++ .../System.ValueTuple.dll | Bin 0 -> 78024 bytes .../rackspace-net-sdk/paket.dependencies | 16 + .../AssignPublicIPSamples.cs | 68 + .../Rackspace.Samples/CloudNetworkSamples.cs | 78 + .../samples/Rackspace.Samples/ISample.cs | 7 + .../samples/Rackspace.Samples/Program.cs | 52 + .../Properties/AssemblyInfo.cs | 36 + .../Rackspace.Samples.csproj | 121 + .../samples/Rackspace.Samples/app.config | 14 + .../Rackspace.Samples/paket.references | 1 + .../Rackspace/Authentication/ServiceType.cs | 70 + .../src/Rackspace/CloudNetworks/IPVersion.cs | 18 + .../CloudNetworks/v2/AllocationPool.cs | 59 + .../CloudNetworks/v2/CloudNetworkService.cs | 194 + .../v2/CloudNetworkServiceExtensions.cs | 111 + .../Rackspace/CloudNetworks/v2/HostRoute.cs | 59 + .../CloudNetworks/v2/IPAddressAssociation.cs | 100 + .../src/Rackspace/CloudNetworks/v2/Network.cs | 46 + .../CloudNetworks/v2/NetworkDefinition.cs | 19 + .../CloudNetworks/v2/NetworkStatus.cs | 25 + .../src/Rackspace/CloudNetworks/v2/Port.cs | 39 + .../CloudNetworks/v2/PortCreateDefinition.cs | 45 + .../Rackspace/CloudNetworks/v2/PortStatus.cs | 31 + .../CloudNetworks/v2/PortUpdateDefinition.cs | 41 + .../v2/Serialization/NetworkCollection.cs | 34 + .../v2/Serialization/PortCollection.cs | 34 + .../v2/Serialization/SubnetCollection.cs | 34 + .../src/Rackspace/CloudNetworks/v2/Subnet.cs | 21 + .../v2/SubnetCreateDefinition.cs | 49 + .../v2/SubnetUpdateDefinition.cs | 54 + .../ServiceOperationFailedException.cs | 46 + .../rackspace-net-sdk/src/Rackspace/IPage.cs | 41 + .../src/Rackspace/IServiceResource.cs | 15 + .../src/Rackspace/Identifier.cs | 157 + .../src/Rackspace/NamespaceDoc.cs | 13 + .../src/Rackspace/Properties/AssemblyInfo.cs | 27 + .../RackConnect/v3/ListPublicIPsFilter.cs | 19 + .../RackConnect/v3/NetworkReference.cs | 42 + .../src/Rackspace/RackConnect/v3/PublicIP.cs | 125 + .../v3/PublicIPCreateDefinition.cs | 32 + .../RackConnect/v3/PublicIPExtensions.cs | 43 + .../v3/PublicIPNetworkAssociation.cs | 17 + .../v3/PublicIPServerAssociation.cs | 42 + .../RackConnect/v3/PublicIPStatus.cs | 67 + .../v3/PublicIPUpdateDefinition.cs | 36 + .../RackConnect/v3/RackConnectService.cs | 377 + .../v3/RackConnectServiceExtensions.cs | 76 + .../src/Rackspace/Rackspace.csproj | 21 + .../src/Rackspace/RackspaceNet.cs | 94 + .../Serialization/IdentifierConverter.cs | 39 + .../src/Rackspace/Serialization/Page.cs | 70 + .../src/Rackspace/Testing/HttpTest.cs | 14 + .../v2/CloudNetworkServiceTests.cs | 277 + .../v2/CloudNetworksTestDataManager.cs | 194 + .../v2/CloudServersTestDataManager.cs | 74 + .../v1/ContentDeliveryNetworkServiceTests.cs | 154 + .../Identity/v2/IdentityTests.cs | 32 + .../Properties/AssemblyInfo.cs | 40 + .../RackConnect/v3/RackConnectServiceTests.cs | 127 + .../v3/RackConnectTestDataManager.cs | 84 + .../Rackspace.IntegrationTests.csproj | 113 + .../Rackspace.IntegrationTests/TestData.cs | 12 + .../TestIdentityProvider.cs | 68 + .../XunitTraceListener.cs | 41 + .../Rackspace.IntegrationTests/app.config | 8 + .../v2/CloudNetworkServiceTests.cs | 27 + .../CloudNetworks/v2/NetworkTests.cs | 158 + .../CloudNetworks/v2/PortTests.cs | 160 + .../CloudNetworks/v2/SubnetTests.cs | 159 + .../Rackspace.UnitTests/IdentifierTests.cs | 73 + .../Properties/AssemblyInfo.cs | 37 + .../RackConnect/v3/NetworkTests.cs | 51 + .../RackConnect/v3/PublicIPTests.cs | 312 + .../RackConnect/v3/RackConnectServiceTests.cs | 27 + .../Rackspace.UnitTests.csproj | 118 + .../Rackspace.UnitTests/RackspaceNetTests.cs | 123 + .../test/Rackspace.UnitTests/Stubs.cs | 37 + web/core/ASC.Web.Core/ASC.Web.Core.csproj | 18 +- web/core/ASC.Web.Core/Calendars/BaseEvent.cs | 2 + web/core/ASC.Web.Core/Calendars/IEvent.cs | 1 + .../Client/Bundling/CssTransform.cs | 4 +- .../Client/Bundling/JsTransform.cs | 6 +- web/core/ASC.Web.Core/CookiesManager.cs | 96 +- .../ASC.Web.Core/Files/DocumentService.cs | 40 +- web/core/ASC.Web.Core/Files/FileUtility.cs | 4 +- .../ASC.Web.Core/Files/FilesLinkUtility.cs | 26 + web/core/ASC.Web.Core/Sms/SmsProvider.cs | 23 +- .../ASC.Web.Core/Users/UserPhotoManager.cs | 128 +- .../ASC.Web.Core/Utility/CommonLinkUtility.cs | 16 +- .../ASC.Web.Core/Utility/PasswordSettings.cs | 118 +- web/core/ASC.Web.Core/WebItemSecurity.cs | 8 +- .../WhiteLabel/TenantWhiteLabelSettings.cs | 9 +- .../ASC.Web.Studio/ASC.Web.Studio.csproj | 259 +- web/studio/ASC.Web.Studio/Auth.aspx.cs | 16 +- .../Core/AdminHelperSettings.cs | 28 + .../Core/Backup/BackupAjaxHandler.cs | 9 +- web/studio/ASC.Web.Studio/Core/DebugInfo.cs | 5 +- .../Core/EmailOperationService.cs | 4 +- .../Core/HelpCenter/BaseHelpCenterStorage.cs | 37 +- .../ASC.Web.Studio/Core/Notify/Actions.cs | 7 +- .../Core/Notify/NotifyConfiguration.cs | 7 +- .../Core/Notify/StudioNotifyService.cs | 28 +- .../Core/Notify/StudioNotifySource.cs | 7 +- .../Core/Notify/StudioPeriodicNotify.cs | 109 +- .../Core/Notify/StudioWhatsNewNotify.cs | 10 +- ...WebstudioNotifyPatternResource.Designer.cs | 80 +- ...tudioNotifyPatternResource.az-Latn-AZ.resx | 2089 ++++ .../WebstudioNotifyPatternResource.bg.resx | 12 - .../WebstudioNotifyPatternResource.cs.resx | 16 - .../WebstudioNotifyPatternResource.de.resx | 54 +- .../WebstudioNotifyPatternResource.es.resx | 54 +- .../WebstudioNotifyPatternResource.fi.resx | 3 - .../WebstudioNotifyPatternResource.fr.resx | 33 - .../WebstudioNotifyPatternResource.it.resx | 73 +- .../WebstudioNotifyPatternResource.lv.resx | 16 - .../WebstudioNotifyPatternResource.nl.resx | 16 - .../WebstudioNotifyPatternResource.pl.resx | 16 - .../WebstudioNotifyPatternResource.pt-BR.resx | 88 +- .../WebstudioNotifyPatternResource.resx | 60 +- .../WebstudioNotifyPatternResource.ru.resx | 100 +- .../WebstudioNotifyPatternResource.sk.resx | 16 - .../WebstudioNotifyPatternResource.sv.resx | 3 - .../WebstudioNotifyPatternResource.tr.resx | 16 - .../WebstudioNotifyPatternResource.uk.resx | 16 - .../WebstudioNotifyPatternResource.vi.resx | 23 - .../WebstudioNotifyPatternResource.zh-CN.resx | 16 - .../WebstudioNotifyPatternResource.zh-TW.resx | 35 - .../Core/Notify/webstudio_patterns.xml | 56 +- .../Core/PrivacyRoomSettings.cs | 3 +- web/studio/ASC.Web.Studio/Core/SetupInfo.cs | 17 +- .../ASC.Web.Studio/Core/Sms/SmsManager.cs | 17 +- .../Core/Sms/StudioSmsNotificationSettings.cs | 16 + .../ASC.Web.Studio/Core/Tfa/TfaManager.cs | 7 +- .../Core/Users/AffiliateHelper.cs | 72 - .../NamingPeopleResource.az-Latn-AZ.resx | 47 +- .../Core/Users/ProfileHelper.cs | 2 + .../Core/Users/SocialContactsManager.cs | 6 + .../Core/Users/UserManagerWrapper.cs | 53 +- web/studio/ASC.Web.Studio/Core/WarmUp.cs | 4 +- web/studio/ASC.Web.Studio/DeepLink.aspx | 8 + web/studio/ASC.Web.Studio/DeepLink.aspx.cs | 26 + .../ASC.Web.Studio/DeepLink.aspx.designer.cs | 39 + web/studio/ASC.Web.Studio/Feed.aspx.cs | 9 + .../FCKEditorFileUploadHandler.cs | 129 +- .../HttpHandlers/KeepSessionAliveHandler.cs | 44 - .../HttpHandlers/LicenseUploader.cs | 10 +- .../ASC.Web.Studio/HttpHandlers/SsoHandler.cs | 16 +- .../HttpHandlers/TemplatingHandler.cs | 10 +- web/studio/ASC.Web.Studio/Management.aspx | 5 +- web/studio/ASC.Web.Studio/Management.aspx.cs | 31 +- .../Management.aspx.designer.cs | 33 +- .../Masters/BaseTemplate.master | 12 +- .../Masters/BaseTemplate.master.cs | 19 +- .../Masters/BaseTemplate.master.designer.cs | 18 +- .../Masters/CommonBodyScripts.ascx.cs | 1 + .../Masters/CommonStyles.ascx.cs | 1 + .../ASC.Web.Studio/Masters/FirstScripts.ascx | 1 + .../Masters/FirstScripts.ascx.cs | 43 + web/studio/ASC.Web.Studio/Masters/MainPage.cs | 46 +- .../MasterFileUtilityResources.cs | 3 + .../MasterLocalizationResources.cs | 4 +- web/studio/ASC.Web.Studio/My.aspx | 12 + web/studio/ASC.Web.Studio/My.aspx.cs | 4 +- web/studio/ASC.Web.Studio/My.aspx.designer.cs | 37 +- .../Products/CRM/ASC.Web.CRM.csproj | 26 +- .../CRM/App_Themes/default/css/common.less | 2 +- .../CRM/App_Themes/default/css/contacts.less | 8 +- .../CRM/App_Themes/default/css/invoices.less | 23 + .../CRM/Classes/ContactPhotoManager.cs | 52 +- .../Products/CRM/Classes/TwilioController.cs | 6 +- .../Products/CRM/Classes/VoipEngine.cs | 2 +- .../Controls/Cases/CasesActionView.ascx.cs | 2 +- .../Controls/Common/ImportFromCSVView.ascx.cs | 4 +- .../CRM/Controls/Common/ListBaseView.ascx.cs | 2 +- .../Contacts/ContactActionView.ascx.cs | 7 +- .../CRM/Controls/Deals/DealActionView.ascx | 4 +- .../CRM/Controls/Deals/DealActionView.ascx.cs | 2 +- .../Products/CRM/Core/Dao/ContactDao.cs | 16 +- .../Products/CRM/Core/Dao/CurrencyRateDao.cs | 28 +- .../Products/CRM/Core/Dao/TaskDao.cs | 18 +- .../CRM/Core/Security/FileSecurityProvider.cs | 5 + .../CRM/HttpHandlers/FileHandler.ashx.cs | 6 +- .../HttpHandlers/WebToLeadFromHandler.ashx.cs | 2 +- .../CRMCasesResource.az-Latn-AZ.resx | 8 +- .../Resources/CRMCommonResource.Designer.cs | 9 + .../CRMCommonResource.az-Latn-AZ.resx | 425 +- .../CRM/Resources/CRMCommonResource.resx | 3 + .../CRMContactResource.az-Latn-AZ.resx | 161 +- .../Resources/CRMDealResource.az-Latn-AZ.resx | 20 +- .../Resources/CRMEnumResource.az-Latn-AZ.resx | 191 +- .../Resources/CRMInvoiceResource.Designer.cs | 9 + .../CRMInvoiceResource.az-Latn-AZ.resx | 559 + .../CRM/Resources/CRMInvoiceResource.resx | 3 + .../CRM/Resources/CRMJSResource.Designer.cs | 9 + .../Resources/CRMJSResource.az-Latn-AZ.resx | 98 +- .../Products/CRM/Resources/CRMJSResource.resx | 3 + .../CRMReportResource.az-Latn-AZ.resx | 411 + .../CRMSettingResource.az-Latn-AZ.resx | 83 +- .../CRMSocialMediaResource.az-Latn-AZ.resx | 23 +- .../Resources/CRMTaskResource.az-Latn-AZ.resx | 26 +- .../Resources/CRMVoipResource.az-Latn-AZ.resx | 295 + .../CRMPatternResource.Designer.cs | 12 +- .../CRMPatternResource.ar-AE.resx | 4 +- .../CRMPatternResource.az-Latn-AZ.resx | 98 +- .../NotifyService/CRMPatternResource.bg.resx | 8 +- .../CRMPatternResource.bs-BA.resx | 8 +- .../NotifyService/CRMPatternResource.ca.resx | 8 +- .../NotifyService/CRMPatternResource.cs.resx | 8 +- .../NotifyService/CRMPatternResource.de.resx | 8 +- .../NotifyService/CRMPatternResource.es.resx | 8 +- .../NotifyService/CRMPatternResource.fi.resx | 8 +- .../NotifyService/CRMPatternResource.fr.resx | 8 +- .../NotifyService/CRMPatternResource.id.resx | 8 +- .../NotifyService/CRMPatternResource.it.resx | 8 +- .../NotifyService/CRMPatternResource.ja.resx | 4 +- .../NotifyService/CRMPatternResource.ko.resx | 4 +- .../NotifyService/CRMPatternResource.lt.resx | 8 +- .../NotifyService/CRMPatternResource.lv.resx | 8 +- .../NotifyService/CRMPatternResource.nl.resx | 4 +- .../NotifyService/CRMPatternResource.pl.resx | 8 +- .../CRMPatternResource.pt-BR.resx | 8 +- .../NotifyService/CRMPatternResource.pt.resx | 4 +- .../NotifyService/CRMPatternResource.resx | 14 +- .../NotifyService/CRMPatternResource.ru.resx | 8 +- .../NotifyService/CRMPatternResource.sk.resx | 8 +- .../NotifyService/CRMPatternResource.sl.resx | 8 +- .../NotifyService/CRMPatternResource.sv.resx | 4 +- .../NotifyService/CRMPatternResource.tr.resx | 8 +- .../NotifyService/CRMPatternResource.uk.resx | 8 +- .../NotifyService/CRMPatternResource.vi.resx | 8 +- .../CRMPatternResource.zh-CN.resx | 4 +- .../CRMPatternResource.zh-TW.resx | 8 +- .../Services/NotifyService/NotifyClient.cs | 34 +- .../CRM/Services/NotifyService/patterns.xml | 38 +- .../CRM/Templates/DealsTemplates.html | 2 +- .../CRM/Templates/InvoicesTemplates.html | 3 + .../Products/CRM/Utils/ExportToCSV.cs | 31 +- .../Utils/Import/CSV/ImportDataOperation.cs | 2 +- .../Products/CRM/Utils/MailSender.cs | 2 +- .../Products/CRM/Utils/PdfCreator.cs | 94 +- .../Products/CRM/Utils/PdfQueueWorker.cs | 2 +- .../ASC.Web.Studio/Products/CRM/js/cases.js | 55 +- .../ASC.Web.Studio/Products/CRM/js/common.js | 186 +- .../Products/CRM/js/contacts.js | 168 +- .../ASC.Web.Studio/Products/CRM/js/deals.js | 102 +- .../Products/CRM/js/fileuploader.js | 2 +- .../Products/CRM/js/invoices.js | 158 +- .../ASC.Web.Studio/Products/CRM/js/reports.js | 4 +- .../ASC.Web.Studio/Products/CRM/js/sender.js | 10 +- .../Products/CRM/js/settings.invoices.js | 82 +- .../Products/CRM/js/settings.js | 120 +- .../Products/CRM/js/socialmedia.js | 18 +- .../ASC.Web.Studio/Products/CRM/js/tasks.js | 50 +- .../Products/CRM/js/voip.calls.js | 12 +- .../Products/CRM/js/voip.common.js | 4 +- .../Products/CRM/js/voip.numbers.js | 16 +- .../ASC.Web.Studio/Products/CRM/web.config | 154 +- .../Community/ASC.Web.Community.csproj | 247 +- .../Controls/ButtonSidePanel.ascx.cs | 4 - .../Controls/NavigationSidePanel.ascx | 10 - .../Controls/NavigationSidePanel.ascx.cs | 8 - .../Products/Community/Default.aspx.cs | 2 +- .../Birthdays/Classes/BirthdaysModule.cs | 83 - .../Classes/BirthdaysSubscriptionManager.cs | 60 - .../Community/Modules/Birthdays/Default.aspx | 118 - .../Modules/Birthdays/Default.aspx.cs | 117 - .../Birthdays/Default.aspx.designer.cs | 24 - .../Modules/Birthdays/Resources/patterns.xml | 24 - .../Modules/Birthdays/js/birthdays.js | 78 - .../Modules/Blogs/Core/BlogsEngine.cs | 38 +- .../Community/Modules/Blogs/Core/Constants.cs | 1 + .../Resources/BlogsResource.az-Latn-AZ.resx | 5 +- .../Service/BlogPatternResource.Designer.cs | 39 +- .../BlogPatternResource.az-Latn-AZ.resx | 12 +- .../Core/Service/BlogPatternResource.resx | 21 +- .../Blogs/Core/Service/blog_patterns.xml | 27 +- .../Community/Modules/Blogs/Default.aspx.cs | 2 +- .../Community/Modules/Blogs/EditBlog.aspx | 2 +- .../Community/Modules/Blogs/EditBlog.aspx.cs | 2 +- .../Modules/Blogs/PageControllers/BasePage.cs | 2 +- .../Community/Modules/Blogs/js/blogs.js | 6 +- .../Core/Business/BookmarkingService.cs | 42 +- .../Common/BookmarkingBusinessConstants.cs | 2 + .../Core/Common/BookmarkingBusinessFactory.cs | 59 +- .../BookmarkingPatternResource.Designer.cs | 35 + ...BookmarkingPatternResource.az-Latn-AZ.resx | 12 +- .../Patterns/BookmarkingPatternResource.resx | 17 + .../Core/Patterns/bookmarking_patterns.xml | 26 +- ...okmarkingBusinessResources.az-Latn-AZ.resx | 2 +- .../BookmarkingResource.az-Latn-AZ.resx | 2 +- .../BookmarkingUserControl.ascx.cs | 2 +- .../BookmarkingUCResource.az-Latn-AZ.resx | 8 +- .../Modules/Bookmarking/js/bookmarking.js | 32 +- .../Forum/Core/Dao/ForumDataProvider.cs | 59 +- .../ForumPatternResource.az-Latn-AZ.resx | 12 +- .../Forum/Core/Module/forum_patterns.xml | 4 +- .../Core/Presenters/NotifierPresenter.cs | 9 +- .../Community/Modules/Forum/NewForum.aspx.cs | 2 +- .../Resources/ForumResource.az-Latn-AZ.resx | 5 +- .../Forum/UserControls/Common/ForumManager.cs | 64 +- .../Forum/UserControls/ForumEditor.ascx.cs | 2 +- .../Forum/UserControls/NewPostControl.ascx | 2 +- .../Forum/UserControls/NewPostControl.ascx.cs | 2 +- .../UserControls/PostListControl.ascx.cs | 2 +- .../Resources/ForumUCResource.az-Latn-AZ.resx | 8 +- .../UserControls/TopicEditorControl.ascx.cs | 13 +- .../UserControls/TopicListControl.ascx.cs | 2 +- .../Community/Modules/Forum/js/forum.js | 16 +- .../Community/Modules/Forum/js/forummaker.js | 2 +- .../Modules/Forum/js/searchhelper.js | 18 +- .../Modules/News/Code/DAO/DbFeedStorage.cs | 59 +- .../Community/Modules/News/Default.aspx.cs | 2 +- .../Community/Modules/News/EditNews.aspx | 2 +- .../Community/Modules/News/EditNews.aspx.cs | 4 +- .../Community/Modules/News/NewsConst.cs | 1 + .../Resources/NewsPatternResource.Designer.cs | 35 + .../NewsPatternResource.az-Latn-AZ.resx | 12 +- .../News/Resources/NewsPatternResource.resx | 17 + .../Resources/NewsResource.az-Latn-AZ.resx | 5 +- .../Modules/News/Resources/news_patterns.xml | 26 +- .../Community/Modules/News/js/news.js | 4 +- .../Community/Modules/Wiki/Code/Constants.cs | 1 + .../Patterns/WikiPatternResource.Designer.cs | 35 + .../WikiPatternResource.az-Latn-AZ.resx | 17 +- .../Code/Patterns/WikiPatternResource.resx | 17 + .../Wiki/Code/Patterns/wiki_patterns.xml | 25 +- .../Community/Modules/Wiki/Code/WikiEngine.cs | 32 +- .../Wiki/Code/WikiSubscriptionManager.cs | 14 + .../Community/Modules/Wiki/Default.aspx.cs | 2 +- .../Modules/Wiki/Handlers/WikiFileHandler.cs | 19 +- .../Resources/WikiResource.az-Latn-AZ.resx | 5 +- .../Resources/WikiUCResource.az-Latn-AZ.resx | 2 +- .../WikiUC/fckeditor/editor/fckdialog.html | 2 +- .../editor/js/fckeditorcode_gecko.js | 2 +- .../plugins/fck_wysiwyg/dialogs/file.html | 12 +- .../Community/Properties/AssemblyInfo.cs | 2 - .../Resources/CommunityResource.Designer.cs | 17 +- .../Resources/CommunityResource.ar-AE.resx | 3 - .../CommunityResource.az-Latn-AZ.resx | 83 +- .../Resources/CommunityResource.bg.resx | 3 - .../Resources/CommunityResource.cs.resx | 3 - .../Resources/CommunityResource.de.resx | 6 - .../Resources/CommunityResource.el.resx | 3 - .../Resources/CommunityResource.es.resx | 6 - .../Resources/CommunityResource.fa.resx | 3 - .../Resources/CommunityResource.fi.resx | 3 - .../Resources/CommunityResource.fr.resx | 6 - .../Resources/CommunityResource.hi.resx | 3 - .../Resources/CommunityResource.hu.resx | 3 - .../Resources/CommunityResource.id.resx | 3 - .../Resources/CommunityResource.it.resx | 6 - .../Resources/CommunityResource.ja.resx | 6 - .../Resources/CommunityResource.kk.resx | 3 - .../Resources/CommunityResource.ko.resx | 3 - .../Resources/CommunityResource.lt.resx | 3 - .../Resources/CommunityResource.lv.resx | 3 - .../Resources/CommunityResource.nb-NO.resx | 3 - .../Resources/CommunityResource.nl.resx | 3 - .../Resources/CommunityResource.pl.resx | 6 - .../Resources/CommunityResource.pt-BR.resx | 6 - .../Resources/CommunityResource.resx | 7 +- .../Resources/CommunityResource.ru.resx | 6 - .../Resources/CommunityResource.sk.resx | 3 - .../Resources/CommunityResource.sl.resx | 3 - .../Resources/CommunityResource.sv.resx | 3 - .../Resources/CommunityResource.tr.resx | 3 - .../Resources/CommunityResource.uk.resx | 3 - .../Resources/CommunityResource.vi.resx | 3 - .../Resources/CommunityResource.zh-CN.resx | 3 - .../Resources/CommunityResource.zh-TW.resx | 6 - .../Products/Community/js/common.js | 4 +- .../Community/js/tagsautocompletebox.js | 18 +- .../Products/Community/web.config | 152 +- .../Products/Files/ASC.Web.Files.csproj | 71 +- .../Files/App_Themes/default/common.css | 180 +- .../Files/App_Themes/default/filechoice.css | 6 +- .../Files/Configuration/FilesSettings.cs | 115 + .../Controls/AccessRights/AccessRights.ascx | 2 + .../AccessRights/AccessRights.ascx.cs | 1 + .../Controls/AccessRights/FormFilling.ascx | 82 + .../Controls/AccessRights/FormFilling.ascx.cs | 41 + .../AccessRights/FormFilling.ascx.designer.cs | 26 + .../Controls/AccessRights/accessrights.css | 3 +- .../Controls/AccessRights/accessrights.js | 122 +- .../Controls/AccessRights/formfilling.css | 177 + .../Controls/AccessRights/formfilling.js | 327 + .../AccessRights/images/fillformsgray.svg | 7 + .../ChunkUploadDialog/ChunkUploadDialog.ascx | 6 +- .../ChunkUploadDialog/chunkuploadmanager.js | 67 +- .../Controls/ContentList/contentlist.css | 16 +- .../Controls/ConvertFile/confirmconvert.js | 2 +- .../Files/Controls/ConvertFile/convertfile.js | 23 +- .../Files/Controls/CreateMenu/CreateMenu.ascx | 4 +- .../Files/Controls/CreateMenu/createmenu.js | 2 +- .../Controls/EmptyFolder/EmptyFolder.ascx.cs | 2 +- .../Files/Controls/EmptyFolder/emptyfolder.js | 2 +- .../FileChoisePopup/filechoisepopup.js | 18 +- .../Controls/FileSelector/fileselector.css | 3 +- .../Controls/FileSelector/fileselector.js | 8 +- .../Controls/MainContent/MainContent.ascx | 18 +- .../Controls/MainContent/MainContent.ascx.cs | 6 +- .../MainContent/MainContentFilter.ascx | 4 +- .../Controls/MainContent/maincontent.css | 4 + .../PrivateRoomOpenFile.ascx | 40 + .../PrivateRoomOpenFile.ascx.cs | 83 + .../PrivateRoomOpenFile.ascx.designer.cs | 17 + .../privateroomopenfile.css | 135 + .../privateroomopenfile.js | 178 + .../Files/Controls/Sailfish/sailfish.js | 4 +- .../Controls/SharingDialog/SharingDialog.ascx | 201 + .../SharingDialog/SharingDialog.ascx.cs | 40 + .../SharingDialog.ascx.designer.cs | 26 + .../Controls/SharingDialog/images/link.svg | 3 + .../SharingDialog/images/link_via.png | Bin 0 -> 900 bytes .../SharingDialog/images/settings.svg | 3 + .../Controls/SharingDialog/images/user.svg | 3 + .../Controls/SharingDialog/sharingdialog.css | 410 + .../Controls/SharingDialog/sharingdialog.js | 2482 +++++ .../Files/Controls/ThirdParty/thirdparty.css | 4 + .../Files/Controls/ThirdParty/thirdparty.js | 71 +- .../Products/Files/Controls/Tree/Tree.ascx | 3 +- .../Products/Files/Controls/Tree/tree.js | 6 +- .../UnsubscribeDialog/UnsubscribeDialog.ascx | 36 + .../UnsubscribeDialog.ascx.cs | 37 + .../UnsubscribeDialog.ascx.designer.cs | 26 + .../UnsubscribeDialog/unsubscribedialog.js | 168 + .../Files/Core/Compress/CompressToArchive.cs | 10 +- .../Files/Core/Compress/CompressToTarGz.cs | 7 +- .../Files/Core/Compress/CompressToZip.cs | 7 +- .../Products/Files/Core/Compress/ICompress.cs | 24 +- .../Files/Core/Dao/Interfaces/IFileDao.cs | 4 + .../Files/Core/Dao/TeamlabDao/AbstractDao.cs | 44 +- .../Files/Core/Dao/TeamlabDao/FileDao.cs | 46 +- .../Files/Core/Dao/TeamlabDao/FolderDao.cs | 5 +- .../Files/Core/Dao/TeamlabDao/SecurityDao.cs | 3 +- .../Files/Core/Entries/AutoCleanUpData.cs | 62 + .../Core/Entries/ChunkedUploadSession.cs | 2 + .../Files/Core/Entries/EditHistory.cs | 18 +- .../Files/Core/Entries/EntryProperties.cs | 163 + .../Products/Files/Core/Entries/File.cs | 1 + .../Products/Files/Core/Entries/FileEntry.cs | 33 +- .../Products/Files/Core/Entries/Folder.cs | 3 + .../Files/Core/Entries/ForcesaveType.cs | 4 +- .../Files/Core/Security/FileSecurity.cs | 57 +- .../Files/Core/Security/IFileSecurity.cs | 2 + .../Products/Files/Default.aspx | 86 + .../Products/Files/Default.aspx.cs | 95 +- .../Products/Files/DocEditor.aspx.cs | 73 +- .../Products/Files/FileChoice.aspx.cs | 10 +- .../Products/Files/Helpers/Constants.cs | 2 + .../Files/Helpers/FilesMessageService.cs | 33 +- .../Products/Files/Helpers/Global.cs | 5 + .../HttpHandlers/ChunkedUploaderHandler.cs | 9 +- .../Files/HttpHandlers/FileHandler.ashx.cs | 120 +- .../HttpHandlers/docusignhandler.ashx.cs | 2 +- .../Products/Files/OpenPrivate.aspx | 9 + .../Products/Files/OpenPrivate.aspx.cs | 86 + .../Files/OpenPrivate.aspx.designer.cs | 40 + .../Resources/FilesCommonResource.Designer.cs | 101 +- .../FilesCommonResource.az-Latn-AZ.resx | 332 +- .../Files/Resources/FilesCommonResource.resx | 96 +- .../Resources/FilesJSResource.Designer.cs | 116 +- .../Resources/FilesJSResource.az-Latn-AZ.resx | 239 +- .../Files/Resources/FilesJSResource.resx | 105 +- .../Resources/FilesUCResource.Designer.cs | 470 +- .../Resources/FilesUCResource.az-Latn-AZ.resx | 578 +- .../Files/Resources/FilesUCResource.de.resx | 6 - .../Files/Resources/FilesUCResource.el.resx | 6 - .../Files/Resources/FilesUCResource.es.resx | 6 - .../Files/Resources/FilesUCResource.fr.resx | 6 - .../Files/Resources/FilesUCResource.it.resx | 6 - .../Files/Resources/FilesUCResource.ja.resx | 6 - .../Files/Resources/FilesUCResource.nl.resx | 6 - .../Resources/FilesUCResource.pt-BR.resx | 6 - .../Files/Resources/FilesUCResource.resx | 219 +- .../Files/Resources/FilesUCResource.ru.resx | 12 +- .../Files/Resources/FilesUCResource.sv.resx | 3 - .../Resources/FilesUCResource.zh-CN.resx | 3 - .../Resources/FilesUCResource.zh-TW.resx | 9 +- .../Products/Files/SaveAs.aspx.cs | 10 +- .../Services/DocumentService/Configuration.cs | 143 +- .../DocbuilderReportsUtility.cs | 2 +- .../DocumentServiceConnector.cs | 4 +- .../DocumentService/DocumentServiceHelper.cs | 40 +- .../DocumentService/DocumentServiceParams.cs | 3 + .../DocumentService/DocumentServiceTracker.cs | 33 +- .../FilesPatternResource.az-Latn-AZ.resx | 71 +- .../Files/Services/NotifyService/patterns.xml | 22 +- .../FileOperations/FileDeleteOperation.cs | 55 +- .../FileOperations/FileDownloadOperation.cs | 738 +- .../FileOperations/FileMarkAsReadOperation.cs | 19 +- .../FileOperations/FileMoveCopyOperation.cs | 17 +- .../FileOperations/FileOperation.cs | 7 +- .../FileOperations/FileOperationsManager.cs | 8 +- .../FileStorageServiceController.cs | 286 +- .../WCFService/IFileStorageService.cs | 12 +- .../WCFService/Wrappers/AceWrapper.cs | 13 + .../ASC.Web.Studio/Products/Files/Share.aspx | 20 + .../Products/Files/Share.aspx.cs | 41 +- .../Files/Templates/getfolderitem.xsl | 40 +- .../Files/Templates/getfolderitems.xsl | 42 +- .../Products/Files/Templates/getnewitems.xsl | 18 + .../Files/Templates/getthirdpartyitem.xsl | 12 +- .../Products/Files/ThirdPartyApp/BoxApp.cs | 9 +- .../Files/ThirdPartyApp/GoogleDriveApp.cs | 12 +- .../Files/Utils/ChunkedUploadSessionHolder.cs | 2 +- .../Products/Files/Utils/EntryManager.cs | 130 +- .../Products/Files/Utils/FileConverter.cs | 10 +- .../Products/Files/Utils/FileShareLink.cs | 5 +- .../Products/Files/Utils/FileSharing.cs | 34 +- .../Products/Files/Utils/FileUploader.cs | 37 +- .../Products/Files/js/actionmanager.js | 327 +- .../Products/Files/js/anchormanager.js | 2 +- .../Products/Files/js/editor.js | 159 +- .../Products/Files/js/eventhandler.js | 116 +- .../Products/Files/js/filechoice.js | 25 +- .../Products/Files/js/filter.js | 8 +- .../Products/Files/js/foldermanager.js | 372 +- .../Products/Files/js/markernew.js | 4 +- .../Products/Files/js/mousemanager.js | 100 +- .../Products/Files/js/saveas.js | 15 +- .../Products/Files/js/servicemanager.js | 27 +- .../Products/Files/js/templatemanager.js | 4 +- .../ASC.Web.Studio/Products/Files/js/ui.js | 128 +- .../ASC.Web.Studio/Products/Files/web.config | 152 +- .../Products/People/ASC.Web.People.csproj | 268 +- .../App_Themes/default/css}/birthdays.css | 188 +- .../People/App_Themes/default/css/carddav.css | 66 + .../App_Themes/default/css/people.master.less | 65 +- .../default/images/birthdayEmpScr.png} | Bin .../default/images/svg/birthday-orange.svg | 8 + .../default/images/svg/birthday.svg | 8 + .../App_Themes/default/images/white_cross.png | Bin .../App_Themes/default/imgs/people_import.png | Bin 3880 -> 3709 bytes .../Products/People/Birthdays.aspx | 118 + .../Products/People/Birthdays.aspx.cs | 127 + .../People/Birthdays.aspx.designer.cs | 26 + .../Products/People/CardDavSettings.aspx | 61 + .../Products/People/CardDavSettings.aspx.cs | 63 + .../People/CardDavSettings.aspx.designer.cs | 17 + .../People/Classes/BirthdaysModule.cs | 83 + .../Classes/BirthdaysNotifyClient.cs | 399 +- .../Classes/PeopleSubscriptionManager.cs | 70 + .../People/Core/Import/EncodingEnum.cs | 3 +- .../People/Core/Import/ImportUsersTask.cs | 5 +- .../Products/People/Core/PeopleProduct.cs | 3 +- .../Products/People/Default.aspx.cs | 3 +- .../ClientLocalizationResources.cs | 5 - .../Masters/PeopleBaseTemplate.Master.cs | 4 +- .../Products/People/Profile.aspx | 13 + .../Products/People/Profile.aspx.cs | 18 +- .../Products/People/Profile.aspx.designer.cs | 27 +- .../People/Properties/AssemblyInfo.cs | 1 + .../BirthdayPatternResource.Designer.cs | 224 +- .../BirthdayPatternResource.az-Latn-AZ.resx | 75 + .../Resources/BirthdayPatternResource.be.resx | 132 +- .../Resources/BirthdayPatternResource.bg.resx | 132 +- .../Resources/BirthdayPatternResource.cs.resx | 132 +- .../Resources/BirthdayPatternResource.de.resx | 138 +- .../Resources/BirthdayPatternResource.el.resx | 132 +- .../Resources/BirthdayPatternResource.es.resx | 138 +- .../Resources/BirthdayPatternResource.fi.resx | 132 +- .../Resources/BirthdayPatternResource.fr.resx | 138 +- .../Resources/BirthdayPatternResource.hi.resx | 126 +- .../Resources/BirthdayPatternResource.hu.resx | 126 +- .../Resources/BirthdayPatternResource.id.resx | 132 +- .../Resources/BirthdayPatternResource.it.resx | 138 +- .../Resources/BirthdayPatternResource.ja.resx | 138 +- .../Resources/BirthdayPatternResource.kk.resx | 132 +- .../Resources/BirthdayPatternResource.lv.resx | 132 +- .../Resources/BirthdayPatternResource.nl.resx | 132 +- .../Resources/BirthdayPatternResource.pl.resx | 132 +- .../BirthdayPatternResource.pt-BR.resx | 138 +- .../Resources/BirthdayPatternResource.resx | 160 +- .../Resources/BirthdayPatternResource.ru.resx | 138 +- .../Resources/BirthdayPatternResource.sk.resx | 132 +- .../Resources/BirthdayPatternResource.sl.resx | 132 +- .../Resources/BirthdayPatternResource.sv.resx | 132 +- .../Resources/BirthdayPatternResource.tr.resx | 132 +- .../Resources/BirthdayPatternResource.uk.resx | 132 +- .../Resources/BirthdayPatternResource.vi.resx | 132 +- .../BirthdayPatternResource.zh-CN.resx | 132 +- .../BirthdayPatternResource.zh-TW.resx | 138 +- .../Resources/BirthdaysResource.Designer.cs | 306 +- .../BirthdaysResource.az-Latn-AZ.resx | 91 + .../Resources/BirthdaysResource.be.resx | 180 +- .../Resources/BirthdaysResource.bg.resx | 180 +- .../Resources/BirthdaysResource.cs.resx | 168 +- .../Resources/BirthdaysResource.da.resx | 132 +- .../Resources/BirthdaysResource.de.resx | 180 +- .../Resources/BirthdaysResource.el.resx | 174 +- .../Resources/BirthdaysResource.es.resx | 180 +- .../Resources/BirthdaysResource.fi.resx | 168 +- .../Resources/BirthdaysResource.fr.resx | 180 +- .../Resources/BirthdaysResource.hi.resx | 168 +- .../Resources/BirthdaysResource.hu.resx | 180 +- .../Resources/BirthdaysResource.id.resx | 168 +- .../Resources/BirthdaysResource.it.resx | 180 +- .../Resources/BirthdaysResource.ja.resx | 180 +- .../Resources/BirthdaysResource.kk.resx | 180 +- .../Resources/BirthdaysResource.lv.resx | 168 +- .../Resources/BirthdaysResource.nl.resx | 168 +- .../Resources/BirthdaysResource.pl.resx | 168 +- .../Resources/BirthdaysResource.pt-BR.resx | 180 +- .../Resources/BirthdaysResource.resx | 180 +- .../Resources/BirthdaysResource.ru.resx | 180 +- .../Resources/BirthdaysResource.sk.resx | 168 +- .../Resources/BirthdaysResource.sl.resx | 168 +- .../Resources/BirthdaysResource.sv.resx | 180 +- .../Resources/BirthdaysResource.tr.resx | 180 +- .../Resources/BirthdaysResource.uk.resx | 168 +- .../Resources/BirthdaysResource.vi.resx | 168 +- .../Resources/BirthdaysResource.zh-CN.resx | 180 +- .../Resources/BirthdaysResource.zh-TW.resx | 180 +- .../PeopleJSResource.az-Latn-AZ.resx | 112 + .../Resources/PeopleResource.Designer.cs | 84 +- .../Resources/PeopleResource.ar-AE.resx | 3 + .../Resources/PeopleResource.az-Latn-AZ.resx | 362 +- .../People/Resources/PeopleResource.be.resx | 3 - .../People/Resources/PeopleResource.bg.resx | 6 +- .../People/Resources/PeopleResource.cs.resx | 3 + .../People/Resources/PeopleResource.de.resx | 6 +- .../People/Resources/PeopleResource.el.resx | 6 +- .../People/Resources/PeopleResource.es.resx | 6 +- .../People/Resources/PeopleResource.fa.resx | 3 + .../People/Resources/PeopleResource.fi.resx | 3 + .../People/Resources/PeopleResource.fr.resx | 6 +- .../People/Resources/PeopleResource.hi.resx | 3 + .../People/Resources/PeopleResource.hu.resx | 3 + .../People/Resources/PeopleResource.id.resx | 3 + .../People/Resources/PeopleResource.it.resx | 6 +- .../People/Resources/PeopleResource.ja.resx | 6 +- .../People/Resources/PeopleResource.ko.resx | 6 +- .../People/Resources/PeopleResource.lt.resx | 3 + .../People/Resources/PeopleResource.lv.resx | 3 + .../Resources/PeopleResource.nb-NO.resx | 3 + .../People/Resources/PeopleResource.nl.resx | 3 + .../People/Resources/PeopleResource.pl.resx | 3 + .../Resources/PeopleResource.pt-BR.resx | 6 +- .../People/Resources/PeopleResource.resx | 91 +- .../People/Resources/PeopleResource.ro.resx | 3 - .../People/Resources/PeopleResource.ru.resx | 6 +- .../People/Resources/PeopleResource.sk.resx | 6 +- .../People/Resources/PeopleResource.sl.resx | 3 + .../People/Resources/PeopleResource.sv.resx | 6 +- .../People/Resources/PeopleResource.tr.resx | 6 +- .../People/Resources/PeopleResource.uk.resx | 3 + .../People/Resources/PeopleResource.vi.resx | 3 + .../Resources/PeopleResource.zh-CN.resx | 6 +- .../Resources/PeopleResource.zh-TW.resx | 6 +- .../Products/People/Resources/patterns.xml | 23 + .../People/Templates/PeopleTemplates.html | 10 +- .../UserControls/ImportUsers/ImportUsers.ascx | 3 +- .../ImportUsers/ImportUsers.ascx.cs | 3 +- .../ImportUsers/ImportUsers.ascx.designer.cs | 23 +- .../ImportUsers/js/importusers.js | 76 +- .../UserControls/SideNavigationPanel.ascx | 60 +- .../UserControls/SideNavigationPanel.ascx.cs | 35 + .../Products/People/js/birthdays.js | 64 + .../Products/People/js/carddav.js | 85 + .../People/js/departmentmanagement.js | 2 +- .../Products/People/js/filterhandler.js | 2 +- .../Products/People/js/navigatorhandler.js | 2 +- .../Products/People/js/peopleactions.js | 2 +- .../Products/People/js/peoplecontroller.js | 80 +- .../Products/People/js/sidenavigationpanel.js | 8 +- .../ASC.Web.Studio/Products/People/web.config | 128 +- .../Products/Projects/ASC.Web.Projects.csproj | 29 +- .../App_Themes/default/css/alltasks.less | 21 +- .../App_Themes/default/css/common.less | 2 +- .../App_Themes/default/css/discussions.less | 5 - .../App_Themes/default/css/ganttchart.css | 4 + .../App_Themes/default/css/reports.css | 3 + .../Products/Projects/Classes/NotifyHelper.cs | 10 +- .../Products/Projects/Classes/ReportHelper.cs | 60 +- .../Controls/Messages/DiscussionAction.ascx | 2 +- .../Controls/Reports/ReportFilters.ascx | 2 +- .../Controls/Reports/ReportFilters.ascx.cs | 2 +- .../Projects/Core/Dao/MilestoneDao.cs | 2 +- .../Products/Projects/Core/Dao/ProjectDao.cs | 88 +- .../Products/Projects/Core/Dao/SubtaskDao.cs | 2 +- .../Products/Projects/Core/Dao/TaskDao.cs | 48 +- .../Projects/Core/Dao/TimeSpendDao.cs | 1 - .../Engine/EngineResource.az-Latn-AZ.resx | 64 + .../Projects/Core/Engine/ProjectEngine.cs | 38 +- .../Projects/Core/Engine/ProjectSecurity.cs | 5 + .../Projects/Core/Engine/ReportEngine.cs | 82 +- .../Projects/Core/Engine/SecurityAdapter.cs | 5 + .../Projects/Core/Engine/SubtaskEngine.cs | 32 + .../Projects/Core/Engine/TaskEngine.cs | 5 + .../Core/Model/DataInterfaces/IProjectDao.cs | 4 + .../Core/Model/DataInterfaces/ITaskDao.cs | 2 + .../Domain/Entities/Enums/AverageTimeEnum.cs | 29 + .../Core/Model/Domain/Reports/ReportFilter.cs | 20 +- .../Domain/Reports/ReportFilterSerializer.cs | 60 + .../ProjectsEnumResource.az-Latn-AZ.resx | 13 +- .../Services/NotifyService/NotifyClient.cs | 141 +- .../Services/NotifyService/NotifyConstants.cs | 4 + .../Services/NotifyService/NotifySource.cs | 1 + .../NotifyService/PatternResource.Designer.cs | 109 +- .../PatternResource.az-Latn-AZ.resx | 416 +- .../NotifyService/PatternResource.resx | 54 +- .../Model/Services/NotifyService/patterns.xml | 145 +- .../MilestonesNearest.docbuilder | 4 +- .../ProjectsList.docbuilder | 33 +- .../TimeSpend_0.docbuilder | 4 +- .../TimeSpend_1.docbuilder | 4 +- .../TimeSpend_2.docbuilder | 4 +- .../UsersActivity.docbuilder | 87 +- .../DocbuilderTemplates/master.docbuilder | 12 + .../Products/Projects/Projects.aspx.cs | 6 + .../ProjectsTemplates/ReportTemplates.html | 9 +- .../ProjectsTemplates/SubtaskTemplates.html | 2 +- .../GrammaticalResource.az-Latn-AZ.resx | 10 +- .../Resources/ImportResource.az-Latn-AZ.resx | 2 +- .../Resources/MessageResource.az-Latn-AZ.resx | 47 +- .../MilestoneResource.az-Latn-AZ.resx | 32 +- .../Resources/ProjectResource.az-Latn-AZ.resx | 179 +- .../ProjectTemplatesResource.az-Latn-AZ.resx | 5 +- .../ProjectsCommonResource.Designer.cs | 9 + .../ProjectsCommonResource.az-Latn-AZ.resx | 153 +- .../Resources/ProjectsCommonResource.resx | 3 + .../ProjectsFileResource.az-Latn-AZ.resx | 8 +- .../ProjectsFilterResource.az-Latn-AZ.resx | 41 +- .../ProjectsJSResource.az-Latn-AZ.resx | 254 +- .../Resources/ReportResource.Designer.cs | 76 +- .../Resources/ReportResource.az-Latn-AZ.resx | 67 +- .../Projects/Resources/ReportResource.resx | 89 +- .../Resources/TaskResource.Designer.cs | 9 + .../Resources/TaskResource.az-Latn-AZ.resx | 82 +- .../Projects/Resources/TaskResource.resx | 3 + .../TimeTrackingResource.az-Latn-AZ.resx | 35 +- .../Products/Projects/Test/App.config | 17 +- .../Products/Projects/Test/BaseTest.cs | 8 +- .../Products/Projects/Test/DataGenerator.cs | 2 +- .../Products/Projects/Test/ReassignTest.cs | 2 +- .../Products/Projects/Test/SearchTest.cs | 2 +- .../Projects/Test/TaskSecurityTest.cs | 18 +- .../Projects/js/addmilestonecontainer.js | 112 +- .../Products/Projects/js/apitimetraking.js | 60 +- .../Products/Projects/js/base.js | 14 +- .../Products/Projects/js/common.js | 8 +- .../Projects/js/common_filter_projects.js | 8 +- .../Products/Projects/js/contacts.js | 2 +- .../Products/Projects/js/discussions.js | 49 +- .../Products/Projects/js/ganttchartpage.js | 112 +- .../Products/Projects/js/helper.js | 7 +- .../Products/Projects/js/import.js | 72 +- .../Projects/js/jq_projects_extensions.js | 6 - .../js/jquery-tagsadvansedselector.js | 2 +- .../Products/Projects/js/milestoneaction.js | 56 +- .../Products/Projects/js/milestones.js | 2 +- .../Products/Projects/js/navsidepanel.js | 10 +- .../Products/Projects/js/projectaction.js | 72 +- .../Projects/js/projectdocumentspopup.js | 6 +- .../Products/Projects/js/projectnavpanel.js | 2 +- .../Products/Projects/js/projects.js | 2 +- .../Products/Projects/js/projectteam.js | 2 +- .../Products/Projects/js/projecttemplates.js | 10 +- .../Products/Projects/js/reports.js | 318 +- .../Products/Projects/js/settings.js | 16 +- .../Products/Projects/js/subtasks.js | 104 +- .../Products/Projects/js/taskaction.js | 30 +- .../Products/Projects/js/taskdescription.js | 21 +- .../Products/Projects/js/tasks.js | 20 +- .../Products/Projects/js/timetracking.js | 2 +- .../Products/Projects/web.config | 158 +- .../Products/Sample/ASC.Web.Sample.csproj | 2 +- .../Products/Sample/UserControls.aspx | 6 +- .../Products/Sample/js/script.js | 6 +- .../ASC.Web.Studio/Products/Sample/web.config | 152 +- .../AuditResource.az-Latn-AZ.resx | 121 + .../ChatResource.az-Latn-AZ.resx | 145 + .../CustomModeResource.Designer.cs | 48 +- .../CustomModeResource.az-Latn-AZ.resx | 437 + .../CustomModeResource.de.resx | 46 - .../CustomModeResource.es.resx | 53 +- .../CustomModeResource.fr.resx | 46 - .../CustomModeResource.hu.resx | 29 - .../CustomModeResource.id.resx | 20 - .../CustomModeResource.it.resx | 66 +- .../CustomModeResource.ja.resx | 32 - .../CustomModeResource.pt-BR.resx | 46 - .../PublicResources/CustomModeResource.resx | 45 +- .../CustomModeResource.ru.resx | 55 +- .../CustomModeResource.zh-TW.resx | 48 - .../PublicResources/FeedResource.Designer.cs | 101 +- .../FeedResource.az-Latn-AZ.resx | 142 +- .../PublicResources/FeedResource.resx | 96 +- .../MonitoringResource.az-Latn-AZ.resx | 115 + .../PersonalResource.Designer.cs | 990 +- .../PersonalResource.az-Latn-AZ.resx | 343 + .../PublicResources/PersonalResource.resx | 408 +- .../PublicResources/Resource.Designer.cs | 526 +- .../PublicResources/Resource.az-Latn-AZ.resx | 2662 ++++- .../PublicResources/Resource.bg.resx | 72 - .../PublicResources/Resource.cs.resx | 72 - .../PublicResources/Resource.de.resx | 75 - .../PublicResources/Resource.es.resx | 72 - .../PublicResources/Resource.fi.resx | 45 - .../PublicResources/Resource.fr.resx | 72 - .../PublicResources/Resource.he.resx | 3 - .../PublicResources/Resource.hu.resx | 15 - .../PublicResources/Resource.id.resx | 45 - .../PublicResources/Resource.it.resx | 74 - .../PublicResources/Resource.ja.resx | 69 - .../PublicResources/Resource.ko.resx | 18 - .../PublicResources/Resource.lv.resx | 72 - .../PublicResources/Resource.nl.resx | 72 - .../PublicResources/Resource.pl.resx | 72 - .../PublicResources/Resource.pt-BR.resx | 72 - .../PublicResources/Resource.pt.resx | 3 - .../PublicResources/Resource.resx | 249 +- .../PublicResources/Resource.ru.resx | 93 +- .../PublicResources/Resource.sk.resx | 76 - .../PublicResources/Resource.sl.resx | 36 - .../PublicResources/Resource.sv.resx | 3 - .../PublicResources/Resource.tr.resx | 72 - .../PublicResources/Resource.uk.resx | 81 - .../PublicResources/Resource.vi.resx | 72 - .../PublicResources/Resource.zh-CN.resx | 72 - .../PublicResources/Resource.zh-TW.resx | 45 - .../PublicResources/ResourceJS.Designer.cs | 200 +- .../ResourceJS.az-Latn-AZ.resx | 890 +- .../PublicResources/ResourceJS.resx | 70 +- .../StudioCountriesResource.az-Latn-AZ.resx | 763 ++ .../UserControlsCommonResource.Designer.cs | 238 +- ...UserControlsCommonResource.az-Latn-AZ.resx | 790 +- .../UserControlsCommonResource.bg.resx | 39 - .../UserControlsCommonResource.cs.resx | 33 - .../UserControlsCommonResource.de.resx | 39 - .../UserControlsCommonResource.es.resx | 39 - .../UserControlsCommonResource.fi.resx | 24 - .../UserControlsCommonResource.fr.resx | 39 - .../UserControlsCommonResource.hu.resx | 15 - .../UserControlsCommonResource.id.resx | 36 - .../UserControlsCommonResource.it.resx | 39 - .../UserControlsCommonResource.ja.resx | 33 - .../UserControlsCommonResource.lv.resx | 33 - .../UserControlsCommonResource.nl.resx | 33 - .../UserControlsCommonResource.pl.resx | 33 - .../UserControlsCommonResource.pt-BR.resx | 39 - .../UserControlsCommonResource.resx | 80 +- .../UserControlsCommonResource.ru.resx | 42 +- .../UserControlsCommonResource.sk.resx | 33 - .../UserControlsCommonResource.sl.resx | 3 - .../UserControlsCommonResource.sv.resx | 9 - .../UserControlsCommonResource.tr.resx | 33 - .../UserControlsCommonResource.uk.resx | 33 - .../UserControlsCommonResource.vi.resx | 33 - .../UserControlsCommonResource.zh-CN.resx | 33 - .../UserControlsCommonResource.zh-TW.resx | 39 - .../WhiteLabelResource.Designer.cs | 6 +- .../WhiteLabelResource.az-Latn-AZ.resx | 106 + .../WhiteLabelResource.bg.resx | 3 - .../WhiteLabelResource.cs.resx | 3 - .../WhiteLabelResource.de.resx | 3 - .../WhiteLabelResource.es.resx | 3 - .../WhiteLabelResource.fi.resx | 3 - .../WhiteLabelResource.fr.resx | 3 - .../WhiteLabelResource.id.resx | 3 - .../WhiteLabelResource.it.resx | 3 - .../WhiteLabelResource.ja.resx | 3 - .../WhiteLabelResource.lv.resx | 3 - .../WhiteLabelResource.nl.resx | 3 - .../WhiteLabelResource.pl.resx | 3 - .../WhiteLabelResource.pt-BR.resx | 3 - .../PublicResources/WhiteLabelResource.resx | 4 +- .../WhiteLabelResource.ru.resx | 3 - .../WhiteLabelResource.sk.resx | 3 - .../WhiteLabelResource.tr.resx | 3 - .../WhiteLabelResource.uk.resx | 3 - .../WhiteLabelResource.vi.resx | 3 - .../WhiteLabelResource.zh-CN.resx | 3 - .../WhiteLabelResource.zh-TW.resx | 3 - web/studio/ASC.Web.Studio/Tariffs.aspx.cs | 13 +- .../Templates/AdvansedSelectorTemplate.html | 14 +- .../Templates/CommonTemplates.html | 35 +- .../Templates/DropFeedTemplate.html | 29 +- .../Templates/FeedListTemplate.html | 36 +- .../ASC.Web.Studio/ThirdParty/Dropbox.aspx.cs | 3 +- .../ThirdParty/ImportContacts/Google.aspx.cs | 2 +- .../ThirdParty/ImportContacts/Import.aspx | 11 - .../ThirdParty/ImportContacts/Import.aspx.cs | 2 +- .../ThirdParty/ImportContacts/Yahoo.aspx | 3 - .../ThirdParty/ImportContacts/Yahoo.aspx.cs | 110 - .../ImportContacts/Yahoo.aspx.designer.cs | 27 - .../ThirdParty/ImportContacts/image/yahoo.png | Bin 9626 -> 0 bytes .../ThirdParty/plugin/easybib/bibliography.js | 6 +- .../ThirdParty/plugin/easybib/easybib.js | 14 +- .../Common/Attachments/js/attachments.js | 22 +- .../Common/Authorize/Authorize.ascx.cs | 50 +- .../Common/Authorize/js/authorize.js | 8 +- .../AuthorizeDocs/AuthorizeDocs.ascx.cs | 24 +- .../Common/AuthorizeDocs/js/authorizedocs.js | 38 +- .../AuthorizeDocs/js/review_builder_script.js | 21 +- .../Common/AuthorizeDocs/js/reviews.js | 19 +- .../UserControls/Common/Comments/Comments.cs | 4 +- .../Comments/css/codehighlighter/vs.less | 2 +- .../Common/Comments/js/comments.js | 80 +- .../Common/LoginWithThirdParty.ascx.cs | 34 +- .../Common/MediaViewer/imageviewer.js | 28 +- .../Common/MediaViewer/mediaplayer.js | 64 +- .../Common/PollForm/js/pollform.js | 2 +- .../SharingSettings/SharingSettings.ascx | 2 + .../SharingSettings/SharingSettings.ascx.cs | 6 + .../SharingSettings.ascx.designer.cs | 21 +- .../css/default/sharingsettings.less | 113 +- .../SharingSettings/js/sharingsettings.js | 25 +- .../Common/SmallChat/js/smallchat.js | 46 +- .../Common/Support/Support.ascx.cs | 18 +- .../Common/Support/SupportChat.ascx | 13 - .../Common/Support/SupportChat.ascx.cs | 3 + .../UserControls/Common/Support/livechat.js | 114 +- .../ThirdPartyBanner/ThirdPartyBanner.ascx | 11 - .../ThirdPartyBanner/ThirdPartyBanner.ascx.cs | 120 - .../ThirdPartyBanner.ascx.designer.cs | 15 - .../ThirdPartyBannerSettings.cs | 60 - .../Common/ThirdPartyBanner/css/img/bitly.png | Bin 2496 -> 0 bytes .../ThirdPartyBanner/css/img/social.png | Bin 2897 -> 0 bytes .../ThirdPartyBanner/css/img/storage.png | Bin 1703 -> 0 bytes .../ThirdPartyBanner/css/img/twilio2.png | Bin 5711 -> 0 bytes .../ThirdPartyBanner/css/thirdpartybanner.css | 39 - .../Common/TopStudioPanel/TopStudioPanel.ascx | 24 +- .../TopStudioPanel/TopStudioPanel.ascx.cs | 6 +- .../Common/ckeditor/adapters/jquery.js | 14 +- .../Common/ckeditor/build-config.js | 26 +- .../Common/ckeditor/ckeditor-connector.js | 2 +- .../UserControls/Common/ckeditor/ckeditor.js | 2115 ++-- .../UserControls/Common/ckeditor/config.js | 69 +- .../UserControls/Common/ckeditor/contents.css | 181 +- .../UserControls/Common/ckeditor/lang/af.js | 6 +- .../UserControls/Common/ckeditor/lang/ar.js | 6 +- .../UserControls/Common/ckeditor/lang/az.js | 6 +- .../UserControls/Common/ckeditor/lang/bg.js | 6 +- .../UserControls/Common/ckeditor/lang/bn.js | 6 +- .../UserControls/Common/ckeditor/lang/bs.js | 6 +- .../UserControls/Common/ckeditor/lang/ca.js | 6 +- .../UserControls/Common/ckeditor/lang/cs.js | 6 +- .../UserControls/Common/ckeditor/lang/cy.js | 6 +- .../UserControls/Common/ckeditor/lang/da.js | 6 +- .../Common/ckeditor/lang/de-ch.js | 6 +- .../UserControls/Common/ckeditor/lang/de.js | 6 +- .../UserControls/Common/ckeditor/lang/el.js | 6 +- .../Common/ckeditor/lang/en-au.js | 6 +- .../Common/ckeditor/lang/en-ca.js | 6 +- .../Common/ckeditor/lang/en-gb.js | 6 +- .../UserControls/Common/ckeditor/lang/en.js | 6 +- .../UserControls/Common/ckeditor/lang/eo.js | 6 +- .../Common/ckeditor/lang/es-mx.js | 6 +- .../UserControls/Common/ckeditor/lang/es.js | 6 +- .../UserControls/Common/ckeditor/lang/et.js | 6 +- .../UserControls/Common/ckeditor/lang/eu.js | 6 +- .../UserControls/Common/ckeditor/lang/fa.js | 6 +- .../UserControls/Common/ckeditor/lang/fi.js | 6 +- .../UserControls/Common/ckeditor/lang/fo.js | 6 +- .../Common/ckeditor/lang/fr-ca.js | 6 +- .../UserControls/Common/ckeditor/lang/fr.js | 6 +- .../UserControls/Common/ckeditor/lang/gl.js | 6 +- .../UserControls/Common/ckeditor/lang/gu.js | 6 +- .../UserControls/Common/ckeditor/lang/he.js | 6 +- .../UserControls/Common/ckeditor/lang/hi.js | 6 +- .../UserControls/Common/ckeditor/lang/hr.js | 6 +- .../UserControls/Common/ckeditor/lang/hu.js | 6 +- .../UserControls/Common/ckeditor/lang/id.js | 6 +- .../UserControls/Common/ckeditor/lang/is.js | 6 +- .../UserControls/Common/ckeditor/lang/it.js | 6 +- .../UserControls/Common/ckeditor/lang/ja.js | 6 +- .../UserControls/Common/ckeditor/lang/ka.js | 6 +- .../UserControls/Common/ckeditor/lang/km.js | 6 +- .../UserControls/Common/ckeditor/lang/ko.js | 6 +- .../UserControls/Common/ckeditor/lang/ku.js | 6 +- .../UserControls/Common/ckeditor/lang/lt.js | 6 +- .../UserControls/Common/ckeditor/lang/lv.js | 6 +- .../UserControls/Common/ckeditor/lang/mk.js | 6 +- .../UserControls/Common/ckeditor/lang/mn.js | 6 +- .../UserControls/Common/ckeditor/lang/ms.js | 6 +- .../UserControls/Common/ckeditor/lang/nb.js | 6 +- .../UserControls/Common/ckeditor/lang/nl.js | 6 +- .../UserControls/Common/ckeditor/lang/no.js | 6 +- .../UserControls/Common/ckeditor/lang/oc.js | 6 +- .../UserControls/Common/ckeditor/lang/pl.js | 6 +- .../Common/ckeditor/lang/pt-br.js | 6 +- .../UserControls/Common/ckeditor/lang/pt.js | 6 +- .../UserControls/Common/ckeditor/lang/ro.js | 6 +- .../UserControls/Common/ckeditor/lang/ru.js | 6 +- .../UserControls/Common/ckeditor/lang/si.js | 6 +- .../UserControls/Common/ckeditor/lang/sk.js | 6 +- .../UserControls/Common/ckeditor/lang/sl.js | 6 +- .../UserControls/Common/ckeditor/lang/sq.js | 6 +- .../Common/ckeditor/lang/sr-latn.js | 6 +- .../UserControls/Common/ckeditor/lang/sr.js | 6 +- .../UserControls/Common/ckeditor/lang/sv.js | 6 +- .../UserControls/Common/ckeditor/lang/th.js | 6 +- .../UserControls/Common/ckeditor/lang/tr.js | 6 +- .../UserControls/Common/ckeditor/lang/tt.js | 6 +- .../UserControls/Common/ckeditor/lang/ug.js | 6 +- .../UserControls/Common/ckeditor/lang/uk.js | 6 +- .../UserControls/Common/ckeditor/lang/vi.js | 6 +- .../Common/ckeditor/lang/zh-cn.js | 6 +- .../UserControls/Common/ckeditor/lang/zh.js | 6 +- .../plugins/a11yhelp/dialogs/a11yhelp.js | 14 +- .../plugins/a11yhelp/dialogs/lang/af.js | 4 +- .../plugins/a11yhelp/dialogs/lang/ar.js | 14 +- .../plugins/a11yhelp/dialogs/lang/az.js | 6 +- .../plugins/a11yhelp/dialogs/lang/bg.js | 18 +- .../plugins/a11yhelp/dialogs/lang/ca.js | 4 +- .../plugins/a11yhelp/dialogs/lang/cs.js | 6 +- .../plugins/a11yhelp/dialogs/lang/cy.js | 4 +- .../plugins/a11yhelp/dialogs/lang/da.js | 14 +- .../plugins/a11yhelp/dialogs/lang/de-ch.js | 4 +- .../plugins/a11yhelp/dialogs/lang/de.js | 4 +- .../plugins/a11yhelp/dialogs/lang/el.js | 11 +- .../plugins/a11yhelp/dialogs/lang/en-au.js | 11 + .../plugins/a11yhelp/dialogs/lang/en-gb.js | 4 +- .../plugins/a11yhelp/dialogs/lang/en.js | 4 +- .../plugins/a11yhelp/dialogs/lang/eo.js | 11 +- .../plugins/a11yhelp/dialogs/lang/es-mx.js | 4 +- .../plugins/a11yhelp/dialogs/lang/es.js | 4 +- .../plugins/a11yhelp/dialogs/lang/et.js | 18 +- .../plugins/a11yhelp/dialogs/lang/eu.js | 6 +- .../plugins/a11yhelp/dialogs/lang/fa.js | 8 +- .../plugins/a11yhelp/dialogs/lang/fi.js | 4 +- .../plugins/a11yhelp/dialogs/lang/fo.js | 4 +- .../plugins/a11yhelp/dialogs/lang/fr-ca.js | 4 +- .../plugins/a11yhelp/dialogs/lang/fr.js | 6 +- .../plugins/a11yhelp/dialogs/lang/gl.js | 4 +- .../plugins/a11yhelp/dialogs/lang/gu.js | 4 +- .../plugins/a11yhelp/dialogs/lang/he.js | 4 +- .../plugins/a11yhelp/dialogs/lang/hi.js | 4 +- .../plugins/a11yhelp/dialogs/lang/hr.js | 10 +- .../plugins/a11yhelp/dialogs/lang/hu.js | 8 +- .../plugins/a11yhelp/dialogs/lang/id.js | 4 +- .../plugins/a11yhelp/dialogs/lang/it.js | 4 +- .../plugins/a11yhelp/dialogs/lang/ja.js | 4 +- .../plugins/a11yhelp/dialogs/lang/km.js | 4 +- .../plugins/a11yhelp/dialogs/lang/ko.js | 4 +- .../plugins/a11yhelp/dialogs/lang/ku.js | 12 +- .../plugins/a11yhelp/dialogs/lang/lt.js | 4 +- .../plugins/a11yhelp/dialogs/lang/lv.js | 4 +- .../plugins/a11yhelp/dialogs/lang/mk.js | 4 +- .../plugins/a11yhelp/dialogs/lang/mn.js | 4 +- .../plugins/a11yhelp/dialogs/lang/nb.js | 6 +- .../plugins/a11yhelp/dialogs/lang/nl.js | 6 +- .../plugins/a11yhelp/dialogs/lang/no.js | 8 +- .../plugins/a11yhelp/dialogs/lang/oc.js | 4 +- .../plugins/a11yhelp/dialogs/lang/pl.js | 4 +- .../plugins/a11yhelp/dialogs/lang/pt-br.js | 8 +- .../plugins/a11yhelp/dialogs/lang/pt.js | 6 +- .../plugins/a11yhelp/dialogs/lang/ro.js | 19 +- .../plugins/a11yhelp/dialogs/lang/ru.js | 6 +- .../plugins/a11yhelp/dialogs/lang/si.js | 4 +- .../plugins/a11yhelp/dialogs/lang/sk.js | 6 +- .../plugins/a11yhelp/dialogs/lang/sl.js | 4 +- .../plugins/a11yhelp/dialogs/lang/sq.js | 19 +- .../plugins/a11yhelp/dialogs/lang/sr-latn.js | 19 +- .../plugins/a11yhelp/dialogs/lang/sr.js | 19 +- .../plugins/a11yhelp/dialogs/lang/sv.js | 4 +- .../plugins/a11yhelp/dialogs/lang/th.js | 4 +- .../plugins/a11yhelp/dialogs/lang/tr.js | 4 +- .../plugins/a11yhelp/dialogs/lang/tt.js | 4 +- .../plugins/a11yhelp/dialogs/lang/ug.js | 12 +- .../plugins/a11yhelp/dialogs/lang/uk.js | 10 +- .../plugins/a11yhelp/dialogs/lang/vi.js | 4 +- .../plugins/a11yhelp/dialogs/lang/zh-cn.js | 4 +- .../plugins/a11yhelp/dialogs/lang/zh.js | 10 +- .../ckeditor/plugins/about/dialogs/about.js | 10 +- .../about/dialogs/hidpi/logo_ckeditor.png | Bin 13339 -> 12236 bytes .../plugins/about/dialogs/logo_ckeditor.png | Bin 6757 -> 5650 bytes .../Common/ckeditor/plugins/ajax/plugin.js | 5 + .../ckeditor/plugins/autocomplete/plugin.js | 5 + .../plugins/autocomplete/skins/default.css | 43 + .../plugins/clipboard/dialogs/paste.js | 17 +- .../colordialog/dialog/dialogDefinition.js | 4 - .../colordialog/dialogs/colordialog.css | 4 +- .../colordialog/dialogs/colordialog.js | 24 +- .../copyformatting/styles/copyformatting.css | 5 +- .../plugins/dialog/dialogDefinition.js | 4 +- .../ckeditor/plugins/dialog/styles/dialog.css | 18 + .../ckeditor/plugins/div/dialogs/div.js | 17 +- .../ckeditor/plugins/find/dialogs/find.js | 46 +- .../ckeditor/plugins/flash/dialogs/flash.js | 34 +- .../ckeditor/plugins/forms/dialogs/button.js | 12 +- .../plugins/forms/dialogs/checkbox.js | 10 +- .../ckeditor/plugins/forms/dialogs/form.js | 6 +- .../plugins/forms/dialogs/hiddenfield.js | 10 +- .../ckeditor/plugins/forms/dialogs/radio.js | 14 +- .../ckeditor/plugins/forms/dialogs/select.js | 33 +- .../plugins/forms/dialogs/textarea.js | 13 +- .../plugins/forms/dialogs/textfield.js | 18 +- .../Common/ckeditor/plugins/icons.png | Bin 12421 -> 12533 bytes .../Common/ckeditor/plugins/icons_hidpi.png | Bin 40265 -> 39180 bytes .../ckeditor/plugins/iframe/dialogs/iframe.js | 17 +- .../ckeditor/plugins/image/dialogs/image.js | 70 +- .../ckeditor/plugins/link/dialogs/anchor.js | 10 +- .../ckeditor/plugins/link/dialogs/link.js | 52 +- .../plugins/liststyle/dialogs/liststyle.js | 16 +- .../ckeditor/plugins/mentions/plugin.js | 5 + .../plugins/pastefromgdocs/filter/default.js | 8 + .../pastefromlibreoffice/filter/default.js | 11 + .../plugins/pastefromword/filter/default.js | 90 +- .../plugins/pastetools/filter/common.js | 24 + .../plugins/pastetools/filter/image.js | 12 + .../plugins/preview/images/pagebreak.gif | Bin 0 -> 99 bytes .../plugins/preview/styles/screen.css | 10 + .../ckeditor/plugins/scayt/CHANGELOG.md | 18 +- .../Common/ckeditor/plugins/scayt/README.md | 70 +- .../ckeditor/plugins/scayt/dialogs/options.js | 65 +- .../ckeditor/plugins/smiley/dialogs/smiley.js | 4 +- .../plugins/specialchar/dialogs/lang/af.js | 4 +- .../plugins/specialchar/dialogs/lang/ar.js | 4 +- .../plugins/specialchar/dialogs/lang/az.js | 4 +- .../plugins/specialchar/dialogs/lang/bg.js | 6 +- .../plugins/specialchar/dialogs/lang/ca.js | 4 +- .../plugins/specialchar/dialogs/lang/cs.js | 4 +- .../plugins/specialchar/dialogs/lang/cy.js | 4 +- .../plugins/specialchar/dialogs/lang/da.js | 8 +- .../plugins/specialchar/dialogs/lang/de-ch.js | 4 +- .../plugins/specialchar/dialogs/lang/de.js | 4 +- .../plugins/specialchar/dialogs/lang/el.js | 4 +- .../plugins/specialchar/dialogs/lang/en-au.js | 4 +- .../plugins/specialchar/dialogs/lang/en-ca.js | 4 +- .../plugins/specialchar/dialogs/lang/en-gb.js | 4 +- .../plugins/specialchar/dialogs/lang/en.js | 4 +- .../plugins/specialchar/dialogs/lang/eo.js | 4 +- .../plugins/specialchar/dialogs/lang/es-mx.js | 4 +- .../plugins/specialchar/dialogs/lang/es.js | 4 +- .../plugins/specialchar/dialogs/lang/et.js | 20 +- .../plugins/specialchar/dialogs/lang/eu.js | 4 +- .../plugins/specialchar/dialogs/lang/fa.js | 4 +- .../plugins/specialchar/dialogs/lang/fi.js | 4 +- .../plugins/specialchar/dialogs/lang/fr-ca.js | 4 +- .../plugins/specialchar/dialogs/lang/fr.js | 4 +- .../plugins/specialchar/dialogs/lang/gl.js | 4 +- .../plugins/specialchar/dialogs/lang/he.js | 4 +- .../plugins/specialchar/dialogs/lang/hr.js | 4 +- .../plugins/specialchar/dialogs/lang/hu.js | 4 +- .../plugins/specialchar/dialogs/lang/id.js | 4 +- .../plugins/specialchar/dialogs/lang/it.js | 4 +- .../plugins/specialchar/dialogs/lang/ja.js | 4 +- .../plugins/specialchar/dialogs/lang/km.js | 4 +- .../plugins/specialchar/dialogs/lang/ko.js | 4 +- .../plugins/specialchar/dialogs/lang/ku.js | 4 +- .../plugins/specialchar/dialogs/lang/lt.js | 4 +- .../plugins/specialchar/dialogs/lang/lv.js | 4 +- .../plugins/specialchar/dialogs/lang/nb.js | 4 +- .../plugins/specialchar/dialogs/lang/nl.js | 4 +- .../plugins/specialchar/dialogs/lang/no.js | 4 +- .../plugins/specialchar/dialogs/lang/oc.js | 4 +- .../plugins/specialchar/dialogs/lang/pl.js | 4 +- .../plugins/specialchar/dialogs/lang/pt-br.js | 4 +- .../plugins/specialchar/dialogs/lang/pt.js | 4 +- .../plugins/specialchar/dialogs/lang/ro.js | 13 + .../plugins/specialchar/dialogs/lang/ru.js | 4 +- .../plugins/specialchar/dialogs/lang/si.js | 4 +- .../plugins/specialchar/dialogs/lang/sk.js | 4 +- .../plugins/specialchar/dialogs/lang/sl.js | 4 +- .../plugins/specialchar/dialogs/lang/sq.js | 10 +- .../specialchar/dialogs/lang/sr-latn.js | 13 + .../plugins/specialchar/dialogs/lang/sr.js | 13 + .../plugins/specialchar/dialogs/lang/sv.js | 4 +- .../plugins/specialchar/dialogs/lang/th.js | 4 +- .../plugins/specialchar/dialogs/lang/tr.js | 4 +- .../plugins/specialchar/dialogs/lang/tt.js | 4 +- .../plugins/specialchar/dialogs/lang/ug.js | 4 +- .../plugins/specialchar/dialogs/lang/uk.js | 4 +- .../plugins/specialchar/dialogs/lang/vi.js | 4 +- .../plugins/specialchar/dialogs/lang/zh-cn.js | 4 +- .../plugins/specialchar/dialogs/lang/zh.js | 4 +- .../specialchar/dialogs/specialchar.js | 24 +- .../ckeditor/plugins/table/dialogs/table.js | 39 +- .../tableselection/styles/tableselection.css | 16 +- .../plugins/tabletools/dialogs/tableCell.js | 31 +- .../plugins/teamlabcut/icons/teamlabcut.png | Bin 491 -> 904 bytes .../teamlabcut/icons/teamlabcut_hidpi.png | Bin 0 -> 1691 bytes .../ckeditor/plugins/templates/default.js | 6 - .../plugins/templates/dialogs/templates.css | 4 +- .../plugins/templates/dialogs/templates.js | 4 +- .../plugins/templates/images/template1.gif | Bin 367 -> 0 bytes .../plugins/templates/images/template2.gif | Bin 325 -> 0 bytes .../plugins/templates/images/template3.gif | Bin 414 -> 0 bytes .../plugins/templates/templates/default.js | 4 +- .../ckeditor/plugins/textmatch/plugin.js | 5 + .../ckeditor/plugins/textwatcher/plugin.js | 5 + .../ckeditor/plugins/widget/images/handle.png | Bin 0 -> 220 bytes .../Common/ckeditor/plugins/wsc/README.md | 69 +- .../plugins/wsc/dialogs/tmpFrameset.html | 2 +- .../ckeditor/plugins/wsc/dialogs/wsc.js | 167 +- .../plugins/wsc/icons/hidpi/spellchecker.png | Bin 0 -> 2816 bytes .../plugins/wsc/icons/spellchecker.png | Bin 0 -> 836 bytes .../Common/ckeditor/plugins/wsc/lang/af.js | 2 + .../Common/ckeditor/plugins/wsc/lang/ar.js | 2 + .../Common/ckeditor/plugins/wsc/lang/bg.js | 2 + .../Common/ckeditor/plugins/wsc/lang/bn.js | 2 + .../Common/ckeditor/plugins/wsc/lang/bs.js | 2 + .../Common/ckeditor/plugins/wsc/lang/ca.js | 2 + .../Common/ckeditor/plugins/wsc/lang/cs.js | 2 + .../Common/ckeditor/plugins/wsc/lang/cy.js | 2 + .../Common/ckeditor/plugins/wsc/lang/da.js | 2 + .../Common/ckeditor/plugins/wsc/lang/de.js | 2 + .../Common/ckeditor/plugins/wsc/lang/el.js | 2 + .../Common/ckeditor/plugins/wsc/lang/en-au.js | 2 + .../Common/ckeditor/plugins/wsc/lang/en-ca.js | 2 + .../Common/ckeditor/plugins/wsc/lang/en-gb.js | 2 + .../Common/ckeditor/plugins/wsc/lang/en.js | 2 + .../Common/ckeditor/plugins/wsc/lang/eo.js | 2 + .../Common/ckeditor/plugins/wsc/lang/es.js | 2 + .../Common/ckeditor/plugins/wsc/lang/et.js | 2 + .../Common/ckeditor/plugins/wsc/lang/eu.js | 2 + .../Common/ckeditor/plugins/wsc/lang/fa.js | 2 + .../Common/ckeditor/plugins/wsc/lang/fi.js | 2 + .../Common/ckeditor/plugins/wsc/lang/fo.js | 2 + .../Common/ckeditor/plugins/wsc/lang/fr-ca.js | 2 + .../Common/ckeditor/plugins/wsc/lang/fr.js | 2 + .../Common/ckeditor/plugins/wsc/lang/gl.js | 2 + .../Common/ckeditor/plugins/wsc/lang/gu.js | 2 + .../Common/ckeditor/plugins/wsc/lang/he.js | 2 + .../Common/ckeditor/plugins/wsc/lang/hi.js | 2 + .../Common/ckeditor/plugins/wsc/lang/hr.js | 2 + .../Common/ckeditor/plugins/wsc/lang/hu.js | 2 + .../Common/ckeditor/plugins/wsc/lang/is.js | 2 + .../Common/ckeditor/plugins/wsc/lang/it.js | 2 + .../Common/ckeditor/plugins/wsc/lang/ja.js | 2 + .../Common/ckeditor/plugins/wsc/lang/ka.js | 2 + .../Common/ckeditor/plugins/wsc/lang/km.js | 2 + .../Common/ckeditor/plugins/wsc/lang/ko.js | 2 + .../Common/ckeditor/plugins/wsc/lang/ku.js | 2 + .../Common/ckeditor/plugins/wsc/lang/lt.js | 2 + .../Common/ckeditor/plugins/wsc/lang/lv.js | 2 + .../Common/ckeditor/plugins/wsc/lang/mk.js | 2 + .../Common/ckeditor/plugins/wsc/lang/mn.js | 2 + .../Common/ckeditor/plugins/wsc/lang/ms.js | 2 + .../Common/ckeditor/plugins/wsc/lang/nb.js | 2 + .../Common/ckeditor/plugins/wsc/lang/nl.js | 2 + .../Common/ckeditor/plugins/wsc/lang/no.js | 2 + .../Common/ckeditor/plugins/wsc/lang/pl.js | 2 + .../Common/ckeditor/plugins/wsc/lang/pt-br.js | 2 + .../Common/ckeditor/plugins/wsc/lang/pt.js | 2 + .../Common/ckeditor/plugins/wsc/lang/ro.js | 2 + .../Common/ckeditor/plugins/wsc/lang/ru.js | 2 + .../Common/ckeditor/plugins/wsc/lang/sk.js | 2 + .../Common/ckeditor/plugins/wsc/lang/sl.js | 2 + .../ckeditor/plugins/wsc/lang/sr-latn.js | 2 + .../Common/ckeditor/plugins/wsc/lang/sr.js | 2 + .../Common/ckeditor/plugins/wsc/lang/sv.js | 2 + .../Common/ckeditor/plugins/wsc/lang/th.js | 2 + .../Common/ckeditor/plugins/wsc/lang/tr.js | 2 + .../Common/ckeditor/plugins/wsc/lang/ug.js | 2 + .../Common/ckeditor/plugins/wsc/lang/uk.js | 2 + .../Common/ckeditor/plugins/wsc/lang/vi.js | 2 + .../Common/ckeditor/plugins/wsc/lang/zh-cn.js | 1 + .../Common/ckeditor/plugins/wsc/lang/zh.js | 1 + .../Common/ckeditor/plugins/wsc/plugin.js | 5 + .../Common/ckeditor/plugins/xml/plugin.js | 5 + .../Common/ckeditor/samples/css/samples.css | 1632 --- .../ckeditor/samples/img/github-top.png | Bin 383 -> 0 bytes .../Common/ckeditor/samples/img/header-bg.png | Bin 13086 -> 0 bytes .../ckeditor/samples/img/header-separator.png | Bin 123 -> 0 bytes .../Common/ckeditor/samples/img/logo.png | Bin 5891 -> 0 bytes .../ckeditor/samples/img/navigation-tip.png | Bin 12029 -> 0 bytes .../Common/ckeditor/samples/index.html | 128 - .../Common/ckeditor/samples/js/sample.js | 53 - .../Common/ckeditor/samples/js/sf.js | 17 - .../Common/ckeditor/samples/old/ajax.html | 85 - .../Common/ckeditor/samples/old/api.html | 210 - .../Common/ckeditor/samples/old/appendto.html | 59 - .../samples/old/assets/inlineall/logo.png | Bin 4283 -> 0 bytes .../old/assets/outputxhtml/outputxhtml.css | 204 - .../ckeditor/samples/old/assets/sample.jpg | Bin 14449 -> 0 bytes .../old/assets/uilanguages/languages.js | 7 - .../ckeditor/samples/old/datafiltering.html | 508 - .../samples/old/dialog/assets/my_dialog.js | 48 - .../ckeditor/samples/old/dialog/dialog.html | 190 - .../ckeditor/samples/old/divreplace.html | 144 - .../samples/old/enterkey/enterkey.html | 106 - .../assets/outputforflash/outputforflash.fla | Bin 85504 -> 0 bytes .../assets/outputforflash/outputforflash.swf | Bin 15571 -> 0 bytes .../assets/outputforflash/swfobject.js | 19 - .../old/htmlwriter/outputforflash.html | 283 - .../samples/old/htmlwriter/outputhtml.html | 224 - .../Common/ckeditor/samples/old/index.html | 131 - .../ckeditor/samples/old/inlineall.html | 314 - .../ckeditor/samples/old/inlinebycode.html | 124 - .../ckeditor/samples/old/inlinetextarea.html | 113 - .../Common/ckeditor/samples/old/jquery.html | 103 - .../samples/old/magicline/magicline.html | 209 - .../Common/ckeditor/samples/old/readonly.html | 76 - .../ckeditor/samples/old/replacebyclass.html | 60 - .../ckeditor/samples/old/replacebycode.html | 59 - .../Common/ckeditor/samples/old/sample.css | 357 - .../Common/ckeditor/samples/old/sample.js | 50 - .../Common/ckeditor/samples/old/tabindex.html | 78 - .../ckeditor/samples/old/toolbar/toolbar.html | 235 - .../Common/ckeditor/samples/old/uicolor.html | 72 - .../ckeditor/samples/old/uilanguages.html | 122 - .../samples/old/wysiwygarea/fullpage.html | 80 - .../ckeditor/samples/old/xhtmlstyle.html | 234 - .../toolbarconfigurator/css/fontello.css | 55 - .../toolbarconfigurator/font/config.json | 28 - .../toolbarconfigurator/font/fontello.eot | Bin 4988 -> 0 bytes .../toolbarconfigurator/font/fontello.svg | 14 - .../toolbarconfigurator/font/fontello.ttf | Bin 4820 -> 0 bytes .../toolbarconfigurator/font/fontello.woff | Bin 2904 -> 0 bytes .../samples/toolbarconfigurator/index.html | 446 - .../js/abstracttoolbarmodifier.js | 13 - .../js/fulltoolbareditor.js | 9 - .../toolbarconfigurator/js/toolbarmodifier.js | 33 - .../js/toolbartextmodifier.js | 14 - .../lib/codemirror/LICENSE | 19 - .../lib/codemirror/codemirror.css | 325 - .../lib/codemirror/codemirror.js | 288 - .../lib/codemirror/javascript.js | 25 - .../lib/codemirror/neo.css | 36 - .../lib/codemirror/show-hint.css | 38 - .../lib/codemirror/show-hint.js | 16 - .../ckeditor/skins/moono-lisa/dialog.css | 6 +- .../ckeditor/skins/moono-lisa/dialog_ie.css | 6 +- .../ckeditor/skins/moono-lisa/dialog_ie8.css | 6 +- .../skins/moono-lisa/dialog_iequirks.css | 6 +- .../ckeditor/skins/moono-lisa/editor.css | 6 +- .../skins/moono-lisa/editor_gecko.css | 6 +- .../ckeditor/skins/moono-lisa/editor_ie.css | 6 +- .../ckeditor/skins/moono-lisa/editor_ie8.css | 6 +- .../skins/moono-lisa/editor_iequirks.css | 6 +- .../ckeditor/skins/moono-lisa/icons.png | Bin 12421 -> 12533 bytes .../ckeditor/skins/moono-lisa/icons_hidpi.png | Bin 40265 -> 39180 bytes .../ckeditor/skins/moono-lisa/readme.md | 6 +- .../Common/ckeditor/skins/teamlab/editor.css | 4 +- .../ckeditor/skins/teamlab/editor_gecko.css | 4 +- .../Common/ckeditor/skins/teamlab/icons.png | Bin 11296 -> 18675 bytes .../ckeditor/skins/teamlab/icons_hidpi.png | Bin 54744 -> 51263 bytes .../skins/teamlab/images/close-ckeditor.png | Bin 0 -> 615 bytes .../UserControls/Common/ckeditor/styles.js | 8 +- .../Common/ckeditor/vendor/promise.js | 13 + .../UserControls/DeepLink/DeepLinkData.cs | 31 + .../UserControls/DeepLink/DeepLinkDataFile.cs | 22 + .../DeepLink/DeepLinkDataFolder.cs | 21 + .../UserControls/DeepLink/DeepLinking.ascx | 25 + .../UserControls/DeepLink/DeepLinking.ascx.cs | 145 + .../DeepLink/DeepLinking.ascx.designer.cs | 8 + .../DeepLink/css/deeplinking.less | 93 + .../UserControls/DeepLink/js/deeplinking.js | 49 + .../CommunityDashboardEmptyScreen.ascx | 2 +- .../UserControls/EmptyScreens/js/dashboard.js | 6 +- .../UserControls/Feed/FeedList.ascx.cs | 1 + .../UserControls/Feed/NewNavigationPanel.ascx | 9 + .../Feed/NewNavigationPanel.ascx.cs | 2 + .../UserControls/Feed/css/feed.less | 746 +- .../UserControls/Feed/js/feed.filter.js | 2 + .../UserControls/Feed/js/feed.js | 98 +- .../FirstTime/EmailAndPassword.ascx.cs | 12 +- .../UserControls/FirstTime/js/manager.js | 17 +- .../UserControls/FirstTime/js/view.js | 2 +- .../AccessRights/js/accessrights.js | 18 +- .../AdminHelperSettings.ascx | 12 + .../AdminHelperSettings.ascx.cs | 38 + .../AdminHelperSettings.ascx.designer.cs | 17 + .../css/adminhelpersettings.less | 61 + .../AdminHelperSettings/css/images/cross.svg | 3 + .../css/images/subtract.svg | 3 + .../js/adminhelpersettings.js | 54 + .../Management/AuditTrail/AuditTrail.ascx.cs | 3 +- .../Management/AuditTrail/js/audittrail.js | 4 +- .../AuthorizationKeys/AuthorizationKeys.ascx | 4 +- .../AuthorizationKeys.ascx.cs | 14 +- .../AuthorizationKeys/img/appleid.svg | 11 + .../AuthorizationKeys/img/microsoft.svg | 14 + .../AuthorizationKeys/js/authorizationkeys.js | 12 +- .../Management/Backup/Backup.ascx.cs | 4 +- .../Management/Backup/js/backup.js | 19 +- .../ConfirmActivation/ConfirmActivation.ascx | 26 +- .../ConfirmActivation.ascx.cs | 26 +- .../css/confirmactivation.less | 92 +- .../ConfirmActivation/js/confirmactivation.js | 217 +- .../ConfirmInviteActivation.ascx | 19 +- .../ConfirmInviteActivation.ascx.cs | 17 +- .../css/confirm_invite_activation.less | 67 +- .../js/confirm_invite_activation.js | 342 +- .../Management/ConfirmPortalActivity.ascx | 2 +- .../Management/ConfirmPortalActivity.ascx.cs | 17 +- .../CookieSettings/js/cookiesettings.js | 4 +- .../CustomNavigation/js/customnavigation.js | 8 +- .../DefaultPageSettings/js/defaultpage.js | 2 +- .../Management/DnsSettings/DnsSettings.ascx | 47 +- .../DnsSettings/DnsSettings.ascx.cs | 69 +- .../DnsSettings/DnsSettings.ascx.designer.cs | 19 +- .../Management/DnsSettings/dnssettings.js | 4 +- .../Management/DocService/js/docservice.js | 34 +- .../FullTextSearch/js/fulltextsearch.js | 4 +- .../GreetingLogoSettings.ascx.cs | 2 +- .../Management/InvitePanel/InvitePanel.ascx | 2 +- .../Management/IpSecurity/js/ipsecurity.js | 2 +- .../LdapSettings/LdapSettings.ascx.cs | 4 +- .../LdapSettings/css/Default/ldapsettings.css | 4 +- .../LdapSettings/js/ldapsettings.js | 274 +- .../LoginHistory/LoginHistory.ascx.cs | 3 +- .../LoginHistory/js/loginhistory.js | 4 +- .../js/maildomainsettings.js | 6 +- .../Management/MailService/js/mailservice.js | 6 +- .../Management/Monitoring/LogHelper.ascx.cs | 18 +- .../Monitoring/js/servicehealthchecker.js | 8 +- .../NamingPeopleSettings/js/namingpeople.js | 2 +- .../js/namingpeoplecontent.js | 4 +- .../PasswordSettings/js/passwordsettings.js | 2 +- .../PortalAccessSettings/js/portalaccess.js | 2 +- .../PortalRename/js/portalrename.js | 6 +- .../PricingPageSettings.ascx | 3 +- .../PricingPageSettings.ascx.cs | 6 +- .../js/pricingpagesettings.js | 2 +- .../Management/PrivacyRoom/js/privacyroom.js | 2 +- .../js/productsandinstruments.js | 11 +- .../ProfileOperation/ProfileOperation.ascx.cs | 2 +- .../Management/Restore/Restore.ascx.cs | 2 +- .../Management/Restore/js/restore.js | 20 +- .../SingleSignOnSettings.ascx.cs | 6 +- .../js/singlesignonsettings.js | 58 +- .../ConfirmMobileActivation.ascx.cs | 27 +- .../SmsControls/SmsValidationSettings.ascx | 19 +- .../SmsControls/SmsValidationSettings.ascx.cs | 10 +- .../Management/SmsControls/js/changemobile.js | 2 +- .../SmsControls/js/confirmmobile.js | 6 +- .../SmsControls/js/smsvalidation.js | 2 +- .../SmtpSettings/js/smtpsettings.js | 47 +- .../TariffSettings/TariffCustom.ascx | 2 +- .../TariffSettings/TariffCustom.ascx.cs | 1 - .../TariffSettings/TariffHistory.ascx | 2 +- .../TariffSettings/TariffNotify.ascx.cs | 8 +- .../Management/TariffSettings/TariffSaas.ascx | 18 - .../TariffSettings/TariffSaas.ascx.cs | 46 +- .../TariffSettings/TariffStandalone.ascx | 14 +- .../TariffSettings/TariffStandalone.ascx.cs | 10 - .../TariffSettings/TariffUsage.ascx | 429 - .../TariffSettings/TariffUsage.ascx.cs | 554 - .../TariffUsage.ascx.designer.cs | 24 - .../TariffSettings/css/tariffcustom.less | 410 + .../TariffSettings/css/tariffusage.less | 506 - .../TariffSettings/js/tariffcustom.js | 16 +- .../TariffSettings/js/tariffnotify.js | 2 +- .../TariffSettings/js/tariffsaas.js | 2 +- .../TariffSettings/js/tariffstandalone.js | 8 +- .../TariffSettings/js/tariffusage.js | 292 - .../Management/TfaControls/ConfirmTfa.ascx.cs | 23 +- .../Management/TfaControls/js/confirmtfa.js | 2 +- .../TimeAndLanguage/TimeAndLanguage.ascx.cs | 2 +- .../TimeAndLanguage/js/timeandlanguage.js | 2 +- .../TransferPortal/js/transferportal.js | 2 +- .../Management/WhiteLabel/WhiteLabel.ascx | 2 +- .../Management/WhiteLabel/WhiteLabel.ascx.cs | 4 +- .../Management/WhiteLabel/js/whitelabel.js | 6 +- .../ProductQuotes/ProductQuotes.ascx.cs | 4 +- .../ProductQuotes/js/product_quotes.js | 2 +- .../VisitorsChart/js/jquery.flot.js | 20 +- .../VisitorsChart/js/visitorschart.js | 14 +- .../js/resendinvitescontrol.js | 6 +- .../UserConnections/UserConnections.ascx | 118 + .../UserConnections/UserConnections.ascx.cs | 45 + .../UserConnections.ascx.designer.cs | 35 + .../UserConnections/css/connections.less | 76 + .../UserConnections/js/connections_manager.js | 153 + .../UserProfile/AccountLinkControl.ascx.cs | 12 +- .../Users/UserProfile/UserLanguage.ascx.cs | 2 +- .../Users/UserProfile/UserProfileActions.ascx | 11 +- .../Users/UserProfile/UserProfileControl.ascx | 25 +- .../UserProfile/UserProfileControl.ascx.cs | 5 - .../UserProfile/UserProfileEditControl.ascx | 8 +- .../UserProfileEditControl.ascx.cs | 2 + .../UserProfile/css/accountlink_style.less | 8 + .../Users/UserProfile/css/images/appleid.svg | 11 + .../UserProfile/css/images/microsoft.svg | 6 + .../css/images/social-icons-grey.png | Bin 3882 -> 6537 bytes .../css/images/social-icons-small.png | Bin 5003 -> 8854 bytes .../css/profileeditcontrol_style.less | 10 +- .../css/userprofilecontrol_style.less | 14 +- .../Users/UserProfile/js/accountlinker.js | 2 +- .../Users/UserProfile/js/deleteuser.js | 6 +- .../UserProfile/js/userprofilecontrol.js | 50 +- .../UserProfile/js/userprofileeditcontrol.js | 42 +- .../UserSubscriptions/css/subscriptions.less | 1 + .../js/subscription_manager.js | 20 +- .../ASC.Web.Studio/Utility/RefererURL.cs | 44 + .../ASC.Web.Studio/Utility/TenantExtra.cs | 13 +- web/studio/ASC.Web.Studio/Web.config | 181 +- web/studio/ASC.Web.Studio/Wizard.aspx.cs | 2 +- .../addons/calendar/ASC.Web.Calendar.csproj | 22 +- .../addons/calendar/Addon/CalendarAddon.cs | 5 + .../CalendarSpaceUsageStatManager.cs | 58 + .../DocumentsPopup/DocumentsPopup.ascx | 15 + .../DocumentsPopup/DocumentsPopup.ascx.cs | 54 + .../DocumentsPopup.ascx.designer.cs | 33 + .../DocumentsPopup/css/documentspopup.less | 67 + .../DocumentsPopup/css/images/warning.png | Bin 0 -> 3537 bytes .../DocumentsPopup/js/documentspopup.js | 203 + .../addons/calendar/Handlers/FilesUploader.cs | 100 + .../CalendarAddonResource.Designer.cs | 9 + .../CalendarAddonResource.az-Latn-AZ.resx | 2 +- .../Resources/CalendarAddonResource.resx | 3 + .../Resources/CalendarJSResource.Designer.cs | 198 + .../CalendarJSResource.az-Latn-AZ.resx | 290 +- .../Resources/CalendarJSResource.resx | 66 + .../UserControls/CalendarControl.ascx | 13 +- .../UserControls/CalendarControl.ascx.cs | 14 +- .../CalendarControl.ascx.designer.cs | 30 +- .../UserControls/CalendarResources.ascx | 26 + .../UserControls/CalendarTemplates.ascx | 206 +- .../fullcalendar/css/attachments.less | 312 + .../fullcalendar/css/fullcalendar.less | 118 +- .../UserControls/fullcalendar/fullcalendar.js | 1159 ++- .../fullcalendar/img/base_sprite.png | Bin 0 -> 2681 bytes .../UserControls/js/calendar.attachments.js | 944 ++ .../UserControls/js/calendar_controller.js | 60 +- .../UserControls/js/calendar_event_page.js | 10 +- .../UserControls/js/jquery.mousewheel.js | 4 +- .../calendar/UserControls/popup/popup.js | 30 +- .../ASC.Web.Studio/addons/calendar/web.config | 152 +- .../addons/mail/ASC.Web.Mail.csproj | 27 +- .../App_Themes/default/less/accounts.less | 10 + .../default/less/administration.less | 2 +- .../App_Themes/default/less/calendar.less | 6 + .../mail/App_Themes/default/less/filters.less | 1 + .../mail/App_Themes/default/less/mail.less | 31 +- .../mail/App_Themes/default/less/message.less | 110 +- .../App_Themes/default/less/messages.less | 26 +- .../mail/App_Themes/default/less/print.less | 5 +- .../mail/Controls/BlankModal/js/blankmodal.js | 4 +- .../DocumentsPopup/DocumentsPopup.ascx | 42 +- .../DocumentsPopup/DocumentsPopup.ascx.cs | 112 +- .../DocumentsPopup.ascx.designer.cs | 66 +- .../DocumentsPopup/css/documentspopup.less | 166 +- .../DocumentsPopup/js/documentspopup.js | 422 +- .../mail/Controls/MailBox/MailBox.ascx.cs | 2 +- .../ASC.Web.Studio/addons/mail/Default.aspx | 3 + .../addons/mail/Default.aspx.cs | 11 +- .../mail/HttpHandlers/ContactPhoto.ashx.cs | 10 +- ...MailActionCompleteResource.az-Latn-AZ.resx | 151 + ...MailAdministrationResource.az-Latn-AZ.resx | 215 +- .../MailApiErrorsResource.az-Latn-AZ.resx | 115 + .../Resources/MailApiResource.az-Latn-AZ.resx | 68 +- .../mail/Resources/MailApiResource.resx | 4 +- .../MailAttachmentsResource.az-Latn-AZ.resx | 76 + .../mail/Resources/MailResource.Designer.cs | 47 + .../Resources/MailResource.az-Latn-AZ.resx | 563 +- .../addons/mail/Resources/MailResource.resx | 21 +- .../Resources/MailScriptResource.Designer.cs | 9 + .../MailScriptResource.az-Latn-AZ.resx | 713 +- .../mail/Resources/MailScriptResource.resx | 7 +- .../addons/mail/js/accountspanel.js | 2 +- .../addons/mail/js/actionmenu.js | 6 +- .../addons/mail/js/actionpanel.js | 6 +- .../mail/js/administration/administration.js | 4 +- .../js/administration/modal/createdomain.js | 16 +- .../js/administration/modal/createmailbox.js | 8 +- .../administration/modal/createmailgroup.js | 10 +- .../js/administration/modal/editmailbox.js | 12 +- .../js/administration/modal/editmailgroup.js | 6 +- .../addons/mail/js/administration/page.js | 36 +- .../plugin/jquery-domainadvansedselector.js | 2 +- .../plugin/jquery-mailboxadvansedselector.js | 2 +- .../ASC.Web.Studio/addons/mail/js/alerts.js | 12 +- .../mail/js/autocomplete/crmautocomplete.js | 2 +- .../mail/js/autocomplete/emailautocomplete.js | 8 +- .../addons/mail/js/blankpage.js | 2 +- .../mail/js/contacts/filter/crmfilter.js | 4 +- .../mail/js/contacts/filter/customfilter.js | 2 +- .../mail/js/contacts/filter/tlfilter.js | 13 +- .../mail/js/contacts/modal/editcontact.js | 32 +- .../addons/mail/js/contacts/page.js | 56 +- .../addons/mail/js/contacts/tlgroups.js | 58 - .../ASC.Web.Studio/addons/mail/js/dropdown.js | 4 +- .../addons/mail/js/filters/edit.js | 14 +- .../addons/mail/js/filters/manager.js | 4 +- .../addons/mail/js/filters/modal.js | 24 +- .../addons/mail/js/fromsenderfilter.js | 4 +- .../ASC.Web.Studio/addons/mail/js/init.js | 25 +- .../addons/mail/js/mail.accounts.js | 2 +- .../addons/mail/js/mail.accountsmodal.js | 146 +- .../addons/mail/js/mail.accountspage.js | 39 +- .../addons/mail/js/mail.attachmentmanager.js | 54 +- .../addons/mail/js/mail.cache.js | 16 +- .../addons/mail/js/mail.calendar.js | 33 +- .../addons/mail/js/mail.common.js | 2 +- .../addons/mail/js/mail.crmlinkpopup.js | 12 +- .../addons/mail/js/mail.default.js | 14 +- .../addons/mail/js/mail.folderfilter.js | 4 +- .../addons/mail/js/mail.folderpanel.js | 6 +- .../addons/mail/js/mail.mailbox.js | 99 +- .../addons/mail/js/mail.messagepage.js | 308 +- .../addons/mail/js/mail.navigation.js | 4 +- .../addons/mail/js/mail.servicemanager.js | 5 - .../ASC.Web.Studio/addons/mail/js/popup.js | 2 +- .../addons/mail/js/tags/colorspopup.js | 6 +- .../addons/mail/js/tags/dropdown.js | 12 +- .../addons/mail/js/tags/modal.js | 56 +- .../addons/mail/js/tags/page.js | 2 +- .../addons/mail/js/tags/panel.js | 2 +- .../addons/mail/js/tags/tags.js | 4 +- .../js/third-party/jquery.dotdotdot.min.js | 4 +- .../js/third-party/jquery.textchange.min.js | 6 +- .../mail/js/third-party/linkify-html.min.js | 1 + .../mail/js/third-party/linkify-string.min.js | 2 +- .../addons/mail/js/third-party/linkify.min.js | 2 +- .../addons/mail/js/third-party/placeholder.js | 8 +- .../addons/mail/js/userfolders/dropdown.js | 8 +- .../addons/mail/js/userfolders/manager.js | 4 +- .../addons/mail/js/userfolders/modal.js | 26 +- .../addons/mail/js/userfolders/panel.js | 8 +- .../ASC.Web.Studio/addons/mail/js/wysiwyg.js | 37 +- .../addons/mail/templates/accountsTmpl.html | 5 + .../changeMailboxPasswordPopupTmpl.html | 10 +- .../addons/mail/templates/calendarTmpl.html | 12 + .../mail/templates/editMessageTmpl.html | 37 +- .../addons/mail/templates/messageTmpl.html | 20 +- .../addons/mail/templates/messagesTmpl.html | 45 +- .../ASC.Web.Studio/addons/mail/web.config | 128 +- .../addons/talk/ASC.Web.Talk.csproj | 2 +- .../talk/HttpHandlers/UploadFileHandler.cs | 2 +- .../addons/talk/JabberClient.aspx.cs | 8 +- .../TalkOverviewResource.az-Latn-AZ.resx | 53 +- .../Resources/TalkResource.az-Latn-AZ.resx | 50 +- .../addons/talk/css/default/talk.style.css | 5 + .../addons/talk/js/talk.contactscontainer.js | 74 +- .../addons/talk/js/talk.default.js | 84 +- .../addons/talk/js/talk.init.js | 4 +- .../addons/talk/js/talk.meseditorcontainer.js | 44 +- .../addons/talk/js/talk.roomscontainer.js | 34 +- .../addons/talk/js/talk.tabscontainer.js | 14 +- .../ASC.Web.Studio/addons/talk/web.config | 152 +- web/studio/ASC.Web.Studio/confirm.aspx.cs | 39 +- .../ASC.Web.Studio/js/asc/api/api.factory.js | 62 +- .../ASC.Web.Studio/js/asc/api/api.helper.js | 14 +- .../ASC.Web.Studio/js/asc/api/asc.teamlab.js | 237 +- .../js/asc/core/asc.anchorcontroller.js | 2 +- .../js/asc/core/asc.feedreader.js | 43 + .../js/asc/core/asc.files.utility.js | 21 +- .../js/asc/core/asc.mail.utility.js | 206 +- .../js/asc/core/asc.topstudiopanel.js | 14 +- .../js/asc/core/basetemplate.master.init.js | 4 +- .../ASC.Web.Studio/js/asc/core/clipboard.js | 16 + .../ASC.Web.Studio/js/asc/core/common.js | 177 +- .../js/asc/core/groupselector.js | 16 +- web/studio/ASC.Web.Studio/js/asc/core/my.js | 27 - .../js/asc/core/voip.navigationitem.js | 2 +- .../ASC.Web.Studio/js/asc/core/voip.phone.js | 2 +- .../js/asc/plugins/jquery-advansedfilter.js | 64 +- .../js/asc/plugins/jquery-advansedselector.js | 85 +- .../plugins/jquery-contactadvansedselector.js | 2 +- .../js/asc/plugins/jquery-customcombobox.js | 24 +- .../plugins/jquery-emailadvansedselector.js | 47 +- .../plugins/jquery-groupadvansedselector.js | 4 +- .../plugins/jquery-projectadvansedselector.js | 2 +- .../plugins/jquery-useradvansedselector.js | 35 +- .../js/asc/plugins/jquery.dropdowntoggle.js | 4 +- .../js/asc/plugins/jquery.helper.js | 8 +- .../js/asc/plugins/jquery.tlblock.js | 2 +- .../js/asc/plugins/jquery.tlcombobox.js | 34 +- .../js/asc/plugins/phonecontroller.js | 8 +- .../js/asc/plugins/progressdialog.js | 2 +- .../js/asc/plugins/userselector.js | 1265 +++ .../js/third-party/ajaxpro.core.js | 86 +- .../js/third-party/jquery/jquery.cookies.js | 2 +- .../js/third-party/jquery/jquery.cron.js | 16 +- .../js/third-party/jquery/jquery.jplayer.js | 4 +- .../js/third-party/jquery/jquery.json.js | 2 +- .../js/third-party/jquery/jquery.scrollto.js | 4 +- .../js/third-party/jquery/jquery.tmpl.js | 12 +- .../js/third-party/jquery/jquery.ui.js | 11 +- .../js/third-party/jquery/jstree.min.js | 12 +- .../js/third-party/jquery/noty/jquery.noty.js | 4 +- .../js/third-party/jquery/toastr.js | 2 +- .../js/uploader/jquery.fileuploadmanager.js | 28 +- .../skins/default/actionpanel.less | 10 +- .../ASC.Web.Studio/skins/default/common.less | 17 + .../skins/default/common_style.less | 21 +- .../skins/default/contentmenu.less | 1 + .../ASC.Web.Studio/skins/default/helper.less | 2 +- .../images/logo/{spreadsheet.ico => cell.ico} | Bin .../skins/default/images/logo/dark.png | Bin 5311 -> 5999 bytes .../default/images/logo/dark_general.png | Bin 2437 -> 3355 bytes .../skins/default/images/logo/editor_logo.png | Bin 2300 -> 1886 bytes .../default/images/logo/editor_logo_embed.png | Bin 1826 -> 1716 bytes .../images/logo/editor_logo_embed_general.png | Bin 993 -> 950 bytes .../images/logo/editor_logo_general.png | Bin 1050 -> 986 bytes .../skins/default/images/logo/light_small.png | Bin 2635 -> 3695 bytes .../skins/default/images/logo/light_small.svg | 47 +- .../images/logo/light_small_general.png | Bin 1446 -> 1867 bytes .../images/logo/light_small_general.svg | 45 +- .../logo/{presentation.ico => slide.ico} | Bin .../images/logo/{text.ico => word.ico} | Bin .../skins/default/images/mail_user.png | Bin 1183 -> 0 bytes .../skins/default/images/support.png | Bin 3660 -> 0 bytes .../images/svg/context/icon_form_filling.svg | 7 + .../skins/default/images/svg/feed-icons.svg | 16 + .../skins/default/images/svg/mail-icons.svg | 11 + .../skins/default/images/svg/mail/letter.svg | 16 + .../default/images/svg/people/appleid.png | Bin 0 -> 1595 bytes .../images/svg/people/currentconnection.svg | 3 + .../default/images/svg/people/microsoft.png | Bin 0 -> 1585 bytes .../images/svg/share/access_comment.svg | 3 + .../default/images/svg/share/access_deny.svg | 4 + .../images/svg/share/access_filter.svg | 3 + .../default/images/svg/share/access_form.svg | 3 + .../default/images/svg/share/access_full.svg | 4 + .../default/images/svg/share/access_owner.svg | 3 + .../default/images/svg/share/access_read.svg | 10 + .../images/svg/share/access_review.svg | 3 + .../images/svg/share/access_varies.svg | 8 + .../skins/default/images/svg/share/close.svg | 5 + .../default/images/svg/share/group_avatar.svg | 9 + .../default/images/svg/share/user_avatar.svg | 5 + .../skins/default/jquery-advansedfilter.css | 4 +- .../default/jquery-advansedselector.less | 35 +- .../skins/default/jquery_style.css | 7 +- .../skins/default/layout-desktop.css | 83 - .../skins/default/layout-desktop.less | 89 + .../skins/default/layout-media.css | 88 - .../skins/default/layout-media.less | 100 + .../ASC.Web.Studio/skins/default/layout.css | 190 - .../ASC.Web.Studio/skins/default/layout.less | 197 + .../skins/default/sidepanel.less | 6 +- .../skins/default/topstudiopanel.less | 321 +- .../skins/default/userselector.less | 348 + .../ASC.Web.Studio/web.appsettings.config | 23 +- web/studio/ASC.Web.Studio/web.autofac.config | 3 +- .../ASC.Web.Studio/web.consumers.config | 26 +- web/studio/ASC.Web.Studio/web.nlog.config | 17 +- .../ASC.Web.Upload/ASC.Web.Upload.csproj | 23 +- web/studio/ASC.Web.Upload/Web.config | 180 +- web/studio/ASC.Web.Upload/web.autofac.config | 2 +- web/studio/ASC.Web.Upload/web.nlog.config | 15 +- 4526 files changed, 295381 insertions(+), 245902 deletions(-) create mode 100644 .gitignore delete mode 100644 .nuget/packages/AjaxPro.2.2.9.17.2.nupkg delete mode 100644 .nuget/packages/AjaxPro.2.2.9.17.2.symbols.nupkg create mode 100644 .nuget/packages/AjaxPro.2.21.12.22.2.nupkg create mode 100644 .nuget/packages/AjaxPro.2.21.12.22.2.symbols.nupkg delete mode 100644 .nuget/packages/AppLimit.CloudComputing.SharpBox.1.1.0.452.nupkg delete mode 100644 .nuget/packages/AppLimit.CloudComputing.SharpBox.1.1.0.452.symbols.nupkg create mode 100644 .nuget/packages/AppLimit.CloudComputing.SharpBox.1.2.0.1.nupkg delete mode 100644 .nuget/packages/Microsoft.Graph.Core.1.20.1.4.nupkg delete mode 100644 .nuget/packages/Microsoft.Graph.Core.1.20.1.4.symbols.nupkg create mode 100644 .nuget/packages/Microsoft.Graph.Core.1.6.0.nupkg delete mode 100644 .nuget/packages/Microsoft.OneDriveSDK.2.0.7.5.nupkg delete mode 100644 .nuget/packages/Microsoft.OneDriveSDK.2.0.7.5.symbols.nupkg create mode 100644 .nuget/packages/Microsoft.OneDriveSDK.2.1.0.nupkg create mode 100644 .nuget/packages/Openstack.net.1.8.0.nupkg create mode 100644 .nuget/packages/Rackspace.1.0.0.nupkg delete mode 100644 .nuget/packages/twitterizer.2.4.2.2.nupkg delete mode 100644 .nuget/packages/twitterizer.2.4.2.2.symbols.nupkg create mode 100644 build/install/deb/debian/onlyofficeAutoCleanUp.service create mode 100644 build/install/deb/debian/onlyofficeMailImap.service create mode 100644 build/install/deb/debian/onlyofficeWebDav.service create mode 100644 build/install/rpm/Files/systemd/onlyofficeAutoCleanUp.service create mode 100644 build/install/rpm/Files/systemd/onlyofficeMailImap.service create mode 100644 build/install/rpm/Files/systemd/onlyofficeWebDav.service create mode 100644 build/sql/onlyoffice.upgradev120.sql delete mode 100644 common/ASC.Common/Logging/SelfCleaningTarget.cs create mode 100644 common/ASC.Common/Radicale/CalDavCalendar.cs create mode 100644 common/ASC.Common/Radicale/CardDavAddressbook.cs create mode 100644 common/ASC.Common/Radicale/CardDavItem.cs create mode 100644 common/ASC.Common/Radicale/Core/DbRadicale.cs create mode 100644 common/ASC.Common/Radicale/Core/RadicaleClient.cs create mode 100644 common/ASC.Common/Radicale/Core/RadicaleException.cs create mode 100644 common/ASC.Common/Radicale/DavRequest.cs create mode 100644 common/ASC.Common/Radicale/DavResponse.cs create mode 100644 common/ASC.Common/Radicale/IRadicaleEntity.cs create mode 100644 common/ASC.Common/Radicale/RadicaleEntity.cs create mode 100644 common/ASC.Common/Utils/StringUtils.cs create mode 100644 common/ASC.Core.Common/Audit/BaseEvent.cs delete mode 100644 common/ASC.Core.Common/Billing/CouponManager.cs create mode 100644 common/ASC.Core.Common/Contracts/HealthCheck/HealthCheckSvc.cs create mode 100644 common/ASC.Core.Common/Data/DbLoginEventsManager.cs create mode 100644 common/ASC.Core.Common/Logging/LogManager.cs rename {module/ASC.MessagingSystem => common/ASC.Core.Common/Messaging}/MessageAction.cs (91%) create mode 100644 common/ASC.Core.Common/Notify/MentionProvider.cs delete mode 100644 common/ASC.Core.Common/Notify/ReplyToTagProvider.cs create mode 100644 common/ASC.Core.Common/Notify/Signalr/MailNotificationState.cs create mode 100644 common/ASC.Data.Backup/Service/BackupCleanerTempFileService.cs create mode 100644 common/ASC.Notify.Textile/MarkDownStyler.cs create mode 100644 common/ASC.Notify.Textile/Resources/NotifyTemplateResource.az-Latn-AZ.resx create mode 100644 licenses/javascript plugins/linkify-html.license create mode 100644 licenses/npm packages/axios.license create mode 100644 licenses/npm packages/form-data.license create mode 100644 licenses/npm packages/get-byte.license create mode 100644 licenses/npm packages/help.license create mode 100644 licenses/npm packages/ipaddr.license create mode 100644 licenses/npm packages/to-byte-array.license create mode 100644 licenses/npm packages/webdav-server.license delete mode 100644 licenses/nuget packages/AjaxMin.license delete mode 100644 licenses/nuget packages/DotNetZip.license create mode 100644 licenses/nuget packages/FolkerKinzel.VCards.license create mode 100644 licenses/nuget packages/Microsoft.IdentityModel.Tokens.Jwt.license create mode 100644 licenses/nuget packages/NUglify.license create mode 100644 module/ASC.Api/ASC.Api.Calendar/Attachments/AttachmentEngine.cs create mode 100644 module/ASC.Api/ASC.Api.Calendar/Attachments/SecurityAdapter.cs create mode 100644 module/ASC.Api/ASC.Api.Calendar/Attachments/SecurityAdapterProvider.cs create mode 100644 module/ASC.Api/ASC.Api.Calendar/CalendarBootstrap.cs create mode 100644 module/ASC.Api/ASC.Api.Migration/ASC.Api.Migration.csproj create mode 100644 module/ASC.Api/ASC.Api.Migration/MigrationApi.cs create mode 100644 module/ASC.Api/ASC.Api.Migration/MigrationStatus.cs create mode 100644 module/ASC.Api/ASC.Api.Migration/OngoingMigration.cs create mode 100644 module/ASC.Api/ASC.Api.Migration/Properties/AssemblyInfo.cs create mode 100644 module/ASC.Api/ASC.Api.Security/AuditReportCreator.cs create mode 100644 module/ASC.Api/ASC.Api.Security/ModelTypes.cs create mode 100644 module/ASC.Api/ASC.Api.Settings/RadicaleSettingsAPI.cs create mode 100644 module/ASC.Api/ASC.Api/Interfaces/ApiHttpAsyncHandler.cs delete mode 100644 module/ASC.Api/ASC.Api/Interfaces/IApiHttpHandler.cs delete mode 100644 module/ASC.AuditTrail/AuditActionsMapper.cs delete mode 100644 module/ASC.AuditTrail/AuditReportCreator.cs create mode 100644 module/ASC.AuditTrail/AuditReportResource.az-Latn-AZ.resx delete mode 100644 module/ASC.AuditTrail/BaseEvent.cs create mode 100644 module/ASC.AuditTrail/Mappers/IModuleActionMapper.cs create mode 100644 module/ASC.AuditTrail/Mappers/IProductActionMapper.cs create mode 100644 module/ASC.AuditTrail/Types/ActionType.cs create mode 100644 module/ASC.AuditTrail/Types/EntryType.cs create mode 100644 module/ASC.AuditTrail/Types/ModuleType.cs create mode 100644 module/ASC.AuditTrail/Types/ProductType.cs create mode 100644 module/ASC.Feed.Aggregator/Modules/People/BirthdaysModule.cs create mode 100644 module/ASC.Feed.Aggregator/Modules/People/NewEmployeeModule.cs create mode 100644 module/ASC.Files.AutoCleanUp/ASC.Files.AutoCleanUp.csproj create mode 100644 module/ASC.Files.AutoCleanUp/ConfigSection.cs create mode 100644 module/ASC.Files.AutoCleanUp/Launcher.cs create mode 100644 module/ASC.Files.AutoCleanUp/Properties/AssemblyInfo.cs create mode 100644 module/ASC.Files.AutoCleanUp/TenantUserSettings.cs create mode 100644 module/ASC.Files.AutoCleanUp/Worker.cs create mode 100644 module/ASC.Files.AutoCleanUp/app.config create mode 100644 module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.az-Latn-AZ.resx delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ASC.Mail.Autoreply.csproj delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AddressParsers/AddressParser.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AddressParsers/CommentAddressParser.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AddressParsers/CommunityAddressParser.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AddressParsers/FileAddressParser.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AddressParsers/IAddressParser.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AddressParsers/ProjectAddressParser.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ApiRequest.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ApiService.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AutoreplyService.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AutoreplyServiceController.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/Configuration.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/CooldownInspector.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/BlogTagsResolver.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/ContentResolver.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/IParameterResolver.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/TaskDeadlineResolver.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/TaskMilestoneResolver.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/TaskPriorityResolver.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/TaskResponsiblesResolver.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/TitleResolver.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/Properties/AssemblyInfo.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/Utility/Html/Html2TextConverter.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/Utility/Html/HtmlSanitizer.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/Utility/Html/Text2HtmlConverter.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/Utility/StringDistance.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/Utility/StringExtensions.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABFN.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABFN_Group.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_Alternation.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_BinVal.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_CharVal.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_Concatenation.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_DecVal.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_Element.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_HexVal.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_Option.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_ProseVal.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_Repetition.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_Rule.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_RuleName.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ASC.Mail.csproj delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AUTH_SASL_ServerMechanism.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AUTH_SASL_ServerMechanism_CramMd5.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AUTH_SASL_ServerMechanism_DigestMd5.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AUTH_SASL_ServerMechanism_Login.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AUTH_SASL_ServerMechanism_Plain.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AUTH_e_Authenticate.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AUTH_e_UserInfo.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AuthHelper.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AuthType.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/Auth_HttpDigest.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/Auth_HttpDigest_NonceManager.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AsyncOP.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/BalanceMode.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/BindInfoProtocol.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/CircleCollection.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_Client.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_ClientException.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_A.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_AAAA.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_CNAME.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_HINFO.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_MX.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_NAPTR.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_NS.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_PTR.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_SOA.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_SRV.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_TXT.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_base.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DnsCache.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DnsServerResponse.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/QTYPE.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/RCODE.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/_OPCODE.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/DbFile.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/LDB_DataColumn.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/LDB_DataColumnCollection.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/LDB_DataType.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/LDB_Record.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/_DataPage.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/_ldb_Utils.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/lsDB_FixedLengthRecord.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/lsDB_FixedLengthTable.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/EncodingTools.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/EventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ExceptionEventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/Client/FTP_Client.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/Client/FTP_ClientException.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/FTP_ListItem.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/FTP_TransferMode.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/Server/AuthUser_EventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/Server/FTP_Server.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/Server/FTP_Session.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/Server/FileSysEntry_EventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/HTTP/Server/HTTP_Server.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/HTTP/Server/HTTP_Session.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/HostEndPoint.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ICMP/Icmp.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_Acl.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_Client.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_ClientException.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_FetchItem.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_FetchItem_Flags.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_Namespace.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_NamespacesInfo.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_Quota.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/IMAP_ACL_Flags.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/IMAP_BODY.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/IMAP_BODY_Entity.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/IMAP_Envelope.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/IMAP_Flags_SetType.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/IMAP_MessageFlags.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/IMAP_SequenceSet.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/AuthUser_EventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/Folder_EventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_Folder.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_Folders.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_Message.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_MessageCollection.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_MessageItems_enum.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_SearchGroup.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_SearchKey.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_SearchMatcher.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_SelectedFolder.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_Server.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_Session.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_Utils.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_DELETEACL.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_GETACL.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_GetMessagesInfo.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_GetQuota.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_GetUserACL.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_MessageItems.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_SETACL.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_Search.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/Message_EventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/SharedRootFolders_EventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/_FetchHelper.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/Base64Stream.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/DataSizeExceededException.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/IncompleteDataException.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/LineReader.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/LineSizeExceededException.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/MultiStream.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/PartialStream.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/QuotedPrintableStream.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/ReadLineEventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/ReadWriteControlledStream.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/SizeExceededAction.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/SmartStream.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IPBindInfo.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/JunkingStream.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Log/LogEntry.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Log/LogEntryType.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Log/Logger.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Log/WriteLogEventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/LumiSoft.Net delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_DispositionTypes .cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_EncodedWordEncoding.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_Encoding_EncodedWord.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_Entity.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_EntityCollection.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_MediaTypes.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_Message.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_Reader.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_TransferEncodings.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_Utils.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Application.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Audio.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Image.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Message.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MessageRfc822.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Multipart.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartAlternative.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartDigest.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartEncrypted.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartFormData.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartMixed.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartParallel.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartRelated.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartReport.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartSigned.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Provider.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_SinglepartBase.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Text.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Video.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_Collection.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_ContentDisposition.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_ContentType.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_Parameter.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_ParameterCollection.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_Provider.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_Unparsed.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_Unstructured.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/O/MIME_MultipartBody.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_Message.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_Utils.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_h_AddressList.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_h_DispositionNotificationOptions.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_h_Mailbox.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_h_MailboxList.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_h_Received.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_h_ReturnPath.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_t_Address.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_t_AddressList.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_t_Group.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_t_Mailbox.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_t_MailboxList.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_t_TcpInfo.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/CMLangConvertCharset.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/CMLangConvertCharsetClass.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/CMLangString.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/CMLangStringClass.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/CMultiLanguage.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/CMultiLanguageClass.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IEnumCodePage.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IEnumRfc1766.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IEnumScript.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangCodePages.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangConvertCharset.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangFontLink.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangFontLink2.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangLineBreakConsole.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangString.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangStringAStr.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangStringBufA.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangStringBufW.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangStringWStr.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMultiLanguage.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMultiLanguage2.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMultiLanguage3.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/ISequentialStream.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IStream.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/_FILETIME.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/_LARGE_INTEGER.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/_RemotableHandle.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/_ULARGE_INTEGER.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/__MIDL_IWinTypes_0009.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagDetectEncodingInfo.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagMIMECONTF.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagMIMECPINFO.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagMIMECSETINFO.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagMLCPF.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagMLDETECTCP.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagMLSTR_FLAGS.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagRFC1766INFO.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagSCRIPFONTINFO.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagSCRIPTINFO.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagSTATSTG.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagUNICODERANGE.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/NNTP/Client/NNTP_Client.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Net_Core.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Net_Utils.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Client/POP3_Client.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Client/POP3_ClientException.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Client/POP3_ClientMessage.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Client/POP3_ClientMessageCollection.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/POP3_ExtendedCapabilities.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/AuthUser_EventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/GetMessagesInfo_EventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/POP3_Message.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/POP3_MessageCollection.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/POP3_Message_EventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/POP3_Server.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/POP3_Session.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/POP3_eArgs_GetMessageStream.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ParseException.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/PortRange.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Properties/AssemblyInfo.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/Debug/wfrm_RTP_Debug.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_CompoundPacket.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_PacketType.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_APP.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_BYE.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_RR.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_ReportBlock.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_SDES.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_SDES_Chunk.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_SR.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_Unknown.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Report_Receiver.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Report_Sender.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Address.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Clock.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_MultimediaSession.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Packet.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_PacketEventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Participant.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_ParticipantEventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Participant_Local.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Participant_Remote.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_PayloadTypes.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_ReceiveStream.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_ReceiveStreamEventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_SendStream.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_SendStreamEventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Session.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Source.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_SourceEventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_SourceState.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Source_Local.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Source_Remote.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Utils.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SDP/SDP_Attribute.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SDP/SDP_ConnectionData.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SDP/SDP_Media.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SDP/SDP_MediaDescription.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SDP/SDP_Message.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SDP/SDP_Time.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_HeaderField.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_HeaderFieldCollection.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_MVGroupHFCollection.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_Message.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_MultiValueHF.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_OptionTags.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_Parameter.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_ParameterCollection.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_ParseException.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_SVGroupHFCollection.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_SingleValueHF.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_WarningCodes.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ACValue.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_AcceptRange.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_AddressParam.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_AlertParam.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_AuthenticationInfo.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_CSeq.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_CallID.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Challenge.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ContactParam.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ContentCoding.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ContentDisposition.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Credentials.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Directive.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Encoding.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ErrorUri.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Event.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_EventType.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_From.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_HiEntry.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_IdentityInfo.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Info.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Join.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Language.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_LanguageTag.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Method.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_MinSE.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_NameAddress.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_OptionTag.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_RAck.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_RCValue.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_RValue.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ReasonValue.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ReferSub.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ReferredBy.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Replaces.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_RetryAfter.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_SecMechanism.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_SessionExpires.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_SubscriptionState.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_TargetDialog.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Timestamp.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_To.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Value.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ValueWithParams.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ViaParm.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_WarningValue.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_AuthenticateEventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_B2BUA.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_B2BUA_Call.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_ForkingMode.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_Gateway.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_GatewayEventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_Presence.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_ProxyContext.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_ProxyCore.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_ProxyMode.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_ProxyTarget.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_Registrar.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_Registration.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_RegistrationBinding.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_RegistrationCollection.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_RegistrationEventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_Route.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/SIP_Utils.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/SIP_ValidateRequestEventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_ClientTransaction.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Dialog.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_DialogState.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Dialog_Invite.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Dialog_Subscribe.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Flow.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Hop.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Methods.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Request.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_RequestLine.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_RequestReceivedEventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_RequestSender.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Response.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_ResponseCodes.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_ResponseReceivedEventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_ResponseSentEventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_ServerTransaction.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Stack.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_StatusCodeType.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_StatusLine.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_TimerConstants.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Transaction.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_TransactionLayer.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_TransactionState.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Transport.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_TransportException.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_TransportLayer.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_UA_Registration.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_UA_RegistrationState.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/UA/SIP_UA.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/UA/SIP_UA_Call.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/UA/SIP_UA_CallState.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/UA/SIP_UA_Call_EventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Client/SMTP_Client.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Client/SMTP_ClientException.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Relay/Relay_Mode.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Relay/Relay_Queue.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Relay/Relay_QueueItem.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Relay/Relay_Server.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Relay/Relay_Session.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Relay/Relay_SessionCompletedEventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Relay/Relay_SmartHost.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/SMTP_Notify.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/SMTP_ServiceExtensions.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/SMTP_Utils.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/SMTP_t_Mailbox.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_MailFrom.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_RcptTo.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_Reply.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_Server.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_Session.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_e_Ehlo.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_e_MailFrom.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_e_Message.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_e_MessageStored.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_e_RcptTo.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_e_Started.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/AuthUser_EventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/GetMessageStoreStream_eArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/MessageStoringCompleted_eArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/SMTP_Server.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/SMTP_Session.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/SmtpServerReply.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/ValidateMailboxSize_EventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/ValidateRecipient_EventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/ValidateSender_EventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SNTP/Client/SNTP_Client.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/STUN/Client/STUN_Client.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/STUN/Client/STUN_NetType.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/STUN/Client/STUN_Result.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/STUN/Message/STUN_Message.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/STUN/Message/STUN_MessageType.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/STUN/Message/STUN_t_ChangeRequest.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/STUN/Message/STUN_t_ErrorCode.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ServersCore/Error_EventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ServersCore/SocketBufferedWriter.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ServersCore/ValidateIP_EventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ServersCore/commonDelegates.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SslMode.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/StringReader.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TCP/TCP_Client.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TCP/TCP_Server.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TCP/TCP_ServerSession.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TCP/TCP_ServerSessionEventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TCP/TCP_Session.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TCP/TCP_SessionCollection.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TextUtils.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TimerEx.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/UDP/UDP_PacketEventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/UDP/UDP_ProcessMode.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/UDP/UDP_Server.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/URI/AbsoluteUri.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/URI/SIP_Uri.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/URI/TEL_Uri.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/URI/UriSchemes.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/WellKnownPorts.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Workaround/Definitions.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_AsyncResultState.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Debug/BitDebuger.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/Log_EventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/Address.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/AddressList.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/ContentDisposition_enum.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/ContentTransferEncoding_enum.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/GroupAddress.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/HeaderField.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/HeaderFieldCollection.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/HeaderFieldParameter.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/HeaderFieldParameterCollection.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/MailboxAddress.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/MailboxAddressCollection.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/MediaType_enum.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/Mime.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/MimeEntity.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/MimeEntityCollection.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/MimeUtils.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/ParametizedHeaderField.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/ReadException.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/ReadLine_EventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/ReadToStream_EventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SIP_RequestSender.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SIP_Target.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SaslAuthTypes.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SmtpClientEx.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SocketCallBackResult.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SocketEx.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SocketLogEntry.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SocketLogEntryType.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SocketLogger.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SocketServer.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SocketServerSession.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/StreamHelper.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/StreamLineReader.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/WriteStream_EventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/Write_EventArgs.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/DeliveryAddress.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/DeliveryAddressCollection.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/DeliveryAddressType_enum.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/EmailAddress.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/EmailAddressCollection.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/EmailAddressType_enum.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/Item.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/ItemCollection.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/Name.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/PhoneNumber.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/PhoneNumberCollection.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/PhoneNumberType_enum.cs delete mode 100644 module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/vCard.cs delete mode 100644 module/ASC.Mail/ASC.Mail/Authorization/AuthorizationTracker.cs create mode 100644 module/ASC.Mail/ASC.Mail/Core/Entities/CashedMailUserAction.cs create mode 100644 module/ASC.Mail/ASC.Mail/Core/Entities/CashedTenantUserMailBox.cs create mode 100644 module/ASC.Mail/ASC.Mail/Resources/MailCoreResource.az-Latn-AZ.resx create mode 100644 module/ASC.MessagingSystem/MessageSettings.cs create mode 100644 module/ASC.Migration/ASC.Migration.csproj create mode 100644 module/ASC.Migration/App.config create mode 100644 module/ASC.Migration/Core/AbstractMigration.cs create mode 100644 module/ASC.Migration/Core/IMigration.cs create mode 100644 module/ASC.Migration/Core/ImportableEntity.cs create mode 100644 module/ASC.Migration/Core/MigrationLogger.cs create mode 100644 module/ASC.Migration/Core/Models/Api/ApiMigratorAttribute.cs create mode 100644 module/ASC.Migration/Core/Models/Api/ImportableApiEntity.cs create mode 100644 module/ASC.Migration/Core/Models/Api/MigratingApiCalendar.cs create mode 100644 module/ASC.Migration/Core/Models/Api/MigratingApiContacts.cs create mode 100644 module/ASC.Migration/Core/Models/Api/MigratingApiFiles.cs create mode 100644 module/ASC.Migration/Core/Models/Api/MigratingApiGroup.cs create mode 100644 module/ASC.Migration/Core/Models/Api/MigratingApiMail.cs create mode 100644 module/ASC.Migration/Core/Models/Api/MigratingApiUser.cs create mode 100644 module/ASC.Migration/Core/Models/Api/MigrationApiInfo.cs create mode 100644 module/ASC.Migration/Core/Models/Api/MigrationCore.cs create mode 100644 module/ASC.Migration/Core/Models/Api/MigrationLogApiContentResponce.cs create mode 100644 module/ASC.Migration/Core/Models/Api/MigratorMeta.cs create mode 100644 module/ASC.Migration/Core/Models/IMigrationInfo.cs create mode 100644 module/ASC.Migration/Core/Models/MigratingCalendar.cs create mode 100644 module/ASC.Migration/Core/Models/MigratingContacts.cs create mode 100644 module/ASC.Migration/Core/Models/MigratingFiles.cs create mode 100644 module/ASC.Migration/Core/Models/MigratingGroup.cs create mode 100644 module/ASC.Migration/Core/Models/MigratingMail.cs create mode 100644 module/ASC.Migration/Core/Models/MigratingUser.cs create mode 100644 module/ASC.Migration/Core/Models/MigrationInfo.cs create mode 100644 module/ASC.Migration/Core/Models/MigrationModules.cs create mode 100644 module/ASC.Migration/GoogleWorkspace/GoogleWorkspaceMigration.cs create mode 100644 module/ASC.Migration/GoogleWorkspace/Models/GWSMigratingCalendar.cs create mode 100644 module/ASC.Migration/GoogleWorkspace/Models/GWSMigratingContacts.cs create mode 100644 module/ASC.Migration/GoogleWorkspace/Models/GWSMigratingFiles.cs create mode 100644 module/ASC.Migration/GoogleWorkspace/Models/GWSMigratingGroups.cs create mode 100644 module/ASC.Migration/GoogleWorkspace/Models/GWSMigratingMail.cs create mode 100644 module/ASC.Migration/GoogleWorkspace/Models/GWSMigratingUser.cs create mode 100644 module/ASC.Migration/GoogleWorkspace/Models/GWSMigrationInfo.cs create mode 100644 module/ASC.Migration/GoogleWorkspace/Models/Parse/GWSContact.cs create mode 100644 module/ASC.Migration/GoogleWorkspace/Models/Parse/GWSDriveFileInfo.cs create mode 100644 module/ASC.Migration/GoogleWorkspace/Models/Parse/GWSMail.cs create mode 100644 module/ASC.Migration/GoogleWorkspace/Models/Parse/GWSProfile.cs create mode 100644 module/ASC.Migration/GoogleWorkspace/Models/Parse/GWSTasks.cs create mode 100644 module/ASC.Migration/NextcloudWorkspace/Models/NCMigratingCalendar.cs create mode 100644 module/ASC.Migration/NextcloudWorkspace/Models/NCMigratingContacts.cs create mode 100644 module/ASC.Migration/NextcloudWorkspace/Models/NCMigratingFiles.cs create mode 100644 module/ASC.Migration/NextcloudWorkspace/Models/NCMigratingGroups.cs create mode 100644 module/ASC.Migration/NextcloudWorkspace/Models/NCMigratingMail.cs create mode 100644 module/ASC.Migration/NextcloudWorkspace/Models/NCMigratingUser.cs create mode 100644 module/ASC.Migration/NextcloudWorkspace/Models/NCMigrationInfo.cs create mode 100644 module/ASC.Migration/NextcloudWorkspace/Models/Parse/NCAddressbooks.cs create mode 100644 module/ASC.Migration/NextcloudWorkspace/Models/Parse/NCCalendars.cs create mode 100644 module/ASC.Migration/NextcloudWorkspace/Models/Parse/NCContact.cs create mode 100644 module/ASC.Migration/NextcloudWorkspace/Models/Parse/NCGroup.cs create mode 100644 module/ASC.Migration/NextcloudWorkspace/Models/Parse/NCStorages.cs create mode 100644 module/ASC.Migration/NextcloudWorkspace/Models/Parse/NCUser.cs create mode 100644 module/ASC.Migration/NextcloudWorkspace/NextcloudWorkspaceMigration.cs create mode 100644 module/ASC.Migration/OwnCloud/Models/OCMigratingCalendar.cs create mode 100644 module/ASC.Migration/OwnCloud/Models/OCMigratingContacts.cs create mode 100644 module/ASC.Migration/OwnCloud/Models/OCMigratingFiles.cs create mode 100644 module/ASC.Migration/OwnCloud/Models/OCMigratingGroups.cs create mode 100644 module/ASC.Migration/OwnCloud/Models/OCMigratingMail.cs create mode 100644 module/ASC.Migration/OwnCloud/Models/OCMigratingUser.cs create mode 100644 module/ASC.Migration/OwnCloud/Models/OCMigrationinfo.cs create mode 100644 module/ASC.Migration/OwnCloud/Models/Parse/OCAddressbooks.cs create mode 100644 module/ASC.Migration/OwnCloud/Models/Parse/OCCalendars.cs create mode 100644 module/ASC.Migration/OwnCloud/Models/Parse/OCContact.cs create mode 100644 module/ASC.Migration/OwnCloud/Models/Parse/OCGroup.cs create mode 100644 module/ASC.Migration/OwnCloud/Models/Parse/OCStorages.cs create mode 100644 module/ASC.Migration/OwnCloud/Models/Parse/OCUser.cs create mode 100644 module/ASC.Migration/OwnCloud/OwnCloudMigration.cs create mode 100644 module/ASC.Migration/Properties/AssemblyInfo.cs create mode 100644 module/ASC.Migration/Resources/MigrationResource.Designer.cs create mode 100644 module/ASC.Migration/Resources/MigrationResource.resx create mode 100644 module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/AppleIdLoginProvider.cs create mode 100644 module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/MicrosoftLoginProvider.cs delete mode 100644 module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/OpenIdLoginProvider.cs delete mode 100644 module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/YahooLoginProvider.cs delete mode 100644 module/ASC.Thrdparty/ASC.Thrdparty/InMemoryTokenManager.cs delete mode 100644 module/ASC.Thrdparty/ASC.Thrdparty/Twitter/TwitterConsumer.cs create mode 100644 module/ASC.Thumbnails/app/isLife.js create mode 100644 module/ASC.WebDav.Svc/ASC.WebDav.Svc.csproj create mode 100644 module/ASC.WebDav.Svc/ConfigHandler.cs create mode 100644 module/ASC.WebDav.Svc/Launcher.cs create mode 100644 module/ASC.WebDav.Svc/Properties/AssemblyInfo.cs create mode 100644 module/ASC.WebDav.Svc/app.config create mode 100644 module/ASC.WebDav/helper/helper.js create mode 100644 module/ASC.WebDav/helper/localLockManager.js create mode 100644 module/ASC.WebDav/helper/logger.js create mode 100644 module/ASC.WebDav/helper/propertyParser.js create mode 100644 module/ASC.WebDav/helper/renamingDuplicateElements.js create mode 100644 module/ASC.WebDav/helper/writable.js create mode 100644 module/ASC.WebDav/manager/customFileSystem.js create mode 100644 module/ASC.WebDav/package-lock.json create mode 100644 module/ASC.WebDav/package.json create mode 100644 module/ASC.WebDav/patches/webdav-server+2.6.2.patch create mode 100644 module/ASC.WebDav/resource/customVirtualResource.js create mode 100644 module/ASC.WebDav/resource/simpleStruct.js create mode 100644 module/ASC.WebDav/server/config.js create mode 100644 module/ASC.WebDav/server/requestAPI.js create mode 100644 module/ASC.WebDav/server/webDavServer.js create mode 100644 module/ASC.WebDav/user/authentication/customHTTPBasicAuthentication.js create mode 100644 module/ASC.WebDav/user/customSimpleUser.js create mode 100644 module/ASC.WebDav/user/customUserLayout.js create mode 100644 module/ASC.WebDav/user/customUserManager.js create mode 100644 module/ASC.WebDav/yarn.lock delete mode 100644 redistributable/AjaxPro/Services/AuthenticationService.cs delete mode 100644 redistributable/AjaxPro/Services/CartService.cs delete mode 100644 redistributable/AjaxPro/Services/ChatService.cs delete mode 100644 redistributable/AjaxPro/Services/ProfileService.cs create mode 100644 redistributable/AjaxPro/Utilities/HashHelper.cs delete mode 100644 redistributable/AjaxPro/Utilities/MD5Helper.cs delete mode 100644 redistributable/AjaxPro/converter.js delete mode 100644 redistributable/AjaxPro/jquery-1.3.1.js create mode 100644 redistributable/AppLimit.CloudComputing.SharpBox/AppLimit.CloudComputing.SharpBox.csproj rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/CloudStorage.cs (86%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/CloudStorageAsyncFunctions.cs (95%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/CloudStorageComfortFunctions.cs (83%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/CloudStorageLimits.cs (97%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/CloudStorageSyncFramework.cs (92%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Common/AsyncObjectRequest.cs (96%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Common/AsyncResultEx.cs (93%) create mode 100644 redistributable/AppLimit.CloudComputing.SharpBox/Common/Cache/CachedDictionary.cs rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Common/Cache/CachedDictionaryBase.cs (96%) create mode 100644 redistributable/AppLimit.CloudComputing.SharpBox/Common/Extensions/SharpBoxExtensions.cs rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Common/IO/PathHelper.cs (80%) create mode 100644 redistributable/AppLimit.CloudComputing.SharpBox/Common/IO/StreamHelper.cs rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Common/Net/HttpException.cs (77%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Common/Net/HttpUtility.cs (97%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Common/Net/HttpUtilityEx.cs (86%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Common/Net/Json/JsonDateTimeConverter.cs (94%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Common/Net/Json/JsonHelper.cs (88%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Common/Net/MimeMapping.cs (98%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Common/Net/Web/Dav/DavService.cs (85%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Common/Net/Web/Ftp/FtpService.cs (89%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Common/Net/Web/Http/HttpService.cs (97%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Common/Net/Web/WebRequestExecutedEventArgs.cs (96%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Common/Net/Web/WebRequestExecutingEventArgs.cs (96%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Common/Net/Web/WebRequestManager.cs (92%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Common/Net/Web/WebRequestManagerNullProxy.cs (96%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Common/Net/Web/WebRequestMethods.cs (97%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Common/Net/Web/WebRequestMultipartFormDataSupport.cs (89%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Common/Net/Web/WebRequestService.cs (88%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Common/Net/Web/WebRequestStream.cs (96%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Common/Net/Web/WebRequestStreamHelper.cs (96%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Common/Net/Web/WebResponseStream.cs (96%) create mode 100644 redistributable/AppLimit.CloudComputing.SharpBox/Common/Net/oAuth/Context/OAuthConsumerContext.cs create mode 100644 redistributable/AppLimit.CloudComputing.SharpBox/Common/Net/oAuth/Context/OAuthServiceContext.cs rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Common/Net/oAuth/Impl/OAuthBase.cs (96%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Common/Net/oAuth/Impl/OAuthStreamParser.cs (81%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Common/Net/oAuth/Impl/OAuthUrlGenerator.cs (79%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Common/Net/oAuth/OAuthService.cs (79%) create mode 100644 redistributable/AppLimit.CloudComputing.SharpBox/Common/Net/oAuth/Token/OAuthToken.cs rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Common/Net/oAuth20/OAuth20Token.cs (83%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Exceptions/ErrorMessages.Designer.cs (97%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Exceptions/ErrorMessages.resx (97%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Exceptions/SharpBoxErrorCodes.cs (96%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Exceptions/SharpBoxException.cs (95%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/ICloudDirectoryEntry.cs (90%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/ICloudFileDataTransfer.cs (92%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/ICloudFileSystemEntry.cs (89%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/ICloudStorageAccessToken.cs (96%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/ICloudStorageAsyncInterface.cs (97%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/ICloudStorageConfiguration.cs (97%) create mode 100644 redistributable/AppLimit.CloudComputing.SharpBox/ICloudStorageProvider.cs create mode 100644 redistributable/AppLimit.CloudComputing.SharpBox/ICloudStoragePublicAPI.cs rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/IResumableUploadSession.cs (94%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/Resources/loader.gif (100%) create mode 100644 redistributable/AppLimit.CloudComputing.SharpBox/StorageProvider/API/GenericHelper.cs rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/API/GenericStorageProviderFactory.cs (96%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/API/IStorageProviderService.cs (86%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/API/IStorageProviderSession.cs (96%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/BaseObjects/BaseDirectoryEntry.cs (90%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/BaseObjects/BaseFileEntry.cs (77%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/BaseObjects/BaseFileEntryDataTransfer.cs (95%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/BaseObjects/BaseFileEntryDownloadStream.cs (96%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/BaseObjects/ResumableUploadSession.cs (96%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/BoxNet/BoxNetConfiguration.cs (96%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/BoxNet/BoxNetStorageProvider.cs (97%) create mode 100644 redistributable/AppLimit.CloudComputing.SharpBox/StorageProvider/BoxNet/Logic/BoxNetStorageProviderService.cs rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/CIFS/CIFSConfiguration.cs (93%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/CIFS/CIFSStorageProvider.cs (96%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/CIFS/Logic/CIFSStorageProviderService.cs (96%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/CIFS/Logic/CIFSStorageProviderSession.cs (97%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/CachedServiceWrapper.cs (97%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/DropBox/DropBoxAccountInfo.cs (82%) create mode 100644 redistributable/AppLimit.CloudComputing.SharpBox/StorageProvider/DropBox/DropBoxBaseTokenInformation.cs rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/DropBox/DropBoxConfiguration.cs (97%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/DropBox/DropBoxQuotaInfo.cs (82%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/DropBox/DropBoxRequestToken.cs (96%) create mode 100644 redistributable/AppLimit.CloudComputing.SharpBox/StorageProvider/DropBox/DropBoxResourceIDHelpers.cs rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/DropBox/DropBoxStorageProvider.cs (97%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/DropBox/DropBoxStorageProviderTools.cs (89%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/DropBox/DropBoxToken.cs (92%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/DropBox/Logic/DropBoxRequestParser.cs (91%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/DropBox/Logic/DropBoxStorageProviderService.cs (91%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/DropBox/Logic/DropBoxStorageProviderSession.cs (85%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/Ftp/FtpConfiguration.cs (96%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/Ftp/FtpStorageProvider.cs (96%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/Ftp/Logic/FtpStorageProviderService.cs (97%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/Ftp/Logic/FtpStorageProviderSession.cs (97%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/GenericCurrentCredentials.cs (96%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/GenericNetworkCredentials.cs (90%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/GenericStorageProvider.cs (95%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/GenericStorageProviderService.cs (93%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/GoogleDocs/GoogleDocsAuthorizationHelper.cs (80%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/GoogleDocs/GoogleDocsConfiguration.cs (92%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/GoogleDocs/GoogleDocsConstants.cs (98%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/GoogleDocs/GoogleDocsRequestToken.cs (96%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/GoogleDocs/GoogleDocsResourceHelper.cs (81%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/GoogleDocs/GoogleDocsStorageProvider.cs (97%) create mode 100644 redistributable/AppLimit.CloudComputing.SharpBox/StorageProvider/GoogleDocs/GoogleDocsToken.cs rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/GoogleDocs/Logic/GoogleDocsStorageProviderService.cs (93%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/GoogleDocs/Logic/GoogleDocsStorageProviderSession.cs (97%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/GoogleDocs/Logic/GoogleDocsXmlParser.cs (91%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/SkyDrive/Authorization/SkyDriveAuthorizationHelper.cs (79%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/SkyDrive/Logic/SkyDriveStorageProvider.cs (97%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/SkyDrive/Logic/SkyDriveStorageProviderService.cs (88%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/SkyDrive/Logic/SkyDriveStorageProviderSession.cs (97%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/SkyDrive/SkyDriveConfiguration.cs (96%) create mode 100644 redistributable/AppLimit.CloudComputing.SharpBox/StorageProvider/SkyDrive/SkyDriveConstants.cs create mode 100644 redistributable/AppLimit.CloudComputing.SharpBox/StorageProvider/SkyDrive/SkyDriveHelpers.cs rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/SkyDrive/SkyDriveJsonParser.cs (81%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/SkyDrive/SkyDriveRequestHelper.cs (78%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/WebDav/Logic/WebDavRequestParser.cs (86%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/WebDav/Logic/WebDavStorageProviderService.cs (91%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/WebDav/Logic/WebDavStorageProviderSession.cs (97%) create mode 100644 redistributable/AppLimit.CloudComputing.SharpBox/StorageProvider/WebDav/WebDavConfiguration.cs rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/StorageProvider/WebDav/WebDavStorageProvider.cs (97%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/SyncFramework/DirectoryDiff.cs (91%) rename redistributable/{ShrapBox/Src => }/AppLimit.CloudComputing.SharpBox/SyncFramework/DirectoryDiffResultItem.cs (96%) create mode 100644 redistributable/IpGeolocationConverter/App.config create mode 100644 redistributable/IpGeolocationConverter/IpGeolocationConverter.csproj create mode 100644 redistributable/IpGeolocationConverter/Program.cs create mode 100644 redistributable/IpGeolocationConverter/Properties/AssemblyInfo.cs delete mode 100644 redistributable/Microsoft.Graph.Core/Properties/AssemblyInfo.cs delete mode 100644 redistributable/ShrapBox/Src/AppLimit.CloudComputing.SharpBox/AppLimit.CloudComputing.SharpBox.csproj delete mode 100644 redistributable/ShrapBox/Src/AppLimit.CloudComputing.SharpBox/AppLimit.CloudComputing.SharpBox.snk delete mode 100644 redistributable/ShrapBox/Src/AppLimit.CloudComputing.SharpBox/Common/Cache/CachedDictionary.cs delete mode 100644 redistributable/ShrapBox/Src/AppLimit.CloudComputing.SharpBox/Common/Extensions/SharpBoxExtensions.cs delete mode 100644 redistributable/ShrapBox/Src/AppLimit.CloudComputing.SharpBox/Common/IO/StreamHelper.cs delete mode 100644 redistributable/ShrapBox/Src/AppLimit.CloudComputing.SharpBox/Common/Net/oAuth/Context/OAuthConsumerContext.cs delete mode 100644 redistributable/ShrapBox/Src/AppLimit.CloudComputing.SharpBox/Common/Net/oAuth/Context/OAuthServiceContext.cs delete mode 100644 redistributable/ShrapBox/Src/AppLimit.CloudComputing.SharpBox/Common/Net/oAuth/Token/OAuthToken.cs delete mode 100644 redistributable/ShrapBox/Src/AppLimit.CloudComputing.SharpBox/ICloudStorageProvider.cs delete mode 100644 redistributable/ShrapBox/Src/AppLimit.CloudComputing.SharpBox/ICloudStoragePublicAPI.cs delete mode 100644 redistributable/ShrapBox/Src/AppLimit.CloudComputing.SharpBox/Properties/Resources.Designer.cs delete mode 100644 redistributable/ShrapBox/Src/AppLimit.CloudComputing.SharpBox/StorageProvider/API/GenericHelper.cs delete mode 100644 redistributable/ShrapBox/Src/AppLimit.CloudComputing.SharpBox/StorageProvider/BoxNet/Logic/BoxNetStorageProviderService.cs delete mode 100644 redistributable/ShrapBox/Src/AppLimit.CloudComputing.SharpBox/StorageProvider/DropBox/DropBoxBaseTokenInformation.cs delete mode 100644 redistributable/ShrapBox/Src/AppLimit.CloudComputing.SharpBox/StorageProvider/DropBox/DropBoxResourceIDHelpers.cs delete mode 100644 redistributable/ShrapBox/Src/AppLimit.CloudComputing.SharpBox/StorageProvider/GoogleDocs/GoogleDocsToken.cs delete mode 100644 redistributable/ShrapBox/Src/AppLimit.CloudComputing.SharpBox/StorageProvider/SkyDrive/SkyDriveConstants.cs delete mode 100644 redistributable/ShrapBox/Src/AppLimit.CloudComputing.SharpBox/StorageProvider/SkyDrive/SkyDriveHelpers.cs delete mode 100644 redistributable/ShrapBox/Src/AppLimit.CloudComputing.SharpBox/StorageProvider/WebDav/WebDavConfiguration.cs delete mode 100644 redistributable/ShrapBox/Src/AppLimit.CloudComputing.SharpBox/UI/Controler/FileSizeFormat.cs delete mode 100644 redistributable/ShrapBox/Src/AppLimit.CloudComputing.SharpBox/UI/Controler/ListViewColumnSorter.cs delete mode 100644 redistributable/ShrapBox/Src/AppLimit.CloudComputing.SharpBox/UI/Presentation/LoginControl.Designer.cs delete mode 100644 redistributable/ShrapBox/Src/AppLimit.CloudComputing.SharpBox/UI/Presentation/LoginControl.cs delete mode 100644 redistributable/ShrapBox/Src/AppLimit.CloudComputing.SharpBox/UI/Presentation/LoginControl.resx delete mode 100644 redistributable/ShrapBox/Src/AppLimit.CloudComputing.SharpBox/UI/Presentation/Sandbox.Designer.cs delete mode 100644 redistributable/ShrapBox/Src/AppLimit.CloudComputing.SharpBox/UI/Presentation/Sandbox.cs delete mode 100644 redistributable/ShrapBox/Src/AppLimit.CloudComputing.SharpBox/UI/Presentation/Sandbox.resx delete mode 100644 redistributable/ShrapBox/Src/AssemblyInfo.cs delete mode 100644 redistributable/ShrapBox/Src/GenerateAssemblyInfo.msbuild delete mode 100644 redistributable/ShrapBox/Src/SharpBoxPackNuget.cmd delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Attributes/AuthorizedCommandAttribute.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Attributes/RateLimitedAttribute.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Core/AccessLevel.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Core/CommandPerformer.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Core/ConversionUtility.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Core/ICommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Core/ITwitterObject.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Core/NamespaceDoc.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Core/OptionalProperties.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Core/OptionalProperties.xml delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Core/RateLimit.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Core/RequestResult.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Core/SerializationHelper.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Core/TwitterCollection.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Core/TwitterCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Core/TwitterCursorPagedIdCollection.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Core/TwitterDictionary.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Core/TwitterIdCollection.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Core/TwitterImage.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Core/TwitterObject.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Core/TwitterizerDateConverter.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Exceptions/CommandValidationException.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Exceptions/TwitterErrorDetails.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Exceptions/TwitterizerException.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Information.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Account/RateLimitStatusCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Account/TwitterAccount.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Account/TwitterRateLimitStatus.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Account/UpdateProfileBackgroundImageCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Account/UpdateProfileBackgroundImageOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Account/UpdateProfileColorsCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Account/UpdateProfileColorsOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Account/UpdateProfileCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Account/UpdateProfileImageCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Account/UpdateProfileOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Account/VerifyCredentialsCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Account/VerifyCredentialsOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Block/BlockingCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Block/BlockingIdsCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Block/BlockingOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Block/CreateBlockCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Block/DestroyBlockCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Block/ExistsBlockCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Block/TwitterBlock.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/DirectMessage/DeleteDirectMessageCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/DirectMessage/DirectMessagesCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/DirectMessage/DirectMessagesOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/DirectMessage/DirectMessagesSentCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/DirectMessage/DirectMessagesSentOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/DirectMessage/SendDirectMessageCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/DirectMessage/ShowDirectMessageCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/DirectMessage/TwitterDirectMessage.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/DirectMessage/TwitterDirectMessageCollection.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Favorites/CreateFavoriteCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Favorites/DeleteFavoriteCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Favorites/ListFavoritesCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Favorites/ListFavoritesOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Favorites/TwitterFavorite.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Friendship/CreateFriendshipCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Friendship/CreateFriendshipOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Friendship/DeleteFriendshipCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Friendship/FollowersIdsCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Friendship/FriendsIdsCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Friendship/IncomingFriendshipsCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Friendship/IncomingFriendshipsOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Friendship/NoRetweetIDsCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Friendship/OutgoingFriendshipsCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Friendship/OutgoingFriendshipsOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Friendship/ShowFriendshipCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Friendship/TwitterFriendship.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Friendship/TwitterRelationship.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Friendship/TwitterRelationshipUser.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Friendship/UpdateFriendshipCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Friendship/UpdateFriendshipOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Friendship/UserIdCollection.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Friendship/UsersIdsOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Geo/Coordinate.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Geo/ReverseGeocodeCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Geo/TwitterBoundingBox.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Geo/TwitterGeo.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Geo/TwitterPlace.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Geo/TwitterPlaceCollection.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Geo/TwitterPlaceLookupOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/List/AddListMemberCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/List/CheckListMembershipCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/List/CreateListCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/List/CreateListMembershipCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/List/DeleteListCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/List/DestroyListSubscriber.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/List/GetListCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/List/GetListMembersCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/List/GetListMembersOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/List/GetListSubscriptionsCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/List/GetListSubscriptionsOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/List/GetListsCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/List/GetListsOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/List/ListMembershipsCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/List/ListMembershipsOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/List/ListStatusesCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/List/ListStatusesOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/List/RemoveListMemberCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/List/TwitterList.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/List/TwitterListCollection.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/List/UpdateListCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/List/UpdateListOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Notification/NotificationFollowCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Notification/NotificationLeaveCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Notification/TwitterNotification.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/SavedSearches/CreateSavedSearchCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/SavedSearches/DeleteSavedSearchCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/SavedSearches/SavedSearchesCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/SavedSearches/TwitterSavedSearch.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/SavedSearches/TwitterSavedSearchCollection.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Search/SearchCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Search/SearchOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Search/TwitterSearch.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Search/TwitterSearchResult.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Search/TwitterSearchResultCollection.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Spam/ReportSpamCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Spam/TwitterSpam.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Timeline/FriendsTimelineCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Timeline/HomeTimelineCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Timeline/MentionsCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Timeline/PagedTimelineCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Timeline/PublicTimelineCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Timeline/RetweetedByMeCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Timeline/RetweetedToMeCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Timeline/TimelineOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Timeline/TwitterTimeline.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Timeline/UserTimelineCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Timeline/UserTimelineOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Trends/AvailableTrendsCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Trends/AvailableTrendsOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Trends/DailyTrendsCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Trends/LocalTrendsOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Trends/TrendsCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Trends/TrendsOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Trends/TwitterTrend.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Trends/TwitterTrendCollection.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Trends/TwitterTrendDictionary.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Trends/TwitterTrendLocation.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Trends/TwitterTrendLocationCollection.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Trends/TwitterTrendLocationPlaceType.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Trends/WeeklyTrendsCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Tweets/DeleteStatusCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Tweets/Entities/TwitterEntity.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Tweets/Entities/TwitterEntityCollection.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Tweets/Entities/TwitterHashTagEntity.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Tweets/Entities/TwitterMediaEntity.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Tweets/Entities/TwitterMentionEntity.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Tweets/Entities/TwitterUrlEntity.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Tweets/RelatedResultsCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Tweets/RetweetCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Tweets/RetweetsCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Tweets/RetweetsOfMeCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Tweets/RetweetsOfMeOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Tweets/RetweetsOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Tweets/ShowStatusCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Tweets/StatusUpdateOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Tweets/TwitterRelatedTweets.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Tweets/TwitterRelatedTweetsCollection.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Tweets/TwitterStatus.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Tweets/TwitterStatus.xml delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Tweets/TwitterStatusCollection.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Tweets/UpdateStatusCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/Tweets/UpdateWithmediaCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/TwitterResultTypeEnum.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/User/FollowersCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/User/FollowersOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/User/FriendsCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/User/FriendsOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/User/LookupUsersCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/User/LookupUsersOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/User/RetweetedByCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/User/RetweetedByIdsCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/User/RetweetedByIdsOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/User/RetweetedByOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/User/ShowUserCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/User/SuggestedUserCategoriesCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/User/SuggestedUsersCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/User/TwitterUser.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/User/TwitterUser.xml delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/User/TwitterUserCategory.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/User/TwitterUserCollection.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/User/UserSearchCommand.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Methods/User/UserSearchOptions.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/OAuth/OAuthTokenResponse.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/OAuth/OAuthTokens.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/OAuth/OAuthTokens.xml delete mode 100644 redistributable/Twitterizer2/Twitterizer2/OAuth/OAuthUtility.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/OAuth/OAuthUtility.xml delete mode 100644 redistributable/Twitterizer2/Twitterizer2/OAuth/WebRequestBuilder.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/OAuth/XAuthUtility.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Properties/AssemblyInfo.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/TwitterResponse.cs delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Twitterizer2.cd delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Twitterizer2.csproj delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Twitterizer2.nuspec delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Twitterizer2.snk delete mode 100644 redistributable/Twitterizer2/Twitterizer2/Twitterizer2Inheritance.cd delete mode 100644 redistributable/Twitterizer2/Twitterizer2/packages.config delete mode 100644 redistributable/onedrive-sdk-csharp-master/src/OneDriveSdk/Properties/AssemblyInfo.cs delete mode 100644 redistributable/onedrive-sdk-csharp-master/tests/Test.OneDriveSdk/35MSSharedLib1024.snk create mode 100644 redistributable/openstack.net/.paket/paket.targets create mode 100644 redistributable/openstack.net/CONTRIBUTING.md create mode 100644 redistributable/openstack.net/GitVersionConfig.yaml create mode 100644 redistributable/openstack.net/LICENSE.md create mode 100644 redistributable/openstack.net/README.md create mode 100644 redistributable/openstack.net/appveyor.yml create mode 100644 redistributable/openstack.net/azure-pipelines.yml create mode 100644 redistributable/openstack.net/build.cmd create mode 100644 redistributable/openstack.net/build.sh create mode 100644 redistributable/openstack.net/build/build.proj create mode 100644 redistributable/openstack.net/build/check-nuget-version-exists.ps1 create mode 100644 redistributable/openstack.net/build/keys/OpenStackNetV1.dev.snk create mode 100644 redistributable/openstack.net/build/keys/OpenStackNetV1.snk create mode 100644 redistributable/openstack.net/build/keys/SDKTestingKey.snk create mode 100644 redistributable/openstack.net/myget.ps1 create mode 100644 redistributable/openstack.net/paket.dependencies create mode 100644 redistributable/openstack.net/paket.lock create mode 100644 redistributable/openstack.net/pre-myget.ps1 create mode 100644 redistributable/openstack.net/samples/.paket/paket.bootstrapper.exe create mode 100644 redistributable/openstack.net/samples/ComputeSample.cs create mode 100644 redistributable/openstack.net/samples/ISample.cs create mode 100644 redistributable/openstack.net/samples/NetworkingSample.cs create mode 100644 redistributable/openstack.net/samples/OpenStack.Samples.csproj create mode 100644 redistributable/openstack.net/samples/Program.cs create mode 100644 redistributable/openstack.net/samples/Properties/AssemblyInfo.cs create mode 100644 redistributable/openstack.net/samples/paket.dependencies create mode 100644 redistributable/openstack.net/samples/paket.references create mode 100644 redistributable/openstack.net/src/Documentation/AdditionalReferenceDocumentation.Portable.shfbproj create mode 100644 redistributable/openstack.net/src/Documentation/AdditionalReferenceDocumentation.shfbproj create mode 100644 redistributable/openstack.net/src/Documentation/Content/AsynchronousServices.aml create mode 100644 redistributable/openstack.net/src/Documentation/Content/Authentication/Authentication.aml create mode 100644 redistributable/openstack.net/src/Documentation/Content/Authentication/HPAuthentication.aml create mode 100644 redistributable/openstack.net/src/Documentation/Content/Authentication/OpenStackAuthentication.aml create mode 100644 redistributable/openstack.net/src/Documentation/Content/Authentication/RackspaceAuthentication.aml create mode 100644 redistributable/openstack.net/src/Documentation/Content/BreakingChangesPolicy.aml create mode 100644 redistributable/openstack.net/src/Documentation/Content/License.aml create mode 100644 redistributable/openstack.net/src/Documentation/Content/MSHelpViewerRoot.aml create mode 100644 redistributable/openstack.net/src/Documentation/Content/UserGuide/UserGuide.aml create mode 100644 redistributable/openstack.net/src/Documentation/Content/Welcome.aml create mode 100644 redistributable/openstack.net/src/Documentation/Documentation.v4.0.shfbproj create mode 100644 redistributable/openstack.net/src/Documentation/History/1.3.6/Documentation.1.3.6.shfbproj create mode 100644 redistributable/openstack.net/src/Documentation/History/1.4/Documentation.1.4.shfbproj create mode 100644 redistributable/openstack.net/src/Documentation/History/1.5/Documentation.1.5.shfbproj create mode 100644 redistributable/openstack.net/src/Documentation/History/Current/Documentation.Current.shfbproj create mode 100644 redistributable/openstack.net/src/Documentation/OpenStackSDK.content create mode 100644 redistributable/openstack.net/src/Documentation/README.md create mode 100644 redistributable/openstack.net/src/Documentation/SharedTokens.tokens create mode 100644 redistributable/openstack.net/src/Local.testsettings create mode 100644 redistributable/openstack.net/src/Samples/CPPCodeSamples/AssemblyInfo.cpp create mode 100644 redistributable/openstack.net/src/Samples/CPPCodeSamples/CPPCodeSamples.vcxproj create mode 100644 redistributable/openstack.net/src/Samples/CPPCodeSamples/CPPCodeSamples.vcxproj.filters create mode 100644 redistributable/openstack.net/src/Samples/CPPCodeSamples/IdentityProviderExamples.cpp create mode 100644 redistributable/openstack.net/src/Samples/CPPCodeSamples/ObjectStorageProviderExamples.cpp create mode 100644 redistributable/openstack.net/src/Samples/CPPCodeSamples/QueueingServiceExamples.cpp create mode 100644 redistributable/openstack.net/src/Samples/CPPCodeSamples/Stdafx.cpp create mode 100644 redistributable/openstack.net/src/Samples/CPPCodeSamples/Stdafx.h create mode 100644 redistributable/openstack.net/src/Samples/CPPCodeSamples/app.ico create mode 100644 redistributable/openstack.net/src/Samples/CPPCodeSamples/app.rc create mode 100644 redistributable/openstack.net/src/Samples/CPPCodeSamples/resource.h create mode 100644 redistributable/openstack.net/src/Samples/CSharpCodeSamples/AsynchronousExceptionsExamples.cs create mode 100644 redistributable/openstack.net/src/Samples/CSharpCodeSamples/CSharpCodeSamples.csproj create mode 100644 redistributable/openstack.net/src/Samples/CSharpCodeSamples/ContentDeliveryNetworkExample.cs create mode 100644 redistributable/openstack.net/src/Samples/CSharpCodeSamples/IdentityProviderExamples.cs create mode 100644 redistributable/openstack.net/src/Samples/CSharpCodeSamples/ObjectStorageProviderExamples.cs create mode 100644 redistributable/openstack.net/src/Samples/CSharpCodeSamples/Properties/AssemblyInfo.cs create mode 100644 redistributable/openstack.net/src/Samples/CSharpCodeSamples/QueueingServiceExamples.cs create mode 100644 redistributable/openstack.net/src/Samples/CSharpCodeSamples/app.config create mode 100644 redistributable/openstack.net/src/Samples/CSharpCodeSamples/packages.CSharpCodeSamples.config create mode 100644 redistributable/openstack.net/src/Samples/FSharpCodeSamples/FSharpCodeSamples.fsproj create mode 100644 redistributable/openstack.net/src/Samples/FSharpCodeSamples/IdentityProviderExamples.fs create mode 100644 redistributable/openstack.net/src/Samples/FSharpCodeSamples/ObjectStorageProviderExamples.fs create mode 100644 redistributable/openstack.net/src/Samples/FSharpCodeSamples/QueueingServiceExamples.fs create mode 100644 redistributable/openstack.net/src/Samples/FSharpCodeSamples/app.config create mode 100644 redistributable/openstack.net/src/Samples/FSharpCodeSamples/paket.references create mode 100644 redistributable/openstack.net/src/Samples/VBCodeSamples/AsynchronousExceptionsExamples.vb create mode 100644 redistributable/openstack.net/src/Samples/VBCodeSamples/IdentityProviderExamples.vb create mode 100644 redistributable/openstack.net/src/Samples/VBCodeSamples/My Project/Application.Designer.vb create mode 100644 redistributable/openstack.net/src/Samples/VBCodeSamples/My Project/Application.myapp create mode 100644 redistributable/openstack.net/src/Samples/VBCodeSamples/My Project/AssemblyInfo.vb create mode 100644 redistributable/openstack.net/src/Samples/VBCodeSamples/My Project/Resources.Designer.vb rename redistributable/{ShrapBox/Src/AppLimit.CloudComputing.SharpBox/Properties => openstack.net/src/Samples/VBCodeSamples/My Project}/Resources.resx (83%) create mode 100644 redistributable/openstack.net/src/Samples/VBCodeSamples/My Project/Settings.Designer.vb create mode 100644 redistributable/openstack.net/src/Samples/VBCodeSamples/My Project/Settings.settings create mode 100644 redistributable/openstack.net/src/Samples/VBCodeSamples/ObjectStorageProviderExamples.vb create mode 100644 redistributable/openstack.net/src/Samples/VBCodeSamples/QueueingServiceExamples.vb create mode 100644 redistributable/openstack.net/src/Samples/VBCodeSamples/VBCodeSamples.vbproj create mode 100644 redistributable/openstack.net/src/Samples/VBCodeSamples/app.config create mode 100644 redistributable/openstack.net/src/Samples/VBCodeSamples/packages.VBCodeSamples.config create mode 100644 redistributable/openstack.net/src/TraceAndTestImpact.testsettings create mode 100644 redistributable/openstack.net/src/corelib/Authentication/AuthenticatedHttpClientFactory.cs create mode 100644 redistributable/openstack.net/src/corelib/Authentication/AuthenticatedMessageHandler.cs create mode 100644 redistributable/openstack.net/src/corelib/Authentication/IAuthenticationProvider.cs create mode 100644 redistributable/openstack.net/src/corelib/Authentication/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Authentication/ServiceType.cs create mode 100644 redistributable/openstack.net/src/corelib/BlockStorage/v2/Serialization/SnapshotStatus.cs create mode 100644 redistributable/openstack.net/src/corelib/BlockStorage/v2/Serialization/VolumeStatus.cs create mode 100644 redistributable/openstack.net/src/corelib/BlockStorage/v2/SnapshotStatus.cs create mode 100644 redistributable/openstack.net/src/corelib/BlockStorage/v2/VolumeStatus.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/ComputeOperationFailedException.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Actions/AssociateFloatingIPRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Actions/RebootServerRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Actions/RebootType.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Actions/RescueServerRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Actions/SnapshotServerRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/AddressType.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ComputeService.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ComputeServiceExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/DiskConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Flavor.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/FlavorExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/FlavorListOptions.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/FlavorReference.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/FlavorSummary.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/IPProtocol.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Image.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ImageExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ImageListOptions.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ImageMetadata.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ImageMetadataExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ImageReference.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ImageSummary.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ImageType.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/KeyPair.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/KeyPairDefinition.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/KeyPairExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/KeyPairRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/KeyPairResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/KeyPairSummary.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Operator/ComputeServiceExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Operator/EvacuateServerRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Operator/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Operator/ServerExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Operator/ServiceQuotas.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/RemoteConsole.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/RemoteConsoleType.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/SchedulerHints.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/SecurityGroup.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/SecurityGroupDefinition.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/SecurityGroupExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/SecurityGroupReference.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/SecurityGroupRule.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/SecurityGroupRuleDefinition.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Serialization/AddressType.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Serialization/ComputeApi.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Serialization/DiskConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Serialization/FlavorCollection.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Serialization/ImageCollection.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Serialization/ImageType.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Serialization/KeyPairCollection.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Serialization/KeyPairConverter.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Serialization/RebootType.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Serialization/RemoteConsoleType.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Serialization/SecurityGroupCollection.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Serialization/ServerActionCollection.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Serialization/ServerAddressCollection.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Serialization/ServerBlockDeviceType.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Serialization/ServerCollection.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Serialization/ServerCreateDefinitionConverter.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Serialization/ServerEventStatus.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Serialization/ServerGroupCollection.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Serialization/ServerStatus.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Serialization/ServerVolumeCollection.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Serialization/VolumeCollection.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Serialization/VolumeSnapshotCollection.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Server.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ServerAction.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ServerActionExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ServerActionSummary.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ServerAddress.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ServerBlockDeviceMapping.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ServerBlockDeviceType.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ServerCreateDefinition.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ServerEventStatus.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ServerExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ServerGroup.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ServerGroupDefinition.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ServerGroupExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ServerListOptions.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ServerMetadata.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ServerMetadataExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ServerNetworkDefinition.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ServerReference.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ServerStatus.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ServerSummary.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ServerUpdateDefinition.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ServerVolume.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ServerVolumeDefinition.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ServerVolumeExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ServerVolumeReference.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/ServiceLimits.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/Volume.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/VolumeDefinition.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/VolumeExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/VolumeSnapshot.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/VolumeSnapshotDefinition.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_1/VolumeSnapshotExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_2/ComputeApi.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_2/ComputeService.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_2/ComputeServiceExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_2/KeyPair.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_2/KeyPairDefinition.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_2/KeyPairType.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_2/RemoteConsole.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_2/RemoteConsoleType.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_2/Serialization/ServerCollection.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_2/Server.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_2/ServerListOptions.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_2/ServerReference.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_2/ServerStatus.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_6/ComputeApi.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_6/ComputeService.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_6/ComputeServiceExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_6/Console.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_6/ConsoleProtocol.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_6/KeyPair.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_6/KeyPairDefinition.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_6/KeyPairType.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_6/RemoteConsoleType.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_6/Serialization/ServerCollection.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_6/Server.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_6/ServerListOptions.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_6/ServerReference.cs create mode 100644 redistributable/openstack.net/src/corelib/Compute/v2_6/ServerStatus.cs create mode 100644 redistributable/openstack.net/src/corelib/ContentDeliveryNetworks/v1/ContentDeliveryNetworkService.cs create mode 100644 redistributable/openstack.net/src/corelib/ContentDeliveryNetworks/v1/ContentDeliveryNetworkServiceExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/ContentDeliveryNetworks/v1/Flavor.cs create mode 100644 redistributable/openstack.net/src/corelib/ContentDeliveryNetworks/v1/FlavorCollection.cs create mode 100644 redistributable/openstack.net/src/corelib/ContentDeliveryNetworks/v1/IContentDeliveryNetworkService.cs create mode 100644 redistributable/openstack.net/src/corelib/ContentDeliveryNetworks/v1/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/ContentDeliveryNetworks/v1/Provider.cs create mode 100644 redistributable/openstack.net/src/corelib/ContentDeliveryNetworks/v1/Service.cs create mode 100644 redistributable/openstack.net/src/corelib/ContentDeliveryNetworks/v1/ServiceCache.cs create mode 100644 redistributable/openstack.net/src/corelib/ContentDeliveryNetworks/v1/ServiceCacheRule.cs create mode 100644 redistributable/openstack.net/src/corelib/ContentDeliveryNetworks/v1/ServiceCollection.cs create mode 100644 redistributable/openstack.net/src/corelib/ContentDeliveryNetworks/v1/ServiceDefinition.cs create mode 100644 redistributable/openstack.net/src/corelib/ContentDeliveryNetworks/v1/ServiceDomain.cs create mode 100644 redistributable/openstack.net/src/corelib/ContentDeliveryNetworks/v1/ServiceError.cs create mode 100644 redistributable/openstack.net/src/corelib/ContentDeliveryNetworks/v1/ServiceOperationFailedException.cs create mode 100644 redistributable/openstack.net/src/corelib/ContentDeliveryNetworks/v1/ServiceOrigin.cs create mode 100644 redistributable/openstack.net/src/corelib/ContentDeliveryNetworks/v1/ServiceOriginRule.cs create mode 100644 redistributable/openstack.net/src/corelib/ContentDeliveryNetworks/v1/ServiceProtocol.cs create mode 100644 redistributable/openstack.net/src/corelib/ContentDeliveryNetworks/v1/ServiceRestriction.cs create mode 100644 redistributable/openstack.net/src/corelib/ContentDeliveryNetworks/v1/ServiceRestrictionRule.cs create mode 100644 redistributable/openstack.net/src/corelib/ContentDeliveryNetworks/v1/ServiceStatus.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/AsyncCompletionOption.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/BackoffPolicy.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Caching/ICache`1.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Caching/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Caching/UserAccessCache.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Collections/BasicReadOnlyCollectionPage`1.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Collections/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Collections/ReadOnlyCollectionPage`1.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Compat/Funcs.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Compat/ISafeSerializationData.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Compat/IStructuralComparable.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Compat/IStructuralEquatable.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Compat/Tuple.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Compat/Tuples.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/CoreTaskExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/AuthenticationRequirement.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/AuthenticationType.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/CloudIdentity.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/CloudIdentityWithProject.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/CloudNetwork.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Container.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/ContainerCDN.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/ContainerObject.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Converters/IPAddressDetailsConverter.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Converters/IPAddressNoneIsNullSimpleConverter.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Converters/IPAddressSimpleConverter.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Converters/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Converters/PhysicalAddressSimpleConverter.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Converters/SimpleStringJsonConverter`1.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/DiskConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Endpoint.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/EndpointTemplate.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/EndpointTemplateId.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/ExtendedEndpoint.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/ExtensibleJsonObject.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Flavor.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/FlavorDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/FlavorId.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/HomeDocument.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/IPAddressList.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/IdentityToken.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/ImageId.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/ImageState.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/ImageType.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Link.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Mapping/IJsonObjectMapper`1.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Mapping/IObjectMapper`2.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Mapping/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Metadata.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/NetworkId.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/NewServer.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/NewUser.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/ObjectStore.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Personality.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/PowerState.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/ProjectId.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/ProviderStateBase`1.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Queues/Claim.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Queues/ClaimId.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Queues/CloudQueue.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Queues/Message.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Queues/MessageId.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Queues/MessageStatistics.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Queues/Message`1.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Queues/MessagesEnqueued.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Queues/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Queues/QueueMessagesStatistics.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Queues/QueueName.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Queues/QueueStatistics.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Queues/QueuedMessage.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Queues/QueuedMessageList.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Queues/QueuedMessageListId.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/RebootType.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/ResourceHints.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/ResourceObject.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Role.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Server.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/ServerAddresses.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/ServerBase.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/ServerId.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/ServerImage.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/ServerState.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/ServerVolume.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/ServiceCatalog.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/SimpleServer.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/SimpleServerImage.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Snapshot.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/SnapshotState.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Status.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/TaskState.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Tenant.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/User.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/UserAccess.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/UserCredential.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/UserDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/VirtualInterface.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/VirtualInterfaceAddress.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/VirtualMachineState.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/Volume.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/VolumeState.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Domain/VolumeType.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Exceptions/CDNNotEnabledException.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Exceptions/CidrFormatException.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Exceptions/ContainerNameException.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Exceptions/ContainerNotEmptyException.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Exceptions/ImageEnteredErrorStateException.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Exceptions/InvalidCloudIdentityException.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Exceptions/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Exceptions/NoDefaultRegionSetException.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Exceptions/ObjectNameException.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Exceptions/Response/BadServiceRequestException.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Exceptions/Response/ItemNotFoundException.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Exceptions/Response/MethodNotImplementedException.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Exceptions/Response/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Exceptions/Response/ResponseException.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Exceptions/Response/ServiceConflictException.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Exceptions/Response/ServiceFaultException.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Exceptions/Response/ServiceLimitReachedException.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Exceptions/Response/ServiceUnavailableException.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Exceptions/Response/UserNotAuthorizedException.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Exceptions/ServerEnteredErrorStateException.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Exceptions/SnapshotEnteredErrorStateException.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Exceptions/TTLLengthException.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Exceptions/UserAuthenticationException.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Exceptions/UserAuthorizationException.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Exceptions/VolumeEnteredErrorStateException.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/ExtensibleEnum`1.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/HttpStatusCodeParser.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/IBackoffPolicy.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/IEncodeDecodeProvider.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/IObjectStorageMetadataProcessor.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/IStatusParser.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/InternalTaskExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/LegacyAuthenticationProviderHelper.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/ParallelExtensionsExtras/TaskCompletionSourceExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/ParallelExtensionsExtras/TaskExtrasExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/ParallelExtensionsExtras/TaskFactoryExtensions_Common.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/ParallelExtensionsExtras/TaskFactoryExtensions_ContinueWhenAllAny.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/ParallelExtensionsExtras/TaskFactoryExtensions_Delayed.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/ParallelExtensionsExtras/TaskFactoryExtensions_From.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Providers/IBlockStorageProvider.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Providers/IComputeProvider.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Providers/IIdentityProvider.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Providers/IIdentityService.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Providers/INetworksProvider.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Providers/IObjectStorageProvider.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Providers/IQueueingService.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Providers/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Providers/OpenStackIdentityProvider.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/ReadOnlyCollectionPageExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/ResourceIdentifier`1.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/ResponseExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/RestWebHeaderCollection.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Synchronous/AutoScaleServiceExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Synchronous/ClaimExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Synchronous/DatabaseServiceExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Synchronous/DnsServiceExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Synchronous/LoadBalancerServiceExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Synchronous/MonitoringServiceExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Synchronous/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Synchronous/QueueingServiceExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Synchronous/ReadOnlyCollectionPageSynchronousExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/UriPart.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/UriUtility.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Validators/IBlockStorageValidator.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Validators/IHttpResponseCodeValidator.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Validators/INetworksValidator.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Validators/IObjectStorageValidator.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/Validators/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Core/WebRequestExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Exceptions/IdentityRequiredException.cs create mode 100644 redistributable/openstack.net/src/corelib/Exceptions/RegionRequiredException.cs create mode 100644 redistributable/openstack.net/src/corelib/Exceptions/ResourceErrorException.cs create mode 100644 redistributable/openstack.net/src/corelib/Exceptions/UserAuthenticationException.cs create mode 100644 redistributable/openstack.net/src/corelib/Extensions/EnumerableExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Extensions/FlurlExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Extensions/HttpHeadersExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Extensions/HttpRequestMessageExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Extensions/TaskExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Extensions/TypeExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Flurl/PreparedRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Icons/openstack_net_logo.png create mode 100644 redistributable/openstack.net/src/corelib/Identifier.cs create mode 100644 redistributable/openstack.net/src/corelib/Images/v2/ImageStatus.cs create mode 100644 redistributable/openstack.net/src/corelib/Images/v2/Serialization/ImageStatus.cs create mode 100644 redistributable/openstack.net/src/corelib/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/IPVersion.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/AllocationPool.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/AllowedAddress.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/HostRoute.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/IPAddressAssociation.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/IPProtocol.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Layer3/ExternalGateway.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Layer3/ExternalGatewayDefinition.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Layer3/FloatingIP.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Layer3/FloatingIPCreateDefinition.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Layer3/FloatingIPExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Layer3/FloatingIPListOptions.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Layer3/FloatingIPStatus.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Layer3/FloatingIPUpdateDefinition.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Layer3/NetworkingService_Layer3_Extensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Layer3/Router.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Layer3/RouterCreateDefinition.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Layer3/RouterExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Layer3/RouterListOptions.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Layer3/RouterStatus.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Layer3/RouterUpdateDefinition.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Layer3/SecurityGroup.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Layer3/SecurityGroupListOptions.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Layer3/SecurityGroupRule.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Layer3/SecurityGroupRuleListOptions.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Network.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/NetworkDefinition.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/NetworkStatus.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/NetworkingApiBuilder.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/NetworkingService.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/NetworkingServiceExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Operator/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Operator/NetworkDefinition.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Port.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/PortCreateDefinition.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/PortListOptions.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/PortStatus.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/PortUpdateDefinition.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Serialization/DHCPOptionsConverter.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Serialization/FloatingIPCollection.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Serialization/IPProtocol.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Serialization/NetworkCollection.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Serialization/NetworkDefinitionCollection.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Serialization/NetworkResourceStatus.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Serialization/PortCollection.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Serialization/PortDefinitionCollection.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Serialization/RouterCollection.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Serialization/SecurityGroupCollection.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Serialization/SecurityGroupRuleCollection.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Serialization/SubnetCollection.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Serialization/SubnetDefinitionCollection.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Serialization/TrafficDirection.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/Subnet.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/SubnetCreateDefinition.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/SubnetUpdateDefinition.cs create mode 100644 redistributable/openstack.net/src/corelib/Networking/v2/TrafficDirection.cs create mode 100644 redistributable/openstack.net/src/corelib/OpenStack.csproj create mode 100644 redistributable/openstack.net/src/corelib/OpenStackNet.cs create mode 100644 redistributable/openstack.net/src/corelib/Page.cs create mode 100644 redistributable/openstack.net/src/corelib/PageExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/PageOptions.cs create mode 100644 redistributable/openstack.net/src/corelib/Properties/AssemblyInfo.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Hp/HpIdentityProvider.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Hp/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Hp/PredefinedHpIdentityEndpoints.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/CloudAutoScaleProvider.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/CloudBlockStorageProvider.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/CloudDatabasesProvider.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/CloudDnsProvider.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/CloudFilesMetadataProcessor.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/CloudFilesProvider.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/CloudIdentityProvider.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/CloudLoadBalancerProvider.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/CloudMonitoringProvider.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/CloudNetworksProvider.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/CloudQueuesProvider.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/CloudServersProvider.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/EncodeDecodeProvider.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Exceptions/BulkDeletionException.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Exceptions/InvalidETagException.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Exceptions/InvalidVolumeSizeException.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Exceptions/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/ExtendedJsonRestServices.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/IAutoScaleService.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/IDatabaseService.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/IDnsService.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/IExtendedCloudIdentityProvider.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/ILoadBalancerService.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/IMonitoringService.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/IProviderFactory`2.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/IRackspaceProvider.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/ArchiveFormat.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/AuthDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/AutoScale/ActiveServer.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/AutoScale/GenericLaunchConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/AutoScale/GroupConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/AutoScale/GroupState.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/AutoScale/LaunchConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/AutoScale/LaunchConfiguration`1.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/AutoScale/LaunchType.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/AutoScale/LoadBalancerArgument.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/AutoScale/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/AutoScale/NewWebhookConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/AutoScale/Policy.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/AutoScale/PolicyConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/AutoScale/PolicyId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/AutoScale/PolicyType.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/AutoScale/ScalingGroup.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/AutoScale/ScalingGroupConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/AutoScale/ScalingGroupConfiguration`1.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/AutoScale/ScalingGroupId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/AutoScale/ServerArgument.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/AutoScale/ServerLaunchArguments.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/AutoScale/ServerLaunchConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/AutoScale/ServerNetworkArgument.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/AutoScale/UpdateWebhookConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/AutoScale/Webhook.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/AutoScale/WebhookConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/AutoScale/WebhookId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/BulkDeletionFailedObject.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/BulkDeletionResults.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Credentials.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Databases/Backup.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Databases/BackupConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Databases/BackupId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Databases/BackupStatus.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Databases/Database.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Databases/DatabaseConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Databases/DatabaseFlavor.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Databases/DatabaseInstance.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Databases/DatabaseInstanceConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Databases/DatabaseInstanceId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Databases/DatabaseInstanceStatus.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Databases/DatabaseName.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Databases/DatabaseUser.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Databases/DatabaseVolumeConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Databases/FlavorId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Databases/FlavorRef.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Databases/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Databases/RestorePoint.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Databases/RootUser.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Databases/UpdateUserConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Databases/UserConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Databases/UserName.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/DnsChange.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/DnsConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/DnsDomain.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/DnsDomainChange.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/DnsDomainChanges.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/DnsDomainConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/DnsDomainRecordConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/DnsDomainRecordUpdateConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/DnsDomainUpdateConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/DnsDomains.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/DnsJob.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/DnsJobStatus.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/DnsJob`1.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/DnsNameserver.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/DnsRateLimit.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/DnsRateLimitPattern.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/DnsRateLimitUnit.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/DnsRateLimits.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/DnsRecord.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/DnsRecordType.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/DnsRecordsList.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/DnsServiceLimits.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/DnsSubdomain.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/DnsSubdomainConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/DnsSubdomainsList.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/DnsUpdateConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/DomainId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/ExportedDomain.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/JobId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/LimitType.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/RecordId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/SerializedDomain.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Dns/SerializedDomainFormat.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Domain.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/AccessType.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/ConnectionHealthMonitor.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/ConnectionThrottles.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/CustomHealthMonitor.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/HealthMonitor.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/HealthMonitorType.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/LoadBalancer.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/LoadBalancerCluster.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/LoadBalancerConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/LoadBalancerConfiguration`1.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/LoadBalancerEnabledFlag.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/LoadBalancerId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/LoadBalancerMetadataItem.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/LoadBalancerSslConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/LoadBalancerStatistics.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/LoadBalancerStatus.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/LoadBalancerTimestamp.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/LoadBalancerUpdate.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/LoadBalancerUsage.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/LoadBalancerUsageId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/LoadBalancerVirtualAddress.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/LoadBalancerVirtualAddressType.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/LoadBalancingAlgorithm.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/LoadBalancingProtocol.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/MetadataId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/NetworkItem.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/NetworkItemId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Node.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/NodeCondition.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/NodeConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/NodeId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/NodeServiceEvent.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/NodeServiceEventCategory.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/NodeServiceEventId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/NodeServiceEventSeverity.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/NodeServiceEventType.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/NodeStatus.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/NodeType.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/NodeUpdate.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Request/AddLoadBalancerMetadataRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Request/AddNodesRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Request/CreateAccessListRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Request/CreateLoadBalancerRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Request/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Request/SetLoadBalancerConnectionLoggingRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Request/SetLoadBalancerContentCachingRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Request/SetLoadBalancerErrorPageRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Request/UpdateLoadBalancerMetadataItemRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Request/UpdateLoadBalancerNodeRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Request/UpdateLoadBalancerRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Response/GetAccessListResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Response/GetLoadBalancerConnectionLoggingResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Response/GetLoadBalancerContentCachingResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Response/GetLoadBalancerErrorPageResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Response/GetLoadBalancerMetadataItemResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Response/GetLoadBalancerNodeResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Response/GetLoadBalancerResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Response/GetLoadBalancerSslConfigurationResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Response/ListAllowedDomainsResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Response/ListLoadBalancerMetadataResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Response/ListLoadBalancerNodesResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Response/ListLoadBalancerThrottlesResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Response/ListLoadBalancerUsageResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Response/ListLoadBalancersResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Response/ListLoadBalancingAlgorithmsResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Response/ListLoadBalancingProtocolsResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Response/ListNodeServiceEventsResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Response/ListVirtualAddressesResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/Response/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/SessionPersistence.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/SessionPersistenceType.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/VirtualAddressId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/LoadBalancers/WebServerHealthMonitor.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Mapping/BulkDeletionResultMapper.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Mapping/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/AccountConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/Agent.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/AgentConnection.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/AgentConnectionId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/AgentId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/AgentToken.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/AgentTokenConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/AgentTokenId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/Alarm.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/AlarmChangelog.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/AlarmChangelogId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/AlarmConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/AlarmData.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/AlarmExample.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/AlarmExampleField.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/AlarmExampleId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/AlarmId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/AlarmNotificationHistoryItem.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/AlarmNotificationHistoryItemId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/AlarmState.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/AlarmStateHistory.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/Audit.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/AuditId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/BoundAlarmExample.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/Check.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/CheckConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/CheckData.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/CheckDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/CheckId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/CheckMetricType.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/CheckTarget.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/CheckTargetId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/CheckType.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/CheckTypeId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/CheckTypeType.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/ConnectionCheckDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/CpuCheckDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/CpuInformation.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/DataPoint.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/DataPointGranularity.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/DataPointStatistic.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/DateTimeOffsetExtensions.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/DiskCheckDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/DiskInformation.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/DnsCheckDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/EmailNotificationDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/Entity.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/EntityConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/EntityId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/EntityOverview.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/FilesystemCheckDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/FilesystemInformation.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/FtpBannerCheckDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/GenericCheckDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/GenericNotificationDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/HostInformationType.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/HostInformation`1.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/HttpCheckDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/ImapBannerCheckDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/LoadAverageCheckDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/LoginInformation.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/MemoryCheckDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/MemoryInformation.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/Metric.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/MetricName.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/MonitoringAccount.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/MonitoringAccountId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/MonitoringLimits.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/MonitoringZone.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/MonitoringZoneId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/MssqlBannerCheckDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/MysqlBannerCheckDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/NetworkCheckDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/NetworkInterfaceInformation.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/NewAlarmConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/NewCheckConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/NewEntityConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/NewNotificationConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/NewNotificationPlanConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/Notification.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/NotificationAttempt.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/NotificationConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/NotificationData.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/NotificationDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/NotificationId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/NotificationPlan.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/NotificationPlanConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/NotificationPlanId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/NotificationResult.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/NotificationType.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/NotificationTypeField.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/NotificationTypeId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/PagerDutyNotificationDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/PingCheckDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/PluginCheckDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/Pop3CheckDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/PostgresqlBannerCheckDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/ProcessInformation.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/ReadOnlyCollectionPage`2.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/SecureConnectionCheckDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/SmtpBannerCheckDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/SmtpCheckDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/SshCheckDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/SystemInformation.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/TargetResolverType.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/TcpCheckDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/TelnetBannerCheckDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/TestAlarmConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/TraceRoute.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/TraceRouteConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/TraceRouteHop.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/TransactionId.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/UpdateAlarmConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/UpdateCheckConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/UpdateEntityConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/UpdateNotificationConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/UpdateNotificationPlanConfiguration.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/WebhookNotificationDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Monitoring/WebhookToken.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Queues/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Queues/Request/ClaimMessagesRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Queues/Request/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Queues/Response/ListCloudQueueMessagesResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Queues/Response/ListCloudQueuesResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Queues/Response/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/RackspaceCloudIdentity.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/AddRoleRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/AddServiceCatalogEndpointRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/AddUserRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/AttachServerVolumeRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/AuthRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/ChangeServerAdminPasswordRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/ConfirmServerResizeRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/CreateCloudBlockStorageSnapshotDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/CreateCloudBlockStorageSnapshotRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/CreateCloudBlockStorageVolumeDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/CreateCloudBlockStorageVolumeRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/CreateCloudNetworkRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/CreateCloudNetworksDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/CreateServerImageDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/CreateServerImageRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/CreateServerRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/CreateVirtualInterfaceRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/PasswordCredential.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/Personality.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/RescueServerRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/RevertServerResizeRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/ServerRebootDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/ServerRebootRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/ServerRebuildDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/ServerRebuildRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/ServerResizeDetails.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/ServerResizeRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/SetPasswordRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/UnrescueServerRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/UpdateMetadataItemRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/UpdateMetadataRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/UpdateServerRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/UpdateUserCredentialRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Request/UpdateUserRequest.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/AuthenticationResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/BulkDeleteResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/CloudNetworkResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/CreateServerResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/ExtractArchiveError.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/ExtractArchiveResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/FlavorDetailsResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/GetCloudBlockStorageSnapshotResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/GetCloudBlockStorageVolumeResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/GetCloudBlockStorageVolumeTypeResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/GetEndpointResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/GetImageDetailsResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/ListAddressesResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/ListCloudNetworksResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/ListEndpointsResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/ListFlavorDetailsResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/ListFlavorsResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/ListImagesDetailsResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/ListImagesResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/ListServersResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/ListSnapshotResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/ListVirtualInterfacesResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/ListVolumeResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/ListVolumeTypeResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/MetaDataResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/MetadataItemResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/NewUserResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/PasswordCredentialResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/RescueServerResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/RoleResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/RolesResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/ServerDetailsResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/ServerVolumeListResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/ServerVolumeResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/TenantsResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/UserCredentialResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/UserImpersonationResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/UserResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Objects/Response/UsersResponse.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/ProviderBase`1.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/RackspaceImpersonationIdentity.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Validators/CloudBlockStorageValidator.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Validators/CloudFilesValidator.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Validators/CloudNetworksValidator.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Validators/HttpResponseCodeValidator.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/Validators/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/WebRequestEventArgs.cs create mode 100644 redistributable/openstack.net/src/corelib/Providers/Rackspace/WebResponseEventArgs.cs create mode 100644 redistributable/openstack.net/src/corelib/Serialization/DefaultJsonConverter.cs create mode 100644 redistributable/openstack.net/src/corelib/Serialization/IHaveExtraData.cs create mode 100644 redistributable/openstack.net/src/corelib/Serialization/IPageBuilder.cs create mode 100644 redistributable/openstack.net/src/corelib/Serialization/IPageLink.cs create mode 100644 redistributable/openstack.net/src/corelib/Serialization/IQueryStringBuilder.cs create mode 100644 redistributable/openstack.net/src/corelib/Serialization/IServiceResource.cs create mode 100644 redistributable/openstack.net/src/corelib/Serialization/IdentifierConverter.cs create mode 100644 redistributable/openstack.net/src/corelib/Serialization/JsonConverterWithConstructorAttribute.cs create mode 100644 redistributable/openstack.net/src/corelib/Serialization/OpenStackContractResolver.cs create mode 100644 redistributable/openstack.net/src/corelib/Serialization/Page.cs create mode 100644 redistributable/openstack.net/src/corelib/Serialization/PageLink.cs create mode 100644 redistributable/openstack.net/src/corelib/Serialization/ResourceCollection.cs create mode 100644 redistributable/openstack.net/src/corelib/Serialization/ResourceStatus.cs create mode 100644 redistributable/openstack.net/src/corelib/Serialization/RootWrapperConverter.cs create mode 100644 redistributable/openstack.net/src/corelib/Serialization/ServiceEndpoint.cs create mode 100644 redistributable/openstack.net/src/corelib/Serialization/StringEnumeration.cs create mode 100644 redistributable/openstack.net/src/corelib/Serialization/StringEnumerationConverter.cs create mode 100644 redistributable/openstack.net/src/corelib/Serialization/TimeSpanInSecondsConverter.cs create mode 100644 redistributable/openstack.net/src/corelib/Serialization/TolerantEnumConverter.cs create mode 100644 redistributable/openstack.net/src/corelib/Testing/HttpTest.cs create mode 100644 redistributable/openstack.net/src/corelib/Testing/NamespaceDoc.cs create mode 100644 redistributable/openstack.net/src/corelib/app.config create mode 100644 redistributable/openstack.net/src/corelib/corelib.nuspec create mode 100644 redistributable/openstack.net/src/corelib/paket.references create mode 100644 redistributable/openstack.net/src/lib/Security.Cryptography/Security.Cryptography.dll create mode 100644 redistributable/openstack.net/src/lib/Security.Cryptography/Security.Cryptography.pdb create mode 100644 redistributable/openstack.net/src/lib/Security.Cryptography/Security.Cryptography.xml create mode 100644 redistributable/openstack.net/src/openstack.net.vsmdi create mode 100644 redistributable/openstack.net/src/testing/integration/App.config create mode 100644 redistributable/openstack.net/src/testing/integration/BlockStorage/v2/BlockStorageTestDataManager.cs create mode 100644 redistributable/openstack.net/src/testing/integration/Bootstrapper.cs create mode 100644 redistributable/openstack.net/src/testing/integration/Compute/v2_1/ComputeServiceTests.cs create mode 100644 redistributable/openstack.net/src/testing/integration/Compute/v2_1/ComputeTestDataManager.cs create mode 100644 redistributable/openstack.net/src/testing/integration/Compute/v2_1/FlavorTests.cs create mode 100644 redistributable/openstack.net/src/testing/integration/Compute/v2_1/ImageTests.cs create mode 100644 redistributable/openstack.net/src/testing/integration/Compute/v2_1/KeyPairTests.cs create mode 100644 redistributable/openstack.net/src/testing/integration/Compute/v2_1/Operator/ComputeServiceTests.cs create mode 100644 redistributable/openstack.net/src/testing/integration/Compute/v2_1/Operator/ServerTests.cs create mode 100644 redistributable/openstack.net/src/testing/integration/Compute/v2_1/SecurityGroupTests.cs create mode 100644 redistributable/openstack.net/src/testing/integration/Compute/v2_1/ServerGroupTests.cs create mode 100644 redistributable/openstack.net/src/testing/integration/Compute/v2_1/ServerTests.cs create mode 100644 redistributable/openstack.net/src/testing/integration/Compute/v2_1/VolumeTests.cs create mode 100644 redistributable/openstack.net/src/testing/integration/ContentDeliveryNetworks/v1/ContentDeliveryNetworkServiceTests.cs create mode 100644 redistributable/openstack.net/src/testing/integration/ContentDeliveryNetworks/v1/ServiceTests.cs create mode 100644 redistributable/openstack.net/src/testing/integration/Identity/v2/IdentityTests.cs create mode 100644 redistributable/openstack.net/src/testing/integration/Networking/v2/Layer3/Layer3ExtensionTests.cs create mode 100644 redistributable/openstack.net/src/testing/integration/Networking/v2/NetworkingServiceTests.cs create mode 100644 redistributable/openstack.net/src/testing/integration/Networking/v2/NetworkingTestDataManager.cs create mode 100644 redistributable/openstack.net/src/testing/integration/ObjectStorage/CloudFilesProviderTests.cs create mode 100644 redistributable/openstack.net/src/testing/integration/OpenStack.IntegrationTests.csproj create mode 100644 redistributable/openstack.net/src/testing/integration/Properties/AssemblyInfo.cs create mode 100644 redistributable/openstack.net/src/testing/integration/TestData.cs create mode 100644 redistributable/openstack.net/src/testing/integration/TestIdentityProvider.cs create mode 100644 redistributable/openstack.net/src/testing/integration/XunitTraceListener.cs create mode 100644 redistributable/openstack.net/src/testing/integration/paket.references create mode 100644 redistributable/openstack.net/src/testing/migration/App.config create mode 100644 redistributable/openstack.net/src/testing/migration/Program.cs create mode 100644 redistributable/openstack.net/src/testing/migration/Properties/AssemblyInfo.cs create mode 100644 redistributable/openstack.net/src/testing/migration/migration.csproj create mode 100644 redistributable/openstack.net/src/testing/migration/paket.references create mode 100644 redistributable/openstack.net/src/testing/unit/AuthenticationTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Compute/v2_1/ComputeServiceTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Compute/v2_1/FlavorTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Compute/v2_1/ImageTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Compute/v2_1/KeyPairTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Compute/v2_1/Operator/ComputeServiceTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Compute/v2_1/Operator/ServerTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Compute/v2_1/SecurityGroupTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Compute/v2_1/ServerAddressTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Compute/v2_1/ServerGroupTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Compute/v2_1/ServerTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Compute/v2_1/VolumeTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Compute/v2_2/ConsoleTypeTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Compute/v2_6/ServerTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/ContentDeliveryNetworks/v1/ContentDeliveryNetworkServiceTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/ContentDeliveryNetworks/v1/FlavorTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/ContentDeliveryNetworks/v1/ServiceCacheTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/ContentDeliveryNetworks/v1/ServiceOperationFailedExceptionTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/ContentDeliveryNetworks/v1/ServiceTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Domain/Mapping/NetworkAddressDeserializationTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Extensions/EnumerableExtensionsTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Extensions/TypeExtensionsTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/IdentifierTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Networking/v2/DHCPOptionConverterTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Networking/v2/Layer3/Layer3Tests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Networking/v2/NetworkTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Networking/v2/PortTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Networking/v2/SubnetTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/OpenStack.UnitTests.csproj create mode 100644 redistributable/openstack.net/src/testing/unit/OpenStackNetTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Properties/AssemblyInfo.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Providers/Rackspace/CloudBlockStorageTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Providers/Rackspace/CloudNetworksValidatorTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Providers/Rackspace/EncodeDecodeProviderTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Providers/Rackspace/IdentityProviderCacheTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Providers/Rackspace/JsonModelTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Providers/Rackspace/ObjectProviderHelperTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Providers/Rackspace/ProviderBaseTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Providers/Rackspace/SerializationTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Serialization/EmptyEnumerableTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Serialization/RootWrapperConverterTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Serialization/TolerantEnumConverterTests.cs create mode 100644 redistributable/openstack.net/src/testing/unit/Stubs.cs create mode 100644 redistributable/openstack.net/src/testing/unit/TestCategories.cs create mode 100644 redistributable/openstack.net/src/testing/unit/XunitTraceListener.cs create mode 100644 redistributable/openstack.net/src/testing/unit/app.config create mode 100644 redistributable/openstack.net/src/testing/unit/paket.references create mode 100644 redistributable/rackspace-net-sdk/.paket/paket.bootstrapper.exe create mode 100644 redistributable/rackspace-net-sdk/LICENSE create mode 100644 redistributable/rackspace-net-sdk/README.md create mode 100644 redistributable/rackspace-net-sdk/build.cmd create mode 100644 redistributable/rackspace-net-sdk/build.sh create mode 100644 redistributable/rackspace-net-sdk/build/Rackspace.nuspec create mode 100644 redistributable/rackspace-net-sdk/build/build.proj create mode 100644 redistributable/rackspace-net-sdk/build/check-nuget-version-exists.ps1 create mode 100644 redistributable/rackspace-net-sdk/build/rackspace-logo.png create mode 100644 redistributable/rackspace-net-sdk/docs/ContentLayout.content create mode 100644 redistributable/rackspace-net-sdk/docs/Rackspace.Docs.shfbproj create mode 100644 redistributable/rackspace-net-sdk/docs/icons/Help.png create mode 100644 redistributable/rackspace-net-sdk/packages/Newtonsoft.Json.6.0.1/.signature.p7s create mode 100644 redistributable/rackspace-net-sdk/packages/Newtonsoft.Json.6.0.1/Newtonsoft.Json.6.0.1.nupkg create mode 100644 redistributable/rackspace-net-sdk/packages/Newtonsoft.Json.6.0.1/lib/net20/Newtonsoft.Json.dll create mode 100644 redistributable/rackspace-net-sdk/packages/Newtonsoft.Json.6.0.1/lib/net20/Newtonsoft.Json.xml create mode 100644 redistributable/rackspace-net-sdk/packages/Newtonsoft.Json.6.0.1/lib/net35/Newtonsoft.Json.dll create mode 100644 redistributable/rackspace-net-sdk/packages/Newtonsoft.Json.6.0.1/lib/net35/Newtonsoft.Json.xml create mode 100644 redistributable/rackspace-net-sdk/packages/Newtonsoft.Json.6.0.1/lib/net40/Newtonsoft.Json.dll create mode 100644 redistributable/rackspace-net-sdk/packages/Newtonsoft.Json.6.0.1/lib/net40/Newtonsoft.Json.xml create mode 100644 redistributable/rackspace-net-sdk/packages/Newtonsoft.Json.6.0.1/lib/net45/Newtonsoft.Json.dll create mode 100644 redistributable/rackspace-net-sdk/packages/Newtonsoft.Json.6.0.1/lib/net45/Newtonsoft.Json.xml create mode 100644 redistributable/rackspace-net-sdk/packages/Newtonsoft.Json.6.0.1/lib/netcore45/Newtonsoft.Json.dll create mode 100644 redistributable/rackspace-net-sdk/packages/Newtonsoft.Json.6.0.1/lib/netcore45/Newtonsoft.Json.xml create mode 100644 redistributable/rackspace-net-sdk/packages/Newtonsoft.Json.6.0.1/lib/portable-net40+sl5+wp80+win8+monotouch+monoandroid/Newtonsoft.Json.dll create mode 100644 redistributable/rackspace-net-sdk/packages/Newtonsoft.Json.6.0.1/lib/portable-net40+sl5+wp80+win8+monotouch+monoandroid/Newtonsoft.Json.xml create mode 100644 redistributable/rackspace-net-sdk/packages/Newtonsoft.Json.6.0.1/lib/portable-net45+wp80+win8/Newtonsoft.Json.dll create mode 100644 redistributable/rackspace-net-sdk/packages/Newtonsoft.Json.6.0.1/lib/portable-net45+wp80+win8/Newtonsoft.Json.xml create mode 100644 redistributable/rackspace-net-sdk/packages/Newtonsoft.Json.6.0.1/tools/install.ps1 create mode 100644 redistributable/rackspace-net-sdk/packages/System.Buffers.4.5.1/.signature.p7s create mode 100644 redistributable/rackspace-net-sdk/packages/System.Buffers.4.5.1/System.Buffers.4.5.1.nupkg create mode 100644 redistributable/rackspace-net-sdk/packages/System.Buffers.4.5.1/lib/net461/System.Buffers.dll create mode 100644 redistributable/rackspace-net-sdk/packages/System.Buffers.4.5.1/lib/net461/System.Buffers.xml create mode 100644 redistributable/rackspace-net-sdk/packages/System.Buffers.4.5.1/lib/netcoreapp2.0/_._ create mode 100644 redistributable/rackspace-net-sdk/packages/System.Buffers.4.5.1/lib/netstandard1.1/System.Buffers.dll create mode 100644 redistributable/rackspace-net-sdk/packages/System.Buffers.4.5.1/lib/netstandard1.1/System.Buffers.xml create mode 100644 redistributable/rackspace-net-sdk/packages/System.Buffers.4.5.1/lib/netstandard2.0/System.Buffers.dll create mode 100644 redistributable/rackspace-net-sdk/packages/System.Buffers.4.5.1/lib/netstandard2.0/System.Buffers.xml create mode 100644 redistributable/rackspace-net-sdk/packages/System.Buffers.4.5.1/lib/uap10.0.16299/_._ create mode 100644 redistributable/rackspace-net-sdk/packages/System.Buffers.4.5.1/ref/net45/System.Buffers.dll create mode 100644 redistributable/rackspace-net-sdk/packages/System.Buffers.4.5.1/ref/net45/System.Buffers.xml create mode 100644 redistributable/rackspace-net-sdk/packages/System.Buffers.4.5.1/ref/netcoreapp2.0/_._ create mode 100644 redistributable/rackspace-net-sdk/packages/System.Buffers.4.5.1/ref/netstandard1.1/System.Buffers.dll create mode 100644 redistributable/rackspace-net-sdk/packages/System.Buffers.4.5.1/ref/netstandard1.1/System.Buffers.xml create mode 100644 redistributable/rackspace-net-sdk/packages/System.Buffers.4.5.1/ref/netstandard2.0/System.Buffers.dll create mode 100644 redistributable/rackspace-net-sdk/packages/System.Buffers.4.5.1/ref/netstandard2.0/System.Buffers.xml create mode 100644 redistributable/rackspace-net-sdk/packages/System.Buffers.4.5.1/ref/uap10.0.16299/_._ create mode 100644 redistributable/rackspace-net-sdk/packages/System.Diagnostics.DiagnosticSource.4.7.1/.signature.p7s create mode 100644 redistributable/rackspace-net-sdk/packages/System.Diagnostics.DiagnosticSource.4.7.1/Icon.png create mode 100644 redistributable/rackspace-net-sdk/packages/System.Diagnostics.DiagnosticSource.4.7.1/System.Diagnostics.DiagnosticSource.4.7.1.nupkg create mode 100644 redistributable/rackspace-net-sdk/packages/System.Diagnostics.DiagnosticSource.4.7.1/lib/net45/System.Diagnostics.DiagnosticSource.dll create mode 100644 redistributable/rackspace-net-sdk/packages/System.Diagnostics.DiagnosticSource.4.7.1/lib/net45/System.Diagnostics.DiagnosticSource.xml create mode 100644 redistributable/rackspace-net-sdk/packages/System.Diagnostics.DiagnosticSource.4.7.1/lib/net46/System.Diagnostics.DiagnosticSource.dll create mode 100644 redistributable/rackspace-net-sdk/packages/System.Diagnostics.DiagnosticSource.4.7.1/lib/net46/System.Diagnostics.DiagnosticSource.xml create mode 100644 redistributable/rackspace-net-sdk/packages/System.Diagnostics.DiagnosticSource.4.7.1/lib/netstandard1.1/System.Diagnostics.DiagnosticSource.dll create mode 100644 redistributable/rackspace-net-sdk/packages/System.Diagnostics.DiagnosticSource.4.7.1/lib/netstandard1.1/System.Diagnostics.DiagnosticSource.xml create mode 100644 redistributable/rackspace-net-sdk/packages/System.Diagnostics.DiagnosticSource.4.7.1/lib/netstandard1.3/System.Diagnostics.DiagnosticSource.dll create mode 100644 redistributable/rackspace-net-sdk/packages/System.Diagnostics.DiagnosticSource.4.7.1/lib/netstandard1.3/System.Diagnostics.DiagnosticSource.xml create mode 100644 redistributable/rackspace-net-sdk/packages/System.Diagnostics.DiagnosticSource.4.7.1/lib/portable-net45+win8+wpa81/System.Diagnostics.DiagnosticSource.dll create mode 100644 redistributable/rackspace-net-sdk/packages/System.Diagnostics.DiagnosticSource.4.7.1/lib/portable-net45+win8+wpa81/System.Diagnostics.DiagnosticSource.xml create mode 100644 redistributable/rackspace-net-sdk/packages/System.Memory.4.5.4/.signature.p7s create mode 100644 redistributable/rackspace-net-sdk/packages/System.Memory.4.5.4/System.Memory.4.5.4.nupkg create mode 100644 redistributable/rackspace-net-sdk/packages/System.Memory.4.5.4/lib/net461/System.Memory.dll create mode 100644 redistributable/rackspace-net-sdk/packages/System.Memory.4.5.4/lib/net461/System.Memory.xml create mode 100644 redistributable/rackspace-net-sdk/packages/System.Memory.4.5.4/lib/netcoreapp2.1/_._ create mode 100644 redistributable/rackspace-net-sdk/packages/System.Memory.4.5.4/lib/netstandard1.1/System.Memory.dll create mode 100644 redistributable/rackspace-net-sdk/packages/System.Memory.4.5.4/lib/netstandard1.1/System.Memory.xml create mode 100644 redistributable/rackspace-net-sdk/packages/System.Memory.4.5.4/lib/netstandard2.0/System.Memory.dll create mode 100644 redistributable/rackspace-net-sdk/packages/System.Memory.4.5.4/lib/netstandard2.0/System.Memory.xml create mode 100644 redistributable/rackspace-net-sdk/packages/System.Memory.4.5.4/ref/netcoreapp2.1/_._ create mode 100644 redistributable/rackspace-net-sdk/packages/System.Runtime.CompilerServices.Unsafe.4.5.3/.signature.p7s create mode 100644 redistributable/rackspace-net-sdk/packages/System.Runtime.CompilerServices.Unsafe.4.5.3/System.Runtime.CompilerServices.Unsafe.4.5.3.nupkg create mode 100644 redistributable/rackspace-net-sdk/packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib/net461/System.Runtime.CompilerServices.Unsafe.dll create mode 100644 redistributable/rackspace-net-sdk/packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib/net461/System.Runtime.CompilerServices.Unsafe.xml create mode 100644 redistributable/rackspace-net-sdk/packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib/netcoreapp2.0/System.Runtime.CompilerServices.Unsafe.dll create mode 100644 redistributable/rackspace-net-sdk/packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib/netcoreapp2.0/System.Runtime.CompilerServices.Unsafe.xml create mode 100644 redistributable/rackspace-net-sdk/packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib/netstandard1.0/System.Runtime.CompilerServices.Unsafe.dll create mode 100644 redistributable/rackspace-net-sdk/packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib/netstandard1.0/System.Runtime.CompilerServices.Unsafe.xml create mode 100644 redistributable/rackspace-net-sdk/packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll create mode 100644 redistributable/rackspace-net-sdk/packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.xml create mode 100644 redistributable/rackspace-net-sdk/packages/System.Runtime.CompilerServices.Unsafe.4.5.3/ref/net461/System.Runtime.CompilerServices.Unsafe.dll create mode 100644 redistributable/rackspace-net-sdk/packages/System.Runtime.CompilerServices.Unsafe.4.5.3/ref/net461/System.Runtime.CompilerServices.Unsafe.xml create mode 100644 redistributable/rackspace-net-sdk/packages/System.Runtime.CompilerServices.Unsafe.4.5.3/ref/netstandard1.0/System.Runtime.CompilerServices.Unsafe.dll create mode 100644 redistributable/rackspace-net-sdk/packages/System.Runtime.CompilerServices.Unsafe.4.5.3/ref/netstandard1.0/System.Runtime.CompilerServices.Unsafe.xml create mode 100644 redistributable/rackspace-net-sdk/packages/System.Runtime.CompilerServices.Unsafe.4.5.3/ref/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll create mode 100644 redistributable/rackspace-net-sdk/packages/System.Runtime.CompilerServices.Unsafe.4.5.3/ref/netstandard2.0/System.Runtime.CompilerServices.Unsafe.xml create mode 100644 redistributable/rackspace-net-sdk/packages/System.ValueTuple.4.3.0/.signature.p7s create mode 100644 redistributable/rackspace-net-sdk/packages/System.ValueTuple.4.3.0/System.ValueTuple.4.3.0.nupkg create mode 100644 redistributable/rackspace-net-sdk/packages/System.ValueTuple.4.3.0/lib/netstandard1.0/.xml create mode 100644 redistributable/rackspace-net-sdk/packages/System.ValueTuple.4.3.0/lib/netstandard1.0/System.ValueTuple.dll create mode 100644 redistributable/rackspace-net-sdk/packages/System.ValueTuple.4.3.0/lib/portable-net40+sl4+win8+wp8/.xml create mode 100644 redistributable/rackspace-net-sdk/packages/System.ValueTuple.4.3.0/lib/portable-net40+sl4+win8+wp8/System.ValueTuple.dll create mode 100644 redistributable/rackspace-net-sdk/paket.dependencies create mode 100644 redistributable/rackspace-net-sdk/samples/Rackspace.Samples/AssignPublicIPSamples.cs create mode 100644 redistributable/rackspace-net-sdk/samples/Rackspace.Samples/CloudNetworkSamples.cs create mode 100644 redistributable/rackspace-net-sdk/samples/Rackspace.Samples/ISample.cs create mode 100644 redistributable/rackspace-net-sdk/samples/Rackspace.Samples/Program.cs create mode 100644 redistributable/rackspace-net-sdk/samples/Rackspace.Samples/Properties/AssemblyInfo.cs create mode 100644 redistributable/rackspace-net-sdk/samples/Rackspace.Samples/Rackspace.Samples.csproj create mode 100644 redistributable/rackspace-net-sdk/samples/Rackspace.Samples/app.config create mode 100644 redistributable/rackspace-net-sdk/samples/Rackspace.Samples/paket.references create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/Authentication/ServiceType.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/CloudNetworks/IPVersion.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/CloudNetworks/v2/AllocationPool.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/CloudNetworks/v2/CloudNetworkService.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/CloudNetworks/v2/CloudNetworkServiceExtensions.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/CloudNetworks/v2/HostRoute.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/CloudNetworks/v2/IPAddressAssociation.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/CloudNetworks/v2/Network.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/CloudNetworks/v2/NetworkDefinition.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/CloudNetworks/v2/NetworkStatus.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/CloudNetworks/v2/Port.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/CloudNetworks/v2/PortCreateDefinition.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/CloudNetworks/v2/PortStatus.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/CloudNetworks/v2/PortUpdateDefinition.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/CloudNetworks/v2/Serialization/NetworkCollection.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/CloudNetworks/v2/Serialization/PortCollection.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/CloudNetworks/v2/Serialization/SubnetCollection.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/CloudNetworks/v2/Subnet.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/CloudNetworks/v2/SubnetCreateDefinition.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/CloudNetworks/v2/SubnetUpdateDefinition.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/Exceptions/ServiceOperationFailedException.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/IPage.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/IServiceResource.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/Identifier.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/NamespaceDoc.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/Properties/AssemblyInfo.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/RackConnect/v3/ListPublicIPsFilter.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/RackConnect/v3/NetworkReference.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/RackConnect/v3/PublicIP.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/RackConnect/v3/PublicIPCreateDefinition.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/RackConnect/v3/PublicIPExtensions.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/RackConnect/v3/PublicIPNetworkAssociation.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/RackConnect/v3/PublicIPServerAssociation.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/RackConnect/v3/PublicIPStatus.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/RackConnect/v3/PublicIPUpdateDefinition.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/RackConnect/v3/RackConnectService.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/RackConnect/v3/RackConnectServiceExtensions.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/Rackspace.csproj create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/RackspaceNet.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/Serialization/IdentifierConverter.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/Serialization/Page.cs create mode 100644 redistributable/rackspace-net-sdk/src/Rackspace/Testing/HttpTest.cs create mode 100644 redistributable/rackspace-net-sdk/test/Rackspace.IntegrationTests/CloudNetworks/v2/CloudNetworkServiceTests.cs create mode 100644 redistributable/rackspace-net-sdk/test/Rackspace.IntegrationTests/CloudNetworks/v2/CloudNetworksTestDataManager.cs create mode 100644 redistributable/rackspace-net-sdk/test/Rackspace.IntegrationTests/CloudServers/v2/CloudServersTestDataManager.cs create mode 100644 redistributable/rackspace-net-sdk/test/Rackspace.IntegrationTests/ContentDeliveryNetworks/v1/ContentDeliveryNetworkServiceTests.cs create mode 100644 redistributable/rackspace-net-sdk/test/Rackspace.IntegrationTests/Identity/v2/IdentityTests.cs create mode 100644 redistributable/rackspace-net-sdk/test/Rackspace.IntegrationTests/Properties/AssemblyInfo.cs create mode 100644 redistributable/rackspace-net-sdk/test/Rackspace.IntegrationTests/RackConnect/v3/RackConnectServiceTests.cs create mode 100644 redistributable/rackspace-net-sdk/test/Rackspace.IntegrationTests/RackConnect/v3/RackConnectTestDataManager.cs create mode 100644 redistributable/rackspace-net-sdk/test/Rackspace.IntegrationTests/Rackspace.IntegrationTests.csproj create mode 100644 redistributable/rackspace-net-sdk/test/Rackspace.IntegrationTests/TestData.cs create mode 100644 redistributable/rackspace-net-sdk/test/Rackspace.IntegrationTests/TestIdentityProvider.cs create mode 100644 redistributable/rackspace-net-sdk/test/Rackspace.IntegrationTests/XunitTraceListener.cs create mode 100644 redistributable/rackspace-net-sdk/test/Rackspace.IntegrationTests/app.config create mode 100644 redistributable/rackspace-net-sdk/test/Rackspace.UnitTests/CloudNetworks/v2/CloudNetworkServiceTests.cs create mode 100644 redistributable/rackspace-net-sdk/test/Rackspace.UnitTests/CloudNetworks/v2/NetworkTests.cs create mode 100644 redistributable/rackspace-net-sdk/test/Rackspace.UnitTests/CloudNetworks/v2/PortTests.cs create mode 100644 redistributable/rackspace-net-sdk/test/Rackspace.UnitTests/CloudNetworks/v2/SubnetTests.cs create mode 100644 redistributable/rackspace-net-sdk/test/Rackspace.UnitTests/IdentifierTests.cs create mode 100644 redistributable/rackspace-net-sdk/test/Rackspace.UnitTests/Properties/AssemblyInfo.cs create mode 100644 redistributable/rackspace-net-sdk/test/Rackspace.UnitTests/RackConnect/v3/NetworkTests.cs create mode 100644 redistributable/rackspace-net-sdk/test/Rackspace.UnitTests/RackConnect/v3/PublicIPTests.cs create mode 100644 redistributable/rackspace-net-sdk/test/Rackspace.UnitTests/RackConnect/v3/RackConnectServiceTests.cs create mode 100644 redistributable/rackspace-net-sdk/test/Rackspace.UnitTests/Rackspace.UnitTests.csproj create mode 100644 redistributable/rackspace-net-sdk/test/Rackspace.UnitTests/RackspaceNetTests.cs create mode 100644 redistributable/rackspace-net-sdk/test/Rackspace.UnitTests/Stubs.cs create mode 100644 web/studio/ASC.Web.Studio/Core/AdminHelperSettings.cs create mode 100644 web/studio/ASC.Web.Studio/Core/Notify/WebstudioNotifyPatternResource.az-Latn-AZ.resx delete mode 100644 web/studio/ASC.Web.Studio/Core/Users/AffiliateHelper.cs create mode 100644 web/studio/ASC.Web.Studio/DeepLink.aspx create mode 100644 web/studio/ASC.Web.Studio/DeepLink.aspx.cs create mode 100644 web/studio/ASC.Web.Studio/DeepLink.aspx.designer.cs delete mode 100644 web/studio/ASC.Web.Studio/HttpHandlers/KeepSessionAliveHandler.cs create mode 100644 web/studio/ASC.Web.Studio/Masters/FirstScripts.ascx create mode 100644 web/studio/ASC.Web.Studio/Masters/FirstScripts.ascx.cs create mode 100644 web/studio/ASC.Web.Studio/Products/CRM/Resources/CRMInvoiceResource.az-Latn-AZ.resx create mode 100644 web/studio/ASC.Web.Studio/Products/CRM/Resources/CRMReportResource.az-Latn-AZ.resx create mode 100644 web/studio/ASC.Web.Studio/Products/CRM/Resources/CRMVoipResource.az-Latn-AZ.resx delete mode 100644 web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Classes/BirthdaysModule.cs delete mode 100644 web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Classes/BirthdaysSubscriptionManager.cs delete mode 100644 web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Default.aspx delete mode 100644 web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Default.aspx.cs delete mode 100644 web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Default.aspx.designer.cs delete mode 100644 web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/patterns.xml delete mode 100644 web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/js/birthdays.js create mode 100644 web/studio/ASC.Web.Studio/Products/Files/Controls/AccessRights/FormFilling.ascx create mode 100644 web/studio/ASC.Web.Studio/Products/Files/Controls/AccessRights/FormFilling.ascx.cs create mode 100644 web/studio/ASC.Web.Studio/Products/Files/Controls/AccessRights/FormFilling.ascx.designer.cs create mode 100644 web/studio/ASC.Web.Studio/Products/Files/Controls/AccessRights/formfilling.css create mode 100644 web/studio/ASC.Web.Studio/Products/Files/Controls/AccessRights/formfilling.js create mode 100644 web/studio/ASC.Web.Studio/Products/Files/Controls/AccessRights/images/fillformsgray.svg create mode 100644 web/studio/ASC.Web.Studio/Products/Files/Controls/PrivateRoomOpenFile/PrivateRoomOpenFile.ascx create mode 100644 web/studio/ASC.Web.Studio/Products/Files/Controls/PrivateRoomOpenFile/PrivateRoomOpenFile.ascx.cs create mode 100644 web/studio/ASC.Web.Studio/Products/Files/Controls/PrivateRoomOpenFile/PrivateRoomOpenFile.ascx.designer.cs create mode 100644 web/studio/ASC.Web.Studio/Products/Files/Controls/PrivateRoomOpenFile/privateroomopenfile.css create mode 100644 web/studio/ASC.Web.Studio/Products/Files/Controls/PrivateRoomOpenFile/privateroomopenfile.js create mode 100644 web/studio/ASC.Web.Studio/Products/Files/Controls/SharingDialog/SharingDialog.ascx create mode 100644 web/studio/ASC.Web.Studio/Products/Files/Controls/SharingDialog/SharingDialog.ascx.cs create mode 100644 web/studio/ASC.Web.Studio/Products/Files/Controls/SharingDialog/SharingDialog.ascx.designer.cs create mode 100644 web/studio/ASC.Web.Studio/Products/Files/Controls/SharingDialog/images/link.svg create mode 100644 web/studio/ASC.Web.Studio/Products/Files/Controls/SharingDialog/images/link_via.png create mode 100644 web/studio/ASC.Web.Studio/Products/Files/Controls/SharingDialog/images/settings.svg create mode 100644 web/studio/ASC.Web.Studio/Products/Files/Controls/SharingDialog/images/user.svg create mode 100644 web/studio/ASC.Web.Studio/Products/Files/Controls/SharingDialog/sharingdialog.css create mode 100644 web/studio/ASC.Web.Studio/Products/Files/Controls/SharingDialog/sharingdialog.js create mode 100644 web/studio/ASC.Web.Studio/Products/Files/Controls/UnsubscribeDialog/UnsubscribeDialog.ascx create mode 100644 web/studio/ASC.Web.Studio/Products/Files/Controls/UnsubscribeDialog/UnsubscribeDialog.ascx.cs create mode 100644 web/studio/ASC.Web.Studio/Products/Files/Controls/UnsubscribeDialog/UnsubscribeDialog.ascx.designer.cs create mode 100644 web/studio/ASC.Web.Studio/Products/Files/Controls/UnsubscribeDialog/unsubscribedialog.js create mode 100644 web/studio/ASC.Web.Studio/Products/Files/Core/Entries/AutoCleanUpData.cs create mode 100644 web/studio/ASC.Web.Studio/Products/Files/Core/Entries/EntryProperties.cs create mode 100644 web/studio/ASC.Web.Studio/Products/Files/OpenPrivate.aspx create mode 100644 web/studio/ASC.Web.Studio/Products/Files/OpenPrivate.aspx.cs create mode 100644 web/studio/ASC.Web.Studio/Products/Files/OpenPrivate.aspx.designer.cs rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays/App_Themes/default => People/App_Themes/default/css}/birthdays.css (92%) create mode 100644 web/studio/ASC.Web.Studio/Products/People/App_Themes/default/css/carddav.css rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays/App_Themes/default/images/birthday.png => People/App_Themes/default/images/birthdayEmpScr.png} (100%) create mode 100644 web/studio/ASC.Web.Studio/Products/People/App_Themes/default/images/svg/birthday-orange.svg create mode 100644 web/studio/ASC.Web.Studio/Products/People/App_Themes/default/images/svg/birthday.svg rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/App_Themes/default/images/white_cross.png (100%) create mode 100644 web/studio/ASC.Web.Studio/Products/People/Birthdays.aspx create mode 100644 web/studio/ASC.Web.Studio/Products/People/Birthdays.aspx.cs create mode 100644 web/studio/ASC.Web.Studio/Products/People/Birthdays.aspx.designer.cs create mode 100644 web/studio/ASC.Web.Studio/Products/People/CardDavSettings.aspx create mode 100644 web/studio/ASC.Web.Studio/Products/People/CardDavSettings.aspx.cs create mode 100644 web/studio/ASC.Web.Studio/Products/People/CardDavSettings.aspx.designer.cs create mode 100644 web/studio/ASC.Web.Studio/Products/People/Classes/BirthdaysModule.cs rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Classes/BirthdaysNotifyClient.cs (86%) create mode 100644 web/studio/ASC.Web.Studio/Products/People/Classes/PeopleSubscriptionManager.cs rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.Designer.cs (89%) create mode 100644 web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.az-Latn-AZ.resx rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.be.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.bg.resx (98%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.cs.resx (98%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.de.resx (98%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.el.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.es.resx (98%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.fi.resx (98%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.fr.resx (98%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.hi.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.hu.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.id.resx (98%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.it.resx (98%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.ja.resx (98%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.kk.resx (98%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.lv.resx (98%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.nl.resx (98%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.pl.resx (98%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.pt-BR.resx (98%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.resx (98%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.ru.resx (98%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.sk.resx (98%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.sl.resx (98%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.sv.resx (98%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.tr.resx (98%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.uk.resx (98%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.vi.resx (98%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.zh-CN.resx (98%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdayPatternResource.zh-TW.resx (98%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.Designer.cs (93%) create mode 100644 web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.az-Latn-AZ.resx rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.be.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.bg.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.cs.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.da.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.de.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.el.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.es.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.fi.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.fr.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.hi.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.hu.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.id.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.it.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.ja.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.kk.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.lv.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.nl.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.pl.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.pt-BR.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.ru.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.sk.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.sl.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.sv.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.tr.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.uk.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.vi.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.zh-CN.resx (97%) rename web/studio/ASC.Web.Studio/Products/{Community/Modules/Birthdays => People}/Resources/BirthdaysResource.zh-TW.resx (97%) create mode 100644 web/studio/ASC.Web.Studio/Products/People/Resources/PeopleJSResource.az-Latn-AZ.resx create mode 100644 web/studio/ASC.Web.Studio/Products/People/Resources/patterns.xml create mode 100644 web/studio/ASC.Web.Studio/Products/People/js/birthdays.js create mode 100644 web/studio/ASC.Web.Studio/Products/People/js/carddav.js create mode 100644 web/studio/ASC.Web.Studio/Products/Projects/Core/Engine/EngineResource.az-Latn-AZ.resx create mode 100644 web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Domain/Entities/Enums/AverageTimeEnum.cs create mode 100644 web/studio/ASC.Web.Studio/PublicResources/AuditResource.az-Latn-AZ.resx create mode 100644 web/studio/ASC.Web.Studio/PublicResources/ChatResource.az-Latn-AZ.resx create mode 100644 web/studio/ASC.Web.Studio/PublicResources/CustomModeResource.az-Latn-AZ.resx create mode 100644 web/studio/ASC.Web.Studio/PublicResources/MonitoringResource.az-Latn-AZ.resx create mode 100644 web/studio/ASC.Web.Studio/PublicResources/PersonalResource.az-Latn-AZ.resx create mode 100644 web/studio/ASC.Web.Studio/PublicResources/StudioCountriesResource.az-Latn-AZ.resx create mode 100644 web/studio/ASC.Web.Studio/PublicResources/WhiteLabelResource.az-Latn-AZ.resx delete mode 100644 web/studio/ASC.Web.Studio/ThirdParty/ImportContacts/Yahoo.aspx delete mode 100644 web/studio/ASC.Web.Studio/ThirdParty/ImportContacts/Yahoo.aspx.cs delete mode 100644 web/studio/ASC.Web.Studio/ThirdParty/ImportContacts/Yahoo.aspx.designer.cs delete mode 100644 web/studio/ASC.Web.Studio/ThirdParty/ImportContacts/image/yahoo.png delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/ThirdPartyBanner.ascx delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/ThirdPartyBanner.ascx.cs delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/ThirdPartyBanner.ascx.designer.cs delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/ThirdPartyBannerSettings.cs delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/css/img/bitly.png delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/css/img/social.png delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/css/img/storage.png delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/css/img/twilio2.png delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/css/thirdpartybanner.css create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/a11yhelp/dialogs/lang/en-au.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/ajax/plugin.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/autocomplete/plugin.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/autocomplete/skins/default.css delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/colordialog/dialog/dialogDefinition.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/dialog/styles/dialog.css create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/mentions/plugin.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/pastefromgdocs/filter/default.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/pastefromlibreoffice/filter/default.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/pastetools/filter/common.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/pastetools/filter/image.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/preview/images/pagebreak.gif create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/preview/styles/screen.css create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/specialchar/dialogs/lang/ro.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/specialchar/dialogs/lang/sr-latn.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/specialchar/dialogs/lang/sr.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/teamlabcut/icons/teamlabcut_hidpi.png delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/templates/default.js delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/templates/images/template1.gif delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/templates/images/template2.gif delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/templates/images/template3.gif create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/textmatch/plugin.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/textwatcher/plugin.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/widget/images/handle.png create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/icons/hidpi/spellchecker.png create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/icons/spellchecker.png create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/af.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/ar.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/bg.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/bn.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/bs.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/ca.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/cs.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/cy.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/da.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/de.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/el.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/en-au.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/en-ca.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/en-gb.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/en.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/eo.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/es.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/et.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/eu.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/fa.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/fi.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/fo.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/fr-ca.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/fr.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/gl.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/gu.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/he.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/hi.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/hr.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/hu.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/is.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/it.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/ja.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/ka.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/km.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/ko.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/ku.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/lt.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/lv.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/mk.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/mn.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/ms.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/nb.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/nl.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/no.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/pl.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/pt-br.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/pt.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/ro.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/ru.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/sk.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/sl.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/sr-latn.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/sr.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/sv.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/th.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/tr.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/ug.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/uk.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/vi.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/zh-cn.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/lang/zh.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/wsc/plugin.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/plugins/xml/plugin.js delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/css/samples.css delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/img/github-top.png delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/img/header-bg.png delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/img/header-separator.png delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/img/logo.png delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/img/navigation-tip.png delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/index.html delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/js/sample.js delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/js/sf.js delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/ajax.html delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/api.html delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/appendto.html delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/assets/inlineall/logo.png delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/assets/outputxhtml/outputxhtml.css delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/assets/sample.jpg delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/assets/uilanguages/languages.js delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/datafiltering.html delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/dialog/assets/my_dialog.js delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/dialog/dialog.html delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/divreplace.html delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/enterkey/enterkey.html delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/htmlwriter/assets/outputforflash/outputforflash.fla delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/htmlwriter/assets/outputforflash/outputforflash.swf delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/htmlwriter/assets/outputforflash/swfobject.js delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/htmlwriter/outputforflash.html delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/htmlwriter/outputhtml.html delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/index.html delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/inlineall.html delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/inlinebycode.html delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/inlinetextarea.html delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/jquery.html delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/magicline/magicline.html delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/readonly.html delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/replacebyclass.html delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/replacebycode.html delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/sample.css delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/sample.js delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/tabindex.html delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/toolbar/toolbar.html delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/uicolor.html delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/uilanguages.html delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/wysiwygarea/fullpage.html delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/old/xhtmlstyle.html delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/toolbarconfigurator/css/fontello.css delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/toolbarconfigurator/font/config.json delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/toolbarconfigurator/font/fontello.eot delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/toolbarconfigurator/font/fontello.svg delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/toolbarconfigurator/font/fontello.ttf delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/toolbarconfigurator/font/fontello.woff delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/toolbarconfigurator/index.html delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/toolbarconfigurator/js/abstracttoolbarmodifier.js delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/toolbarconfigurator/js/fulltoolbareditor.js delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/toolbarconfigurator/js/toolbarmodifier.js delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/toolbarconfigurator/js/toolbartextmodifier.js delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/toolbarconfigurator/lib/codemirror/LICENSE delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/toolbarconfigurator/lib/codemirror/codemirror.css delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/toolbarconfigurator/lib/codemirror/codemirror.js delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/toolbarconfigurator/lib/codemirror/javascript.js delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/toolbarconfigurator/lib/codemirror/neo.css delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/toolbarconfigurator/lib/codemirror/show-hint.css delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/samples/toolbarconfigurator/lib/codemirror/show-hint.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/skins/teamlab/images/close-ckeditor.png create mode 100644 web/studio/ASC.Web.Studio/UserControls/Common/ckeditor/vendor/promise.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/DeepLink/DeepLinkData.cs create mode 100644 web/studio/ASC.Web.Studio/UserControls/DeepLink/DeepLinkDataFile.cs create mode 100644 web/studio/ASC.Web.Studio/UserControls/DeepLink/DeepLinkDataFolder.cs create mode 100644 web/studio/ASC.Web.Studio/UserControls/DeepLink/DeepLinking.ascx create mode 100644 web/studio/ASC.Web.Studio/UserControls/DeepLink/DeepLinking.ascx.cs create mode 100644 web/studio/ASC.Web.Studio/UserControls/DeepLink/DeepLinking.ascx.designer.cs create mode 100644 web/studio/ASC.Web.Studio/UserControls/DeepLink/css/deeplinking.less create mode 100644 web/studio/ASC.Web.Studio/UserControls/DeepLink/js/deeplinking.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Management/AdminHelperSettings/AdminHelperSettings.ascx create mode 100644 web/studio/ASC.Web.Studio/UserControls/Management/AdminHelperSettings/AdminHelperSettings.ascx.cs create mode 100644 web/studio/ASC.Web.Studio/UserControls/Management/AdminHelperSettings/AdminHelperSettings.ascx.designer.cs create mode 100644 web/studio/ASC.Web.Studio/UserControls/Management/AdminHelperSettings/css/adminhelpersettings.less create mode 100644 web/studio/ASC.Web.Studio/UserControls/Management/AdminHelperSettings/css/images/cross.svg create mode 100644 web/studio/ASC.Web.Studio/UserControls/Management/AdminHelperSettings/css/images/subtract.svg create mode 100644 web/studio/ASC.Web.Studio/UserControls/Management/AdminHelperSettings/js/adminhelpersettings.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Management/AuthorizationKeys/img/appleid.svg create mode 100644 web/studio/ASC.Web.Studio/UserControls/Management/AuthorizationKeys/img/microsoft.svg delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Management/TariffSettings/TariffUsage.ascx delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Management/TariffSettings/TariffUsage.ascx.cs delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Management/TariffSettings/TariffUsage.ascx.designer.cs delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Management/TariffSettings/css/tariffusage.less delete mode 100644 web/studio/ASC.Web.Studio/UserControls/Management/TariffSettings/js/tariffusage.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Users/UserConnections/UserConnections.ascx create mode 100644 web/studio/ASC.Web.Studio/UserControls/Users/UserConnections/UserConnections.ascx.cs create mode 100644 web/studio/ASC.Web.Studio/UserControls/Users/UserConnections/UserConnections.ascx.designer.cs create mode 100644 web/studio/ASC.Web.Studio/UserControls/Users/UserConnections/css/connections.less create mode 100644 web/studio/ASC.Web.Studio/UserControls/Users/UserConnections/js/connections_manager.js create mode 100644 web/studio/ASC.Web.Studio/UserControls/Users/UserProfile/css/images/appleid.svg create mode 100644 web/studio/ASC.Web.Studio/UserControls/Users/UserProfile/css/images/microsoft.svg create mode 100644 web/studio/ASC.Web.Studio/Utility/RefererURL.cs create mode 100644 web/studio/ASC.Web.Studio/addons/calendar/Configuration/CalendarSpaceUsageStatManager.cs create mode 100644 web/studio/ASC.Web.Studio/addons/calendar/Controls/DocumentsPopup/DocumentsPopup.ascx create mode 100644 web/studio/ASC.Web.Studio/addons/calendar/Controls/DocumentsPopup/DocumentsPopup.ascx.cs create mode 100644 web/studio/ASC.Web.Studio/addons/calendar/Controls/DocumentsPopup/DocumentsPopup.ascx.designer.cs create mode 100644 web/studio/ASC.Web.Studio/addons/calendar/Controls/DocumentsPopup/css/documentspopup.less create mode 100644 web/studio/ASC.Web.Studio/addons/calendar/Controls/DocumentsPopup/css/images/warning.png create mode 100644 web/studio/ASC.Web.Studio/addons/calendar/Controls/DocumentsPopup/js/documentspopup.js create mode 100644 web/studio/ASC.Web.Studio/addons/calendar/Handlers/FilesUploader.cs create mode 100644 web/studio/ASC.Web.Studio/addons/calendar/UserControls/fullcalendar/css/attachments.less create mode 100644 web/studio/ASC.Web.Studio/addons/calendar/UserControls/fullcalendar/img/base_sprite.png create mode 100644 web/studio/ASC.Web.Studio/addons/calendar/UserControls/js/calendar.attachments.js create mode 100644 web/studio/ASC.Web.Studio/addons/mail/Resources/MailActionCompleteResource.az-Latn-AZ.resx create mode 100644 web/studio/ASC.Web.Studio/addons/mail/Resources/MailApiErrorsResource.az-Latn-AZ.resx create mode 100644 web/studio/ASC.Web.Studio/addons/mail/Resources/MailAttachmentsResource.az-Latn-AZ.resx delete mode 100644 web/studio/ASC.Web.Studio/addons/mail/js/contacts/tlgroups.js create mode 100644 web/studio/ASC.Web.Studio/addons/mail/js/third-party/linkify-html.min.js delete mode 100644 web/studio/ASC.Web.Studio/js/asc/core/my.js create mode 100644 web/studio/ASC.Web.Studio/js/asc/plugins/userselector.js rename web/studio/ASC.Web.Studio/skins/default/images/logo/{spreadsheet.ico => cell.ico} (100%) rename web/studio/ASC.Web.Studio/skins/default/images/logo/{presentation.ico => slide.ico} (100%) rename web/studio/ASC.Web.Studio/skins/default/images/logo/{text.ico => word.ico} (100%) delete mode 100644 web/studio/ASC.Web.Studio/skins/default/images/mail_user.png delete mode 100644 web/studio/ASC.Web.Studio/skins/default/images/support.png create mode 100644 web/studio/ASC.Web.Studio/skins/default/images/svg/context/icon_form_filling.svg create mode 100644 web/studio/ASC.Web.Studio/skins/default/images/svg/mail/letter.svg create mode 100644 web/studio/ASC.Web.Studio/skins/default/images/svg/people/appleid.png create mode 100644 web/studio/ASC.Web.Studio/skins/default/images/svg/people/currentconnection.svg create mode 100644 web/studio/ASC.Web.Studio/skins/default/images/svg/people/microsoft.png create mode 100644 web/studio/ASC.Web.Studio/skins/default/images/svg/share/access_comment.svg create mode 100644 web/studio/ASC.Web.Studio/skins/default/images/svg/share/access_deny.svg create mode 100644 web/studio/ASC.Web.Studio/skins/default/images/svg/share/access_filter.svg create mode 100644 web/studio/ASC.Web.Studio/skins/default/images/svg/share/access_form.svg create mode 100644 web/studio/ASC.Web.Studio/skins/default/images/svg/share/access_full.svg create mode 100644 web/studio/ASC.Web.Studio/skins/default/images/svg/share/access_owner.svg create mode 100644 web/studio/ASC.Web.Studio/skins/default/images/svg/share/access_read.svg create mode 100644 web/studio/ASC.Web.Studio/skins/default/images/svg/share/access_review.svg create mode 100644 web/studio/ASC.Web.Studio/skins/default/images/svg/share/access_varies.svg create mode 100644 web/studio/ASC.Web.Studio/skins/default/images/svg/share/close.svg create mode 100644 web/studio/ASC.Web.Studio/skins/default/images/svg/share/group_avatar.svg create mode 100644 web/studio/ASC.Web.Studio/skins/default/images/svg/share/user_avatar.svg delete mode 100644 web/studio/ASC.Web.Studio/skins/default/layout-desktop.css create mode 100644 web/studio/ASC.Web.Studio/skins/default/layout-desktop.less delete mode 100644 web/studio/ASC.Web.Studio/skins/default/layout-media.css create mode 100644 web/studio/ASC.Web.Studio/skins/default/layout-media.less delete mode 100644 web/studio/ASC.Web.Studio/skins/default/layout.css create mode 100644 web/studio/ASC.Web.Studio/skins/default/layout.less create mode 100644 web/studio/ASC.Web.Studio/skins/default/userselector.less diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..72a5505e0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,29 @@ +**/obj/ +**/bin/ +/packages/ +/redistributable/ICal.Net_v2/packages/ +*.suo +*.user +web/studio/Logs/ +web/studio/Data/ +build/*.log +build/deploy +/module/ASC.Api/Logs +.vs/ +.vscode/ +/web/services/logs +/web/statistics/logs +/web/studio/ASC.Web.Studio/js/third-party/jquery/jquery.core.js +/web/studio/ASC.Web.Studio/js/third-party/jquery/jquery.blockUI.js +/web/studio/ASC.Web.Studio/js/third-party/jquery/jquery.mousewheel.js +/web/studio/ASC.Web.Studio/js/uploader/jquery.fileupload.js +/module/ASC.Socket.IO/node_modules +/module/ASC.Socket.IO/package-lock.json +/module/ASC.Socket.IO/typings +/module/ASC.Thumbnails/node_modules +/module/ASC.SsoAuth/node_modules +/module/ASC.UrlShortener/node_modules +/module/ASC.UrlShortener/Logs +/module/ASC.WebDav/node_modules +/module/ASC.WebDav/logs +/module/Logs \ No newline at end of file diff --git a/.nuget/packages/AjaxPro.2.2.9.17.2.nupkg b/.nuget/packages/AjaxPro.2.2.9.17.2.nupkg deleted file mode 100644 index c57d3be260db011ada76c9e5f5128f899d9faca1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 84731 zcmb5V1CVAxuqN8Jrfu7{J=3;r+qT_3ZQHi>w{6?DyLax5-Ppam?`^zyBC_ICROJ_$ zmEVa{9(gHHFc_f!c#c!JB*NhZ9Ik<_PAY^IaF7IeZ&rEOY;$&}PL`?YGn2%VIk&B(p$k>RB)5M6C)zHY8 zn2_QBivjaW*hEa5i8tvU4<{vv>Tb?dWV_;`G0m_df>2$;8ps!pOww|5b;9 zjmy-8iJ9GinZ?+M)tJM`fP;(Gkb|9xjf2&Ik%g6w-rmW^*vS6B`^U-5X~fFL$YH=} z%E)TM%=VxDG5nXP@yq|C3@Q)^5Y&HC#uKJcMdcq|j6i^Zkp5BTe;>I28*f|^W#k4K z;X-dDKOwEmlAXKzCFjAIqjbm-Qb&kOLj5%@}2$dC*3vgjmutfU9?_ttIJ1< zXrchmkwx&$~YGkI=R6yH*eQ+O1I#LyWO?$pKCFj8xv1sG{;N_NKP zG_4MMCT9Sbj;!4^Tl z4+HlhU}Nm=ZLATd7RcYLCtf&ARm0Z*(T-Z|Z?tX239MlFui;GGnE`1=gP{p3@U0$T+wzG56v)4>gx4>Of4F<&Hbx8ln$5RRk$|C_Y#XR~4 zxFqNcf}z>3o@{xP4!mqvo-;Or?!SHY-hQ+*EK> z6c_2k@1H1@7?T2LMUxHZg&3+A6LJ;0j*3rZzm+QGCUG0<_>pdxp|F!ASlD$HSJNpt z%`lw>^t&s46iM7k0To=fD=(pX;u0i&@efmJPsW2L1G(Ap`PzdsGhK{V*Dg|qbKs2p+lG>2xC3 z%2A>`?s0Sj{bgL zOA8pE!P8oROz_WU{~l<(lIuf#xgUV@!9cPkhXs2Q5VHRD7o_ybAyO*%+W^VZ3q$l^ zd4i16bbVA{))P;kPxad%Oc$1EK=xanO`-&U>~Yu_el%_yUJNTN8=_|{T5}zKxUeG2 zW-8rc$UFl$uoN0;%n%1;;$Vz5h{yarhtJE^7tCg|v5TVNd|sRC)kKr(8-y9!wj}Wt$kXxR-z&ZKmU}Vx^yD5p?HMZ>DeDu`JzO+vx#l zoeFNghhmr&K8|Ts1{aLfphG%n-6+~SejbthdqBfn6{KO*cmzYZnI;_aHxKBzikM-7aN0I`G#fmzV~mb6ZA$T)VtoTx%CML?8H^%b!?casLM z6kn`-Bk>T-fqneb2SB-S!-}CQ(+}!(Zq?J-?a$ld=Mk^HHP%amk8uvy^}Pd#v&%8} zZ;U|i$5S1JmPgPp@$HV9(}=4_mqj9*_gg-QLRBX_`w2xMn)Pw0airobg%pcyPC(hc z2my0V#EO{d{ZJuEbed^qiXrKutTIuz100GI>;~>|OvwbKMCrWUm7=g1@!{xA%Fdic zA*^%{YGvXou*3TpnpZW*>svLFLJ8-s)LiqD2z3zyrK ztm0hitf;8QyS?57c(nj1OT(cB@8LI$TM-92|B4t)Bf`&Xuvb%e;hC9!Ju;_Hp+EFJ zV9GQ#Qv?U2!-)3xUlB*QwDo&>0q*)8bvDzRuQ$NFAUS+X+Pv!IRS5PXHtv|lVZs)D zl=&2JCBO@==6h$Xg6Mmr+;C(F(eC}$ed?Cz!4=o4`DbMs@!jW6T-sRi$r+fX#^U-h zf~*>2%EW(U+zTVN8<1;-%evkhn9OhI zW*Gsgu;GY}DaSN;{9v+cVr8KaRM#QxC&>X5Of@vGS2v|?VI@?;ca<2-F<+fn6EvwN z*ax#=M97R$ggL12GOB1jj&(JSbZC2w$5?OCRJ~#O;{1#S@D0Gs+2t+OABs*TJu7^# z?WrqYSCiFZF;BvjkHKSEhbVsuVyjt`TVhYuJB-!s+-x^e1U@&5erPeVpF@@SUlws| zI1I)0znXjTXmpEu1!ipO2C%>6uf{CKNU{5OGrScTNFYmc_hnp8Mu!nr1*faiAQiu% zw0A?6^vjmzhTdlY@%Q3f4#brEYv~=Z0!Y?U>V~JJhY(<4CbV{<@ZStgs=SwRc0^8qKpjv^8xj=kr^Yh0L*z-iEd_?oeaGAZX;gbZZ9 zge|#{Sp%$(CTEdm3>b4Moa4&dvmJN`ju~Vhk*p%u7xv)DS50*)+`G8>jp=ir_^>Pq=l} zt!Z)peMBM86Rx}ht=($<#L*b8h-xwkTHv)t5!d}Yh4folA^23Rsu7BtMNSzZ`*TwrNpGbP&@0=OOBU=JzqVJFdbbSS6w|WuQ(aH2++K0x zxdra{EJEyl{xBCf9gic!s#`|7b0`w}U?P$J)j*9%Y92}?YkH22{gP(--Cu=-7}N+| z+mpz~Mbre<0--QxCdOx+KCFh zB*_=xdA{Evi{fIN!9nq~*LNSS5>2?&3=}b`;awEIm-kELy;k!r;{7%x1&45`$sNqAL<*|O z9nuFg!>(z;nJeDjW+M=b+sF4c(8VduIerkF_qh;TVJ@1JIm(HcuK4Mhq3d^vuFJes zSJ2jduJc;SH;6+Tkz0tBU87}omUl7QprC~Wc4Ez{Z~bDIV=Yo)btCP;-l*a@oRR?* zRz0vHG(Xa`A16+4tYSWVaJLk~p9mRW_VScp0CX%MehBxxu9bPL6dqU8EtM)QGG{6x ze29F%auqdtomMiXp*nM9TRO0G)D2CCHf8YmUT-6xjJd2388Eq91mr3TbjEHqQ(Cir z7UYVSH)PTQ7t&!PrMm!iK|;{g)b8o&$>)f*AwbBbT{QYO6%Vcw#(Uz_=nObTz!N~= zL7@X_U5pM3fYO~%zAkY(-H|B!dOO4LA~x@F2zP-a{m@|SFtFZ&?wtVfoC~&wq=NhM z?k*41Dpu8MTv(DBCc)%tuud?y*^1K`2EFm(Uo~Q&DGwiKV6H{Qxx;zQ#rjP2y`x%w zy|;$vIef{CauT##^I~7)v#G@J;FS8)O`gETqfm#Pi&JVI^WKta+oTpU&sTQEKYE9L zxc*YJ9)z&`R)N&bZU=J6<_DZ4-aQqJ3=@T7nCC`XnM2_2gePQ5-8-$lF-cmh8D$$< zF)u|{=RXE)>b#fh8Z1As5AI_af@x0`G>5!mZ!Q(`-yl*Earypw-K~!hpg)e2dOcA5 zZi8D8g6_^D`(k6T^r@}WPUcmqP+zu0Y*4jG4Q9uY4O)wUCpA*yTc(VIwr3H)ij|BAYSuxJs%&aVDlvD}Zs_bxezL{mWY4 z{Tue0^Ego^;u!H7%TiVoUy1+{SdWl__k#+^H|S4N31PSBx9_MP7!pI5B`pLzc8GdO zmHC1wiq!?|^X1xLdak0s2Z!1A*AMoCZIp)=ffC=^k$mFlc;!7LR@%Y7FmY!>%x5wi zdji55S<&ZJfWt==yGDIY}|XTyYsD`;_9jA&RPzMMLPPTRc^1Ah-lMbCtJ0cD^QxEnfN z7OR+U+3zVGRd+y)TpTkbX{KOjxt!z8==e8hC#XV>TcvzlN;SE@Fyg%N-AJLJd9=c` zm#Vs+ybP)!IzO3D*xP0B9tKwWP3nO92w36IB_;|jKn-OXe?77{XA zAzg-#i6-}9e;g;7(iMX=kAIwk+6t7J6&v!Q_kla^K^sOn*}GPMR*im^8pmaNHx3^p z*%sfDU6~T~nG)@#PL||ANzf-hh7qr-wFxrI%e4MBBNYs24MK_731}l{bD}6fV7^W7 zq?Cg7Xbp(o5s4{#x0;1}#~x+!VjxsqZJ{fLjxiR64k zg>Iv?d7@7+iRTZ|G`$&K@Hmq;7W=aX=EY7fmx4f0N@Rj{`rmZVcj7w4sNZs085Kh{ zC%>9@W;NHLfsgJII%6;fw`zDD z4mEn&shb@Ycxdy^?=>B=j5xay$R@#Y@CU5*ShY)7t=vqKoDKFNwDR&~m_~G~@QZYT zS9{LC9?|c3)i0l;do`TOlVP32{F_*HnAe_HG$Yw0$v>v2jVi&Zp+anaU~s{>L?Lnh zFa*G8{28K`jd z!Nl06vv-a8!CICRTUwShs?)=DueJ3ES;E9|BzdN}ePKM<2rWIrT6datNq=2)hDwA_ zjJ3gAlQ>$w0wG(lX_$%S#Xgi5nqta}>+LDA5h%<+P_HLHA*`&6jxe6hlM+2(MBeYr zjxC&XgruX5tf_Lunvmhcni%2!8o=MLwHddHz~ZI!D8d%=W#Dl7AuNCq@s@#GFkZn1 zQB_X_^ZhdV@uGJfv-q1!5D;OWu$Wha{QxH)TzmMmL~L?g;EWZ)Rl+57oM8s5mX=03 zNr9iiQGKVLp%m1gaVO)XO`@_lCyU6KoI3*IVtM?JuvAV6sQ}Vea)Y7W<+npq0J2EI4IETY0U@kygWa*$J z`tp?041lFZ(V5ZPLjAnv5C@qHO?LEmg9+azIx$X@zy`y0-qnCm=Y!1o2PNX*-mmv7!KoJnN#h;SI; z4Zh(bAqVH`rkqiK*rje8ts<7mrOdnG5MaQ07ZI9Wje(RjEmO{75r>D-4UR%F7S5Dr ze;ox4L$Ayk#C77!Xm)jm$m5HclUjE8Ve!g(j$jik^>gxE_(tg4++6=+#8KS7RUiJe z)%Q~5W!wsyjm9VWWFX`j38E570Yf+^@`KE3V5soPMM(3o6=v3#soKb3cpq%ra%8#v z*K2QZZ4O7Nkd70U4R;KT1hoo=0g=S0e?+QY6ccF4Fl6UKiKAqo!aRy9ZttNTE|Ud% z25J3INTdIuX+f|s=^Iv)9V6de80|%cX?Cz+up#8*x2gGJrZ#PSw|4t)+=|ugmQvh` zE1Jaf)izT0%6@&rWGs!CsYgTo2Qp&wP5!scq}&MrSx>y^ML*ZwG{=Dg9=xfuu3V5X z8+1eaCDn%^B1onlAvYV~+xZi<=O<=P<;zh9^WpASfrDZXbEYn%FG4BDaf+lL4`Kq& zSFURR5LeOz%u#+W?szeAt5N3^Q8P6|WDk8)Z5G3h+38fib`NUk$@QPM$r&(<)@tD> z#jD4$8cua#>dBK^R!PC49~XQVH-x7h^?dwIZDsIp9oU=aA-P5ne(0l>-MR(AdNSmO zeS3gIo6K%|v4ud7ucOEhy6^;nSmjDEeC9pmxn3>e&x@li{K@!Q|7~+}S&GEs3O5d8 zWM$(O)c50rZK8Xh4Dv`4b=R6KY?P_hOn*|+mW8RSJ2&lAeOt1cAgFpb08{Q3NcSO% z_;tTa@uIhWPBkgLn-XW#+HXM0{w=gVXjHa$uUCB`$N= z^j&*3t!5iOCSu|znZQ;AK}K{`B6|n z*zy_j(`gIVe2=HAP43sln)?tl**alP!=~B?G9Vd}`qt#vdVFe(FBswGyWYoPIEL08 zw3mfNPGI%>MurbyJ}al2jb#oxehi*ixtwiD@jBc|&xXUN89XBm%4ZQyMw?{hC&rR~ zc`jI!uO&a(6jvuG8hZI}tf_5XK7#l`H|oyTKm`0MqvbITYtbvTcz&tDc>nlm)heOp z1jxh*nfT3ZS9GyAxDAThaqI2v2nfPgil$cxo0lR&-NYAyCg?9Rph2F65DT}w2WrmP zSAFetq@`n9-~Rkw%M^>ws$OV$2?AP$y?yv=)vhmkf@wi7`8SA@lp8kVnIzOo6^2?4Q%z7Vskgw~>&> z(Q@5ptVVpZFpC7j1q%^=_SW8W&ty(N#b^y7T`oTw;&mCasj1P{;Tkm*RJSb&Y^kg@fasupn32RR8zMPimRt2o`vNi%zRdQHfu@v6@i*5hX0hC zggwSYCht_WhmEmL&g6&W*f02xL=m>s<3d59m67HJNO3)w&VJW>i|b?&CyHzufuX(~ zCDG9dNFw4!o194kOgdrTU_^%Cz>AVTgDZV)FXWG{T!i>DZ^*@iS>9~v4E5ryPk`2+ zb{kW9Aic%WD}!cmm8K|9)YCJpzNY*?MIhW83NE6@Mvse$^R7b@BC)X*s1k`~=GV4h zlSjaUDV}>zoqt#%)wme9gT@`O`R)$HNun^-cLFo{AvidNNtef4?%Mmi&y} z643MgN*e3sn6MDdv8eBL1t&F7SqzmpY}&gdk9?SfJ%fm%#3 z{UUi5+cX*V4t)zz|8#=8v{LGXXodG$XodH9N56k;D2FlXru8YOg@h+*#fyICP62m` z@comtx@*}9ml-3`Tk+ePVD`NHxEw~Pc%R!_#aQ)Nj##7B#zh`tZu&L1-F7!;ReDOx za(5g~4slS))}6YI;sc}T0QT+BmDcmK7N};#k<7oGjy#of3kWR6VCcwPSlI)PzB_B; z6=PQ!GGNi6@owqx+)JA=C_-$H;qRBCFaEo6p=;&+m{PyS_6lYJls3O!13i#{T9!s*`5RowHgJW?#qjSbdkR zFj6Q$;F@N$_FV6VhBxng=ZXebLcQ5T?^jj@{+bz>%97U&OCZgmYdc?CGBEc9+Rwzc z)pj-w4M*yd@3cVICC4ncmlDB{b7*i%CK7cTS&ttqHBc+>)m~5r{9i!G;lR{relAb~ zAze#CymXvK7d&45x2Jcr7g_Mg&)}pOHO`j{#FsLaeBJs&Ysp`^ws{)7cTl7m_YV-_ z{lAoc3I*&CgtjpNQ)n>cj#U^2&|75KOW@sNbajMX6LfXpT{rTyx12a<&!(dv-Rs;# zngehzB|wWmI)4f0Dy2E3&ogr>_9HHJJg$@S9y>*B=TV!%*=Va;%u8Pz`0^)@gWZJC zYHtB!IKfTm9LLD=Aw+S>Uq@C;`WjvcEFI7i=>g)U#fI(?N5$T~+Sc{}*wNqi4gnI< z;MIn+qeeyuRh^u+7Y9~=vE<{qu51H>)iU=Th$mIP($;(J;O6o@!*QvaFWf>CSF+Kr z(D!YjXAdn?4srz+ue$bE7}-SDkH@O!K-Q+1&V zOL_HP44&+=WeUvK;cEhXq|>S;D7J!!KHGR93tM*CF#%G+tK+soXt%=0KDy#k3xDsn zm0Ak?-Fuj&yai$%Dx(_h2(Izf%hTyBcDT9A#LQ%5u$ zTH8x*-VGZ(&_}vLs}Y{r;Qh`?n5#n8K_8X8<`|vi08HM47Kv&cse&Qw@Ayd$lW@?w z-@q&2S>4p8{3lbM#rANBWUoQ4Sp6AGq>_ucWJHDuw6kJ3aHjKEhMFr%D&n#rJDYEr#QK?FEDC5d4^lH?oC@0Iy=7GLTVs#5}F=w6R)=`oPL| z(C+l2zzC4cfD`~(!Ze?*vbn%%{Kcbmor|FRNc6PUlIHG&iV7mD+^(GI;@iUAbwJm z!(QKZRk@+D8%|J*6d|vnx2p?Iu28}O(AI9@f*h-=KwIxY*?_0yW9)R#hyp(p<)ZT! zcvwJ!#{~P`0BoTx_ClE!c7BTr>_%HwhK@cQyMz`bLFc7&^xZ-~WNlVnJD^S+@9LPK zJ$k)deTu`E1Ah-lf%|tdQ_tBa+xgTMb6FyqeEAw>IWqF{_z_{)x^tE75%cY(s$O7p z0~1uOPIH#5U~-_}LE@^dMeT-w+993cXLz&CXNh3o`E%rE^HmnOC9li5)br7waXoXL zb(^G;Syi6sqceYsV$Ti+IoRx_u;$5Ks~H+vqDUdh*!3be*;f5h7G|-b*Dx-6u5xL1 zFJZ~bf%xS@S231tes(R#;QBRC5atonTJU7IH|e}i$927S2ByOvI2Jm}YfX{Ov+gMF zbF=~9tFU03YrjW*nJ`C>13X2U+14cSot86pi4wRB9UQooedDSgQz+f)z|^3&LJT=_dC8c@lCU3EM}O*6dm*hX&V!267@fg^8pr&qCl`^^ zFZnQcwqE5MQqgn?LeE*3AZz?`f=#j<5;xn?yE z2Qwd;c0OvPvi)btRn-S4`>r&*HSu(9`z*|h&m6njVarsGcpdbA?<$*3{6{j)l1HnXO)Z3*J34iB<1n8K8 zt(@7af}n^M0#nND5Brey!s{_Af^$oWj0E=<y}VA1ilm-?-FLoD-Utl3%Ko@sh<7NSORrX=m=ur z>yrEQPbb3oy2~ue^IQMgo2IYdNxyzcJ?j}Zck{*U57$sd?RFwp>q1m0wmX@h>lkD| zxcRxX+v$=B9f*%K2(@ZH$I?RnOKZr=;S}N1pmjiV%jVUBaKjxne9sv^4g*4krS?C1`q3 zmP=?Tz#74Wmkt(5X&C=I@SsThQcRPm;4RyK;uBt8$*{$sMk|8isuA|nJ{?is8Z8-f zFy9(Ri!~9VQ>+(se0~lw;-WT>L+D}4DE2}UeYGS_aTOnUFoG=)?kTPgdUmTl$OO2U zh@+t5^oLc1rf|dqJbI4^y;h>V%{b*A?ds4OFe5OGLwZ0~mO09LbfYn6CGWfBc{Z+P zAq)48mWBCRHaDmbn88k15r@{RN_LY_ z7(gA)^vR63QrFD|vYmg=zF8r>D%DuZ8T$-BQbHJSIE>74w z*LuCyWC8F*X-#th+ypZ3e8%jh5OLb32Gea~#0PvxmxEnW&&z4;3ik-nR5udFlH&we z=vIg1kl{&fSmQlSH!Fv12rzh19!(mbd~N;4I$9NrRnu+Dwymk!7brxhSPW8KHOmuzLD=W%sK$*VAfo``|8jdU zmx?pVV~A{}*1l4Q=%5yLzq{h6#uF$N;N=ISVfu?lollpH6{=B#8@q7iO6!7-{nriF zOs2jD$zg<>FFiR`YZy7AuA*A#!Gy3=W-nmTu`l;?_MIsN(9h1f^e=`_g zUi8A+FWPgWZZH-jCwCv004w1=lYkoo5f%L$d3C=IW~7wg zs~*R3<)ER@QbHtP&dW2$9tONg9tf-XXUbDsq~D-b2(Gg=4(Y-H-jMHlE1~6y6EaFD z7fXLiineEHQf^EyU=RwN!xKU$K&7qKgg4jsDqp`}CaJ2K|AI5k#w8XBxCio>sCB#4 zFW`5nks>dR=82;S28W!p$EBbtglt62L%w(5S_&>D`*@Z+Zx6?{l3S>b`(5F210G#r z5g@{qLi(@0X-Dq^4<+XI6s%*`^re|Pj9nzWr@sr2$I@+(8BXE%p|x7aEpm1Sm-fIx zCQI?PmUeRsqbC`yT@uZC5LWxn)Nf^f~};gFdzaF2DOKLzVMUKz8ULGp$yamFncOyBlG@QNdCo?=G2F9haR4 zcctc@8jzH3lu4jD0ZxtB*XlToPRa_|gW&p76W-Z}9?@<4e4<{ZPLnxOvOV#lw0v4D zkH68h`aB1VZ6C^QO!iQ0;}*4ny~NevDqiBxt;(XefLxyx&|4awI_sdrTgiL+`vqVKIY9-rAIOpE$n7Ci{j-C^#b zI5BP{Wp-mt*M&oeRVX)2o04I6cC7}It{z<>Pf3=z3kU8;NhapMs30XH-J{^{qKbp1 zh9^s)bdEzly}~Dw_@vxiR}^-HfSgO-z;nGGidrHx#?P?kaYGBi?JRUZNpwgW@Gjzu z=3m*r@?5}OSb(9=b1%#`P+3+^1mm@bCcIQM+*gk}-oJ^Ws3H>*qYa%91}X>gqjm^= zaCtE89!q+i@>C@mK3YHvrc!Jc}@lJ^RZ=-s;w-IT?yDeE2ecVYh}aaT>ei

KogRzbZ6=40`dSR1 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 diff --git a/.nuget/packages/AjaxPro.2.2.9.17.2.symbols.nupkg b/.nuget/packages/AjaxPro.2.2.9.17.2.symbols.nupkg deleted file mode 100644 index 7d2c930990a9292f219ec37a3eea46e1d315de58..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 250192 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

KogRzbZ6=40`dSR1 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

^Pae6|@L9h;eAoF1qqe8+!T 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%0D0D-isIt6-4w#Ze9$wPBXb`$gwp2H; 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 diff --git a/.nuget/packages/AjaxPro.2.21.12.22.2.nupkg b/.nuget/packages/AjaxPro.2.21.12.22.2.nupkg new file mode 100644 index 0000000000000000000000000000000000000000..c42e72e51673055d39236a3a04e3ddf6b9fb5a16 GIT binary patch literal 52565 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

BQ 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 r+|@qoeNyX9dInwtiLc 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%0D&bLd&>|B(x89|PDhBu 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 O0tQzy6<`R5`2PU$$x&|r literal 0 HcmV?d00001 diff --git a/.nuget/packages/AppLimit.CloudComputing.SharpBox.1.1.0.452.nupkg b/.nuget/packages/AppLimit.CloudComputing.SharpBox.1.1.0.452.nupkg deleted file mode 100644 index ad8091e2ba2be7c321261797931b1a2322c93a4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 83021 zcmafZWl$Vn^d$rf!Gi@)LI@BnxI=K45L|=1yIZgX7#xBPPVfN1VFn%CT?Y$pgAOpv z{PWxWu)CkO>QqRt)H9XoSzu(D43Q z%{;7~Jh?dkyQU|vDr0aHN1tOs-|T{h^Vg^ko9*f_>ngcEs=e>%+542jadS$bUnqy! zg^SM%9Ym$h?^zM;#&T1gV;eSpf9W;DwgU;DFyJ~nv4ObVA z!I?D-8!irCe^^!Ur<%77lKUz0q|T*sTbksrlm z@_B*0E@Lf%m-OY1&1xiYeqr_Fe#GxZTnoQT=Dj^KO|8pn?Di_Q z6a?`#6tSnoh!KLX%P;}gfkkTJ^DVWK=$iizk30p_VOIZmH2=pV z^FJP?+}u>{o$b9iWt?2Stz=xC-Mqc*U2HkO*qM8{NxS-Sx_EoKSzCIiOsYTQCWf59 z2lZKLWOq}yxAU6Ygh^NREXW-Zyx(wv#TrG zbt$~avD{dXK}8TP!}E%iJXb2;HE+m*7j31;Hp2flbHMm}5ueQ)qr)qM;>fJS1x$v( zU043bJyTcKIL+SFEsHFL!Ta&k*p$96Z#%CEC zB6IDYDlWuw=lTs1&u6x!J+ui`CZ_$$gece+gi3a+^N+AZTuNc<8iad=L`)YhESsIp z>l7oI3d-us+7nI71_tw>EV{R?+l$0^AH&-IIQk_K&cAe?kG%Wps|jhVVR8w$`2L;$ z*`7I-F;V^!VZM&eXOENn^XY<@Fy(h5uOEX`QlnUcDm_n6ZWs@xLR0ouUS~Wzx^PP0)6b{ z-5<+sq=`j>fuRz=@5HXugKhWnd^hZIxIl(W($8a%&`yKcm~RL{m7ojo{B@|~;-b+& z<`uy8PV=l_u*Jyjl6Cxymu9*F;$H5{C4LsqTSUG-2wYm%_m{t8we>o`DwOHa0H_PO z2dSu-|M_lt=JbYeWO;kgmd@Ra_I$K=r8bN01$T@Gg9S6ASOkUl z{M>dE#-=Xji}i%`X3(h>=7IiXNu-0?31rL&?h{xmNZmDlLE21*?$9vjQzQ?jjPMS6 z521wyS~s?BVIJc6H-2MV0gT4AWiVZSm+*2|N94`qu5uhdMk1dMxWG>-kDGgVzQ#+a zHJOclg|ivoVWKpQ}9$|8|x1 z?rF1hL2luRXKsLz>8uRmmNz=(iTAxoV5d{&zUlf|cDlxxxaA>)CSS5Y2c~@eli3B) zTV?AMI>fM7(-ksHqsJ+61D*BF*iWorn|O%VFwkm9oFdoN?F!6x!CO zdE4qsyy517AU{(l0W=A)D=I2jDaA0AL>bJ?eDjjd@N$wC`JiI>GihQxg;QOTvP`MS zw!wO_*SKeJiX#wrX6N^e;raTyQ-2ZtD2 zuNwJCej=JlNbw=lJJlvc;d7YxVXFrzDIwi>Y-lWb-49g)3IbD)VT8Z^yGYmpuU z^vt9Fb%;_hv@!4E!)48%TMb@e}+UpA5wg?dP_bING*RW=gn!vM;LU3Wh>~z zsKVi&$E30K4nqo{3YLjqVNwUPl#ORV?MT1MSN*emyZU_Aeqp_?1^dK>7pTu+4xpTf zDwk3|c9fu$tEbMKCaJ?+upY6%;4da&Uu$zF6BDHd6g{k&^6_VPkC&X>P|)=M;LRb$ z1)kT1#iWq){Q^zKzYizTjxznx=j@vQ-0JG`7{?dB)+s_az2!E#txKVnQIu;*7{#kc zQkBw9s)2~L1 zXkcClt^Hdoxo_E^zO4ev4O>5(RGX-;+SJnI58BsAfBtRFlBuNyx03l31C6aH1#&qK zW0n=0q@gKq;;}UjjmB0T6}E#Q+iTPB`1K39Lew#;(XfA zNhlJnT)$G06{dX==9N)9owqU9W&M|PXU_aFjQoj|G=ha3s&YoZolMf)NC2w5QF47u z$}@lh>BV0`LzqA=f)-|nU{Km9p|)^r7yKh(*LcdDB&YvTe)E1f^N zaFj#0#){pUY>hV(94kK%hZs?9L~ZEWwK$hkbSV;IE&dRg;?nMT7Pq$N<#KF#EhhZX zq+9;-i8039gV;Xj&wbZGQdGmZhCJlMmR9l~13lU(#Bk7Gy1WYY@{@PV%9S^t=L#vG zo;MwPqUt=J-=6aC?f3tb8{Mz8ufIgcJ=pOw6e#8TxcFiAcTS79VLwWUX;2p{Q_jnA zx9kSLe%lwI3T^XX2W;rP2LbFq^^=Zd$7)%gcy&271zK6rf`#v%Ci2Y6R1SjK4 zv&h6o16!z?d|@%fDWcLiEYhK!4}Y$8MA40y>()|S>dy7==zdYae2KtQ3N1Si;}>IK zV4TvpezdiJ10r;L*#3MnbyhXMfDW)0kguP3@@pf^Vs(q1snw)z9#;ePX}Jq%C;FDN zg^i*=cqaKU^Mu3u!>D)GP2@0zO;?Qpm*~Z7F(L6i^cDA3CjWiy?>9bLs9teycabme zV8TU!Ua!(#v0jl>=%Ehxv&^mraP7P5?uuxIV$`m+i)f_BlqmMu*2Zppz726iZ6~|Z zvOUSYk4oCpO0PQjB(sZ6V|n*Cu!EL=&VxbB==W3jWnobdOVY8t>I0=cMIXP{jz<~$ zuXr)N6cyjOc4wtiI?FWW%ZsGG8t^Qu$mQ3vnV1hAr2rOf7PpCgg`5=SQe6GXfdsl! zJZY@t`sE{`$(`zDDBp6_T_K973{^f+EV-p7kj3Xs0&=x|^7;_l+jg0@vg=dc?!r`2 z(x$3X5ewPskJ-y7zFZB^ieUt^ZDv{p@7Nc~2TC12w8mcnKMEUjfl(~ma8Z&_nr#Ix zE~^wjpQ(47U1WT94OWX)a*LlRnWU1+f7Ov2CEpkE-x8n5dL`DzfxmcLdT@7L;+KH8s01 zB{b-YB|03DX~xvD@k1vHf$8Fs0eEk_R8z}z2S90^n=tcyK`{D%`v4AqnA=^B0WQ(!1VxIc)|@ z285(rTD~gZU$C2V=f0lGnr7V3D>r)9F6QKZHe_k#%C(|?rH@mb!$7A1<{Oi3N zpi|xuy-ae<63WN}IePRSvZvuS(#>N`lY(VXD2lEVg6p!1y6rmA>#V{wfJNKVv3l^! zGO_Y=hZjzE^QxGsSySFtt;#Rz9ZtwT*Im5y6@B|g?=@>jN4CE-ExAxiSU2*wi`7f> zZ5H#NumPxMJL^(?HHIqPq<<~l7nRG-nz1XLi$Ju2_4yE0y$+|Oi{B=yGG&!Kt^w_; zrLUX=!klQw^p;J$iMR8K>8gLTMhP<#&O`Z9j^_`EZ!47KJgO^Vs~gzL#ViBxEU7;6 zA6{4NvB_H%8U8vKRjkMo$)-x*Z99o!{_fW>LSOA5H5WXMATGqB#)Aa7wK-gfrwi@m=;!C2 z1?jI@e7|uy(nfxz4?2`OSQFcki!tZnXza01=q>2C_OudUzc-RCQL;>WGo?^L&8?b( zO80N3_sdvRz$n?dnf|kG?id`3b8<#_HH9q=`z=PG;gjS}WvZpcL3Dxg|FrxP!3##qrk z;I}ZbqYyc#suLx{pmnnF?lb@3q0Nyk;>!_8SR3l}dUf<>0E;zt&qA2;?3k=lLqWxA z|EM6*<6YK!#A^}d%GWMlUQQ_i)|RDX^15MREbful4NQd1sjjcV4~($ZpV^BPT%jV;MdZPA=Ca2KUw)w{fS z{QSz}fU@FFwK1BVp+U@;aW`Vgc{l<{XNoI0`eMAjH42cPCWRVxCe^ zif&v6jX#y5FJCa%sm}+6S9NWt4!gl>aVuU5F0;glhxo50-9x|j4hWrswLpR)Y7W2H z<18)d%``*a)ol%0G}`4#ni*rrMfD+n^b(roSgPgpKTxsw$%ZcA@JCHp@o5(}TF@Ln zeATzC=9thvBY0@6#1)lezBwkpo-V<)Gw07eieP~%fW->r|B#b6}%PvVhYA`AwHJ*!VSGFWxt3>3_`rZttfI%MeMaGZXy?DUt3pB zX_*aelk|&AbrQ=88WJQs+?dEp?u=Xt#&QbSuszp|=z5pr%^Z;tpc2DYRdLds^nEq8 zdf4-6)i*qCj*8!dJK=sqrn-t7YJc6d7`PQIMPJVjQe3* z>-QrMiBW%q4#U>b`L|6LMad*Od4^Laf;2G}wOB^{TmYU6XJb?oHzim6Okb4WsZ z9fhh73{*|I5>!Sl67qV$r(`JVDpoTY%UgrvuI$EWD`(E}^Ui|z>^2|4K>9T3mNz?k zZOnBBl=aP@vhd~oN&q9T;1tkVcl{OR7AZ@w8cT0N!*h7t0&94m`@pH5{A4F58x<9E zkY{CW{N?noCjP&DbM9qt03~wM&Brd5gafawP+R?-2NVxEEM9D~mbIjf-^#r<9vUp& z26$Vyx%EWW2^YWqAm8{d9o@oh=>nj-8ve%Jq5@gV|0qv~HA7%KZ>`#V4KRy@kv!>c8ubwfiRrssvX$4L5HhbrhX)pc03vwxO?Qw}5*| zQ&F+|6-{AUQ;ze@UNY=;CUGoBOy)A=pwz3c{yJrHz_&tW`s=cW%I6UM%=b^&frTH{ z@e|IZ^ZaF&HblurHw<%ba}Qh|>;XD4=;o47&~Hup^ryq-fu}EgM!o^V?*fyozeLZa z2rJZ0a@3p|OxrfqaXhpM!gv*`2%d`ns?)OW_z(|oylC}|9`sf$u#MTH$BWq)4jdN8 z|M>L|dnT|;4lDIa;e)61X`9FMlWaFf;`PC`WLEs)sk41ry*6)L&xEAwp!${x1~muC z!RfD!*vxx!Kn4BGQ*~*Wz+kZW)Rx~s@5ymm(o)Re%b&?bfOz;crKXZF+o>PJVy?4l zvH72t7dJfP&lbDO=_H7m3viPEaOw%(G(3DVam`g?5KP)|oC(C48O*tcVH}dGK42;h zegJ*~N}DKK>NE(uC>EM#CeHUypNbS}GR%1Qda!+ScB(Jo5@zCrQ?_m9k%}+O@BQ*l zdNaDN%Z#w*F@OYJQ_Se!()CB1aApR^2!uWpl~HN4wnHUmi8gsPYltU2TbjedP-QnMLk8s?!K9cW+(AqPq+5KAaB6#MN7ANLyM7$-XUDzBQ<#%{@6 z+bUi2*;?Tni70TIv2(+C_X;e26)cHU1y&T7Fvcci#)2l~cq>$jfK>-T?P*?J{C!sw zI#SFiA3Ic8f&9|ZV@0k<9a=*X8`Q!bxe`l@EPAvWLZ`}Q2)K>H1elFvpqNcHn zbL7+^F`$VB`bK`hQ+ufR$uF^;W3+H@k)*{{4ockVe2@c&Z9B{6HyG$gKt9$>-o^Cq zI$EoN_{b)8yqqsM`Xyr9={`>nu{W!7U1pF4&rTUtx&oVbGmidNV^}kjXeLFj&(RZZ z2I*Xxbg1=4JNT{0u9*E9tt-8VUw=PJ9 z5MyqB6og!_2*J^Pt^_eHn_*athut^=!v_sBLw%HSRQ^w@1ulVE|*Urq{J6TkJ?8VJ#6$}@iN8?JC~Kttyz9;VU+3N zzqZaCY_XcYC2q2#aTD9Ri+^4nU;-ftW)P1lm!Ap0J0b6UjR@DIfxlSRF@hu0N+So8 z%PeE}zfjz!|EdQ+Dtt^Qt3GqrKF-OV%hjCdC;?wA28ge2HKOAT3lK;ge#2udvG{Rm zq`&)~f4Zh7KsE%w>9>4rop}QI3&HH58{TnL6#tVZe8dHAsWZL9dIbE$XtJ21Cl+;t zn;#(dp#|>mcl*8Jp~Te&d!ds4c6(qUrOrSzzJ*O192R`}HBHLqr+iSlx!K=EhLNrW z32m1rZJXc;$E*6213;_0#3u0uK<|g3kr97#dOl;O5?KE+ zFQ6$l&{8k6$bXy}(;qvpojsK_<7>!1A%P}MO791lb49Do?LLduAW+O zz`YwrR%dkM{kuOpJ_S^`sMbpbk%er@eRkLbpyF6xY;SdBL%LX|F!Vje#&w9%lI5Oq zp40nt&p>Lmd#dF{R1^s5wYF#ghk62ngXq184N0W?Hw) z590{}e4{t=3IQ1T+s6oWU;(VDyTuN{elbeTKt?Pfrg+gNm2F0eQHMLTQ~Zk4%Puv5<`rx>7!RT-uD6Vw8UM zkg71QSvV*jRrf5EH!`#q=1BEm)Qy_*+t1+3t0xH&xd`FFHAU}K-suPjC-u^jEvRTdxKc^C_<>*mpDP6^|Ebe5sy?C)XFM?QiwhswHhaWrJ zBlht(K;k`~2aViDWJa$Ny@F;%XAnS)AVmdghAP-{cNIT~2L6NkeEnw`1AdUc zZG_~rpacl?K(!oYXDb5MKtoACh7v8+mB26)h^k+A;Y%<_*3H6AH|wk}gsGh1p=Bw1 z(7G_Q#p~ub-(m=qcG9V#m_g+bNxBsJ^L+FWrP&`ky)u=!`Iw!7*fcP@FKSm;X%6kd z9}~UcO;kaa5L5!y16}u>1A^>Ah3aJSrC5XnjU|ttV8T8+*EF1V^9LV@0WOsgs;9CK zs80s_hC!=JVZRG;^QHdHm){gOC>bJ1s5ThNc43l@1d9F+`-9UpmDgqm51fV?{2Y;k zKLMDz1W3?ySX~rS*I|M*cSOS(rr?y_(a39>P00jhOG||1nt(`;XoC8zZM=F*%#%X- z)!@<)w2%s=fX+pe2XW*_YC%vfVGQ%vdJ5&EcY~_PSKz8Az^h1@mnt<4l4^3{`U}Jg z61u54%k>N-y(1cSJ`dl*e=6^uv_lAPj#F#A`hk?t1=w279x8P2+yC!;A-o8rT5F1Q zL5UVT*t`50Gv#r>jCRq^qK-*5&=nz$GU}le?J>u(oBn~f3 zGAj<%qlzH1*$(|kin(bP1guHXis+x32@jPNO}TCxk}^{8lEESH|}^ptEdJ-W7Xt;wz&BL_SF6 z_z=N94ws~Z)6zlns2-lE5KXEiOXzXes%HfjWW7SW*9Fg+f)`6kdImqw#i$aK_m zGnn5cQDb!QDonEa(Dxa%4wm{~9t)#i+DbR7A~9s&C7Msh+ofpX$-~PamW5w5#Mi$@*@qgQN(cE1V>RB1UW7^3qLaM*}TXYX^ufSqB4#qmp~%L*AdO z`#qfD=63{UgZw@^rQKm}^7?4(+)#tj5tIpo8q8wO0TBQk(^}Zg>?{n59dviihs=kB zq9FsZ^=wQ2NyJM0pHK*PFvDh~7yK$b;U;UoJJa#OnRn*&@js1x80`j6Zcg*g!VB;v zjCR;;%l^)bY-)_F?l9OPurJK`=s`F$tBeVE({QWJXvpF-XiAUS6kLJo0l;%z{?Fe_ z2zYEt(E8%Q05_kPjV`}_>)%&nb4l}{YQj+Q3ukIcOhgwxvIasI_;SN$67xL02gmwi zizDC@2R49p4-bOSln&OgcQ6g`tXJw}B+|Eqc_zLfGH#MMuQ*3+x0`dQVgr zuHLQsMMz-zy&wZzQ4LH1r$`WdhBDomkvMRDEPlp_=*C^|m*AJb1LoTZ#ph&(D^2nRN=prbkLi~^I3Q@{?k9~KbGC!JiBnr zK08rp^#=3h7r~xw{&PgJ^cz(NEC+k{43cT$59JXmt~t9lY=y zYA_li8?ybiH!6};OgOUK_JVe^joJ)5s0ji45Dzx0IckF&UrmVCys&$|}QUeW0^>=HHIow}$X;-e-ee zz>BF6Kk<>Z5Dkof59a_LfOUhEo5R$TSf;;VSdm+!JaA8Ha1v}1-h?mEt4E~vXp1n! zra^Y_{=;=XeiyS0RQMlKHzuf1yu3(T8^q)o9LgmFBnrAN#0}vmg;xE%2pyY3Wfg*R z{z2({y$3;alWO!y2>iN-F>KK4!YKiOCZWEQ-c4zbAphEz-whYdMw0zD=kmjXtEruf z#a}eXl)^Zv8L*s|%rek{A^Oxb554~i2weum;DLzudYsyqE}x#u7jxs#ZJcOxhi~Fc z&j|M+DqG?pr2B3edT049R5@nyjA$*$Po(Pv*>r;JI0=hvrkEm50kux^@8Sx@w}H$(l2zHNA2GjBWKJ3ZyTY^9cHW@sP`Sv!?GxQq|KeidfUf!$;L-(2|MsO z#&g$dpw-=ZkfnJ*(8w8J_-udZjCuI%9A5B*v;2g){6x3>MB548j%k-@cqDIlq-uC% z+K|0Rmi1{EVj|g<-Fzf_;|dhACmu24FP5JOmT%5a0s?)+#XC0go9b*9JN%z+7)2zP zZ~k^rlpX?ckRZtNO|P%iB*(&~a3g5wI5b+kCbKj9Sa|tncKs97XfV@+Vf$v$lkZ6$ z4wl(D3DEQj7VMBnH(>~Vn;PQtbV^AT;3Lk~fy^+Oq`5&qHd?+ZKM9zw`Z0?buiZzG zT>E+>+HeSQ;z?J3wG&}AV%t*LzclRx3Y-{F=0r4a9`GD&n4c*bw+v;FfP^(itjkhSSBZSSV zn!)389q;Y$>L(UQu)T1$+o|0;eYo#5T&DX6-^9dSMn*qgjr^C z!WKF@^<*il%rHE`&~Q~cVL1acN&3m)@-Ov8mV&0bMXhEgCjgCC#V0{bqR_0gI4)7> z&;&zB)|eo2#LxHJ;)A}2-t`z9f8|5&*M=g!-SGB^MbWoD5B$P4S(7oh!knL7zq}I- zNS}{75C>rkXHI0Uen$6woWB9{m(It`yA~Z&x1IvyWR`k zML*D7f&LBiA0rb5o(n5`HUbSbJYG(Xk`*C|PkWu*b&ghDD}GWPUuwp*BA z9GU)Awm)iddlnz50N%fHyV0>`4j{!Lm+VdUioGCmPZK&pDD13#7?_s7Zm|fk-!U+D z0p2r-$5W2rRwwoRREfkMOgAB97;_xfFsk>z+Dl*@A>{Pm!6>~PdASp28ufzZ9Fbys z-At($N}`u~{74C}Edz;t8nXsfv{+|^;Nlu#xAaPSZ$iD#VM&EZhd z`33U1uw?(jS%R~o=zzo9FDH7=4JTFa{M{gUsIj1R>(@P@9|QA(quz0iwSvD?d2bCP zY*=G?10@eufzvWGMRfR0&xrXGIF`-=m4lW162zzd+@0zV_%r&UOp-3OE05*FfxN)A zr3a#G%eCnTuKO+ii8vXB9E)8jjxeutwp-zZr z9D^XDW2>Cs%;%?Ia-UxL`^6F-BDaY<#hs8LNKWjYiA2u^oHW>It7B+XvShS*u=g)8 zdNgJUOQ&hCcy7&dU&1G3^1_d@!%4t6-!ju!y#qejgEbHE1T$LfD-AOFYsF~Jui{NsNWUZxt8E8 z=fqW6H<2-LM&?*Z4K2#XlY=W4^o74S2?|Uq7Uq8ug(S#w?U|tjYStAQL-UPh$_LZiR6r zwxjN)2}Wsbz$f!+7RtDsRuZ`l`Y>&<<)sjaGUEAcyPaxE9%Ea6{`rhhUI<|Rsl%}P zoY{$?s6g|Qn#B5bZ1lbxb};6L8M$5%o&U9w(K}a;o~r2zd{8=xV+_k1_T0N?XEa?jpI{aAUlI_u0hG{>7(S9c<0sjetGCFN7 z3Q_}7TOj9~dM!G2FahB|53J<~*zRs=uFtnGNgg5*xwL(9Hklr>!9~*=vWq9Ark+M& zuVWrPFB1R^fIza)p||Li>fiWG$@Y{oA>y)D1U9sv-ScD?*I<@prz*HLYBTNNa7XS| z@k*E{jq=wqV-RugLW|S|ljc>Hl>(L2ba_m zOfN)&p3rQ~!0&jz>@<#R{j&)kcFKSIDY_D!ZFm4zVn&DmT!dk>nB>XO7FN;};M_%V z11&i-R{-fF#_nsELiS}V=;nQjoZyv{+pt^~+?053-N$qB&cNMwa?q0h~3YK$P4oZ2#__rfF+(mjx0_=(O6PKZ5(Z{dx zPo~^TIPb8IVZc+cRLV1pV}Oa&)is^syj_32)kR1oY)X{ zuN=5KzZDfJ5e&rsFk0N3lO^;m+Du7E9lJHa?by*3mhS3ni?=>~uKs=nn|mdQL}mKY zD9=vx&yVipbvvUBTvLR+Y5jcPje#hm+=QH$j{3VfkAn50&$E4w7jt`_%;b9OPL9_T&S zOgvs(wY0lf)1``4HM{&JGuBuV`!2jM_v3FDD$4~cBsSv?tGTGAYD8{X3dm#}8MD)U#xGdgM*dI6X z;R=s@)i>u`DO$;QGRp(<=5wMiOys z=d?ZsXt`x81~CP!V~N5$AYZ^?`LT~aZlDJw?;IUk5g1*hHzX7DI+>r-w$tGKQ*M4n z6ZviGkiMR3@rxh&koI?GT_a-2Yy64(Eyn(=>$k>Eqx>5xCl?l>58`Au4TS^qJL&M| zRy&*rqEdSIiOp^X!-j?@fQ6a8*SgY1(d$#B}86Ftk`oMpmuqX_@2V^bu1&eO#p4@FX3Zmi@^=Z^tJcUC^hT7HAJ#uR-&B7UQ53fRi6nCR7Z;Chy99I05z zlj2@;He$kkdka&HKg+dyN&QI}N<|zwBK3aI8zB4Iy|H?ai5|@Am$7TR z<9$(Mn9OdC8^xBMxS6Ru=1XEDg}ddB9`b^dKtE=7u-9jG{_Y@|#l19byEe*H=Q@bl z@K$*sYUdo z?2|i*l&2ma+tJh)?$*8Hbq{&)o*pnIgzjCQgko&F5p(c{M(l&hqF?uD)B#XNPC;W$ zZ*`#d5duXJJ`0W1YvqEUlmIGjKMFaLxf;saXYCPeK*be6wr*7?x_nOLG=1u^W}ScZ z)<#agmkq4Y@ncn@c{4~H9T|@so#}3#o{bqXXHS4-il~l!%X+Th8(bBEHJv798ZY$f z-Luqz>&4>uSUMK?ZL9l_!y}Ukyhy1p? z@=#$yNe-Vag}~GfKVKe1h;`DRfBm7mA#eH*j=@a7e1dhi@LLUna4uc7jpbw2K?Ukr zLW@uFfssn$p7BEPjrn}+wy-a{42=ZaQ>41B8l0fWm2H?bh{O#?$7o0JnmIRb8xX|x zAhxWYQ4klc(2&%2NoKF4- z@3ee>zU|c45Dzov{CeNPaQruciupUQLjMuV)puiKgv5@zHj?~^&fqwJ;F2(h`M)))xCSth%1*WzgSADZ0&!73+p`eOjgcXT;k&1^C>|z z6jN^lD$caC`s_$wIqG?a7~Jl(IS(O;{6a;D`B*a;HElZ6t|lO~+kZ@OH3T3sfPEn4 z@E^jAgx#dn3%kp%XzvGA%sJn005Jd8tJ1fQqt{;#g)6R=xAA1bS*unFwgR8YKR6K+ zgl}wNQn0b8Gn~w;GmOoj31vgpM}2Sv3UPf}p>DrN)i(|!duUcHFe7mtm1BO7?7FuV z(AOuojf_h^5T68FPipi*m{~?kq`zRaC(~#y($BnA zrUnJ2u2yiJy1~`XiY_|sICCh9{jX=66r3p6)K}gr&Gr|sT}LXkmXgP*rQC4V!Maj6 z1~KR#=^0Pm7G0c<-FP$xMtL@W-lR6Y(IGy%sMUdt{hh|XbJ4|#qEkveCvU6%iXAoS(-p2YeaHbxrB+TJ5 z9+Eh8_K8F{UkwQ}wg_Gl4v2~kX_=w*$-F$*v0fyH34v3 zF{bC{uZ>=GpEWq2X=z0K6i4{CNO2K^x3-f+hP=5R{|v9>yl<1{1eYlDI~oCIxZW4T z+IV(pD+YlSq4*lZ4sWf*OXEx@!;{ky-l}&8W3u(`J03U62lt>o{b^UR*b7%Kz&VL-eWHj??&0Vwxu*!!EPO2JJj5Y-76*aCtNp6Roz`BFQ>Q z7$kLCz_Pu>b9&>`LMA3j@icko#ClSM5;-x5EDKx+rSh2$CAn25TvaSzW$P2L(hqg) z<6If}oyOl87uANj^wztOb2{jg;oC>aaAyuIrT!efmFT<|feVjO;*Lqbr;=+k%xRRC zOk}U{KAhYs^ZU(5>BK?~U6DJymKzne=|d{ln&Cl95M znyi8un9y$&TuI7L3fAr3uTIB~Cr8;gJ=A89c3FB1ce0T0Ro398W-r!0h55r)h_Wc_ z=st3pb`1PqH;~_ua+?u?W!oGP5f-+csX`L$?NWVit5WLRK$lNIEv(q-KTGDS;PD%m%L?RYg8cTKy=y zmE3}G1(cbiSHDDR_e^S`{Bl8g7ko^&eGG2C?tKdI%spAjl&9z-_ug60hVpn#0j+!W z{_I0(aZaryteYaW%+UA=PZlbF;={xXF6qi`-l8#rk zGU>87m<scK}XHNmOJ0~K7N2CCl z%ltbfu~}Qw)t*)k2|H>IB9w76nD=dggwFpnHcm9b2}lc9k$zNE)oXMdptP zo8-5q0O*fB!&psZ$B))CQ|Z7W3@Y?x(aD5IVtAC#CayUeHZyJ-AXMcVnH!mO{QST#Y>n{&xH}wqy|3-H zt1;r_{%H8lul&{aB5aq5WzM$r%c&eUb*Dp(HVgt zC2z3U>&1s)$0j1qT)Mj#!Y{_APe=El8Y4UfYD;u)9o`)oNxdVw{r#NVDT(wZD6CIz z+&JiUDU**OI=MbNv*FLwD~y~)#x=>o{lcu5IGj6c;_o7V+qeq}o`CjsK2ORK=DGWGg;dl>Q|djLk26?K zUE{AhiN~2i%#DJF)V=5jKK?Wlwsun3-`6_N%(z*@inBP%&c-Wru471QIe2zSaWf0# zFW#XnyjeR`X9VH#zzQXUl_9=vV^MQI8Fb)w*q@7aluF4fWT#Dsq{R8Uma;&z2!-@B z7Snf{63Oqcvn#FxGYx%>#;Zo1L%c{dVmgzo^CkyPW1H$+o%A~XUI(`SL@OD_8qBTg&tVVc}dZz_M=zr?J=1 ze6G<4kmwiJ;Ry4tzN0|&SqkaCUoC{z0+vzp2bwqO=Jqa9w6LlJkAklxesKTbnPzu2LAMO8P70g z-dCJfJpz0F4n`X=XXY747_WXs?q`L>XKWT=3yE;lBe@U#EFBSUvS zi#KGmfWNza%dyALzaJx=x)fFmdQT0jBPf)-OfYzxe-U|-o3gpnKN{W7GBgAQZTO~K zMc1D>9U*Dd@~nz08iNuK!90g(aVy@X^PRMPvvmf^%Bo>pqY_Uq?(DN5o3DLe9>P8i zo4@Y@&?O$8TgF%hj1UfTMy_m`#yvh!oVd~QSjPk!bOPzltYJtWAtjB~Evxu|P-WVf z`u)D%oWqJsrQ;mF-uVjoZdWIM&LQ0z+6BcO>bS;C?1NG;_wj`w#(pN4{}4Hi->QcFj0p~D?ZstL|7F{C;Z%?1Qa##+_csEnL7S0S58Z)B!Q||E{m|Dz-U`QsF zY9j(Ll?cxF1F8-8i7k|?2qNLg>$;P#DH}6T>S>R(O?~DuEZUL4KKa ziQBk$P>T?&&ls{CXW%?pYEQKYIsKVqD6W6uVU61d_BG^W9^vXl!=&2fZ=^9g?Cbjk}V&3R@(ykY2_g3^rqs7m`o)aXNlz5<%y}J z)qXDQ9pNXOgNP(hA>h1F98lw{E%oKX&#&ur^8S^l)E3FD^N6YyZ^c)m7qO|ut1khn zG2&8`#Kv)*E*|v#J~<83!OAk}vJ-?IC_vrXG@MW4YX7T75S{Zv#ul6>ZK9Nm1hoZLSL{xcDk`zf&B!fQvwJE%Ox* zlEdwmcEk3(*~0NRdCQ>#UJZE?@FI0ZnI~o0>)j@oea?>)b;UKT&I3w&f}L#-cGRus zzYf12JkXC5ZI_vMY{}pD8Gnb+?}O6j_ayJBN_!t=#Yc?BnpQy=8hK8evIN#jbv&t@ z?0E@edxf3s;|LTNb20_T4%@i}ZTRU(4}4P|*>5T#F$B>bZ(+U-PZz zuQ88=1v31kl3;sTcG_X4)qD;30fh^(U9ud@P&?WQmJU14!ekn4AQ`XC;m{=W8}pa1 z!LPe=jmeK*mwI8?WZ&;Ho zf$~G$rlgrJY)*i^k|_FC{N2(##*us7f@raWiyYj1bfo;io-k60$NC$VZ8_#ZH21UF zd>;z{v=y4Gif?dvr72ae57awdDaTs!kYf4hIw)4{wqjt;QpUi6o(fK{iejk4ZDx>o zit@4x-pSUMT5*U^Kc`5pA|>PEh>1SVqls@y-Oh==Yw?jW51Yu&!x`!4=+7|Tf?5iy ztHm#*md(b(rCbI72pq{biHsd>?lEIlX3?J)!Aygl6i72NHJI6w}7&I z?PAd>S(|{xn_A^s`Z&XceY|DitXzO@!rK8M;%SQl;b71vPXeLIgo7(XZXH1Fpo~0$HDTpY)g6Lmc?6q-D;;_?{)OZ zjJ?D0{@-@WgIrLNgSDuc_uV)+TPD7rZFj6Q$;M|pUieE(-V)O5km|R;W2yl0hTb;e zJH{HJ;zcRY^uRH+1W_~RX9vgn#}@KnArmiI;7Tu)i|t*VI>vr60s{ z78#EdaMR_J;-(^GOt1$}&#E)LPKLSU4^@0BDx)@wTzMtS^Hr6F&mr4Eo=@b06HfCK zOAdoSZI7& z4N{;~E=_wHoy{Qefi+~pI9^QtdPC(&9xJ^^A1k%WuEQmugg!=bQAj?j+Ulqys$P6V z711n9A3UXgkRR-{s&N)IkY`Z~<-)+Tr~!2%c^0+MnYZk6US(vXjg#o*Vxs@oL^%|H z=}~8!sB?-(Kd64B8n6O_z9mTNDj!`7S-*m7il#2|&?&0Q+T^`psbl-Y<^42Nx@xe# z?AOW;{T+%w!^$rGMTL60LI)XNxgBBr|B+nJU$=4`MjX4gPHq{uhpyvBSh9!7)BY`a zNMc#*1$E@ws!yhjvq0F<6bPUdeTeDs31lRYq0~ZqY@76O?n?Za`NX`CgPvt@${6{i zD$V_+6maddIQ-M;FhBF`tL*Sk((+d-=CKeRU!;%6guI~zr`6-J0eSg&oEO{3i#>Q= zoPl|9KR7REr}8pN%iQHGtAt1xkIAkqE8zAv8~+JJDZ#{UC}XEQ_4<)^=JQ z&O~SHOD|D;o)S8kaaVNYEuz(_%&U;6hhXz?UCXBC{W6;OzNDIjbLmfs26?vgi;Wcy z%pr~Y{PM|};e1RrPa7ZT#CvQqNzNuOtxgafOdjS*f8U|k z7LK1J~ydm|9QtGvo zRm(M{Ua=e~sn^n3BB@t$lk9CQikOeMOrf<$?0FFMDn5J0_qve&!|NJs(}wOV^$qbd z{jV-n;D3-u+fgB@4h480%T9e48E;I8UP;*ra>*G=%0|3j_c2SYm-J{F-_68bskf65 zZgG-MIGjRAz&-ZZsN+F49MAioIfSJxVaWv+6R)@ zdmMTA2Xgl|d8fe^wGJ)r=A3!|kn;>1_ zZ>hYWAk>~AgO`?Fk-4aF($akCjcfIyjfv3@61M2~)^)w`_JYA;aiUvT(DcM>cAiu6(|Dr3*qg>y{<+kX{q`~NZAs5sml!*R3~?;|m6Q|}J6O-X(Q zX?~53eH^YGu?0zPjWGI6KGkoM9Sw(=!J-{GP&TR_-FIXj^65O(b)BWwfZvTR(_2_0 zW_oX??hyaa((#Xvz3in6Ah$lGb3f5C`gpv4rLME&@V2_s3I7@&qduapX+3%t-@>}; zQvCUcP=~m4wzB`OhErWIrFknHP|Yv`cGy`Xpm;yK1olf{uh0`bPnK4z+hAbcPj15- zDN?Tc3v4~$`<4nDscqQK%44bbS$;cp7U@z8KLro9cu$CfeGRyKP0DPZN5>Wwn(UTsp6r%_-toei%A2!y*L0Cq{US|+ z6Z<^6!Bg*3hGEUSa_Wl1BtK6N>R+Z&3S2oqF<8I`vzP;S=Wvl`Nuf^z<(2ttkoi?| zFUNu8{!GG$L3oWGQ?dftMp}{^;yNS)@&*I)`p#ClpX+whrr>7yo9wl8`3~`CW#6tP zdM)+cp*FYq>T+AQcSmmKu1~jD-f#NbPWqFs+(D86yxqKBY}|BKd@5Gi#4n>XpUd!D zE$dP7cv;Vl?h|~g18=b3p1)1ja0eE_S#*zHw=U?I_xUjY^b>=NC>T0wngx>KY> zp$HVJrbB&Jh6;q*6dMdovt0y6nxZ}~7??uhhf^@(lg$|x78PnH26dJ!Whc*7*y~i6 z*Nb^wokBK!qI11Qyj{JfF0G9_gHkY4*E}XkHtUD zy*R&M`oB#2zf8Kt^naW5f5&??rYK$beKw1{m)a~MrGIyv$;@7n|D^T={3pF9K}y)7;MN`*I(*Osi4}$Ji;|>?1_mhvhYBi>9#Ea{ z`-2Y{wEbD*Xr z-dERxeID!ozm&8!a;K=xtMtioVdF_B?8uNwff_CoiMq@=bI6e zotsQ=C&geXt7fw>@YJ1(x?{o?BxmG$xtpw)w+P(Rf}Ihh1{8&>c32eD)`4U@80j0* zC3?t8w!D0hegcPZy3|jw4z-T_zNGD3D|iuE7T8#-aEz3>L<_0!@X?QG2@l@&P8wp4 zgj}b33!<-CxB!Hlj*b^cUydzH0ET5r^4#Z}jfgYt3EnN2xhrH3F?y*qccATFB=`c? zE#;esX+sw(x@<=@RvAhvrGRgh)9W3*TdyR=YZ|cs!Cd~UEwA;c#2PQ6L77(xmVz2& zQb92yU}Is6LR+(ppiFX5Vzo)mT!)(@dYkbbD<1B8fPH97_Hvo7*Z2+P(;qaSfx4wp zZ>pU(nM3!GzSfuBT0nnbHg|{R>5sIU=k=QC;C6FM97JxfFkfryR`R8Z zvAMh$o6Ae;xFC`m?x5`w3aVQrMpb0n!xTv_RVB>BU9}ey%U}C%(hbWz(NXR4 zPb}zQwDj~>q0(ZYRiA?>rL8t#zg*sJv$)hEV_RStURy5^U8 z7HzSSnVSDsRF+wWQF|OIyxPKo$G-J$I#9n=Q9>(zm+7|@_cm+i%NsKr`x1-Rd$q

a@kif4Mf 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

0^<^@G{;p8srtzf&+<;BOGj7Wlga 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 I2}m#i0KO99{Qv*} diff --git a/.nuget/packages/AppLimit.CloudComputing.SharpBox.1.1.0.452.symbols.nupkg b/.nuget/packages/AppLimit.CloudComputing.SharpBox.1.1.0.452.symbols.nupkg deleted file mode 100644 index bea98d5a748d9eb7c0b35ad89d84ab42fcb1ae5b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 354345 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%0Afq(ZFBG6AEs46XTzm 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�UgKN@n_Q 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ₕ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�ig|{}I2Qdm<1sl5Rf}t&m@()(m zVzBHI@$$Op^1dYwM*|Pf1vcl;-#oB&IE%X?fZ1hM12@GiN2Kx|CK651FWM4`c;6$jzxH68Tna^Rod#jb4GqY03%Zq;7*TJ;0~y1Q z1)%@D^70+VMP?br=JO_mLc$>?s&a75q+Ypq4}|vzE568qkDVksTMyO%r?jKknu1jg z`GwlF>22H-H3S0C1hYWZG|TWaVPSQ@0VJ#JYQT-C&gRimTrmJ9ekRyq#aHa~mNqDn zBkMQ>2=zL)M#BfanjVHY=gK*e`AXky#Fp!J4uoA}@y7^%zH%uiwI>df zlxD4_GzU-c1Q6Y)cnuF9XAvG6gjF8YIU^6&HSKjIPdxwyQxuF*g!j(=B1sNEZ>E=j zT&&oX3snGa5HwPfm(kyfzT|)ispTlPU>KJ99)j4Ii(P8!pPv{;0~z791BD=sv@{o~ zjEk2xM`0-Z6g?4Cd#_NZjE(i6jefB>{&R(z)Jd`8CUaqg8sJDe$}SuZLUiE={;(w0 zmk?X@9f1gd&+nG=VFZ^5n=s8-w?qU)544P9_TUAhc%M-B8GrfWX2(znlXgedI-D9U zaQXsU{is7PjCQ{K;sqS!N?J@pJA(bZns5%`4$-ou!AR51;u!aUC*0_5(cnCV(57v=&SvWJc^|Z9&KGj>kTDaKqTJzEhO_y zAy}{%B*+11|0DvdlsK7;2?t)D-st&z4(N{8y|Ck&d+hK))hb<_A=R}$U*GtZ3^3uj zq4hPllfZPCiv18zs|jnnV$jfrZDv$Gq&pe2?ig{!MDIe=h0ZQ%qK-W~v}!xsE-G@2 z${;|*QWj~Y0?!pM>M7&D;0=w&RG7D)f`b#M@t(kt7_Ae4t2GLz5{OCX0fgDV_d8L~ z8<5RCH@+yIsT2|%({L|{uE~N95^3LUu z*Tj(5X>S=5#6p5(jFBB-)nUhX#=(q7m&1`vFz&@Z_($Zd4ixr={jnf8XJ2+L+9ycZEI#Xmj8z~y3(vT>9?z6soIZKKu} zOuPj2Z|plA9RB9fFKER3C?QA%vfishmxt;gV-qBcse)BC#Z8Aq^>ln%$L{JGAgBNbtPGz1tE?^CLFH8GcKv(LeH08GaP-zVAf$&~~!b!*ce(>AZTJ36FE zxwz$#AjYX0YJ?EfQ=9@ZvOrLQ;LGITWNMku)bKd28dUb9R5#B84>*aS1>yIt0|(iu z3R+2$1U2X75cctjthGg{BTZ(TbvI~j=1Cm1b%n80il6>IaLVFwv@+OE{c6CEdqAio zuNlZsgW$?y+B9iDqca-QY8?tGh~Rj0TXF`vTYeek&Jimv`cwXvGZ5SshMlmdQa5pX zEU5M-l~qgU;;}B0ik7ME)H&EeLieDF-R|kfwiN&XuQDOPiWzF403WI?cAGQQdDFK8 zrNDNQh=}JDKx?Q3zp+Dz3h+zR7ZYyTX2s{vS^lM^`Iy$3H{X2b1tD1_2Q51qhnmcD zPHPXv8KGS4Y7CqcHYBZU{q3mN1w9oFFfNx(nav^0@)5jo1lxxF%>(negu=q<*se5! zV{2a&VXx_Gaz828dRqe_@%dFsD@h~);07a~UgRh7Y~q+(LOp)D4-O1(WmdH)b&c>w z^UQ*pA~zJxb3?<*`V>$%LFA{o@pKZ@$0)VETKsN1Q9swHqZ?)3u;p_5Ti#mE7Ut!j-K;Q@rQ!$Ul(uNMu_jSO>QGJ4G*3T>1dUPYD+gz zc5YJR7;Q*t%H}sBi|!laLDRto^${a38tq92@YNmJI)~V0F zu51z|tO{4by@RPpj?sImX|Zy+7&mJgrtH~vexJ76Bc^m!*`PTHp%c_!G&2BvrX~05 z$2nQJ%Hy8xl2r{*qrz&P2ZtOsQhi?J?8q5U6qZw8Asn`f1q zB))tmhr{JiEZ7Ie`44e9Rtkw1cDzC-_XY`qmzRXX$<^J+c40eVhJud>0O6e}4Ld+Qrnk*WPU z;i~cdJtMQN*FDR(e)rY+DM8K@-Aweh%Agg$r6|FbIDdI|M)!yFs&MmrAzsMp4pS;VaY#E$l7-BgEK_WR zbW$cZiA?m|%e6J`ve4^0n(-Po#jwMPI*}Ql#VCTC4rSan` zh@s@t;KwIz8`?8`ad}3}R5w3;;5+bndf=(Z%DH&}b$qAZhD`tSp!2 zaT;#=9l7^cVdY7`DZP7jj>#uj$Bcov@YU7fi(`03_;);FMTy|On8E9|KL~)4?7`LI z-;M%vA38ZGMGzUccNYrvHaa1=kOMj^Ys*AF{60A7dWc_<#E3Jy1e|}RBB#G46YTi{ zzzC6cQ;XmNa$!nBDU*yfyy?Bj`l#Lly41lwl)0y!5_X|Pl~i;xL^i64K=)Z2{86O) zmad9DF3VCNDHaZ9G{qKq4l=RcE3*T)1bLA{9Q)YmnFTO9@GpNuNWf0O4Y`h#6@nFU zbj>C>AM1o_*KRGvjjitzz608>B-b$jGlG^l8fWY_M88L)b} z7;PX;9rrXl7O{)bt=Ottf6uIzt@``hiPOMwuCejDG8DrmLAws(X=8He3g&&whJ?V?h@ zaT{gzh0bU&8zEnjIZUU6{0fH+lh*;!{*sDEAU`2K2HR?OHQgjyobnDvmV;tPZy^>wR_1n}_)#DoH&EA$Fy zvgJ8uaqP|9>Dq&+J#PyHlY&o-Lz}#P`KixR`~c|D2bWXfEXG-&jQ0y%bTv+g-Gu8} zBd?gE8A_S$L06pRQ#&mqkITH)Vz!T7b(PwDlr~gVXQF7jM;+V$?Z-bwEvA#0Rl%CF zMPQ=g_1{#!g6^nx?|!mJ=%+Q!{C~(Ix}RYTXA^hl|H!4E4^A#NCXWC4ky~#4T0(i%eGQAViTKT{v7aoca9uiW11| z+4ts`n`)wf8Ys`voUBP$nH+6n(-@iNJjQvo8Obw4Y+-dmh2N+$5L4BtME0wn1$Y=_ z!$46h(7vbcg&ZnDw77zIjf+&DFFAn0$e-YRtVD>rQy6-5}>mS(~4zM11_+HKYu zx^EaJc-$L0-7A>klif-;_#v}jE2}Bo(*SLjWf_%aB}z(6^IGdPQ~m>KU-1(S1PA>m zY!pP(#`nnL8y5y+Fg{2RYEZ+w88Q3+#&hE92?zN4iCw`@44M9)VyI-|=xSl~&ydSM zas3HlbexQB-#>aQ@s410TBUa&gs9Bx5t1<2`t${cyWML>z?JM8XgrG5^6elA=!J|} z-`;jduim~sgZPtF=TH9fY%VHlpv<6PK&uO_WThmxs;e!Qm9FuK6YVVS&*pC2Iu=$h z(BbPAg)VLML0o0!v2BK>#+<)@ML>WM)Eq;*oHQ%$U`%cxOdswkO7wE~jbS#L;k;Vk zfNT?jW~{?x1glMU2XK%5euX8zw244X(hOMHSkeWDHqbv7bU3Vc6RXAZJ=$GBhMYbI zo7FnsjSqbH`NE&j7ICyzjIkT?!q7%c>Vh1^Zvl=hXZUi(<(TpwhnG9pwaZND|F>}J zAqqA6!2kfL3;v&hole5W-ugf8$N!{=UTS$dt+OJ2zjMxC`;|GdF48w_jyH6&yAC^S zt~rJ8)gLx7rAvnp5=0_J93bS{WSzfXG)IC#+z=>~&9`m|kE$brQ@f(QzZd|bP)k;g z9tJpYZhwE&_r09y%;k;{b=}0=iww=5Z%ms;{w~>PjOke#Er z-9JYT%9_O3Wy(R~D+xl;NIAmhESk4K7WToW8!-mNA(t2Tz$~m~*pJ~cNwUu%^N}`J zoESB6&U)z>IdzX$AoV17y>)qeJ9)W#FrTi>n9{?^iXI~Cj)`uH94ERU!NQfdSdtdF zU=rQR71e!o+8k6cBfdw(t?1 zTKXdfsp8 zHC1R_(I&ln;*>?W?yB~HB})!Cr!o?Nwj_CHg=jQO%P5&zb=`=WWGagdQPhJUb#K0A zwbF#RYvE0D{DxW2ExQH*Z6qA7A63ZLoYe&Z3xZ9lqS7&(&H_;q#SJ~RwkvO4m z>G*1joT5CDh*Puc({9(1rm-w!0D|c-m#>{un|f7Vh-&ton3cCR~=h z4EOVWuad&O^W5)JL=!y6Tf~E#N4L_$lcxc>XT4gJ`VX8HlC6nsARrv;`vt}_FX3Rv z5F{HSogos~Cr!U-4^YSckwHOT+7w{MWE>fYQ^_aZQwZPDK@`LLOAn3kKsY5X9(Njd?fe>nJ7!^_k08`RB@)VaG9~-vM#v7?#>W9W97-A9-%==u#{Q^OetBv!^ zMAk5rSK!abB$JISq_#4qPi;ZFYeora)9mbanX&&lp0Ac6W&$LufH^I|wfb zRLu#|SbS)lo*L36JD6Yx#1M1vt!3zhIb z=diM(Br@cqw&FC>qKuj0;^0**pmYdi?P`wzEe7g2UQAF2l?8LfLmVqKijmUPhVE1& zs0;ifZzvQ=kkj%<7+HeF2L>OZl_KnuVCMY7IvIbBM4%Q@EJW%1SAl<^-QftAh6IiA zYVNmI$)(dng^Zclb3!^lS2oGRw8yhVaoS%`tXbLbj{O}G$7chJ7x1x=wSaL@tXV|R zkZ`wn(a6E>Bc=2m@u!O<2OYRt;8}>+zJVoOtS62L$h*K_q8cN>>=^Y8t0K4osV8{7 zWaAHP?Y@z{!uMtvmQR+t8tEnY;_!nwhn%5fy^ZNwcvejb%HhkacwpmWe%msj$Hn^-q<&gULSy7wM_Yjtb-exd4Be?h*8XFKi9dH{2Ez!S@7?D|0C3P>JGt z3I0nuZ-00t@8KrPBu)x%#X>F9Ae|@Bfs=R^5m*dpHE-22WLiTs82_p+gnixKX|H81wYbzZA!8RQ=ol*1?fxgk;y$u)S5 zP%RZeqngDPo4s>5M=%HRh;?LxlQvrsQbwV7mAjz`@$3*0`SUh^BV-S19z)32;2t)P zMC#)nZX^j^{1ALvV(wk(3r>-IWFu)QJ&hQMGN8sknrv}!&UwSEM;umLQd0wOLrBS<8Oklf^ zG;UN`T9-lkZFuy_NBR`RWrz2jHupo0koNv`w_>;RWkqe?m)A;>!-aSk#vDB;D%TZF zwAIMinjw&WLdvmLvR(`5rL-EH>k%Ua@qplhBt#2FmCujJ!c_rx*Xiw$tqWTA4scHf zixC4ELa8p-keRAR6g4EXW3O>D$~q82%|h`8A(qX%O!Lk?|1K3MiKe=YS3|1~!i7(+ zKGa946eW#M$_#fEX~SYmaiyR{JCzozS48`ZaZDLYZ^*1~D6ME5&+k)JY!!k8K&sRs)wV8DQYgrd2npYHJDQYPbEvOvC z6FNIezlB38MSvSr&Gq9%rLw-vhXzFa23fAb1C2TI(~mQd4m9kndkc%X2@@3j>~HHr zjFHYyF+l0}+reBiY%>MMJ@#T{hli5mB$83;f~pr&-_vcI6t$aHK?9|iPi5bY<74SB z{Z#TkDmz3B70s2~2Kc3Cl|QJh6=@nGw7-V8d4}L-F)oCy{1xLFetqU*_Jg;GB5@m2T(ZJ_Hj@E#+ z^^Fw9%HrN={O5c_f+1-7qw{AodWZKuS=~-n%#pCB99;!8Hw(I(e*g z^|DSQmc-bQ!12y!j>&Hu+XUPHmWwwufNs%>Lw-A&xghhNp%_!#q@}NdZ_v3^LW5t3 z{`D>xfE}#PIz~~h!v)_?y&-Qn+uo+*r}leX>xaT6kz%9IM^L2c!OyX}quAr9?iI|2 zH2~p+Q<~qTGuOdTQ&(t%tF;fXT>R=8g>Lnbd^i%h{x{&xXWEkKb6oE$=rIg;e{zmr zB4E}}&}#dxuf%ca=S7sv^wmrSiAR+1L`l1{^%idli&x#2OX#KKO3+MVucJtkPm)QI zL=cTwpgKh98%|hgj$obsGr)ViqXGY>WPW{dCVNY9>m|vZgE*b@H|8rqVTZhAKF}Y> zA2d{l#MRzW67KsW`04w)!Mu7EPc5NZt(@MgLd8bmmn92$4MxYTK`M$GYUV(2xuz+N z@A6H@XVBVLUXlh=JWJD(GF^8=mRWOcZlWg`n2o2BLcJ~5frVlGW3AXZodl~0?j@rH zF+mmg#F0;r=~p&o=;>%y6_=p($;Vtn!;esbDEK{|?5v>j)!8n5S@5M~bP+3>% zkW3%BimsPUR0n0{v#ZsDh*>QGgsE=E!^8QV8qr7SdcN|Bo@(6ay=&Yk{2gm)7VoxM9)Sy0n8AqH}MgzUC_#S-ZBHXYj=vIx!Uj$d2j<{MHw##^T3&9@JTRg#R-qczN_*3P@tSA@@GZ*KI(a_O z{(Et*f_L~;;)l>ygz!I9!~Pj0Rx>fA6Lq%#=lIX9@XsE2Q+?8QPyAon@e7WOh>Xt! z!jj_XB8k$3`VGfJw1QMk>+_o-xhQf?q27hw!IiGfz6{2sa~D@pa05y0-EU=QPA>7? z-yc*;+#VsXvKCQd$7YY!Uzgy4k5$TI~TV=!b3aQs$gFDybuS(CO7 zT$3!=u)BgL4ig7gbrwJRF9GO}nnCd5#+WGd;H?AvP_0D6xy#^GSpkI3Ni+)vY!@P9 z#F4$gd{-t3&$M(Qt)~l|cX)>R9FYsNkBBa$NFuS3?Un)=G(@;PieV$h<_V!rNK%;5^Pcup20Y8q3zzl!U+iJC&J1 zVcZJFMz{VRa#4{opk|s5`eeUt=m%li?JHk@fr(@smVWe_Na_y~eXL6UY<9kN3@Bl@ zRTEXgnV8cpX55}3Z29I3EKo({q^q})P>beSWq$u^Zb?evV@9({D|~^0Lc@#dB{Eg% z@m?>Z?AKCc(T}!<))&b!mLkvi`kM_mtduv!)oOZ&v5t)Ofc+&1Kdljw9|D(Fx<6HxJ{lwC* z@<4U1^(<9Qfw(CN&-m7*Ni$DkfdYXGhU?X}RLw*o)Vf@2`;M{gQgJ)lPuSdDK-yo# zD{zNG=A5X|fE1PBY5g}d?5AAA7o{FAt&)3gw;7;yH1&lwJL(C}<-0>EhTO!crljH_ z1c+%ACE7L@CI^gS=Qw^Mu^P<$pi=w;+wVpz4S#WH8Yiupv$H*0DhCgR)S+;kP2>}u zv0tbn?Sy=+upnAh%k=595Gbq$(yOHP@1<~h$f&ZVLWKy?4O^xh=`cNBTXOHY!35$l z&Pah5&UZW$cDTn=c?34Ai!Re_qQRo*3*DD}F=(AnjPi_fqi+RP9;(2<)c6A}(Qy`L zw5O~nGLMwFc08|=Ys%2EP`Xcf-gpLlrrc>*>B)#g|oYRH*> z%gO&vo~B(u8ablO^iSyO)e2iDet0X`=Q1bwJYY!Ys59TTsWQ)A*{*G#Fnq4^Zm-y0 zpn~)`!g(6Oo@X!~c?0|s5`#7wrOqy+DDbQ>S9q(U$Y;N}6xQ;6xz2gH^z*G1%JI3& z%)LKD$?`ju_F$1p@7#aerMmICAEUni$N^&stAfp4R43$HY^|CISk7F; zpQPyBa7e-O3>_qIGxd(LM}~AQn0lTokUo&|%JhSngqmz#_+0(|H=)&QJU0KR~bpIVX0(wo$@3SUfx(-pd6c4&SlD$^O-Tt9|c{& z*5;o-OTdq|p1YlJ;{!CRyYDp6Q()HmZaY zEV~N7xyj#amf5x0K*A=qwrnQy$yv`pv7-LmYJ`Y|#6DU+a;+*GFAa{Vd95GaZyHI( zQa(8>2>D^=j`z2dI3wa;-2=ip9U}={PSi0;Ho$kCKO)?}4Y|)`;>bLCl{gAeZ7$BD z$z1Fx8T&jzYf{AKmOtTNZOi0PooQx_6l1nJ9ONjwWGQ4jfIQUfBRAk!*{uSi1e~Bp z2Z$Q_cR$)n0}cp1#(!m0SOpb_`c1Vwxq^YZ+1oX><=tfpRi{_H-YMpu!;YG$LZy2R zF`BoG*Imb*`!Ty{(qKc|M~NOj{nrXmNBq3Ff(zKb+NXVceHc=N6E4>Zc=i99)9>bxT_!Ajm{h3 z`2aOO0M!9u+JdXWpBO0-V!iHRl>0@zD?mc01~MYdY1He0?K|DW+Xq570~a>YY@wZ7 zD^w&JTgk&TNYqXw(%#}SdLbcFQQ<^#j@2GGq96ay=Dhkq5^3Vd9zrHJ*CB>*V#q8; zUam@?XIPP^7v#ySD*9|ed7z?O%n$@^Zp7lCuh=wB!s-n9s-Ozz#Y_$ISpV4fOUn=2 zcElbC7aON{2f=brQY)A;3YeTj5KinLttg_0xENw^pkV?Cv8rB&kwzpzB9MB@U*(}n~Gnf5;0YTSi$i! zk7AaTOQ=YtYY|xH$U&Y?dKn?B z=;sk7V6F+QQSCj2F2e~q`ggD>HY|y4!iU0w8@cf{vVlAWJs;q&N(73Sl4u&!t5jBA zpjLWircRESKZrpK4~nG1P!vd#A;=hW^+zeBU2cSLcD_!;7wAzKuG3MQ+VzO^_y6Jl~7im9}xBTPTP@ z;YVE_BsHl46d|)52=5Yjp}p)xOz{BvBqMZ11CJXIRETvZt%_nD^sfE5!&_f++D3ri%y_Az+lbbZ!}AOVi?UZ`ay~PFG*Y)vA3sxHx$Dc=(Pl z#3&-mYPtoHmRZDJS}~^}Zvu;yV6t$5lqnq1h!%X5Uv*VM&Z1`LVs25|jSuLbkI0~^ z7u}mi{XrPIcM)c5=}DL?d#2P#zE?x89Z*@U{D{1%9O59By$2WvSt$W}ltMJ|aQ zS76bX4{WCNrx+1GTrrp~&~$)8N=XNKKsKHsoxftd@K_2S2|hH{L|@;Uaw(Ix%?QWe&R6`GYUD*HKErn=JE?DqX79*&8bk-<+o4f|z6A6UYVTE)j5}n? zVyXTLe5II5MjAwzGG{q^lRm5^fE9k~>mN2(7v1nYR#wPmAPBy5^sYy{_(auuiI2a= zFoIN~%T>_A-g;+X!JuBwMsU8R<-F!pnv10U-w|G$%Qdk!PE9+yJBteam${l`@v-kP~ zqarFrNiD+D_j>cQ&Lz>5(w*#5EX6bUMPlz`#aGopg@w9D66vePv> zx%-!csn`}63hL^V=WE{z=Wa8cJV_SbewUQ~q?1N*iZ} zcHdTe6Z3A9x}5|ILEXf))mie7NH#tluiG|ID-(}Annv)pgzC``GaQufj-{gnUWT*K z`F=uWFWe)Wr>kGJo(<&o3`#PFp<$ZSkL+ztv(l^{fuD3{LzsDWHRnIRQEFv0n zP{CsLa3*6u7gC3EDex=0XW_p*Yu{96*T6f&r$4E`{_WXtt^w|(`{&s(`v-7n|0me| zYlh^1X*K@GnkH}H==3jLf-EH&xqW*0PPH@Nn%}vlLDV~Ais6@p%B7m{yQ(VY;$W7Q z-8E!C4(6#2J7kx7Ju#WAFe15+gHs2Snd#3LzSVX`Xm8nv)7kPkZd{Xcyo`#50)6Jw z(+UMhB3O)F=3=CRd=*9|OR_-bTR3PedaEb# z76`i6XLDz?$Zz=6hsMTikBffvlObeIn8j7aZ?biSg=Jv{bOP>A5G_Gs##5RVktdMk zS17#}QOj=@9V)J)l;9gj5z#V#_38`F?L?jTN#|iq%OWnD;Is|aWU zDsxUUf7vu7*t8V${37rYV%W;Ote5lIbf*SVvs=I=IaX+ zxbaST+PymJt_&={5eRfDOv-d$-nT0W<4P$)<`wXkgJLxX3$&3gACnza!){FusQc3W zHYy6)$dt}D@&b{##TMEYQq8fdOZ?J@ueuVg4dT@_iY*3fWSZRBt+;%u+f=ggr7&4< zmdlcI^oe`c0xb&7B+1c_p(RJV)|Kok6CC|pO{4yp?e0F&$!hjH`x3p)1$zAs=f77h zpc?M~M8RnZCJCH&-3OD zNS8@csB?Utt!=9=(PK?X@jo+TqP~7WLW_VPNny&t+MqS zyFSe6PKjBkTm#}tmHEv(gx(qL9Jb`k%x#^)rCorH^Up=c4<+u9toslbe&1#pqo1Ac zpFAN_`plvZqllxGCfDNob0dUV8x~5I9wwR^xKi=-=Pmui0~nBf<^5nUNr5gRLFObk zdui)c01bsmGZ z<1|sM??YyV=9j(_DJapN+($SktFAB(AF?SVr#1gb+-u0b$rjQ(a8Lf;mZloraZ}ZP z?#g{|A_fIMI4wdDqOn45istF?qRB54b=U3RIs!%{K3r;w%&5CZb%pFVZQCS_?m1%dI6GY$jm+ySYN74}0p-ZAkb3!46|p96vlHFM*$4_+gahg^Wjd30uzVk@wQWoZH`Kx$ZB|?#4dy<8Nk5p&WSrIE z8w1kSPTuMxe<`cxvr%=vh zV@tx^O=AwJ!(ad@q^F{5qJq|Cm#_T!8E`0BsCR!uRRdrQH0(%!<~lT8_qpI2fO4zF zzAyYe%U>&OzV4a=-_m=8r#I3$BX~qpFJIw80k{18aM;ZZ@t62_G;_Zo0hzsTu--h+a_R$Fk-6;d1#06^p+BiPu@!a7m zd!At8avF8_)?sHDuWF^xqevTNR~K`SItK3xxXX#S4&l|r5$~45;@n77T{+;ZJmyZv z+9w;|B3vzhAQghW;zZ*|xrTXZCw+Ly(LCFvB3vBtf9aHIO7$ib86+>F9TLm2*4MHW)oTJ@{ z!Oq3d2y<8bv1)y|zawewS3biHV~kf-R+wwhP%9@qk(0_m>!PPiyX*nFw6IRoqI%myNY` zWI6_l#E+DTk~pmAgULm&=(mpYwN&*34x!PSn{tu6WlcPt5o+T|+VafJ*+cuM$N{Hg zb(hPa9HSgRGAM%F=DF(~Gec`wJR%a1L3d4y140X18k@&$*+6fHj0TNaC^DD@1@i=# z7b(yCC%QUQp5sHQhMdAHWSM`y(ZO$>D;@@{B{DVhjsRr?O^N(7oLjxGnB&x;-PfR5 zNa>sogCq1p!}|gmUF2J&(Z4;aQPIv~&t)!Y_BcFoUt$L`mv%9jXHS&4KLIAfU#`aOgd)8<1VcMH4zMU9b4AKE+9GQJ-&a7pGw?T?5@t=(ZkGg)f~c^VJlTh%d}~U8Zjts z0~FP2}mBqRe@HcfW-|*bHaO+dBkuy3KPLc9G)lg3x}g+F!hA zqz1`Tlr%mYuZ`lpUaZqUFUu?0WmoYwj0cu_O*((wKR$XM8~8~cH zoZHRyri=`nMrSXl~ zH-Wf5#d!lT6L}A{wZeiO@>l*~;^!f+#pNlmKAmIWY3oAkc60F&kqvl00fCKRCy6$b z<#Z1B#^&$<5hfvM?)NO7JkCzD7s8+*94Z39(hBU)L>}XMG-%o`4|}5GkV%pvkrG0Q zo$P~q8)6UVblN#-xV&HDLefbOQ%CSWTwPm2g0ihl&}Sln&iZig`C!6_1g2%USJO^1 z`tz_W=bDrWt|W2|fQ86D?}U%v#D<;96Yi!y-yHLtZ-!bQI(q_Vd&*gbvf?+$i;mhS1?!B)oAfrQOJFG~Z);*P+A zN>If?bpF?I4|ekzX-{_xNKU>SEmFy0NSK(f_^QkLTiEWx}Xw^UC3yAK$G zJAht`3Cg7UpxK2Ry|B3VP1UdQ0jaE>RR6Fcx*k(Hx>nUXGc!&`l+hTKh`EL&hZMpz z3zPbV2P3}$w-x7MO*LIo&?tA7r-|*{44S3%Y8-q7U_rQS6>MQg616N1X zwmF+)5CV`Td`k zBTAc6_|Q!cgXD8A zx-T2u73%L}_getrgDbYi*^e~^)%bOgo$n&Dv~bUk~!+}akf+x>8Tet@aXv+ ztNLUoJ;ijjbZl^^QYC9lth(HB7g$pzLf|Or29FNLD5m-W8?RqL$RW>Tx(oPiiW4FS zT77$TQhu`M#fOWw(vP6BDvB*dXUQ{dCWvmOjvo1KPx3Ix#L6Zv=EDhU$V79Jjq7C! zK3GGh%lIZo2_i7aU%yXu9BbcUL*3_zR#=D7N0wAMbxE4s*D1aTC}n_=GX6it-hnZ& zZCMwMosMnWwryJ-+qT`YZQHhOJLwo5J2z{uqqX08@74sy>v1kQMIDvTjr9 z23jPDZProEHvimdlt<4RDgS6eYzTv!oNdNZi-}lssC@i2d)u7->f}Q$_>P$`rY25f zF`Ory)*E~xdqBU*nCMm#e&g3wFY|^$8@Ua{L4f6>+a9j4Q#Xf*mEb#K;DY$=z*xIY za+P+0T+N8Sfmc0RIM_J@b-}!<|BPg4zM$j8pgvq>!X_;FrQwT8n&eVrhy76x4g@D5i>E56`f zsfJq0b(gboOMd|0Hl`zU6TR9Ib`#|iQc5pUDQuXKfs&P2vx_}E@;YsCf^@aDCt zPl|7gHrAh750+o=Fs+Ga(dgovF_;rZm5eHACY& zVRjsZN_f#sz3}YHxKS-nTFLYp9C}$hzGK>T_V$KlTP`Z2f9vo#H>RN(Ox=})7bHfe zhR+;A7r*;dq-^ZWB8EoruCLXZFoV@fBi5b1ZYWeoOkXebqoIZQ+?z$LeV9Cfpvkae zcksR#lOc@~?-TRzG*B2aqrEcI$^OYt*VnVuY+LOZ&5Pt_|I~lmIxvdh8IC0l8Kkk+ z{PQ$@e!ei4LM5 zCdg?eTXO9@sp-pY3&Ma*xbV^n^||M*`dh;q|2*-J*79lJx}=X0x9XjFglU8jJcBgO zzz5Md>QgX;7sV@2q&#wImm6c&cDex4E3eh;u=8a{Z(kmZXU8ua?#COgJBosHX~7)v z$u&b6AiQWVt8o6_%q&3VHAzWPkM1=>1T8nx^Tj=pZAAoj!aUVX&w0)8bV;DBPs@)V)qB}yQ z)V*aeX^y17lgVZ(^zr~+zLp#wzW1nHHKePA@9Ieg5bkk;g_&TJ=QQu>uVOCS*pttxsdb2in06n!JO$zg zi5_bJH>f+g87w$ax983v<_b2CQfpa$&TXy#3tc*G=0#r3NO;I4 z?|DEkh~$}&(YphhSj8~sFvv|XgUw&=CHZFBE6osqcxM6%X^5MzLfP1 zFv3UTiwJvG^k1C?vz$9IIIMb*N>*(5GDRe=an=0L&~NVK8@a$Bx8sl02C7rP^y}re z-%08+(E^+@D}_%|je2p$?u4nEq$SaIC&?j(6m#{cg=Ev6ER-9)Bn`)i2*scXi@hIi zK|i^ccCVP1Vkz5m!}Z+FZz9FF%e*7PispcDCIiuFzzr`0nL><%XDH}$$OVop=G-va02WRp{s(L_d8MJCQRF>@lY zq|oCSTy)REYnE-XGhs|&^7+BfN|Yn0|02%+im~3QIvFMvTmd@$bNq>LEDvl$I^ve{ z2%Q@BLWF%1V%gM--^ypQtE?23Ut8d*orem|RK>t={!CVe2l@KT6*e7^#xaKrcUDB- zn)$>YR0kGvd;EuTg}={P=MTFBs48sggsawk$9!C;IbN*xge4p#W2?zNU3M}aoI@JCUSkpFPBWsTiCZmB01vV;H3M7c$gS#;Rgb2-gX?*+)=rj(V;*TOa_89qORsev+4TevDHF%qau0qXB2|VoXl7JAa0_X3!Yu4MwY@D9dVMC% z9m7)z&31~vfXayetU`3QeIz7d7#V~J+(B{uo0c+$aL+*m99<~g84NcV@Ebh$6EK%3*b4M0rpIdUXa zfiQ&WG*NXZT%a`VgBX~kY_o7`-UCV$s2at@NxKI{QjJP}ke%@N72DoJ?FZ@-pfl#5 zk}9*iIIW?)+U!QC!YA5}x+aD3bg%`Rr>ZuFFlQI5_g5cMi!pmH)QPlm6>5NzR)Ma= zRkPB(oe=AKCCR%~tA-gL&h6Gw)M3xagaRrKL~YBdnY?DoHxQ2HsPexL%7zOj2PYP) z4xLEmhd~@*n~zD!Q!*vx+pf1s`;(JRkU@j?NC$I`)T!V(h~0Knk!#cds}N3`Qenpr zYu`>UAE5fv72us9#I%^th?}(BGRs7CaJy1U{lph|7j14#%q>ywV8LT{%c=I6ZeWNz z^OTJh7OA>@Xxly|&^?k5%1;|7L{vU~6C=_&-|!d-lF3Qn5P%2>(d!N|HC9_;EHD>2 ziO-}-i?Ys-yPI<^knIE9T5N?6Iy{9N9PM^um)pFhwJh)m3!FAcPmX_E7d2ddASSfw zUNn&(ulq=_;$B+Yl-tyVQ5_3PGbK7()E1Mr7%4Oh-JXsE7L#}Rr4w7v<7Z6CKVEjG zPLZd3W=BxZP2Dci^#dMeEh>6WSO8F_Iz{Z7Kw`k4j3EinTe?x>Wig{Jx(X78tHov{{d zz4q*W?o&*JjVpK(#cvc(8)^0nM}--)AQKWpO%Ao@v9k=930VYMU!n0wF*6B(iQ*tU zeXz(K%Oz(nkPhdw>!Fsbidl8$`_;wtAJN5THD}`sO-luxm5+dgo9y7cFJ)y0I~cJd zrVfyF;1WX6uG)Ro9lErjSeTGs1T6iHq)#02r!8P(nXf8={XE9<-aBp?&(wI+3cd_j zYeck-OpH3OhHAR;3k5zBAbUyIBqq$x_rTJEEn}P5U0$#M7OPOez@OXSNz z%GypOk6%Y?(v*!6^YrctMLzY5MT~g%-IJrbqGJq*{MS5#ATZ|~PzF0&iY8(XC04Ui zg-$N{-c-!Z)dH>?wz*||a*ctKT95P4PYI#jX!%MNptzw5lnY>dmP3IPI{6=7#i$~b zXsnXWFcR`X(#A}=T$f_sqT68wTLwF+BmV$4HeBBu%xF)alBAVGuUq63+JEM>E-r3cyKkI)?aU4+{|Dbr_d!nRjc!=0}=+^KI%?G(hb$AsX4Cr5jDClH) zkn+mJH>8i>;5eJ@@5>_=w>+v*%NT8$cm>9JX?t6ZJ_CcxEP0@XBIfVP+87v-eX3=0 z&GIf&Q5+*{+fjOoaFGS>8VkW*VObE^xpZF_csnC~lwuw}iz0pWEE1DcUO*mw&;kjvwAlV`I1;Rz!f6OI zI|qEKw<-5?*-^}>A`p-^aEK}svUHw09j=IK3 zEt3qO_THw37nxWyB^pS4&^8^M!F)EtJ z*{~AetwNqfGkgCAY1=+v$x(gZ8t(V4G5kM4u7CdBB5uZp&Q8WgA}+=@P67_5e^ud0 zaZ(oF*B9~xwqqcxf1RNyE`%B!&yRDW*<8}x>!HkKfg|}hX;0awP!hf{LM68{@dg5B~V2{x#19 zQi7tWtIlsW>Tbh`H#D0$VI{sxRWn!`QR^ws?FA{-gsuKNcl^J8ecAJHh`EhmV`~;b&ZOQW zV!}SCmYM=_VZUO`T#VI!L3w9N-|U8zOf}a+BWjS%nOnn4QP8 zxZG9J;faLk+c;W=8SzHpYyi=Glm9T@Um;|kf0l9$&h7o1s{+GzkLlU>iPwLhKI8x0 z$$xvQ|Ied$j*8;+8Q_)M%nbQc~fOGsD(4tU@IzP8lWB|II~8IppBa#Xj8Xg4vv>1 zAMfFp9|rVr4q;+V%Vr9AD@wW*?q^DloRW88O678iG=b$LJ%Fs?moB1)@(O)xc_KXe zS^^SNz!zJR{5m-8#8P(t)G=o*0l&k2-CE=nCGP@ue|9;KzRO)PaP{M6EIJz4R+2Vs>W8nk=>CiRpF9WqeE+(_^_{=RsJ{O?R7qNO2^eFDZZ_bvX{m;1ly z7ykzkf^8YI(fa2N@CA+=jol4F%<~jWhShA}DxIc2LnM%tSuQL?;>sarLRwTY^^g>g-+1=@y>WS4OdsbyQ zSQ<6|2ILtlb=bWJw~l^-Fv7dpix9uhk6 zM5jNvLx3`l9*sIJ0(yfl<hd>kTJ2aZeNg;Y_$TlMIdqs+W)vWx5cZnSk&(iF`E% z)<^gl)2`fmrX*(|n_&k#cuafFS=pBDp9Y3$H~v!j#BzpcXC-WHL>{J>*DxdFbX?}0 z7I8)+iu6U(t(7zTAovk*9J}9dQ4>b)Tiz;6ABiSTLlGw+gMlW{s99xlXMiJWRtl1+ zF_yaKqazekppj(hrYsqn6D~?S+sr}X#&yYLIbl2n_&9zb9L5e)hI_v_K>Beb9UAh6 z9uj^Zw44jaM!yDCst-fKiwKa9t&+4E-3cZLo1q3u`ZFeCB}?QLimETOm}=i3I@f@H z%)))z%RElMiatvt}{CyV#tw zB=UL;H{N9MwKxpD8jzKL)0>ErWV2Z3I4^}oRYGx*QSff z@^QZrZb`*Ktb1A^1GVBjC=lF?z7On?PF!vkq>N!~3+HUsWH$$+B^WJ}2J;2_xB9<| z)xe})yhAHhiHX%?V&@2c?$>*IZ4^8!)nAq0>iEo_hsG75t{t$Ox2x zLS>i5T1PXToj5;o5W}l#`$sMS!3+GGOxN%))TgRCE-O1T%Q^e1;W+3ytl9B?q;U@u zTg1uiWd0x09sJ&$0UDBzrEhc5a@=#IQ3Qn9N$%6owJ{^vR)ydR%Y|&&C|}lx(etih zvrXa32<`1plAG6rf(NUq9M#d)hpy@PX@8i=C>YxU&UIEHw;gdUOa}$;{+keUF9$VGO6TN`^>7{ScSpwz!?6^ejz0Ad1-%kYx zx3U>Fh6fS)eW=MW3bc$v-=UW6HYIfv<0vXQic0`%DbksELJ5uTihdJbu$O2|)z-a1 zJ)+FWZ;c7pso*f~uNWiUVRrhfaaj%rZ4C z=fySX=$f;2^ZW*03!&E#G^$;k219DTD`6s*QvE_1O&<|l~iI0OxIXrd02E0 zrc9+(N= z!KG_P0Mp-2zv;xuR_1DM6AGi-6u#7HcWE=a&r3#)mW64;6&ydbZC)V+)#uk4~Qy zmVXF!kL0y_+Adlpd3-7h6cT)AgLEP&Z;@tP5N8yCAdK#AKrsmmfuN&eCHM`fT=?i3 zp$5X>)wrDKuhpe+%ud42B z0)eN(yrxM7M!(Y|PNM%A;rup2D*UMZ()}x4v)r#+zJ6QRN|l$iIkzeWCG`@49I7|F zc$yb$!hEfXL=%c}xm0YPVhU;0m#U0+p(9JIXQ=(pj1N5{J68-Rv6`Yudkb-#1CXIz zQ<^rUqXygo5G2N9Zf`-(``}=#Vy`i?$q0&2d1-gvdR4f=PZQOW04_@4ZOl<5I8eRC zLd=X0WEDxrD=}5#yE=`c4XR{!wE0-BKNzq^PfICu*@wq9gnY(u4{0dps=>5=29emPn89`w^?f46MPumGx{hDR9;DB^4xQtYkL(KC)fCxcF!>x zsl+jHwM}m=ULO8G7Rs;1+-&*9vUd71{dM-Rl~%dCRLW};j)kPkQ*0$$v#S2qXBBGw z=c4|zDzP^L&zGQH1AtwQW)=uyms6S9RZTIxqhRe@-A^ipn^cu?ILgvTg*}Lty)}I} zg|)TsgaBwFEpU$r7=fI4ogh5bcw!~=dYv7E9Onj&tlZ72mQrT-^H{s<6GRSfb;xpn zfJrz?Mk+~OcQPxo57gthkXFbi!=6(2PmxigATb#O)@;T}!| z7Jj_A0s&3bfzkzT0A0vNV})V1AV}_><=A(~Sf*@H70k0*YAJ-KMbU%*q});QkDP9s z{aN}X*;1n%G326x@Y}fVM(txmEkW~w2UrP}aor76c*e4mD=b*1GExc-l>l$=!g;dB zIMs=%TJun@elB7-NJM4nmHg!9&sozs@4{=;@TViCTQE)U)n@@|AKj(R>Jb+Umsy~d znc!W&5mS{gX^Z<0J0GMn8;pdR(glg;8LHv?-_7Aua=AK(KQwE+BnG5$0ze^qStC9) zIe2$O`H|M_#Flkzerz_~Upn&cnd19Q)$u*D4rEC=XW`OanJpNUQlIw{5znD4(#3mI zjp3?mzxFHyxWt`YHRVs=mThdUVINvrVJ&8K1bx+uex-6%InLIRDKD6A)|Sd6u;R+7 zbO0GyBnh_ipt|iF3tw#{U_FhUz`31Yo?~_E6vz?*e{iMb@_-d6SX~hoRj6vkdrAMg zyd??Pjm0bx2t};>41mtK1%1fDQ8t}hJ)-Ic=!b2`6Rnwio^#C`mz|UVdbAQjnkn>7 zRtG+ZrmoLE0O$3LQX_L`IIgrDYACrqBf~YgG#u-QO7?Szd#?>rrU{GVXLKhRV=qrtkpJV?gD?@_BG#>)X}ImwvLbGEN+31_q#%{BvZ8FT++= z6@zXy5eikZ1_7Q{b;+zEPqgHWKevzT6j@o#?$Gdf2e# z;29vssr#o?xD4ckj<;)uP)8-m0k2Qi8C5zq#D4F z)GlkTzUr*cJM(g(z+K7;^(cLkh}6elJhJ+#mr>}8duxxWsP6dbz;#g`i*Ymn}vDIVfhLb0C#W|-2RMIv=c5T8G`#c1@ah6kmLLc2z zt|h8e8?+~I2bs6sxT0#D08fwBU@E&4US4igCNV#&(yUK*!#msPS$ioQ+GD+snaTzF6W1p za0HBN;s-qH$GHPFwUo2yMY@)%t$rZI!K67&g2%Z?S>|ZCPF$T}#fNSD@mSW|L%sSi>sA)xnAC-#&*U zgSDcLhUHEm@p+nKl1*w^s2j^NdkbU{$41cEi07z7TyfAT_l2|#+0&8pkSvm~hBgr^ zuZ9ly3`PC^gcCl#uY*|fnpXNBWzLCs%2Ibs=q-gzBOa#f;pv;CFCT-~x&ms3t zGj9Pg$a5e^is6S5d*79V5$+XMEhJIuEDJ67h6T2MOqy~&>$d>my6I4rR`60qeWJ{v zIX}A-Kln|2e2fc7z~#A=eq49@@h5a<&F%G^a>{d&<5=4ceK7G_IY3reJD_Q3by!W1 z{rI%Le4nTNlD~-)gbG_E4W0F3CUh)c@{}@B@ZSLVS-cE(KPDZtV%sc_)fh#RNK~*} zJf0yoZ00Dq?2t3fkZDV}(gQ_A+ABC}UpkrIzIZsfon;3~uQbBQi^q-qWc^IeYkz%M zs3xg~$*C%&b1>YbdeK1+haKo&hH_)9feg?X7r=3x6Sinp5lYKJ7RWu>COOv8va;pb z*3z<*Wfs?^CN3_q=58DrTD{fX`v^3%!F4C?Yr~XVWVD$W4IQvo%A03%>lDRp6HkY1 z7TixR(?Fe**VR$P@rFaDbHhu_UUNL1^P(y<^$}x|7ua4(aQkWsrnXN%( z2?yKu`jVFEsaNOS8Zp4Qo+_H0N$Hw-?$T#ajxbDW!p9u?zvk_16M>}jhQ$j~f?<@bKTzKLfZ}?-42;?) zLc=io-NiSzd}<(53I{%$NIYL-gzAXGn+!Q!9COAe5&_yx({sY5UF!e{tsMAAl3&U{ zGexp4xc1Y|b>YG_My$iYVXF}dseF1R2^EwH;W|47)LX8`8um`QV637IoZa9f7t4q? zfHjsY$&ce?iMcCKcpwc{uaUWmufwEt{y8Qn!f974t49fc&=2cn!?oxVo+h#dvoA?M z!o(@$oP%BpGvW15@P5*cQyi4$z>ZMPWygVf%flP2O4?9G$Pl_vcbCPqn;<0)&w;f+cCY&iJssHMtNq1h(A!5_l`s(sc0Ifbb+Qs}EO6YuO7=-~(XtBK3B zGO{ow06wS{8VA>7^f4Ua8lV$%c7q(c@Rw_^QP3%6h@I zkpXxH@Ky=6824|Vr?vr; ztA4v|uRleOGeKoMng~l`Vo@&{Sw{pg!08$&4;BdFO~(7bipqIk@A6{_tFY6O07JoN zVBB5}MCi0dPq+M8;h_kn0OVHAL$re4vG{h;d_KYcdkXp2w<0d*$5ksd)*^e{-?_ z$9tn>X5e5aXzTW`cSp$9==;(6O9Cci`z<)``d-SOn(cv5SS-`y*Un!)$UnQtTHg3H z=Ri&?cvaufQ6Ej%QyKbM_!&G+O)*!fDIBpMsUOdDw|$Y(ayO&r%VkC0Xa=F%c!{;K z%C5J}iz2ntNTJ!1RzrFCNcKhV`=DPMz1(5v{^$4zbadxuogbc{1?J%hUZ|KY6_%g2 zMXwv$TswgJ3O^jHnIyi+%x!fpJ;;{Yva}} z+L$mo4;#8E|J{^!0Ca^M51+*a`z!*AL%=z*V}Le3p)Gj?a&F_p6*uo{;EGLT6HPVA zVY1#8<#w1=5ujf77Cunvp=V`Q|)}xjdyy;KLU~?iOacVIlQwj+|so~`1r~@MlsSXJ*s2;4Zj_d^;J%^FI zmo)B%_f$s2nx;o56TSrbn89&i;}R^?G@L{O_ErhhEx^rIHdf-Hv+^wMQRhj0KOoa9^Ot4O4)cdN zos@Wg^dJrX)Ek2FNsv8Urb&=g7dF*&m^PIi7 zrd&@$2IJnJxw%a9x);z*dN2;dEv-ID(e)%}4iB(G&2Yw_Tib-;%hTvqwe z(3c=%d``$e);(tH#2O~o>F!fBn%vyTw#~MW*MHJ+cS$9M=_U2S5pytJd6_VL;F4m& zhqjXwQ=gJwSmDf(o=dDDS<3Dbap&aK_UPIiHpFCB%Bp+oMe*DO06sD$Ob>5(Km>2iZgj##ns6L*#+-O#nP z#d{3Raz^nK%}D~0oAMp0Ike<^ucv4;dPz*CK3X(8W2l<(edtz>|0XgzXU13!87VP^ zfLFn7yZtDROwKnCi8WB;s&4Z_#pS9z&sEK$c$vVYM?Fs39$Bg>Gg{Af{lbSV-B*ua z@eu>`;qGeVDFyBN3-q56UcuBo*!~^iC*N2D(SIN`|67FrXOa0G;76)sHb?^SU1H;W zi4OH7mQnl=kcPzcTEzI|qWgaAji%!gR6JWUbS^OC--6$SXV7xb;f6IN#AGBQsqF3Y zv#xTF1-@?Yr{oEyRNKk{F9)|-Y*O1f=Mvd2C+-oWUm;9=J@g|aQq|Znd(nJt3RnG*{1OldNIG(}{iX}ZDsDrwJl**8j zoxowU7m$Fm{wrF)WjQl|$*DJpTp9!#4AfDb6O7hSUfQy6L0l2|xE1!^P~^AZZplyC zH&7hvxGTmR`0}Tpz7mFTNi10)(pW`vt+4haI0y9tdPXtyq2a6&C}KE#2RdTLCn~dh zi%~FIrMi2TyS9RPHz;vntT5GrC?6OQwiJMt+UV~WbDZiqcd4=k%OvBhmTZ210MSZ{ znp`6^@gs?z=-}x`TbIiF`LE}HLiX!Nc!@}*vE|`yppvvVF`~gs4_llIl7;q!cSY@M zV62;Ch&A1q3hVpXX8kF<1M^2~T3NW2;GQ+RXF;yy^>_(&MZHhrWA=eoF9r-&m~^|?Jh z^`jQFf!!^fmeWh=umz0sf^aK_mb>Xy?mU+4l7}Wd%ZBLE>MSYWJox9l5OsL<0JB%| z6a7Z#JoZEpp^+3zKpWGj!~7-&q%-+#?sz0ItB56PmqtkISs)AsTG+_@xOXu@ZwjSM612-}PK_NtmO*6>$9NhgsM zsPV%(r>CnQ0l@^)C3auafqS$kZaldY1Fxcj34<$!<{$A_!2}D7;b?bu$9RAroDliT zm&o(it(gkIoZM^M*1Ghx^c~*^E4e~-?37hOe>S70o|Rx=TQImq>)b+>{>wBTn~nP2 z0Nm@v^#SRhp(3cRhvUN5RHg_;K{1$Y(i`Y0h{AK?$rTSm2 zEHDice*A*V>Tp=*>I^V)pvFf~atq|TBysRI8Iw{+ z^=;)ArWE}3)Tz~UizeLdI5B>_31cAB(G`@%85*{lDE)qQ>-Oi&f+20OX1%EQdWReq ziOJhcQp3&!Zh%qAOXKKq$*Uswr33@g#Q%MLq+Z zRlP`>1b}Nd>oYi4kHfGaM@cHGKo9lWH)6?SIw<6GtVz+ounm}+{GRiKvX$jIrVAln zf(F#7_%Xj>D!4u#z{*C%k9tE!h0`_T#|%QXgki=*VaT@XYD#UVnrs)&s_#y0({VB{ zy{F^l-cq&e$5@k8O|F839*~G}U#^(Gg`YZ$OKLXP+OE1DiHnX5#IhjFly@Q2lrcNT z9bS(%pFi!5Rt370NXVWu_`SIRI?9o`QH1g0z=!@C=jiIK8u&4H|1DE3&yWvF2DaL% zAy$>`rklk#Km7m(;!&wpQ@%ppdeQ0li8hU=$W`zKWnIviAG@OIw4BtqF|3q}r?9DZ z@m}BJkgxBo9V>aiCh_G5ur zp?9LmBkVvJAt$8o2^$^ij%wADi@~)ooo7~VOp2-}MfkpF-0$=2&OrizEt7fC*K#=-2cl4BI z1!j{qm{)T_bufy=Ez6b1NTAMh-y3}(?z%R|Yxl#3^K#4^nZco?CaFx)W*}=fZ(d1wAl`V}88!Wbene6E6AlCv4 zc+?O9*I$5$DzS%c!tGxDQnhM53^>$O2=Myb^WX=a=ZfUH)EujiIB_}?HxsvG!Po1- zj3857{E{rzD6O1ax?YLv(^gVUFg;i~zTpWl~r8-D@Dr{^J zresvUV2war5JlMKDXdOIbA4wRdt?{oA9xVkbqBz4fJ}!Muw`j>^hI{+kaFax(86e- zbcC)rP&sjs!qK)uNa7$!qOHeOkAO;ykr+kvh;ij_-_kFmKN(8Qbywa$#6xYWj9A0+ z06F8d=7P6c96(Moa!2XB9S_-kb{I-DQ+nUM5B5aB`pxRMYTbl>Qf9ic2yWW&!>{KB zO$u-bA}W_c5yU!+t?j}2MB20o7?ViZ;QiSRmSlNJQ_dkf{6RtECyJfjpN+Em#xK2G zRSxjR7hMRY3?+u^YR&IOLjrz36d!@k{vvtvi#ELZ%L_{)3H*Fp`X@b78|mCMzV*H! z1#{vxrEqN2aFC6}*t}({%NY>$AtJfJ%fugdM5|IqLlD?RH;8JC@f@(HzKBks)i<$1 zoMU1&MioCJL3Y0AkO0$oxZIyk*fM)UWd}o7krXk|vaszXQ1DSd@GZ-@vCi${wgc|; z2u<##^|vvmt8$nC%{pJQ@+v>C1^FgT846$x~3#;`1uZkf%xR?lx>pKSzb zQOUIL=$Qfh_Re<OWr~5yd^#Yeh^tPkp>=Nn20cjM?S7yoe-* z=57OxmaOZCT|=-F*UUM&;&*BkdOs4ov94=$9?^`V7Mo&RGoDP&f3EGcSzx2%PmwsQ zW@~a(l09ME>R>qtNXMrr0+2wbQ2B%Uua=5A`hUyh}9cQx*( zO;<7LDFqzwX|d=vv+w6rusn^W7vgd)v3TcQL3HRXlK!+3a#euh{K3*j)$Bi)XMr7$&AKFMTu&YhIz z#6wM+QgH$fIC$>dG5_B&dYz8VVn56D8%yR0UJHzOJcc1bYUTiv@bPwzVWXyi;wtbVIEyaoE%! zi{$wvw6=PQ{`uJE@z!@;gZY-YdZx$LB)%Ex?opC~D^Qo*7kPC6@J3rjOnUNpr0!8dm+D?=p4#3`7%EU>GH4H1TTMe?AgNW6sE zLQu>_lAk`(Zwa!Ic}{gd?I=3!=WH)r`wDD;o<2gg#gOwH>|<=kCLftBXMz-l^%gRG zZPd9Gw(j2QDwqR=qQt^5kulR`I6ZbiKSDA#VS`{Gr zvPbgVyen0`fX1eDYchQgbi9f5WGZ9frV0#ppDTgfdN1&QP&heI6uFD8&dI{!xOjGS z-^uieHC*jXqAVC*<#iFB^w7ViuI_WA`2qntc1?1a=PZw?2x&KUkMr2i^F$=if?%a! z!%^?K|Kj@OVvYyr+gv;W&suhw-4f%^eN-WmM-rNjHw}kjk^f+%_8f0N-UrwCJ<=@b z>J0t%yp4p*Sy5@T_lE1O63U=BR0zXi`7+2SyB>2bP&lkGSU$}0XJhA|b(gTERJ87Q z-9-Zq@J)9C0C03Lr2D(>`s-y;a(8qxw*Jq;t4di)7E1trOY0U#(n_#!l-Cb1oF9<@ zAt)%x+p~DMy0D{S0dEw0*`%rJ7@52t@7At1N4mqA-yoKz17R9rm z(I$@|nqCRe5+bfEohV3=-tYD{Gt)39uVeCR3lrS>KF9!M0TdJ;YmO~#2ge+iDJapO zXaJk2w7ehQk7f|GAJY)Hb+;)%NwK`rTEtRl#!V|$ib-Zk^dWM8+_dj<@-0!JW3z}x zPJJP?M3E;0b`zPD5~NyO1Yx1JdhR+m3U6RDjA4IWMYL_^mFy@*Y7seW zZFGc{pV29q{8j>`ZuD+`$U(LA04_`@#Pl5vG3RH1q-eq`F!FBb+UR7y z`$_w2*t6#6PQ1YSK`DWnM4~EIbTsY2kLDb zvtI6*uE7q%y+gB2zRQRSGRY8OnKwi|dYex^8nT3sy|*zc;ptOUU^nNL99@n<<795G z!~$Bn6j<0F72IbV>hWl8KfBt=JkOHT6pA-z4g&F3al-&f?h$>H@2GsVU9C23CCRw8 zhe27j;+cv*;JyI=`Cnof67_a||Bv|JWKFz({Fh4Ky}&o5Bw%RxjV=8rr&%RN2CAPQ zCip3Mn}E!W4nM$!k7EZ>Ff$FV!-6eIOQG0w}6_s9EXN5lD0=xM9-E%#+Cji4&fSK4z0-y@klz-Na z5Y7{s6TswhlnP7vM|XhE2+&2+y!{}0GWR$$K!@2FtmboZE;w_DFmHA~1=du!&+3_P zYwA<-l-!_&MXR?EEosY^*<-S==A-dk55PY^+#hCAR`+`b^7OqykMMu}a7T9=Ly7Ml zRSqTwhQ|Ni4;ArK(sh(t_S(<)@<)J3_*I)*5F;duCl&6ai~P+Bb`sZ|BXhARq0yv+ z(IUh%-8L=6+xgzrK8kF!GW_$854+h{vMcaYTJR2>h0bbV|vY0sAv2Hh{Ni0Ya3F3bJ$!*;G-7G&=lzzAUzORCuhJ-w_A0 z850VYx^aMMECaA=ACLgD@h{N%Y;;WgcGL2D<#iw?%P}UB;Bx8ike8D)5PC@>@x+js zm1|AV=ysH(B|@}Lm6^>JXUilL3R#@1&TcdptM-jKg9Va)ief?CBy!suso>%6kHa@$ z!?fl_EE)q8Zz(Ev$3&qr6fr}7l3sk2C-OYTQUemJ-^;EuyON}YRm^q$I&&Zw6=3TQ zbnK$^GNn%V0l{OLqs#Vsc!Y&?27S+rOXczQXj@wcg%KTclt-zIk;;&#!tuMnw-NVY zyPRSsvrCZ2j5bYLcsC&PLp21RPQRM(IB3&MK-sZ;*vk=ONKNIO-6s~8+CT@#BU&#X zASN@g!b16>hY#XLeE#&pF6b5&gd?>$hHH|PpzN`QA8%}vj^UbWG!4GO#4oo69_`jF z&-Nj8#Wq$pHjqU(&(FKr4dOzX4Wi+hAG=-3D4Nh@3x`D?UUjT$oHNui^G?U;Mata8Mc%~=7dV*WU`Fj)_K{DR{UEs9JC{;(R)*mQn}$C^#OZ_dohYt{>PXPR~TYm zs*l&{9XQZRM#WAvBPhjdPaiila3trS(Fx=`CD;9(k~qHAvjqQJbbfD-Gch-H{~#W~6_J0CG0 zSMEK)?(5CLbeUBue@@R|&kP8`93T(WcS(n0xv>#O!2(f_CIAfYQQ2QgWg!S&nPAb6 z1*Zv~Q}scWHHnzau0=VIN)g@3fJN~xy=-&uWZjSf_yj5MU~vj&HH*)~DECks@a+_3 z(IsQr9RPlmniF@;R{=+cNSpX!7iy2PFW!ZsC`--<(Z4OLS!0!~YgGvHrZXN{EPgcC zY(!MSURV3>zu#(NCLo$KC1A@qgQ#gl{gGz8VvNh)6&z07GSW?UfNrTYJJqy$?JfGq znlvOnv0Ul1Bw@dSA(M8!QM`SGQ@jdxpHxTlP!+8{1Vf~*yiK$-W!*3?4J%3vDgHg1 zk4Tqj@RfJ#vKTzC%*4PJIGh@xMZz2@LJ}fSzwxf~Hs=Wp$KdGU6vZia@h0S&HT88q zIHf4_!qHaz*sh{e@3^&W%J?Vi>S~Inl}-`~PtEj^UNA zS+{V-wr$(CZQDl0Mg=$s_wT>@3Y_22j7omttutU+-KOME7l%ye}1e2FK z^8!Q-$(AGbnMgAr`ZBbi#As!=Qqw~DN4VM06$xfJPtkicXeO;g*7FxJsDR0nfHJrz zw>OM!<2*n`2HGJ5oi0a)qDj%Zs1VwREQOi9IeKQ8R1%-ko21fEETusYhtab z3zFJ+7+~Nnnip{oayX(za-C6$8h?X!9Yo_Upqn~JB=J#-V4Z)%BFYf9l8_vesb9QG z1!t&DIf#a9kFc^gZ#G8A=#)){9H!U>=ccqHYthv>qiLfPh0?8Om#iy0c<$a;kwbC( zWjseRQ#+UisornwL9S2W`iNjJzI*FgTxdsTcw-tpxtSVl-%(D z0wdz+$|K+;PohXV<;+Z~>iJ8q@x-H}I5C~+)eH!^?&JS7Tc%6Cp(@zzaRB z+5bmWvKt^c2RkPNjrrJ31zn_`fNi;9m@QVXc@da0!fq2mDmhW#a*}$PpSxtRmQOjH zVl3fgOv1m0{Qi9Ol`{+L^vS=MdGaUhNV{uVPHv9v_TzVejqx-V9&42f_P(Mp|CS~x zrAR2YWQ#tnvnf4;&b6R zMprbaBDFLbq_7Nx0%Bj(#)7CX1v^hNVga>VN}#Y@xicXv^+~}xG6P99?A{C4e&8tk z#l^&IgeHr~969dQqtd5B$0&9h5GdAo*i_|FgR18(%ps9xPS4U-A1CBX#+?a?4`Jej zTV$f#VJeabcfCNyv#AUxA?}GF48rRXMIX=C%Z3*=dMMXr4*w&aAWyGdY6QdOatLTu ziz&K&?Ai=t9AT~IV#g7=-i*h&&()01?Xx6k7kSP1i82M%XCLof3Al5?fQAhP5^o%& zgXXD#*i(#69b?4eF z2a|pl^+QT(YV2s>6t@9MzT0t_MHpTR3d0Z!$Nk6&lQcyb z_yu7oH4ynZ1jmieH2TQs({9b?n`%?FiYahQEX5-yZrS=;sw+xFrAv@g4?*qBptXXN zBn4S*H!mLDzm$4cDLZ0DWP29dmVYFY{-(^GILOvPLv{k21qPOvp6Q_Dp6c0ssW}as z{C=FM4ly$w-O;N%T=u>JPYWm-Er7aXhiT{;S?z2RS(|eExo?zjK#5%$LX5#a*HQGV z0Rttq&yFHXNeT7@c0Yy6y?3!FAwp4P#$C)$WMPkGXDDgNDJ$~Xk;opBYCvwbCoL^F z+ctKS*+0>adlBn-idjug6uQ0jbS}X_mVkLi%viEwOb*(wcvG-D+jnwKVC-gTGWaw6 zZBS>Uth4;2UQdSlp(@p4N$J#C?9&YKPvqVb%J|BG9*ph!G?peV;wkWG_{g z+0f6M_g@XCSi(^3q=*YU>#-t_rMt6VYOq`BINX1?QxbJ^d=Kn-yXaTPOT?a3Jbhb_ z$bnB@_@T3K=J$SaWK)V$;(D-NzLK~)zZA~r$KvvMXj6ELG=w&buR5ec^wfV=k3npfEjut3+y*w#$V(bmDpR0U96{4;|4 z#_IvL4WNeXz-)=&qnWZRBAK5dfGO3v0$F*RM;*l;g3}IGO+lm{qRZNvjim4UN?q-B zj*pM8j#^v`J81HGe51alon_UyPH{JsvH2B(+(cOnBK9H@K{>1y^2rJS| zghp&MFUVNoTIfc7e_eX;6?+?|&}JX?O;SOm96xGgxFVuJDs!hYStVim^$~6-DjBv(;`3#&kdrYAeBXd-uD?N^ll%~#xH#G=nOK$Y1{WN z0?kO;N}`vXaoB!P72@2?AO9 z>bL=r0(@yTY0_ZgTi`_cYO>M#m2=_XTV{CPa0WvTIh=(1KK!l(cQkYykqJjF5=Kt`o}&=xOpzE-(z`Y)?V)QozZ>U`!qcW|n`|@@l#v zByF-^ji@u`@6IddeXeJZp;lVz*j$2NJ~^qbXUCQk3O|@zTI|@kfCF zP%%MFCgz|dP^k)`UKnntE8R0I{_TC|lbSAqr$y5SfKu_g+Pp6mUYrJdCF8@ z9elM=Y(nit-Y*heoZ1hp?g_J)U|WM65W*9Srg6^{HiQO*}n_aec+$DG6>NSu-}f!gP;A zn*6@x;DGSYHnT=0l2@V*kPjSz!(C`Odr|m#3r_(f2XeM&EtnTWg)tO1L2famIt1QUHC=qO(r&Ao-$yT(vEwgRJ)I8 zr)f{p^&|K3>tP??Ez#D6=Ef2WM4+8qGSQn~icxiHh zyQ~}P)LVn!jN?1!=6&3!TQ{j%qv?{TY*O_yM>It7B{B7Cn>&x<&mq07rk@uBKwH@J zGLD1v8YCFepVNOD6G3?C589We)ge$A(AfE)wI-7`n(DVk%GJi6TB4NppQw$M68YY< z8v+;PDwUxK1WMNR+L^>UaVb~(1sd;Ae2MZsvC5aI1bw@hu69Im5^~;T@e<8-i#&Ii z!Rx|Zs?c1Z`S9GesEi53wA3-Bii^8go=n>K-r%ssFZ|VFp}P~7-@7UZ8F8Sjmp3Qc zAIsqlZgrSU&tyS-%XvR=fcHG?3)Q5{)^y{i*YjWU*FVHKUPzjBVn9ve3J7NZ8f^aC zyZ;Y=EcLfP{$)~EYx<8s-Jiuuu#Zc?f$v{3!N)c(i3K-6-CEqGv9&f83#66ZHi!AO z5PXYzgufS2%6zPkX_v~?KlI0)5OLj2a|~krt`+#GVr4*QG_VPJ`El3ycy37?(zoWQ zbu#Zwt9av~b9fXZJ~IoQQb?~A<*1j^%6n|iiqs%Y&dfj~a$D#*qWU7wAAKZF-O}ex zmdY!{*3r|;CGhIv2Y!zQfP%{_c`dAco3Xsc+Q9WORm-=&=}VWuj-kqi=3P$2`vChe zYg(dz?VrZh9Os~^BMywRVnim^CS(9Rl7t}_4B|Qfo6FP|p|Zk}EH1#ROOH{|)u9nA z)V{^k*@)>pOHm{B*i?QrpWOEJ4%xT>Dfzx96?#Jgv^%YLO@?kdM&ip7QfK;&Lf-6G z8X5-{l+%`8QK2U$Y%U$!$m>HJ>5>-w%Bn!uSLm+x4&2UuJE@Qo<&cux!A0@i>?B~Q zZG&)y)8E`NO6L%r8-pD~zwB`xtmm`^ydrzuZO;R-tOv(XxEtdbDILWiE#*{0$t?Od z2iPJgT6^fQCW(JWN;Bbz2VHNN#kHCg#9>wKTLbpb;$ea`{yYiu1N zFsii{Rc1YO#O!9F-D&FpC3)cDKqHAwuQ${JOL4E!p-b7v5ZG^!J&9Ct8&vb=_t|`J zpbbi#GWBf1WhxDqN6F?#881ADdKDw-oUyPxcK-!JnKYxv=R8_epZkFx$RD_e4^uSj z1P$+F-+jG6el~`Id;2AYdz<3yw~TR<%6)LGoe@1_jB^j_(&Jrs45gK-3bNu$CQsox zjKdL{UL=gcbPg;Fzmg8l^N9tH61EVyx5_!h`PC3LJj|l=HuQmG>+HveCJ?GHz^YUr z05?zC>uw<&+x2Z3UpJSnY7-dR7nVNB^r8|^K5$%N!t@KArQzGB=buls@ZELPeg2fu z>P5KSo=7wjsCDjKy@0GTIh^bmu+;Fbj2cZpUu2~cPiAV)m|8t#xPWZ^PUrGlhJaR{ zw(Mjh{1_<5$=yUSMc0cFq)cP3tZ6w0*5S^cOvX*fy}eX?J!eJIoImzaDEWeX`*q^kD_!5S(>h;_3e)mBXPCI$qIXSk@()zk)kN0&?{rW0c zn|5Z(`i|N0s;XFFaWT*?4O<~7H8SOyRk&BF?_Mx|3o^hT@|lXDPVmeWlKLxeKi_<1KRy)wCf7U7C(oz0 zF1hsQxjub0ZI-1TwtrH{-D41vE}P=eb=HKJH3oUAr6^-vHV(j+M~PS)3jiq{(E0FF z3XE6m&#U{qk~)O7tIZH2Rh_*hE@1Igm(OnyvY`+yyI#7+yW+(^j#@kRWmUYlSTHbL z#&OS+%Uzst8)q-qZ|$K+hSauQejLWk`u`;ZrU7KYxzT0*Jb(%Tv`}>%C(9wwe>jAZZjY~*aFU~CPbkvae4`+ujD{adozkSR~?i^!I1 zqKjl!6H7|95UwqTn9|&S_^=u=UxE{0Np*wXMf@W)52=|}0p;+x{RGU9f*FC{JCV=V zCxPT6W2qw?yF6ZgUQ2-z6)n^_Ecy`nouDtnjiHCO%^mw>9H&ijXqgjEYFL{J$6RWa z&YrpY+ZJ_BU9Wdj(W)*`tI{=GAjvs;vvDr~nQr9!2=i+fq+`{=@58w#q5QnhZXSY; zUyTHzwBxGsv{7W7`q2_3o7+*v?Z$9gUt%zJb0|!Y?p@2f>aiE&jjjlj>$nc+A*P~4cC8`kb+GHBU-csmy!GWlqTtEzS{$|sGE;o$8$HjZ9 z?%dzVy0DmiF(=2|Ts4noBxe#kRz?>Tqr3;dLOsDpf(V;=|C>!4z+;h}VcVn3!ITm_kp{X_3L+4sod}UT z48Up~&p<~z?|7C!dhXbip$%=^Q?g=)k*YIWcw0gr((@!F(PfSA{ zJiTCp4f04g$Sw)8chD9DJdmX=8x)9)!Jt6bDdJQMO)NmOGm6Z;y|X$N&SBw#l5AO( zx#`Dy)Ak4XG>;Y(I-4gI)O272Wd(P=CwN31D@pKGuR`R$$ZnE(*XEEGw9;h^UC+}-Tam}D5k3>aL;Q-)icDy zo((aW-QqH7MHi($MICIa?`$mEY(r6N8%Sh36b<6e)Ue$&v~I-b&uqa-=49)+VMHc{DeauD(`Dxy%(Oy5XG&URI}8 zfA0^aL$FLqG?hbJ5ra{n|E>FDnQ$YXpWT3)#(@3X@XNCHx?hb2K)Vjy5 z8JDSnts$amC2(!oY`|}Q$yT(M(2ybBsJumbZ^q}1_HQSUs{U2WzGs0oD~hE&9Ph39 z82skZAn{c=0e;hM%?-I<^mKgS9>>G6Y#Bhx+kR2 zth4-l9b&QxW-+$6rFu`dehRKR70P_Xy56U3VmXOaxiukmzO3NUoy>EwQLGx##$&+m zJ`<3_&=;Y<6b*^Bt*dxSFCd5imiN(EPy^Cm)v{Bk`6Pii(C6OEn)JKi+Z zZlzMmn&#m$d;oz;7Qm|i*SgLBlZX66{`eQ|qxj!^TTD|D zpl=PandmXu;NpuzVbN|%s6celb}h)|2}wFcW8)ns@m?Vb#AGrJhYEusM2Ree9KYfR zUtV7os3G<8wa7__g8XWKuM*T#eiK9%5AlaV<%E(>G)mZvPm}aVt!Krp=aeeWc(SmW zO0;>L%#|9sfiB6ilZ4zPkf)ev-znq-kKg?wkMK1bjF+~DO0r{;v^1a{#NpjUMgj?LsQjWO`4@?rk(FrMI#5>f_U$wa2UunV ztdSk+uq?7ZGN8a3#E@Jy6D1C}P3?Q-BAXH7K2*6OOyW7p+==_a#*u%fr9U7!`{&>r>n#?Snba7wFT+|18RP8EjapJv^Bkud@)cnWsvM z$lG^}qbhw|QGZ$yIV69{Fg6MTkZl?*&ZRsyM=VCRbHiR)Bk-T)vc{&A*ha*Dxs)rp zS~IXC=S0|ASB~@hJcN&Q;G2eL!JmjV1Ii)}KIjph3T?IyJqe4mrpjx&er>~3C2;kj zH3$ENE*Xjd&-LsEeQb@ve7z#*f`cM!U+O5w+LTMi`pzL2%X!csc-)-vA@ zk~c}AF$qN4pFo^qWOpsJ-sB8=zz=GT@fcVYIWLJ$zCV#TDJR*zoq6?1HkC#{+oXwB zMSbdb9s_54@HJNKh2*P8DY6q(o;&eEY*&8nV##j;^emgOI(dG){EBlG*r0Wg(_ZJ8 zO1+F0I8NUf2v%p}8&W=B8CMGKxCeoMdn`GUA6%?IHzcp%wNKO#xL>!<}t&B zCbO&kboOb4ST@ktwwSH=HAIa;?VPmI2-g>)tKs2xVx}DBC)eDZi>SFgUC-NJ(#}7H zen-9m0T!UpPr&%{h4=q5*ZCiB^50m((}2b9fXTQXoNFH2co?ucS?}t6)R>DlVC11_ z+bi08tyg{+rsLw z`-t&g+-sO`?l-<7%)K{OPYje5skG=9FNNl~y2G@slb4gqB!yGeht#x^+iD{JW!MNx z#N)Vm%S(da86rGk7ghj_lFzmll2+$6DMlX$wV|rM^FXrq(1PY=wWNm@^3HP#^J~YM zonL;OInPwKt=Mn9$j^W3g)Y2{jXB%RBe5>ltlx^=N*X-E+H7aKOalpT2x%m{3#>)? ze7vYy+`yXdna{{k;Vj{d#^+EYc-MG@B`&<34Nc1-2)040a4Z;A&Q_8@@K(CW(9oE= zf(rjtW!{bVN{}y@hZ9q%K2Ug|v92onbp%uY+WXN?5trAlL}#h=r@z;Ii)x%IpJDS7 zm6!1W&j7A(v)6Ld<#j-jO~xW)Udx(EB>4gLDU*-fQmnX45^Ha_m^8qRvF2v+S&7psv%9i!X8Mg^U1q5!^L3}m4 zi`J5~o<(cun@FoIp1~FB7><7Fqlb)@YRyLq@^|fe#W3g&=nV}@bE2up3fgu>$^M9~ z)6{@mZ{=OX(+Qz4S5W8KhxWoZ)}y98+!$x}Oy z_4&{ShfzYGldT}Hzoh4GC4(QcS(xzxOKcqZfQ}96-CWUda(eZ9mV#Pcb$a!jc-aiu zS~xg1`5;$qV{!c4V}LAeL%=!XH4k|@zRf4Xv4H5L}Of#FVC5g7p|NlNgmrZ zrJPIy)?!oE+NsN*LznMx@m`{fLj;U-`|W1>4wcdoO!}8BR%*By5)5d{c1eUkaWLGS zd9mY1fIZ)=?;#@}l|f>gk99%u`1{M1-DFP3`iG2yM#(|uMJ2Xi4z*gzhZvwnPs(N@ z*pQs03}U;tN-~w$G*TdOxcJ%dW@cXFXs&DimwGv!HhSd{(;B)*slY9C0^3bW6sb+L`)~VE#ucLi7JZ$_zFSzuew=|owr_2h7Lu;F_SWiMWS;$teyri=}0;^>}Bzc zDB>26xK+w5X(BCWv9WkW5y-`?c3+C~sM5Zmf?MeMqI|oxCq?&Jn5^4Bt0aKwHxz%y zAU&0OhqLzh65bKlMRx>Os%{de8|?8(%w@x=dsSJ4ZBTSf`wG+HwA~hmSf(6CqZ?Lc+p!-nLGCoy!5!fN55@2vLxvTBaGV!3T6xV?JfT5?Dc&V*3gC}A(I zft=bp0=G5sQV&X(2L0YU_k-bSH28Rj-F>c^HMhzQ9Q7PcIL;6AXc|a<6ZAq=xwO01 zK&bb7f@9}1p}5N}y1m)6#Sa1J7W-v;dPzu?is&;lhN)$8p`}kuxg~p>8X3qK2AjKI zCQgtV*Io-Pj!3wJsl*86)s7v`vAhqbPc}yf2Y(y6Tkkej`9u9?1gwtd`oFl^|FJ{f z{qJS+=2k$V@Q4yr)~#$&=<*d-raz@K8e?$k+%fh^hM zdFM^?{_?=1l;qH-kUVl}%30>Hm2LKokdf)t{U>CxW1+HiQ(XE74ejBIc4@hnyrx1^=atj zhD;FID{wpt$X&tk9euchaJUkk-^?-Q=mu0QXIRBsHyKk(ue#w=_lH*+3dT^(2a^0V ztW^UcN%yRyk@by&ynv~_FJ^Jj$T1(V>1-$Yl|nsR1)S(q#vZ#%JgI2i=6{ui>SOQe zM)Z;(xAz3}>VWi+b1Nx!{pPIjo1SpPBV@xfn?%Io4(E>VTG5}>G49Yk>;QU~T^5Po zcb6q#xMPv%X5_=hf+^jj(wQKB2Qh`^89;XNGC8B)D$~KXX(Tw-)?ouX%aR?K7;h{| zJd)En(9diNVZc7kwrlM~?YcT)aH7|4Xvx4AvC9l^;SPgQ`!FH$7+CwH9r}uBp4UY@ zIet@(r&uf98B!*`P^bXi> z^^*-6H{?_YXdWZDVxzP&rOt^nk<8&J^xOFsF(6|)W= ze*~}rS`dJb|3=7EmQxrIMC?wy7m(#d3#ul|`67ji!Z4JEy6^b|-UY?2bsZrI(Jvjs zKf%9$s|7L{+ZEyR>Y|IoVS8ruN2gL!E#WqGwC$m5#etpD6lEcIT207!$u~6dA0CLu zqUi4XY+=oDrXdQ-ww9sH9wUc2wzS*ljsuElq{E+$YilIdwT*Y&h8mcmjOw9G#I1=Y zmN_J4z$zU@R7KwyPA4P_%6IEfk&$Jl@Fp?D)($CU8{Ed&l%OjT7QrVGE8MW>p>=3p zg|>*YHhOxR#aT zE9WOd0|0RBQ#>XH3Ez;c2?$sSwh%R^QsVM(>;_#VchM`+cI@l#6Ex?PC6+ z1avCq`YrF(_MBSfE!V7lXUq{-YYW&@uWyH{s09Q*a%kQLmkP=RYwk=OEt*^z=+1;0 z;v$-IyL|Vj8_#uglZb~4ESN~SEm$s|GsK}&RMp~=NS%AF@pj4x-|dcoJEXGEX@FfFDExE6Eh7bBS*)7j?rrF zD4esS`bq5jUwc@)Hd_8Tz?9U3slo;kZO?2Axd&@g^^+-Jy31 zUcOZCJM-kr&p&EU%GW|3sYwkf9~)bsyqM#(10ms=CG`r82{~JikiK)|KyKu536|4P z${)K)g)ktw=j~mePyCiHP}okmJC&ns0WxSohl=3>=l;!s4y%} z0%cT!VN?P%mr?t7jtFk9w3M>u74XoC^DctWAR_2X{FF`N^cTCnH7PqN2N^WP@-pE} z;Ihed0-Qz`hUEB&=Tgsz+l4zJ8IS&?u-WlqS&4+k0n=MX4{U2=7vj{nmmkTz^@YxDZ7?yRm)v`-tLR*|5vX6iVY398y5k$h6;$TXj~& zz2?|LQjpOIRdiLrN|r&Eh?764ENC3?pfV(-vYc!Indvy#XR$JBy_u^aOHO%^h3yu4tVhWjhyFfI(0b zdT?s!Tb5$$PFJigqOEU}9;MWFv!vzso49xuE%rNpEtO7Ap7Tq|YUDD+4O&0cQk=qa zc~13x*DGs+0Ukg0*CITBv#E3Tz#c=~@4dO~w5l~dt2L3kdGlCpton{oB24o=7le)x zZr*ZY@R??*O!&MTdrPF@7`{X~g?g8&eEzePnvJ-+qbMyDw+aP~kWcEKM!6&1x(n;d z+1KB(yr7w92tGAG%K%FGs>9TO&9w|>h^@4yg)eyen%7{Bj{M1xHpcwt-F(kiExY3z zDZN!0tKaUbc5WSFO*tFbe%A1zLz__oOau=TxVvI1Mkn}DzlDwF;lc`x;jBYx@(nae z(>-y%^|%+Lvf;B|LyE0q&uSR0l=So#%@m3~-!jMsib(+9y+ng*qISc7+oEQ#XWv`; zdM|0GItLL48}H{ySnSRRwk+sU&U66*gujcYp)-+sZ-_L64{GKFCalpv6r2gzh8+2o z?ap+J3{xD0xDe0HT{o$VeG6{>)Z|(1SCgBUfqkwOFT|D^`VANPt)$N5hvz}ne9t9y zT1PPz7p7B|wy4oqNP^e*O^_6vHDL-?)$A_TFD*D+cDj$-p^RIi7f6v?IWvu?a;RJ#^5Y4Hf~v;*?T(1F|;+W%5P(^Y5?_ z_`;+lqv%j~y0t?hE|>h<4?bdJBLiippJw#RisdEkOvvocjXAc(<^$XzUJ=DC_?A2# zT1uh2%Y;%i1yyn}3*B*ZziCBF!hl<6Pp3q&7Ty zgb0JNk5kEIdhFUtMCGoVegD!Yi}O+{P`}*}RKc^AA+`u*+2Q@ORIgK!S0}NOr+EGG z@I>pCUDxl5|7FPK{lqTEe^;kwP>edlgQ&$bf6vpSIiJ~|q9;T{Te8C(L;1pE%Jlis z*z@_wUA2C;Qb4n4*@f6*hd9NCo_W*xp0-e@#0k6pd!qsEZ0!Jo@pNIK#ZGJvQkzFf z`jn+~K@zo?qnO1iS|ShprhXzo1R_D=<0(Z~3az$FvzV9Ic(f?k)s#$B+7ASHeCr3L zv9w+!18rx|q*fg(1m$UqK612x1>7{cXBX*W$dsgE3R2CO6Oq;w^R!ikImPt8x9Vn` z;pBE;60jXgo){a=FLBc>l;d|{Qz!$Bgo1U+r5+sq>B&^mjk*(64h-q#9*$ za$S0=JVaiBpU45^hj@fxG}XWoF?CHpAdr+aQTKn{e>cOV0 z>AUizGR$?l*!@f3o8EjVXmjlpIgI8vN}2_itYna)M|-#PCp>_-C&^4WRP)u5p_%-B zsKA#FaF=6VGxnxzGec~r@g8*9Q<)M`A`-`1pa7azZ~3GY%y7mARl%BRQ>i1>Z#tL5 zR~5TfurLoJzOH8B2a$<2kBFsXY`m}90h8dP46&WpETf^_hjL^NV9CRzZ!WeI%EPca z-o&>@(e-bPDn&@bwt6>FU=UJ_91)lMm|$3@5sn@4ydJaGG}~jWWpL&p3{bOKgYctC zpoYR}P+-0^grOCj@d}Z^l7KOP!xXGDUKvsUtr+P9+C4Q-M$?ei6a|Gt>q%;=Rg;Aw zKJf6sd{jSX#b$qh!*%Rt-EE~}|5V*Zn;tU`?go6e+CCPNo&KVg`(o_zZVhlw{Ad%% zUq2f@BG9995C$M?txgs}mCS%g(eb zg@tvY7$LvwU!}!B;Xq)c59WpGt#AdCG2dm2`xA>cIlK| zXN)8wkc-oH2|@;`#DS{6Q(}UYgR{|S5vOlp`o}mnpD}bc*}yVAa9J&Q;$=O4#SCGx zy*gOSb=nH<=1>6S@igP5rDFbW$v;23#~D!&T?x+v%}j)liR66lIXCX|3M|f7{jMJR zCoB3eE+<8h%`T36@HzD8A_>G)mhGh9RZ^Iz@hk1jF@LE%V0R`^!xYCs@v%XU1PkB7 zWa@oysMup`m3P}Go0Fa@Ag$tMqgA&%yyS#V7lsa)Y6l7oR<<4bPNa61 zp&^`}G`C8seVJfnUVf70YcsY4QK2a>GmQS7G~Spe&G4IaO=S1W!9rXERnevrUA0YP zf0@A%r|MxIW^;)+%i7>tr=)E!mNYHYp({Jvl92o0NeeBT{IFdLH1?105QVGV1W57&FL4XXP zc=vdD6VhT;BFtPjx?#$Zewr85&0ud-I5MJI;9B?a>Y~2Ym8`aP=`z)JYB3376ovJ3 z2hr(d!ez+7Fz)=hp$#-+D}kqQB){HVlrEjtG%AGr^fXF}6_1g! zQSe#e$kf%4(saXJuxxPl=BJ~sBe`JVUS83$n8hSfWl6UJ{ZWe=LC#T3r_ktKG7~7$ zk#kt5M_EN?-xa*8&{3(V!3=MgESAn%!A@ zIXIq_S45w&9RpqkNnd#*?%J2%st|JQt3#D-P-?C;I@}X>_6CwI;%e^`%8=h;_Q`gL zs_^cv#LB#7X!Rv*S*mP5`%5JtsiXvC8v8fBXwV_bIU;=!-slJRbpkbV!JD64#U za5yqoIg`rzx(n20Rp`||S{tCCBL3J`CyRz$$hv%(5)T&wo*?`x>KQRpt6agZ7o@JS z`nj>tbTwczPSi5gufX7chHdSL^0Lk&=89awG4~}p)1J91?Dt*L7-7WOgy>rPK&_cW zRJVVy-Fi&%P!bT*P8r(wQ5YYadbDR0aKdlG>ANIxkdQ*pQcW*!11vNSkYkQPh;sx4 zl&abNJ!>hno$IYKUyPLBX5DU7tqK>pCd7IQDl4Y7R~n^Pj*GZQLZ7;EE&?cSQXYb6pspE`=0q`wwPJ?y zJYBVSzP282Ue8FIVT`A^_R#)QM&iY`PR0)iB69Xsxsp_e!b7qlM+&NU6#27a@x%C7 zypRK0g6pRw`j3Uh-V-2mx2oy_x~QvXpDKfBo>6M@>#iQ+qMT4z8AEOsA{D>ktv}KW zrud&NWqIlv@oB)MI;%)~cR>_#lR7kb@qgp(dtszQtTjM->)8|j_-U~S>IjNF1Oun zsXyJ)RaP%04GK*H;mm1$ExTovLJ{I&j9`8i5Y^&aTGw|nwQ`b1*#`~*qqCIJl!+$t z+c&NRH&KLklBjr){}SQBuj+3b{~q9~A{SxOXgHOedq@Lm{NA3eE<=?Q|CFIUJ5yrY zSs;XP(#5#f^RCW>a!8^Kk)cO8!5~2%MRr54Tszz0W}5RL@hNv~S{Ue^MROD{aT1d^ z?L+q4eYt2@MLsxaI(&gs@!Z#Oye-sixb5!UBCEZkSgrEoJ)#q69yC^Xnr)Qfe?|*Z zptC~{0LLB&Kn&>qPkHU%fyHbU85EJcL0ODJ=&!w7BLVGm^pyjpoN z6hAtKsYvxpO)kuSb1~aTOUEACMnP9E$>O*@cVL@&BbJYwkhHZy4}L5@$0iWU64&km zWKvld2sP+XOz&I}%h+=Ui=lS4Cm!vCF3qY9H;x;d5t*{*RXSyoFQL!3*QrlIH^+5$ zPQ;mRT8AOBOMk*x0e({`|1(h3%J z6uHS$eSwcf6V|xcv<)BMz8Z3AC8c6#OI8(@^3Z3%Dkz;#MlZTN0kXu$uiuq)X${lNo_K$55Rfelk))f8|L7v1#WGvHU2ZK`h5iI; zX8117@LujCKJUPu0oSjriWPb7kt!%-41=4fdxJUNOLTfWJ)+YH#O~FNuS=a+62-2e zX5DcJ-9!@1rhp8I)TRt>5(d|P^MGs=O%yD9;#bfp;XevBaL+SB5^V3V25%_J$$#az zas;86vu?e(8&wL3AI8NJwd(9T$@^s@4&Ch}_}&XaBc9g~Y~#%YE>=&_=3l#b@tvh%YN&cp1-`udxT1QSoLd*@C_a_!|`cB20&>K3fv#8Uy*)BtdaIsU`D|KF_X z?BMETVkRZ}&k@Ie2u}ZFxhCo+V7ca|y`-q5kxU46o)58F6R~a|D}8ek=gJ*Sf$%@q zYu1_Y(6J_K?ur?&vmB&9Z38AotJ^$?*0qUfB!n1FptPCEPQS&=P?dHars-m+WgBP| zS1AQ0+KLJ3t2s!8qRnBje?Ln;hE0G{J>os-hTto}z!(#p`$kzbhGD9tSR5w8$E>zo zXLx#;gO(83fGfc!voafE&gB$pn6=GxKgBhymd1W$@7Zq~iW68zzDqc-4!Cu+hMGZZ ziFZS3VI7nO^C?YC!!Hmv3BV|r-U(DsunW8^#?yh_3eHYObhxs+>DX(n#!?mdtY+XG z#Y3(qKuB1vyN6I7Som;ZqbAIl zqB=Bq4({N$z)IgSRuyvZY7^|D;bnenNMwU7g>sVvzzKgI+&3$iBX!GtA8R9BJo}z@ zyNQ^}LD!|}17ZsYe9mHM6=y~rw>>>#nRz_{uEn55#Kzz2@)jQ0KfGD^xqr>ZUA4aa zMM?aRtzAoRzV`uatqSn-{|OZqcX4C@{QNgBuR3W>!i3m`^9E4yC&hThr+~o`NdbcZ zKFA89Z5;*fR+KpaP4qY5<2}b3eT3)12wYmOsGyf4%(p4yA1};^mNS_ppFq{Q)|%~91kx=4IUQ{CoNKd5Ah)-25AWiS{meD?so$&@d>76dF#<@d*J zP0_}anb&8}aD*pOPkMOeu%F|IZK1ws1w{e`lxr~MRhaJ8HSR+vHFl~%~ z??*yaOp}UItAo6tBk`5S3AT&7grtPr?}hf=HpVP6>R7(D3(Z$HMMbrepsy}CYepFv z>w)a}5psg0i4!Yj{n(8^Cvy&#js0ro!w0bWF8cEki7eEq z*1F5})Y zL&ss>#qeOQz`p}Ni0-&-Wu*W?9pGg*0$hgw6!8D+BK$94tY`p?B?uep-tXR~l*6E+|a)0TyeX``%Sk$bP4WQrVjPdus6EWNLrB{ZKkrX-=0c56e>dsps{bZVH`d zPg*H$cm159(X=F+cKQ}{dS>Vn73_eZNNkMIo!15S_`5%k{!a`G@a=C0@NWof3uoPFll^u}?E^xy zT$=MI6B$prZy2rJICob?0$DPvv@%(_GGci8UWSMxkYZuDhCvI$tJEvZk7yH*K<6EfO{j0(--v=spk^x+Q{>xKb?1%M^#VKTuSdGtXk&|e6_Xpyg$StW1999EcDk?pcd$40MIW9BSpLE|c9ZkO@F=u^ z^}yHXk^hghcMKA(S4O&z|ZjzD1yw21^ZHjs7uOM6Jz>kl?RGs3A}iiGud=#SB!9p zkFXcZmhK$uDeyXt0t3NNDE$fY>oP5edpAfU!p)=$Y^^uH7Y3h$gUGk(xv@`gf;&dd zbsQ!2ICN-439>^Z3N**up>5n0a54OV1r>`r{hmVA*U=@Ub9=Y1I1jdWRYH*Vt29T;G`gAqi;{s>m+lq-Kxk7y8 zDcA4B!c2>f^nP&s03lP)Ov-q__Qpd?2$942a5cP}GHR3F=Vzcyp~5Uh$gKu=PpY-Q zWFH=V2uqSMQ8N_`OsV%fwA**Bc_=ZDGRfl2W{ zw8;eeNI?`)u+`&-i9ktfL&3pfoS|8{Y0okJ6D%>l3PffxR_iepf)Z6;YSe5B)g0gSX{com{m$@s(V(k_d)azPljXs&$S?4_P} zux1-woVbvg4C=2Qw#y#RuB%IY;qW?zIlgTC6sklRfA#Vkf*m*}yNIBX@wB#VrHkC- z5yg)qL7V^+084qFnJq|kI#1xe?RD6mcwQdLeF&Nsk{M8V1QVfLNuy#FGx*%a{VxkW z7%SPJNP^~+=!LFpCn`7`Qj}P3Th*2Z5J3%^i$XedX>Um$zXPfnm6S-R%ml`=Yq46h zF7a}Q;%{CERv3hk(cpRGN_4A_=5U$n*xBTD!bwvE=o@O5LAaaOHe=RAW$G@zUmy<0 z68o4|rE+FY-vSCsfdR>T#o=0u(aMo3!^xIm>hUneyVGHCQ)mgf1Q!MG_>=!oSj3|;}~fKzULO$Z8- zOqanyo^=jQK~R$Yl0noqr6u94XV{cg+`bz1AEvXO6o$uraFBM^+m6_Gnp4jvldDyd zLc0?cCS@Ms`MW@M$jBTe!@T$QEZ$KL+GgqUm{tV&8};}Zb_zc7ag4**KubbWr$C`? zqG}zY4k;7<#3!xqxjb3VP~JHiY+n2o4$pD^`rs#7uGnDU6?9M>e;Lt?Pz)G&9$u5y zy8bU3*{Bl`q=u6<4kA3Rw%F5jYpY>L?#l4Y^M9)3POt;omc>W%Oe2+&0tff z0o>DJ`|+$<>v*O4xn7a<5A+SG^MmGh8-{mHE$$cl!jEHTAr~cE^h_Npsfq6eLYfYO zv?PEGMYXEx$C?xw-&V5??1Fl5Crwt(?Oe4-Z{r^wi^X45TMXzNj|VC^@+&?fItWIK z*-n)7@JV~9rK_%>U})pAR1qHBA|4{#!)q?S#}6gfvKXGCOQN_KZsVNTEI6tfH}o*r zYA&m72`Jf$*bgaROBAZ(cO~)kZ}ZAbI1hA5Vv9v*(}QlF-HLgkNU5++vkDcPh3Z}I zV%4`cE?Pdfyvycs7Rk?49=j;T>;4cCbIxSk3W7E|BOPN;jI(BE9Zo8W0CPf!D=rzo zD-pgIiYZQTn};sbEG`z;BsFTHx=jbglf1OoCN)dX_!7P=!f~arv1*~1J3v5ZITAvy zaKF4I^GIJZA2+jM$!XHOr z(ci@}n+QvL^%2VNSvC+sUTHvqSaietC!lp)goH%EN#2K9VS5TW;b2HQz(3vR_CT;H z9p6xAx&faKxy&T1)O*4S|8zW(WBF{=_lA*AQE1K#8)>sw@+%nC`ROoBMcBojzg@->Kcwg8$*0$ z#ha`cqb6Ool~J3>Ab}S|CgsUaPAnX=k}+u zl5H!gyyFNI$SkGWvOj#E7>?g?>d4rh%5t^R@Y*D`h_CTNeSIn9Q0BuaC5cHRNISF^ z$yz9_!AUR@Y^gs+n9H!nXiC@J2$q4U`XF)*EFNlQ?htF-0UC3Am>$rrGdx3csr8T@ zBjvt0DMzz)8`ZjL4jKK=(kRjLVEA;43#5Wk#5A|NtS&TCQI4o;$H5P7EJLnZkTp9=rbW5PCfJlJx z=M5*(zdKIREfV^m5pa!f*J@F+s{nxTG(x2MMgZjq1@)z8cVAg%&lBk9=cc#Th}c(Bc)o`$w}IyKoPozfGQ zVuK*Wq7c0D#Q(aBS6rS4-Si+*pI1p9^pk$*lQ7g50gNHt_2`Sx)V9$b7`A#rlHy9j z!eeJ1zLTYw?2Wy_9kA4JhVQPR2PE zRb0)#OLFtKii!k@!notnc+;&8#>?*I>jte8h#^n>EJOS6a78D6NAR(m~D*e|*Klz7DfYv`ozrBOob3sP6*--GNuD zVEY+-LgXO3D}YPo#1kWfkuS21M@ob3Ul(wbl$}#KJ)BXxG!lDBZ+En8T-e^4LBVvy zYA^uxu()`5M-o3QJdCyP;LWrG`dpu3=+LeVOS76C&;{2K^!U|R9su+mdoPJb2k!d4 zRW-~HOAsF~X!Rx!FNb@I?#n(j!RrJ;i+yX~sy)Bk*#w~hCQPlM|t;qK11FrD-r zvr9DI-1;khpBd&!m(VTy#ta;vx81oYfvKHeI#0}RIwA8f-^u+epDZyvdlMNuQHwJu z@7MhHzOf$Bt-J~<%))1X`C4Pk3hTH_pdG zG(tez>>cixWg9o%8<%`ORgprh&EgkV0h_(ZJ09uT6I4^>SKU)0hB*}M9|GAI#B~d{ z*MoCfIG9on%O7h-El#{%?cLsr@0-ChTFv_XDSFQjmRpS4ImI{w-R1iH1^G+(ca8GC z(F%VvH=x9Ws=!bbpem8}=&4zXlNbMD$NT;te7u(*!2PRm_URwMo$v?C`~P+u`fs>> z&OCq?*$eF>gja`|%)7S`PMQpY8sZ6j-Zdr23^AT8U@zq{FQ-vQRbReXXw4Z9@rtYM z7&}I1MWY0xge>8%SKT3i5-iEkC~jB|+iuf8 zIkoqj{HcVV4PsmrTWb$uO@GsOua&Q`PraZk4vquGbP7a-rc_m!QzqAexWi#!lzmBo zZvB_8a41gBAh@mJW}8z++h3bVXDF-^sbTAmzr2r=N4cM$ANzXxBTBMQ6dW}P+}s#! zNBtf6T_4N+d~W`5S7=)qQ9x&|%J~8!rZc$@j>Ls6>2ntfe?tAm;uI@Kh7WZ!^unx& zTi-uTvdb#m6eC_g$2$PE-y=UJ{RjT#3hJHjnw|MG=*~Zb{_ljT|K%Yj;AC%aWbG(m zZ)EuMFw?XA2W#_^|FL5Hhx9ng1Al1*4E5ga49bp%4(2FSXNcGP!y{RY*Ay4Wg!!z; zc@ldM5|cw*J5k4hiSYE4@jTes@$pwE4jP(vn@_o4dr{v?h{=1M%q3yT#c#z3<|s<4 zN#6!rQ{@7|gDw{e;qKCX^38>$NVLHV z_-ijI*2nVQ{-Cc_i6A)8uSzl}O1?Bh-~|QzpM!GfG~3BQfXGJ-MMPr>kquyiT~V!FH%!fV&`%ECnU;zM=lojzsC>)rP!Z zD9~|DJ2TG0@u=~6t_WRZE_mZL@t_XR!7q%(%d%o3<0!Q>4$r=;xL?&L_C$hvNedrk zjn<`lzJmXsV1urvOf>or)wTNvaQk;A4gUhJ{tMjj4{bZf!8eY^bpIeF9nB`{3rVE0u)dxRw$)WsTS%&#|@2z`h2eY!vWzFyC-4|rRTQMd_cEu;B84$16A&`ciUObKScf`UjaX`3xlP<2 z!bU^066v?Rh&xA#+u}TASu@boo)52W^yNWG;E=L_osRL zOdG2ofYaD>lIHi)t);E@MRWw9L2wW3!sCS5bB}yHZJjL{iGzkSQ2nJD#ahtBhlw$Y zLvW3aGyk+peoCtG70^H7Jj?(v1-qMHtrRP$#Alpggb<2y2h-1#Phq%9@SCpU2* zQji}L1wadAamEO4VWXfOaVVVJYXu+4o>v>It6$shD9FRztOq(l{K9JUn*E>)+m*FQ z=~W$b035g~EB-ArqCBq?Dfp*}mm40#b*trC_)&oHdK9?iZoN+Z8s24h&P7E$9yUG?g#^AOjP}BCM^tdA7h|zK=T^> zusyJ5WLI1_X>I8r-P(tfcnq^gIPfoAxVIM%G7|I>pEH}hEaoY^T`G>D+s6f4)Dip{ z00};)$xx7Qt^JN5A$SIk+#DfnS#ce!#msUhE6!cjs(@HZE|D82F4J_~gkrls_O+B= zG054^24k`Vi;A(3lc;RX-|juYAm$pV5JZA#L~3JGXry3(hKziz)YV1XG3C1O%h7f60kD_5M`81)v?m8wzgtzYUN<<}(g6%wrwOWtpm6nxUqN&AJayjlQ;Ii_& zTZ+a;VK;!1$W0`?f-5lpU{05R3G$GU(3&X0up9>ZBXlN1rUS36o|hhDLMpLoMEWgS zRwr$4sb1k4s}v-$1YDhUcVXcGgYb^~%w?^9cKdDTcPZddI)si=iV-pgDXuj^Uj*Xp z6?hi@-H%rs2$g4}-`?+5?|!!^NZiDPG-l4RbQ13+U-7Sf{EC{&uDw>=*p(8k0Y}ze z?Vc0$*j(+rJ{M53n}x1`(i%oQhCB*m(S@(+PVf&mFKMFP?OatfuiCdAJnM`F-#NWFiqmlAgZh2pMWo~CXF{YRLP{{#m16t#1veN z9B)-hRYE1Caz=9(kX&3$@t6{(U*aosUX!z0rjr&?Zb(Qy9D66lpObCvOv ziDR_BX^vq(ad1V{L5gTM->N%VyoBs{eWhYmxobC*uT?5E^kHAEnldiJe7{KxOQ8fp z+1@u0Xrj^T%lZXKxDWh-dPF21B`^bQ(tIjqZ%x-)0%U(Nb82_l2tLFp2hP>o zD_8q^ceU`IQTn}iy_*dnLz<6c-TC_W{Jv z)G@^h(160ZQ@H|}$wxxu_+Sr4N;J|>x#@tEXgpt6vwhOeU+I|lp1@p%ZKaq`hA?qVlc!lrpKT%^e8x*{bZqP00@VbW|BjOGxLxH??r%dMdK1dy~swi-nqTmP-U2YvPij4A~4|dsB2*a+V#3~ zTXBhfh`V958Q5zywD^hlFZ*t~WqcjbUs*nv}=$qvKshi2y`<@e9gUemn!lUzN1V34qequkTJbPfRY z0bxh!yR>qa=(%%V_nMfEw$J0Soo`G7jFM6tpQ*CwYxAe zt+~PZ_J!54sf*2{ZX?bmFBQsq-mi_;i}mF#tGt3a;aM8HyDDUo3MeKSEU!(n(84T( z5ohs^M^%;wg#h#(FL$hu0gEe+QfG~Zk+8v?=`aj z`!w;NowEOW`!A-4te(BYf93(GZ`fnCA$YkYeFLV2FqO>;jK?RbcG!#OH(Iz=NvN$C zQj{e^9rzL+r1en&$m=&h7`cs-9-__v(!KgS6yAA{AJ4EAb+w?P`>^BlZBp#&E>Vpt z-f-}qLQ~E&kQZveysTMB@ex)ed1se!@R09zoO!ypte5Py2f4CKi`rmH7H?U+>+Q-^ zM9+Z;SIZwC%7{IGo9M-aR*CGbNa%%Fz?yk_n!x%!ESzzl#Tlt)48NIqEDkMZvsQ%c zfJ=vgtDViA^%_dyOxF&~k`G3sQ^=@CQDE@&m>*q4`^*!j!R`IJ*OEd5p~<-6+ex%CHKx}M38I9UA+Pa@TWw`mf z1jAcDI6dt0a)IQ_ZsANNWz-~QO?ffW$#CKsJj5%wj;=naZ}8%EHSy)*8=1* z3>lO3vgzsEjo)wwf4S!DjW2YLKHB~wutgFgZ_AdkybusL5+p5mxVI^6s0P)# zkK6`b-8Q}S&)=x8-kkf=E$;&CQ!KJ8Z)i?k*^ls$15$K%*h6p3JQEclB4W|YeSW#k zUnkHhrJs=h{$pw{=hV-Z6bx`C(0#xsEoLgm@v*+7KT18Jz)K6dU~8r@H@DpRS20ED zzD^<_A`>R}#8E>&4!u`cggrP3w#FTuhMORZXEkKEj6DR%jzA_}lBQPQam-~W&-R3* ztT@+`^dw-xRFLTse9Kx(5!lFrez2Tw`9!2Cxs^}~&1!ufLT2=P@FAFB=TslHW+V@C z&;V&QX*9Q(fJzr$UCPLD6`e8xt^5?e#tK zv@CRSOyGs@943JH+Ud!LD@2O10X+A#6@)o&(Mncnpi;%JFw0-}oVwvKQ16kzfQ-ip zakeS762jvqY&10!A`M;~S*E@-(cum^M(NTBy6qRVHdZzk7)=xRZA76Qcjam8f$-k5$~NpG^r zs^(k{l!m2vsn9+CX3l1`ebZ$=AQ#2Jl`nI5)>xlOFDw?C3Itu4F$&-SIXzBH9y+i@ zd}FnN&AbFm#lVu)8|+llS+U`7)yQu&wER#DyLjR4^@mjo&ZL?NFw{-Ok5L2Xc=OuT zWTcj{UUGOAqw1*E+e6r^Og=#0?fdT;QVY^2o%&MQ(mWX{gzt~;-C5-k-Jy7Hh_lK5 zS}kbG0}UXMTPTDvQs~G}5A5GZFcgM^#ALp6&`ZhjkfzI51TL>2LCs#ebjYgTFAm^C zNY7q)5)l=x@BC^qMXfD**UmXYZr8{zcTjo!_m7NO3#lRy`$3%3)T|-RdpT#VK!mh_ ziviKjM1qs1P!Rq&vWY;|)&Rl18_~lg{Z=CUF-wqV(~-i#g@BP7(UCB7Ln+HYURiUt z4R0E7X-rI$We1xfXQ;LqPV9jBU`S&CPw4otEsncwtvXp)uS5Dn%T*h)XvUw-sM|mp z`=IpEwqJEr(wCauDw=TEUE99GuNE73RyQ>LhtI*@zhfmXGtmd_6A+jN8d=Qfv;DD+ z+VK?=S|xEDll5;06cYsLBpu=z+iO&T84!BrjocSZv+75%-5MCbB zxyO1QDW+mJrBoQ#tAYA~7SlFeXb!|;=A>TJiDxQ~Kjr%8XgqzYju7#VhAuNJ42yfGjH$jDXs8GVe6IR!! z{<+cO^^msDc#$xlv`9D5&sVdHjS|PMrkLDCc@cBbe$-(algOxj1w^|!^6e%CzC-K0 zjTxU>F?`Q4?4j7?;88;3d36HgTY+R|SD{2<7jpF*Tlf(|rCR3=X5u1kJm%;Xnv%&f z9jM<8g73*Eg|pB;(Vjf4CQ}-HoQ#&sx?#P~%QBgSyMYTVG5~(+9&FI!m#sy4^p9fu zy*k5zDiGsyY85tqlp|Zbgzq=;&yOz-xc<2;)12UM?#dPm!&O7{f}i4(h1*VoI+Oy! z7d%lWglU`r2o}-ybVxqDsfL+per%PknKeuxzg3234{96T=(_cqQ&4xYGML@|^%r$z zrLX7KxPz+A_)k3&t>oC$Z&8MCW|M1v19V1C^1lERCMC=3lBHMs&e< zq;3#i{$m9&zn>M|*rCoYa6@P<3POQxs+YOWG0qVEO5kfHYCD42g4 z02fcqWZXVOIK8WK+&TSK4AiHOZFm7`NwhEB z{b>MRA$D3lJmqUh64r&cDKq)Bg4pohJ1~$e33pMMnZNt!57SIIQc8~XkkbwvDXr!q z?vl|mlpBdIBltR~KvXR|ZKArX>sn(*{%2IjKj*K#Of<*SpR|K7F8~1kf5TGxzde8b z3w-$BY^6)C4g1a3x@(Q5ZvdI{`pk8;@r{=TcgI z_cxX(C>dz4zwotc?x{ST6mBa87d2d%Oz$rB5xazPZg?g*H_8mWRaGjsJo*DF_gFd* zs0lKUb6%_)ZJ4rZtWiA`tXN`9l(Yp?J_<)`;#1LfcgH-Rr)l!z@pL|L4$#S8-YTfn z@9mMOU!HQcQQKU<5mUnmBjlS!3+fHaUc zp=iBfVO4{WhQ!yhhdk2^!*oln7n-*h(ah>~p?r^&X{FZq8_0L8mgA=GGFVG4ToKqKl? zwR{I_+eNh^sfqcyrU`5A%;QQ(K_#bt>1!%26?@v-(hAq3qJ&|x3uh2T?(Tv@tP$#X zJozMriCOh&>gZep2&X1i)|b9o6X0C|z&>7a(cY#-$nC7}??9Df)`Qzdux*KjAke!; zS`S0N?~=S`SVn)me!N)UA@>n2u@FbB($g%XY!vEhD7!(4Zw--Xl8Y;!23^v0vTIV^ zu~!_e(c6rav%OPiU`wvYVGizQh&pqL=GN5U`#%g2WZz( z>uEV$-P{IW0(#p3bAwnbpXT<%+9-5G_CN>NsQiW07+cG;4AlC3vH~y2Nu~xgFv9Jm z{U8>veYAq{<@t+qhrx|C%4jA}7~b$m=x%2&b*^FSln$(<H8z~ob?Y<>x`^=#ef{!x~LYAh4tN80Sq6~-7f zlAfo^6*M`9=m=~xlwLa@>{{XzeHw5rt)kg zfZcP}IX)igMgv9p*&PIK z$cRtY{GC!&_=h@Kb{ALyLWjrxv0Fq9*&UEaa^4Hd;XK?h z5@}>Xr?dfp{kY%zM>vc9D+xJeNbxTCtM7oB8-O^BxkAt}Tj&g&54>bBgxTHx>nkKu zE6GANdi1RmX?cg}6$F~A9H|*qCO+C*NLWvOw`%2Iy4`Z4-yxh6_yWXfI%uz$Eue~u zkb7$gC*A13{go8+t)V$fM|H({ODZ;cgv_P_AiJ|2M}p!%MAMUPwYw`89$H4jFZiS9{=nAqB>E7*!QA*a!9PMwY zBV;zWz&j#>$78KuqMeUUqAG5P0pDu1*-2APA!fnVt@lQJ)>CKqA#avJZyt>y4Q>wH~u zdrRdByJADD!b)2|70M*LKVD6)gGNo5`S?lc?C)L@UHo)l`Ni^SnT%7xGKv0T6fllW& z-<&6Z)be*aoz9h5s>namC}Y3M-d34QAx@D)Iep^;m*MQ>VNJ8D^6i%}@@$SG#w0Xx zXWZqWujGy6&&EM3!3Ks?1hkp5_C-P{8|8?a2(0@O*cYw>WJ+_#Ck4))JS<}O!r3mb zz&3}~`qQL$=iJ!1HJn)-(b4FYrv%3H=km;auhm^|s243y8?G20KeiOki+XMdQK!_o zQNBnM7|iJ#oLDivu4}AR8|u!2;EzX=nALUPRp?#=i<}y0vEwvm=L7EI0LE2Sy?SWC zM5=A#mhgkSu}FGQucERh9B>oS$;!Yq z)eG&j9EA_kw*WBx5;usV@-XZ`l9h0M4|d{Igh08yO$OJ^u3ReU7z6YN{OdlglN}?d z1DbNgYXfiIQ|ONEMyu*Xl&mU4;$u46bDES7Re{5K9Q|*{ODWtT`3kSZ<%-7qs&d_> z%iw1zGVUdyAXy*gu!IHY#ope#BsfFC2wg{9V4s=YG^K2y+(jVUv)y1&qbhWDkoLkC zV7lvH*lJYQVEIGMJnX-Y^z#+~t;W<;c(E(o-srqx>YU<9ll~6$f)!9F3-}sFz%iD3 z2LteYu2Ycbr&lpJaIblHVSrun|Q*tlBB`i8|D^Ja0fR zuU{s)IinPbd4j}Ab_~$Vbz?bkOi(H>XGm%PjV5%iq){P^>1U=ePIVQ*qhM`tX?2Eu zz34G%$7BgNu(Ko;A5ypNLC{4r0mq1gYz-YZB$zRQS>7uRvAGihxu6wf;iN(2MFZc# z_S_Gp#LJR<=OUyvoY8=C)T%&#OgL)0z5qnTty(&Y`~wgV_skd)z+JXg)er+?CcNT5 zLTg}bh1eFbKE&S1qMA-REE#y1)O^bSdkF-Li1kudDV7%vR{8N!QmQag*aKb+Y%T{Y}@`S~lmNzR`lN+X805_YKcH6XtcQhlP0(8y!Z9gY6{f zc4=itB+4F0M8-5GqWA!+gb7Pt^cyBBbVFWztKl?nqAE)UyQ(!yyfo(e4u2I7E#PKh zC`NV=s~1Q!6syE6m)0N8Rdr(g%%sJxT#Uq!wP{z-^C}r>1sARKWyRct_AMcEgmvlh zNkxrtc+M<9dZkgqW^6F>C_L5fsbOm`>R%g>jPWEfcmkT}WfYQ}RK}Qia?LyjIPL1@ z?r&VcAR8Ow=kqI);UMQ}k=qh!NXtud(jLJJ4{PHe(MBYK(seUYybuOuu-Km;enLZs zxfoib^~!YAv&m$zNzGu}pC2#4P(5aLrS<30LSoT)N=cXXwYVPNc^yYT zK5!4jh;wjw?rrMz+PSnsnvb6aCmV>XtRvhrj<+U>#q`R3h_mN}Ki^kwtR#JwM&(JT z8^Sa8=Pq`rz=Zjl-$`6G-zQ40Ov{@fNhVe|F*dGgTP2Su-cF_KF}FB> zb0|BXZCl^JOG&_p~<@F=J?OtEJe+=H}1 zGPSZ6u|1sf0BoXK>}?l|>oqV@P|tyf-kWyt*kUPXM-`J&-pUs)TIvXd=Zcye1s5FN zG~hg24F;7D0go0U_Krog0}W>cbfplux_^{35Ahu9QNSIGs2r?g+?NYMJv#{cJuYI- zFZUKb*a^A@G6f;&sEUf5bKLqVl0LKTdFtnXX}Qn?cnMyd>{sME3-H>;w2udni@(Vl zYs?N3p9Yqz!h#jLX~jy@_m)j+vE9F4zC_ztt)RODTc%P{c=x(K-9tsy^ zKO6K49iF76KxdBhrN(;IQ+Zx;--(9KZFd43n6~{B8>YIX#Ti`{;g??k;-`yG z@`97l9Adlsa`C+%h+X+1xP6DieJrhQIuk~ za(=Z#l#a7z#9m~16t^{Iz$Q%T)oC^T^0$}L!qb))k6SMw&A_C6^~{Vo@^sm75yMp| zO<|_CLJa3pm6$poJ&x;*-#=BP8yFYP;Az4vN&o(!VSjj_&jTK=3QPPjeImVsu&tlH zi7&=xGr`2&+^YcJ>Guw*3#7k;5dxCipmmh?k8=53qVin%-K@Bt?ue##j~^kX1pvM1 z&5v-sIA+xdhN}NYwq;$K6lwje@i{^xsL76xM_4WMF15Y#+WI{VU5H_`3tNF8C*ZK( zYrLbf;~=$wNGfYPOwBIX#Tm&3pd1y^k3H*I^UF2=6z`9MN=TK59oRmLb_qm=mzN!f z)B;EREyg~2K`jsWmmh5Uixle&y>_E%!hE;Dq|a&C4G;!YQ76PkOoCWU> zV)@14{I%^A0P4LS`2Jqm7RHi{xm>)}g+$VOgY5!Zogi;{41F^C${D3`)p7UrF}xx| z{zy18_cZvDtxhCPU#-E{eq@QFWru3T{FkiA>toPH!tFecl~uN$c$J)%<;>cxj;TKx z|LH~qjvZ0!P|~tClpaDat8h>sFBm=$B;ba+fQT9k^Ny;^Dt6Q()9QvO1fEZ(#49q0 z9(ikm2rC87F1uV~fHkWB(|7z2bE;!002Knu_{b)~2W;4Y%r|W=@NaKHy5b+5_(CmM zp`(UBJLftf94JR$0H+vW-=$5-@qA8~41)KYTTM1R9O^o`-|zR06c>&}M=}ssU;N@K z0qxS?T9yP2zdu=pT4Ya=7~q@@d*b`XPuj$sK*pXA_5p6E!;hGo>7zyFDoKA=s<|xQ z@uQo77HiPI(E*s9NQBn%iB7K@$V?e=J~v)5<0PANn;;b}AcZQEP^4!?&aH0_>*L5i z6FL~Du3!5rkcR$KH|D z@4xNdvsDL&si}&oJwc=Q+lVl-j-~Hs` zfL4o)Bq}*s_=4GRlr70VanX8ozO4e7f8i%}?hQPhaJ6#Hxyhy58?L_jz&7Zg^#_8f z(dR_G*XLA7JDntrJW2mCvC%5P#cK^Wmh)u;+Xr)Mj69Hz|ElSF_L!{zDkZW83u`WZtCpLf&O8czqJB0I@)7FVta;j<3>43m>=Y^e!5^|~&uiHwF2HDoqv0;$eH`bLi-KzQ<)#H6- z2|5TXk>nT4dsscd$d%o7&~|J#`-}=!8jmhn*n1n(;`^-%w4)iwdud*t z4oRy7mVDtr$z)^Wyq+^?jA9CQf8W9dWAjx)$MOw=ouKqMoV#kuve~}jOIK~<_rxAtfKCaksKVpgJWRw&rXg`)NVcDP)HrVu^D1@ zo4m!J)~RGB=0n6F^U4U5A*>~HzmUq>QZrDEC=YG_>ATA_=UQmU#5JTr`gH)11>Z{hKLMA;Xrr^PRz-9mgR`EMt<^8l?6!K5` zsp_LhP7h$|9y5@WlK{u#I7MBP!I#T}sb9U&gP5g6ph-mwjeUsK`d^DaXbm(ku<@!r zG8}3g=PvPs}(gGjGqQq6!b$9SCW*+u!A!=dn6+Dzgl5;l=~;$pthvxS-K1 z?T(&qvDYw@A*j=5?owH%r5_$>#nR09&bd>g*vgkRk+qq0;1GX2lJ^;tjy%kF?-Xg6 zS~+Zv*i@EmPEB)a?xcB>^F#=a;!FNV)EmjqKWxE1EcwSYH~fc5{Qr{L`A_oOf4FUk zSsUBfTj@FeXQua5+`Pv4nd(0ZwzvK1>r5*w&M{IoWKWr$A0R%BxsyB@nce zyO!Pb*NI@S)s?Z>?K%?;FS=2jX%NWf(=<}+@4}1Z!@%p7$iU$X$j)6l;krSUwJ;50 zujk8qLfblY>cGORT3<{Zrp{jq)*0nqqmJmyUCmyC+(%2Su^(dAj;%D!ROK&}nD(6> zvLhYgusu%2VcZvUyo^^jmRL_P7NOT@09j;#IFt!aEQ6*093-9sbcb@dS#Zw$I)XDZ z4~N72lFJ2gOfZ`t*?)oj?^W_oyM>}qpfmJmg_I)vduQhV-ER4>=f4^xEU9cr{}zGw z64}F};M@&uvP<;p^99i+=C^OGV7l<|D7I`lIn90mO~&u0f_p9e=vUSm&c@i#vn@0= zfMmEjIC5Wp%gjeiv}ogciNG%}%KAVd2pI)OqYz zt|RCH>oMZ5#Bp?PB%-^8U!1wjnqt6^u#2A361pgrbQg$i!IKyjQ0{68ET&LYTaq_a z;S#rkd0xziq|eTyH5KA*w-XR|Q|}q0nsI_aMCkDtEo>gNZCwm6oad zOr$LmQ6r7Ep~rx4S`XF`aogZ&1#H0_Qn~mJy}u=44W(CCoYI92=P9eZA}Jo7LB#4^ zD-SYhGL4rh6UoO|g@Q|n!oZS^Okr%Xu~OX+A=${|WES~FtN$)VHJ?akz7yincLFcK)i@Y=NI2`}-RiLLO_ zR_r)mDt57(Hpb`HcuF6WUE!#ZO;f@jRz|4~%UXmAoY-G`&EotG$N2q~h3EHy##oYE zD%(XMr2yL&x((ejV^pF=)98E>=L+VNRK%+|d(Q!rL)#Xxx%aPOqq-jHuNzgb%P_8Z zv2e%(7n z6Fx`vRkI5W2d36t;9hadG(Xl3c0k>31uoLbz26WxrJUv7O4$fA$Q0WbX8*;5THV1g zn7>^=cdgTZGc~hvV@3y&qZSkCn!{Rub?;p>?!Jx&QDYrzNS( z;%gxRvG?VB7cjwPCaG<&p5X=(!L?mv11s&qsL&FoJH{ffn7l!lyYkq2v|yat*JhBK z=5J!XO2$9EWKxn7Pt>+Cxm$8=T`erbJ1&{qNH`FOprGl_9^6AVDWS$L>I0Ds!!XG| zd}qXYoyWTX&a01J41SC^Q~pT@(8MOL3=@%kJGt|O-*$comMJ3Xx0Wx7@)ESX|BLIm zT=5A+e5QDfwXf;Lxy67x;BHQk!a{vv)!B&^5=UEXA1{@5Kx292DNA4_E7|J|zQ8+_ z)h~S{dCY;7FXUR~W+_v|Mb<-5&@>S1bkhsMWuNxG9#prjKn`jUSfZ+rs%V1wshs^x z8AE3dD~Z0ezJ}bCJvE>=DyzW_m#@sU2q#lC)Vb4EI+=)QTX9*-|F z=HlG-^nmNgrAU_QI|w@S4e-RDc{a%oZ=XDLK@tACb&uS+kAgZ!A=f3}S#uzw#L@Gpgp&%nUO$=dOsjpHBjiK2$}Ixq64 zh&~)rcP3eV895!fRZ*fSwOj^C{%16pM=1Cj!!#!%RbXOJBju^cbNnwy=&pV6;ndQ+ zd%c(thZ!!1>)o%{M-7Q_>p7yN@WBCR%5P4I_!-9HaaV0iokaeQmH$lHd z=uDo7<(`5LV1PELgAE9gE)sk~WRTHbk~JZ{Wq=7U9Qvl*I+`q7;Hi#BIq$jH`6nKy zD@#W3xK|{@9!_tMvF;(S_pL;EjSzPqhD#~z&^QfG)`fS75jym*7*$?^>oH~s&3Q7K zEHjb)5dR4KQk^!UJLmWbz*(OkK2x8C7!$`NAtqmON5`&@YZ2>$8a;-|%A zoo^qE?3k-j&22T_?JuBC^9z5r?M-DKm{+EJ>(CY@_w9JKSikt5e8V7C&wn~afET|eXz#csv>qHB8Nl8US*GeNOj;^Vkwp$y`A^~3n zS|P^#zgT<6=)l@-O*<9awr$(Cjf!pCwr$(2*tYGYVkZ@TdHa0(?0w$uJ-W~6{_gw@=Q8sf1M{{kAu|d}nSvYpH2;S4O7UT`7f{6-+*s31Z zTpV-S6_o0gI__6?)~S|e#6@S7sGdWtY&5@KlCZGQci<;1`8 z9@tG)4}fpu{Y(1o-{F@3X1of{wody0FkK}Dn+1OOkJu!xsMjOq(HLyRQs6kVI5VoU z2*tr?((Og8H7*m^tU={?4|sM6yv<^?UC4`d2HAZ{nrt{v*N5Y&jaDvSpO5LLI5zq* zmRqAmo8%2w^H&ApfKB@-4fbK;snNqNi9uzL2+Nfs#H1?{vS-0U=HSZ|BZw7>%-fL( zvY?2q8>(O_1c#$;HM9H6W$Rxcq@4$Ma3cV%kMqMHTX5E9Zf?HM_b3kmy7bsNPI}Dz zJB-AtKS8_kfOUHCvR-7;gp=#qdjACK&f-12ZDBT*r{wVb%%8m9ZJtgDDu1`m?uZEr z(U@%dQ$S2w1WIs{k_9aJQ)v>Md>=leP}UwMxEn{-HkWy-q(04>qV>tEh2$ipIY@b! zyQ)GmcMlSg)G_UNkOloD?*UFVfRMjXLZGC59?VHM(UwZm9ZDeL4a60g>z@oj&GMG$cf zaU@pjxwQBifi|%T>B?fcl#9*gZ~}$8@C_)jo|~#Y@D++uhSn-s zO>;VLsm1JCD4tV&z2iX17Wz>Fl|yYkcLB*)@0fJsZ`pQ7WlXn44rGJE?#xnp6s0@* zxtLlxeM+z{iRSXwjp9GnpMd{N@&2OLxgr<{4Sy%me=!dK9sBBU#`F(dU9KD)FJ#T^bYWfXL+rtglYGV>{E z4lxtDSk)&>6UX(?jQ^-0_tzQMR9KZqG%zktC%}~6iGCO?E0d3P7wTImq{DbB(vj~Z ze~D0~vS*9y=Fz%GoGc&}hSlE2FUlH;U!){8%8@-f0X~77#}UsEOtml1cJPR`1>nWh z_e}0?r<&cOnn!4Q%JrJ>ZBkU8XcvK%Vb#oyY)vE=1YqS8M`3uI9-#9w%R+MuwWxkW z9tt8|Patz>tguCx!U}R~aYHWI`&@ul@Ndc3C9dh|;`bx}(92fgZ`-UAkww#9WLmGf z;69(>A+S>(#+c^(lr*yvfqKS>&JM0NeX=j=BI2xh&v^0sRxUfxwoS;mTF+8yz@kc{ z-{gatDDNPid}!Oo^~ZUc&U1x}&cz)vYd^2qeW&x7*XNp~itQ$V|MSfH3-rs;GH|3t z*@0vk1tbpGoNPY-mN>e3>lY^VLd0SIn?Q+H^`Kq4{WvJ>8}9UF(mFx>M+J6~kK&%r zV5zZrdJyo_7!{J(l;(TDJ^I@g{v849 z|KGv=+ep>-p?ImO6}!=a-~$);)td-Nb{B5hT5H+}!Mbf_`BRQ{RVD&Z1kWtIWlg+5 zP;r|j;JtzOf$WjaDFjW2qSht*uu~bXTmEp;?l=>x`0MkHuXz7Y$mT_|$=cWg*;G$s zu(%ScC9MO?8`MPlp}p%k$qb*Gp{a=d7*maDq;k@jppi(qzL!g5af2JtmEX^laIcn5 z%$~zbok>Wd{udUuZ@{_!b@A$L(Ae7A+tC49Pkw|>^(Tl2KSU1inE9}>fZ@|mMY&ur z_t&6amfV>!DHRip|`VV){FOhah|HQ500ZlRQL$wCxjH@WRcn?`6snK#b{wbfA zs%?X2k_d7l0~xdC#BI>g%ajo4xoePG&aSbDXY&?7Q;}K=AyG& za=9FWGrC<_nR~}I)HnTcks(%l;IbB+Fq?ZvWI3TdxK!IaE#)(s6 zNQ?~%6849Uf6c>|8eXU$Ab={T0JjfqD$fXgltV_vlSVJ`lq|F&y?B22U0<(m)FHQN zWv{qZaWlYm3o$pNq6$-mND;Cf9zu!f-ZA~!Y&=nfW+vH+!g$`nv6Tlt*8>H+M>q%~8VbS3Lc9c9SA|R!V0J$=njvghXA~(&U%>qhIO=&^*KF$@{n;_~qA*WfO zDEI-FN_qbUZt~4%KLe(E!<%N4grn}^vN88I0>GF#yo+fNRH9B~!9;BNJs#JQVp?;_ zWp3kf%l1&<(prH-pq6qwQy9A` z-c-K?%MxJ#1h@9rgABL$s39?Ow!@FZ{-7)RtVyzX7L{t z7DbSm93X@@F3NZQJG+-7rnyjYYfrMtI%fnMrosSlRx0u>=BpX189+WyQe?{?1}s%p zT&5e)VIAX(KvpAu$0@cgac|WHFhw77W9q4!oji4g3CLOxjNrxB0#`i^O@6Nst@Iqf zXN6j7_OW>8w=qQOxZFdad>k1a;3X&bdY>`toVpg_)!EI1fd-2wsK{6 z&yUQ5lh#Nk*y=7~4`=L7h9ckCuI)uF2)}l}E`G0_XBei#@1oXe`)LRz%94eh4WWLM zQ|2f%fRNSI6a2OviW@J`O&0DT=f)>9>0vpPhDo8l zH_}v#+|C!-hGD)E1gh6i3C}MN8E1+rxen^)NByz(DhbuUs#0p5ydGv)k8tZxn1yt2G9{OMQU}Z;>mLYrhh?HE~}-pU?D7{XX0V zxs^2qjFc(4z>XV;erYV&5wHn|$O0da7IBBXKQ_S}Et(Y$@4D`?%Zf%%*g)x?e7->o zH7G*k#r-4xu}M9r7Z*0Dkd1lE~8qa=;WgZ@N{ zIhoVAQjX_TJAJs-eiJr3csP>Lk3K*s+@mf)^-T$8P1f=EdB&wM0bgu)^`g3A248RJ z7#v^VL_jcJW^VX4qU_4KM$+9qx7#=s5yRgHy48jM@O^P?@}8Z)kd8NyCPTrq(yt}! zhQWs`^~n^8m%LeJm=aT0QM{|R%W2rafiS)j2r{e^EMv_!6&g&ey1D`C7`fbQ3iyi4 zbVvDG8YQC#6%%UkUjywWB3_Mpqzc7@cKN1d3xhfF==}r*rV<93vaL{byny+X>HTN^ zqs<_ofhb6J2guJrA~0Iu{35^=Cq=0)NogV9X_Y)@aWO74>--4+b4yO zzcs+O3O#}lK13tq>m&Qr`EdR8*1e-FFMiciY0KpT_Z@3^7arK$o0);|=#-C*oJ%2Y zIdrkmq0Ay)9c65E!dc5ga|Othg?rZ*`WVDym^)e=rHk!7h2q3{Zu%r~J5hfslH{HG?2%PM5H<)3&X^Meo>#u(6k= zct6O=Su5;muFvRf=Pp7y9P3+H;K8rRRQ28}u&u~3k6)fAv4Fb_y%Zk$@s>?vKPg3< zivQ)78D&#o(eSuFV$l%?*a?Hb9Tn1jZQaRg-(z#FG~AD94GnXJ7Wqt<-OA|2Af&rd zt2a<<$y~Ykq@|Zf{zxoj&p43LH-`o?KZv^MRBVvfz+L~G!3)>nx z(*5)CyWZ;Pq;KQ&7XT$$b;AaU0p7cI(YH=9j$g%9NJ<1mkWj{4rI~+mmW(#pP>xl# z^L*&Z1xn@{&%qn6_p>u~v*W^s@Q5It=_JbZ!tm?kgRFocEsYVsi@h5;zfM=LBIsEK zolI5OpDjxs2d|RL)-1W?2Xs>h3U-rgi8B74FhgUPb5EDZ4G|#}XF&qJL^J`Eu%5{9 z{AdQYTJIK_tf(S=Keiuwr9y(2s!|y0nED#XJF%@qDdVhrBgF=M8mN{`+6e1mC#%gG zfwFu`J`4ufYLBwHZl8Jxwjs}nrxXyj8uYr!rIuZrxRMAO;r`MZ?R=6qNJ*`~<_m!K ztOf2MF#(Q*x?sz$_e`+$0OtsceNGv3Ld2aVO(lgCKhm6^MX!vmj3Thw$nhI9Y7ir+ zo2~)LvDl$6B2>4b1pt)`A(Exx{rYn|YU?ft#-JgkRfVg73uLcj!wBH7qJvll8!uSH z4Ds`kvOu8wk?^ET7CU;%xk>vRN*u*s1*)pMi&wwkF3sn(sw}NoE)5mPdLy!)C`T3> z#ymn(EO9w{U+o;p?bQqH`)i_&uG(P=uQX76vfdmSOtKs@N_0JTDR5riwyyH8?V_Rf zRHQ>>@QX~$M_xEgk#v@$TcWZw*Ee$r8+Bbz`QvhnJXB(`Y`*j5edw*Gf~++Tt<290 zX)X1ivhEec+bPzOIHspAla;6&6;?VP2e=cA&;6{Q?5UYtXCVe@255>fptVU;o-R4j z-^hdL;U27O(7`I!;yyemEhz)jE1x;UKvimGWpfuF-KmAZoJ!Ln(KD%YB?_@5899gf z;tp-7EhNhzIqDMjLBuP`Ynx6;CQGgpHTd2wKh5V?!EM*<>lr&K5`tycgN%}}oao>n z4c$-b;{%PTgFg1=w&nCby`)SCKX0FV<7>-T>t_j>A8s=7=T+t(5{NQ%1dtcN!En!d zS9mzpICpYgz=3p~HXjy@s~EcgzQjK-)(^8zXr^H3q?3Ohd^A^GlrJ{PG+;lgXufOq z2wDaoOI$kFwdlJ1vTe)bE>DKtUI>7}=;l~%pV(G_yTAhCl~yX<3_ToS1V&A&Xh;n+nyt24t66Ex;CAlNiIkD>5C5kgfJ_Bxy zMz88KA~1%~@EN#dj`sg>EdeL{CSk=M5G!uH@kkoLLqP2Q#_u-Dn%rYDov;JN&Shea z-Nk_t?rA-Hj>DATjP3`3kSKZuCnN-3?@Tc^;eM|{#rObF0Oa0Ly{Hjez32+Cr2yy9 zZ{?rk7(GTYWvcdYLYh{pNzBiCJtWY(tFpCFEn>Kdy^+tnC)mZ`k}*yuY^*1Kzuf|a zW9CP`nl*Nrj&DCiFR#KLc^n0NVT8^jsI#qNuRvJ>)xIBP`0fyz++>F!K%762gvgf+ zAf>oYuNhJXQV=1E2qA7>Q6!$Nn#23SPr=;7%M^}o0ZIa4$}XHMIy%eS44^8_Q@-12XlFzAh;k(dd4sx^YyT<-Y~j`U$_%+pyt}tPep=)ZE}?Z-GGxp3Fu~ zr%9^1dtUpjk3;IZyKPopSJ3qiWWvAj*$%;QSN*!ztDpE9N|)ii=qr#AK@U+-W4Q^E z^8|Ix49&;8sSok!wgY zz#L`bJ}y|dURiOrf>iwQd{b#TQO>d|Lei<0s`&BR z6xNv;d$sF1I9e#ac_C=RfzpMmCZ{z2fUh@XXrGVnF*dU=%*&qgI%!?mab;PIqOik+ zi}V}2Mz>Z@UG$}Pg7Ea`qlx#a2A1`j7N`0K7&*BC=o$v!o0u~hK&#NuR9v-s^A0+f zpK*JOO$g*M7_~p_sySUYDr?3!k{|3!eWdPoG$hU7(H8NvD(F?%$Q=ywcEE(hhH6Df z?4|7ZDcW84$P03@i&^Da@i|Ct%c7cOQ9wlmBg~ng5@pE9^0xjH=D!vu9!7H#&%Wza z1K$<G7R;^89?<(8y;L%%$$!fwkY~e?Sx2r6n2{o)|En^ zH08a9jyfp;jc~By=#I}iLWIPBR<2}HSO3yZ_XUHCt=_pp zCC@8YXay?XVtiYIyA&3Q3xZY+{lSCmX2>VD{ndD22+aXzxzo+#xvlC#o>dlv?e=N9XEX>h4t9y%4gMA@u`m74opoApKAB@*iqhfyxyu|gOdfwZ|c;H z;@n{1v+EK(-c+ClOHVvf!;lLubWiN7lTKZ)uo~L4htQoXxPKbZUm4a>fVubjcR2Tk=@c%;w<3G>9 zCDkd}O?vq5+NSH>s~;c`?azd;@6^nc!6+N5T^VlRs@bcn(+OoaMm#s--oewvYj{to z#n_s7`$mlS?#G#9(mr1vl%3;BtW{oqRy`Z(-iIfYJ(;QA{){(N;!rf&Jx#EkYNXFd z%|ezlDkXq?%O!Cmq>Uo{9C+K1rm@mvp?&P}{k`e&3A-Nlr{pf+i7)@t))C|l=@7xlrwsOg$WZ41MIE76U(xQu!Y|Llk9qR)?hSIRXh0HdHz z%U*MSljl&4>EHV^>|A(GBKy<8?KJayR!!-r|6cNn3Nln>hfBbjOk#TdUUWWq6di>W z$CTm5SLYoBVis=PW2I)}#Ww-^WaQKgox$J539Hfr9sS)@c$PSR)JE>R`*9Zb-UT^E z6M5W}Vm2=s1h$+=O2POZ<$;IFVp0+EFuwgsiEI%F9+02rg_O{&50fB9PVhm~E1D{K zxdI?Tb`{?sgYT0>I}m3_1c|eZ#U`=$F&Sb3dtg6N_2u|SR^itAF54q|r`|U)sWf`o zH~ZnXvrp9Dn=nNS2K)WiFJa`fv3F9;s9_|f<*)^2sG$Wtj9)58jW;V#{l%v21{>uvW5b$?x?Hacos#GP z?9*gEbwPKLw<2B6O9Le#gIVTXgY_=z>L>sDrlg_vh1?oM67u=HBlv!JQAUra8nm#W zst)+n@^;fjlxtF84Pj0&5349>E{iovE8hONCQ_}YkFBYZbH&&plUEOJ=h!kei9bZN z0bij6iJ(PD#S~3TGmDT)rdRAqoj^$wB)y??``C{4k*h;K(ykOH*2E-wdd88nr==wT zQ!aq2@AA?M0OeQJB1G{p69kHCt!EKY%NO%0P3%WRv7SLqsvAUjI)7?4QZxLI z`?ga?_}Zb3o7GLB0ul2tJ7cNpp#h-*X$;#!?p3;7u6m5U5(}-jC>J`8Rr9bo1L;2D zrP|DVIWsc#<8nGrve>Q9Cc&Rlm2tCyzbQs7Uv_{Yfx#Z-IXZ0(}y zccU%C`E;FZO|2=l0jZVl9+jVB7+vlcI|94K+9ZoVxBx9YdyUa^+ITxH2}t8mNz1lM zEL*}hR(#tMM#$^fS*8QxgZ8TMh}SwOat4 zsO=#n%&!GbiCa;qbIMQxl?${?{QKL9t|3F{s-B%+``;ecDQg*}U;!R6q zzS+y0uIu<(PU_8r&?yY2(pUdaow#6^@PLD1i~0UCT8>{!qzA}R;}d5EofV=;r?qTt z8*VFN&*j|!|2$a#x?vu8;ArU~0sshG0|1Es?|;&NV5I(e(*7;B{F7vGgmY=LA$sTQ z*9la(uXskeAnVD#*?fZfDIK&LuTNp^kIz24FL@kHsFBc6-Qm;lW*_TRYsd5^Ks%=6 zAVFmwVW=QQ=xS`v~491@Um=AB$RJC&F?oUfME z9Ya{nX7}sNpz>_sw#egT63(}L+f%6FeOUlI_YW1HV@!bAC5}j?TXvtfsmCSht-cc& zd9|v-Y1XDOu{1*cw2nUe37NxErJhsWbj{my5SG5^Rfk-wAAKZm+-&F9*jCn{1{#-%y+Hi4Yq}Vx{j7Gy+h$+@Pz7qIxc}ErG760%QJ|Lie*Q5h>r# zhp~?ZGE{9|covAQ-V89C3{8<=kULYXN<2;_bQ`XvZi! z2u|X~B2E;QI!(eebz{e$VTD4%VBLl8C8{bBUlbFr~?HQ*bV?AZ#WtOKSI;iq?Tc?nYm)}hX5#b4}o&@CIcVabP|@)0%T znbrNHUnU_@CCl0he@tFL7zCXmIq1EC)ExAlT{0{aVb(tNA|<<)njfS{FJ}_q+2Bm2 zKP)o5bIPxjfg0$?=~E?jB!!)eZPspFAVB^Vh zH#YU$UaO?LQ4?_7*I_X~8~sCA>M0eXB2rNk*?z`fq~znBwNkZG@}1bQ_Ow{-CT;@5 zV37!(edW!XsVN}a9YJ{^Rwr_ps~H(uXvzvqrd9SiY4pFv5*yo8@5bu(Fd&Ess-6;+ zM^dPvDb>)9SAYorY7s4CvDa440HKT-r)FM&rK^}4#)T|P%Nxj3pILobhEqL)Fq2{q zft2yY5Lq)v22dE;makMmnJiE|3ViDd-FicV`{h{E?0Vhf1Asq8`H9SaM{`D%wp7;J z85mGoM+~7jFZsPSE?!q?6!D55sb*?cR3V=Yx0Yz_K7xx~jiTg@v(S2|o(lB*Y%Bf#&yZ0B zFM_iUm7lW9ed*xLr~qoU74M+kFNBtkwN)hRDCCj4T=vHVqTmF^R02^a zIU@1gba-z8!Pp`7@w!TipG~Sp&L9dm_j^EVk_5%Mf*V2pe6iMLcn4>h|gy~a_{jv~HEQ9#O4szS0TY->)^naJ^$#@)^ zH{^f*Q?*&5A;HYAwFdvxNMW?%!>M>*Fs70*Otgz%gEV=R_o`JZ-k&JQn%hZq)kB&Y zuhPXq;+S@SgK*#&CS>?2j|pBQHsx)9+^U}THHV&@_F6P05z?{o7y-<1Hv0f0zDg`G zvUD6*buv>O0=xPElV?aIs}d_v%AXJSJawjl=vfD)>0xVx6SzD1j$*$p+)5+>7-|{$ zJ`B&zepc7YvCC2lOE(D2Z^CbvCfo974rW={Tw8x%I%OvOf--4{G_qrTkXvWBC;CDWqK(C{@8PAn3c`>j;G4ft{RZWV-?M5sVUn%_>` z%+-CdyygN7WkR1Z9I?0C{N=Xr;b8=RI^jj(P`E3ZXmL!M*h(~=byE>N5C2Tk+Fe%O zL!EStX^Qu!oyxagYUe?4xqlwJOUKlKm=isylF1TZZqi8ZS? zsz~gc!b;kbbnwJF>#b;@3C`2+T~f~L+*G@X!3JFgN#q+xy^^Lw6ZH;o$qLT^O_0$; zh$_(Ad`VplQhrs8SwxUAcnQ`hQ%4HF!^_%(8`;$>O7)a(x}pLT15j?P+=NlFH~CJD z@1Bz5u!1*t;XKuoncmsG=Kv!871s`;(is({i{&RaJOLWgtdvJtLjzK^UlA1FVUi8) zG%z!*#pd=1F@s(y#}Bgb@Lmq}REdgp_7v=9AJUNSfOnFRm*Hjw-75Z@5qUUabKb2X zRN|0lE|`Hy#>*;sR)uxKiS#DE4@qpdP&2z#r;rqZ9^&wINa{`;Z=o~!64lh7Z}PaV zp;xf*)YbK5#pv@#K9a(Vb$gB&cUQOVa)n9EA=OA0mA5js_D-sp+4T36?(*%r&sL6Nd4_UTQZr>`yib)XOPMmO zQv3%paer+R#WA>}GhOm)Jq4BxlD(W?X{LyMF70WxApH}s8+&hE(e%)s$s`W6{E7u$ zVSSpR)VqCm@cPW=F{Pm*d?i$1opM-d68osz&h|W5P z3ZZ=Qr}BX9(lVx;+P+wqkMd2I4z)}s+!pZDv;Ln#@@+3v>K+~3wfPJu&G?ZTFxUBX zAmYw7F^uxf+t@xSGt!<{9EA0mNtYLO(|}8sU;v-fcrLSj&*}6`6T^27joApOXc~PU z{w7ylr%@d@tEk_v5mVWLf5tuZ>*C2mSSxyNEHf2gzJJn9*YqbMYO)m8o`p&&x(e)I zUj^pvd2(jyPY2LRjOgT4#T0sD^A!I*`^9fjMbP;3+fG?MW2H}PMI7yK3ZoN*l|Qii zMKLrQ8uPZ5a^Gfa@iv(J#D1Dcq`@YTgmPwz98 z8TurFWl6jW8GnU7j!eM0D`#SSL7Y4#Hse#=GW+Op-dVj;rP(HYO;ReeQ&d}Y-Q z21M#w^J7=DDRplFB0ZZl3WtScc8tZofuJ}aPlv8A{R++j8k8%XO(&imhZ&xrmmEcqRv4R7v4W23(P2GQ z!3(j$Q>lkx|t;T zHFZ{DzMnxFzN$0nj_BcN^kIE{?Q85xcF}BuD<~@{Kk`UYVZ8f`CPW>dDw>kidJ?skB)MM6n>rQ7V-i8419WSG}4?V2;1ws6h zfCb(f{-5C!qpwnI{^SyX`)X+fF~$r)`hm(AMzo-4z2j&QdzI>S2!^yR*nhSZ1YR-Y zj}%BWD>-stwkhJWY7Cv=6o+F{fm<7F9>mb8B}}$b2Z+6Y2<-3l28=1Hq_^SB0pV|V zwQd@vvogVxA*?&Y0Kp-Q{_1*;NET@@m}&6Oztk&RKgs&(_@_{&N0lXef)v~{PrdGG zr?Zv=EsJNdB8JaO(GgNFok2u4JE8X;O&zdCh%!Qy$}b4bSTBmBg)SWP*k_?xCrk591l1lq7! zJX5)63_11ty;X?dleL>7AKCUEngA8+9UuVmJ?By>nui>1yKHRb@*`!ez^w=}UPjT^ zdYf-&4{MGHCgtET!2q-D)?H+bPl#BaptuIwrAECG~ zZ~6m;!}S0?kT);>L5az();_&EG@gobGF^mPy(xD2N0|X5ELwaZ$Xz%b^|I z7PX>UI-k?lB4(GQKjv#Qy2RCojUDP#`2wRG-H2qC;@tr$z1R81`U?ISu3;rHk*@Xd zW`8oj3#1KfO>d41iv=c>Mt=c^Om$ITBP!v~@gF=-7TFn!?qvip6YQ9ZxrpJ+MY=g+qFD6GNd9 z1;uP$jEBPq!38d_=X(eOT>enCKsHUZkMv{SpN){4(&*x@)swhpM2Ob{&PYX>4; z69s~qiW0^(8+N_01EI3!PqP&p=u!yCvuHV7+57>+nfi z2O`zUZh?7Ju8Os4>C=D{!GzC)Tr`_$sF|43%~o8Avvf%p7=}cf8dasTqw`x_5>&CT zm;-N{_8}Rc;%c)G;G9uhV%5QL0F$L4Rk@{~fcLcK^EGItcwR|*Q0oL@xU9dQ<(Eu% zEj4x(`yefA>%iuoeeD*6@SoXr_7y%&D(1tx2MvVn#Cu&UXZ)qvQt75QhUTUZ7@Xpv zrLbFXbV(ERm^TFe)+}D2GEI`PbP^WE?Vr}VAP<-MoL^df$YY^`*8EhQE&f|pi|@9f zd{b%D^7mU!_27FKa8s9k?%W|_$nu1!{YDJOZ`R=%^W)2<7)iLyZ zy-TX>V3SX)z;822PbtQwhZ^MTT(pz$m{?Ah7R=8u9^cLG#>ZbUX9w$;aQ1~{Wv#;x z=)*`J!m6I)J9fNeJiU{SOsx*AnxR13c4H`|zi!hERex6j_@yHEGG|qj4k%Nb@M2)b zR1^g~QR89{GVpXu<6^*Q_jb?q%<{XZ$j8>NmYYot+$JupvjI9eUGv`*r#f?uTqy`2 z7mk6ASZCHP5iAZ~u#9gY!(ER&4-VmL9mPPg$=DVSR{GfC;~=z8)$KYA4F0GviZZh`d3jWPlD{yOi!&DqqmVu z&1YD0tbFSg;B^U@dCq;l=Wa=}s0H+di53rsb9tQ8>!r`*`^?>5TQPxTEM;8uxKIv- znZDvUb&D7|o+Eem`s;i!B9-#uFbY}LU80qkGmz4(pU?Dae&4Im(OXeFrmOp6Y1;CW z-RebBhMyyOvE8&WNkA7d#z*v*7{nJ_y}FX(Ea+OLxw z+F`wtWwz-YkQ}+Qi7)PWm(A3XJa<#i=pin7SyGWbUt|L9o9X}m|UWW9*hNWQu0oZe_X_&j|fxUp}l8|W^JC!oa*WLuuOgE-QmkQ*zFM6{Vql)y=MxZqF} z0{$Ej<%uh2yDm$R!*z3B59PMb*>BYs44G84GH#ePS9c~pVAqUQexcAx5DyriH`oKr z63-1GxlyF-0L8B_wpE63tC#KEF)*Q^Fn)L8ag*l}qd#fZ4#W*d@mGR(hccn5u6}_j zhHfxOxu&MEfBe`RF%&hM20<-93}g>0M2PY-mufbxCcg?WH@MF~P`0+)5B{au`B#nc z@ykM2?YmPBg#CZix%hjHQQg|=KS}jV>eja3vNxZ&_+S1=^unUt#X}+ar^~S6mrmOa z>|u#4{0hiTPO@!^2||kH(S%>#Vi6wo(i|11p>oR;Q8PtR+iUR`@{?GUl67w{7&A(fXI5I}GwCn)cSZG)g7 z3e(!C{braT#YS6o9uPeG)SuXmH0^!Uj3S3QBlnvNE*ND80i`(+eW z5Vpd7oIS2R)qo4997hf_hUE}wQ}A0@--!7R>BRs=8&Kr3TXliqg=IzP3h9hrMgY$U zTtXO$BXepkKt*1evBMzq91K4Y#so1q*N{7)rSX*mGq zHbT#hqd%7l`=&;UHNFx$D*l3W5Pj5ag8~9HyIJ1Zo{Az3LwBSF0;sQ2PM&p}UKv!A@$BLV6^}r{J<}6uJW#!oW}kIqHiIYo$iU zM|=F`WZP(Z{mao+^zH0w9Kzs!T z&3Jx`?sFe(K3@S#>!hDVcvXI=v+7(*Lu5~kADkiEL13>v#ms+76h)x}s>BWqDr<73`Uq7yg{} z=z_ad?%>p@PPcT8fgk%^~E}# z5^d!$)hHFqYa=Ps6xb8?+vP`e{;ML=RcfKbWCtMAZ9>^bS@yq{s!s_;}p*GNAeZkLAfkOJA-3(no@YMS;hg6zi$`z z$X@Tl69S2)iLy_@uc%3XU+tFeF)IOb$1S<8>3z(J85V-G^hP0LyMg@5od4g z5?fy;06l5Yzz(9ex^AaZ`hvq@aB6)8wpe`HjAo=Xujs zduet=*6Mn5&h^Q+05#tM+FG}y^W`cVj=-)}@v_jhZyA1)7 zo){?Gql{>!8#-9KLcm$&~pdvd96EqA~U{~0srOaDh+p}dIt8DSk|qo1Y} zB@fjJk18>3xm6;|F)Ky4_3LApP9lrBn8NJYsWKYanluM(hWkEhfjNH;IXX2QfA7zp zfW=maZ$hcI6UK<1nRqZ4xcV8KI(jMK;gws6`+Im-8$P7Q;Wpx4!-VqWw5-7M4Sqj? zguzAp7V2<-JvYg7)>OG%8`ozCwgV?w4f6>52N=|N!9d{B-j9IFjmv^aVi=*x)DJmXmQYxlDhouS`K z{5(nE;NwENetrBUDiP025C;9t3E^H+59u#Tl0bOHSpm+pj&$e-aw1JF&W8-S%2h7s zQ@Vp`2k>J757=~h#E5J{rM^tU7Z@}e;|MVaku{nsDo4Ua+>)=c?(%3`(yG)nCq`9) zYQN$!F!+mNx!GCor~_Q`ZHvWp*ZWHRr5ghan8DXif|oT3ufEdtwODS|r4dW{RC5{I zhN3)}%mHcah>MIQgMzp4^jc(F2_-8f3q4)f){`L4SA< zl52qiGE|O*$RgdUXns1vAGI!DwE#u(5*f~PN!a}0a($dD;knTbTC)MG6gAE#65LxidV`1F0k^8}VVg zSH!OfBPI`rMZiivmQ&{^^T>}!sgoGI2?M{>`o!1G1z=X|%Lh6(m-avj+G_9X z;{+^cUFJ}s>Q{W0xoctdR;~7-ZA;q8f*p=lN?FKr<31aa;Lk>qRCD^M+d0Lh^;TkjM z_$8Iz2H-A6cI&f^&2>G9CbjsCcpJ?Ri;Ha}*iBT-z1w9azcUnPXgfc%ys1T#4mz^P zp?zUZb_sX>Meb)DbthjoA3wR|WmwKip~Q&uvQj;s@mRIY-|<`u*>GC*h7PqHP+tH_ zZ2L;uY-%L$~b6Ur{tI{mj zC)KBXfx5Ct&M6-&e9{>#C0{F^GFO?@Y)A7{T4<^xf1vKN8|IP?0~$5A=)~x38f@Y$ z;rfl=S4L}AgF;P}+pg@-EwkC&*MFk@*RcX$8}0Om@6fsQeJkVn--XcsNcjH|I{!9O z@IAKfEH7gj^u5{*^%<%!CrPl~TDojA(JzR`{eXR-yf92GT~JN^`8;eX&$t(J~7Z46K7*!BO zI*@LJR@POrJ<+9o>3OGxb_}Da!epa0#?;^OB@vQJky@IcrM%UH+5-xCNo?U_?S5P zw~i=R#rc=dFMt;c`2w1ekKI6;gw-LRYl9yp2}4qhT2dlvGq`+`QmvnUVhGt(!-l`V z#0@D|J9!UB!g9e7-dK7S9#an_KE@a#earc{@+&@1F9c`tgTVeo77}xqP?fw33@+I9 zs!3Grw)6c07rsmI1=Uoe!X;wN#VbDB|3&5+;4L<@F%Zo=V_q<{fNN6H2A<+X`9cC! zeiK`A@C&F1nm9Yl7Z~SKpzmVAgM5|bQNcL5wK4<^OvVlmB!?rXg#7OqDkwPLCgGLMl;AQY^N8#enOs4MOcRpVVNYz zcq_CQ26zIbDD0Rt1o8zHt*ZYW{&vIOM|&;LhCda3BsAPD!V5Sr2bw`s{5#70A1kUZo_98v?xBK^;1^BYLG*@PHM0 z#E8Y4LcaPcrQHKnQ!L@ippi-Vivek3#Oyl?2-#y#9Se~L4G4q!M6{7F7Hbjc;n7*n z5tnWi02lRb3S4Sdm;u*bl_*j1J>KdjU6j$v=Uih>cKlp0!NbLGb{G;Z8u&xdlR*fx zx}jSUu+KqxG-1 z_TpY~XMOe)x`X2=a+Egdbu{rcf~tgW3@Jw)o{4vwg74hFO3;HpK6?xzsJYUJgL0%X z>boRvgEm{EA`QzLhQngYl22ET=G<#sUF5eET)Uon+|Y<`lx8>LDM7%`GMO~{ofcs< z38kTcpjQ$LsAho@=O{^D5(b43RTL3?Gmc<3WG|5NmQK8%t2ZdcyakGkGy#AF@tV>{ zXV3t6uONj9xrbmETgpE{H-h6BUmF83!6ZTOhkioAQ<7;lJ9~PJ)>7NA7fqI6* zjL!vo6XFsMvj0xt=_7kA0M(4ult+7$_lHTu=|vEJ)&(j zi1R~Gac#SA(~aQ-BMx4AW|&!C8m*c~ZDEuaRztJ#@K0no6C178_6!){M#~QMge}K1 z!j`ff#moGZa$>baN@Cf)!Fey?V|xnlQshC_$Z8>1<#ng-S`GR>~%S z!d^J#b!A#4YyZfY5_gkTh^DMDhjKn5uoUDOis2#y|x z1^xQowE$&lk~h*A_sS`hzD6?1T0J(#dUsn{p(JoxRl{X~a!d0W*3)sy(iaeG>6$ca zp+SxMiq15wxM$=&Ql-~9mXtRICq9Dm%kFUEi5`&!*QV-)JMGADLC_G8wmMRQMOpT= z4|%X&tf3;Qg1pFCdu4q+#rADmN2|Qg37x zH8+z@ug^wtLq6|HR(WIblD}K=Dw0$vlMnjz z8x<$Qf&HLiXQ}GruU|#m%>&*{uNDuUt($uJeIl|Eq&6T7Y|VT5Al;eChWhCjS|L7v zSNx?O=`gj9{(Ku4O3j{|UXcooKcsoBW$ zjym8bNX9TEz7ri8fkDir)rJEbwb{%S4u80_m|0jBGP%O&L$$IbBU*o|-}W4GfXmZN z?v-yg4QMEdl%~c2Yeb-T@gB^bpZPB5OUZhiH|Jnp?qiZdL zq9RwSVHs5|T>boVJe1N`OFFLWk{ge4bwK*JDP}C?!-A5o2q+F@B^AW70)yvc!Jp#S z=7&Xef*JY4=C4J?vfU9A_q`@Uo$EF%Ll}L5Js(o+>`;OvXiLu_2n^G1kJS7DZH-a& zy3M4{_M7ZNiN2sX(>kTF$1@Ter+5;TgSI#tyJnflIEG?9S1F4T=4n55KDJjPE1)lZ zxUICeqpITa+1*UBa@XNaFP92t911g0(0;izbM`=i7WPp;>l#{@)kO>2 zsLc-1Y*mR&a?sUvF<++&y)bdk!G|#q+SLY3#5HJLaSu;zWgk~@HN159K1Sf^ELijC zY1y}u{>FLOQa6><1kF}FK$x}|)kFh)Bx(7E|K$oh4UnlS`(OmUcnYKMVu5e+_XYr?-4Y!>U6Y|uI-(52;?>(e} z!mV_%s@~Jkq5`tYDx9X$^o}{El|Wy7*A3=@5Go1}gHR(}uU8JQ=CeG(S?u?_#E3YA zEU_SZ04I291hG9+?H$LhJWF@6F>#aXT5VAD23Qdhj&#dtB5k5sQc0cXD*2J6R9I{D z78vsBcu6uNtCy(P?g^rUTS=7fj76F!HCI%}P@Yt$N1lxwUHPg^r+}ZUyW3A6Hm20O zU7R!53`)<=nJ7n2uf<)UyBhi@QO!r-VINs<7YAii0KlCq-UX;-cpb$Tr z0P)Y+{yS%h{}KZJDW?AG(rjdGuWUa(Oz?H$8yqhjh)Otn?Yt_jI1fP(FMpCd36TsX z7lpJ^b_DRx4*QI)2-UjZD+2z<`@P-q`#n`n6Y5_Y&x&Jv6fxDMkw|5T@FEJQO7A#q z^rhDF^c_!oB3%*|spnXTYjM>V>8eGm0^trDe2HcZv37QU-n1GP6$|7q9}sjMLPGl)klE2ck*D6^k1+J`_#BV!!JDm(nxGY0wOS??qFB4;UDE&w^)!-$*; zbUZ+Lv65^&bCI^gO6Da2gc?0EhR+-MFdkfrr_uwNN@RjP;jsL0t|FIOiTEK4m3mR?ceoMvy$>%kgot3T?V)#tt@8CfOZi?BhwUGVFf`Y zOBp%{$@&WSt2r{Pm8*#^G@sZ8ft;Q_CmsvIN|8;;*G_Y`+ZC0z4|lHrrpGHg&w7ow}cS-)08%QV4|6Ltano z9T>K6-q|!YxaW~?_Le)^S;V%hd~d8$-0`x8uI?LY-#Q2X2fXnQI$*kS)79>$ZjpYB z{{EeT=>My3|6`=`51W}ol}+0lH(ZInq>;!%fBhNT!v1Ijc0xdf zr@wb2__qWR30cjXsA#(+z70Bqpt^mXJ63xEHFULsryt;Ml9sV9;ky9Fe|gw-#=QRy z`&Z;{dRR8c_&_KTU=)c{R{Ra&4l5MqxBxSU5GvaH&6YB#u zvu>44$;T>I+Kuo8Z7eXv1EfTP@di>cw6iE-5-v^fMh&FILpg{n9q{2>J!(%A<067i z@Us!y5=oF1sdL>eYnCF%&EIoIZypj%UJnst-dL+4Q>Pr3mPKE3|L$NbNtfVgL%OD& zS;bs~gND_YYAkEJ#$WDTkfQEsV2c zRAcK^a_g>i)0O3Y^#k;OmANr{EZqoyyh^KnQh*u%|8&{^Dq=}TJKKK(Etl$(4nIIk zm*>#+pPQImQ)X-UOuP*COyM}CqNvN-i&Mh*TYh3=LQqIB02`{Ptcnm`MP0&n0E;l+ zq=cpBJN?m~uj^*Mh34+>Q5L!CQKwQ^Dcy_f%5Ut50t9MC?IKRMam6CH1!8xqg9xav zAR?9fyOiSWbhBf3OSe9M3UMo@uBkestl!8HffM_&(sdg>?({nHiJh^o`igNQ%im zUwD4U{X)j3{@zYz8obEdM6o%V@p1bRl#>eww1${NF*q?H*HPsN=AZz;ED_|QMyX*W zsaQc-dVZT&6ScbB2wXOI!4#mZNSEf@TytTzGQ7OY6EU7M`VNVz{-b4in$G5@w?p^~ zLHG#uJ8Q0{;PJ#v;{wGb?I+|oRMMA8A#$fM9KZXF z)n?)sN=RxelxlS8J$FK2Gvo+uArvopLr4Jl{I#7zTMu932l@l!{3bL-oEI*(Ykmd@&oT`BPp<&k0#^~X>U8Z^N(fe;-M ziaqiYoScLhiv{ZqPN$+&4Sfd?+IJW~*7I6(!%|FwMGg~s{xTrgLgZ<@;pw_>9`Qmy z#-hX|KSb6bezbT6c|4T@;+Y&OZqxVQY z!9-}`Mg41?&qJ6!k4i!Q5h4r_>lU;b4ge@;J$*+oY8M6m)3!J16pE~c4FllyhkO>Y zprn>_?OHpvG(PIeBxh!btD$mg!zdbhzHlCA^P=AQ!(h^k7G_kHLm}1Z)EJqH$$8rU z$PP1pHgtFpEVkGS0H*bK2EeNV3rg+L7c*f}b<5(b5YPSh*|HvIe^dd&6%DsHd0U^v zffo?ILVt*Z(;x5|Ev5W`aV++4{qbmms;HGKf7&6B61g0DxkTJ+9}P0CoIxy<43(h8 zrb}(f7EZI}iysoRm{e04Ys7BgeKcTKGwTDK!-X}o1q^WRy$*dPrx{XpQv6YbN7{f1 zepsZ%{lk!pRT$Vyrp~{X`eSEKnCuP>;IwWp+5u1|ezyv!4WJF@CA{CI7ah0h;x%(? zfi281ZjHjE7CY**vMC@hm6!316Xxg7;WM;im$(n4(4N$Du|=xRz#rC5pXbQ>U6iel zSvLO7c0Lo&_3-VophSd<#L$9@m8+&?2k?ox-C4=oEQ;H;Rs*sAD`m`=HpP&aDs>~- zEClNpfm;bZAv!^sUlu89BKz zw%>g$p{Y?Q*#>K{Ga7V2{?sH22LwPJ;`GUaBU%J}1kZfc9fTAgwj!1^#iEDS^TxIX z-SJg`J1opgj9u?L{q$;cA4e|7VoOpOl>Pj{O0 z>xE-hcJLx@7g7U7Q31le2aa$Wx1FttyRak6)sN7WrtWtmFV{^aUeh1;p8SF#EUSW8 zs|`gj>fZmuneHFWLI@^h;KdJ`vG#*z{2$*R{?jw%pBToZTCeRsD?%su2p>JKwaG$x z8z2-4#73We{3d_}(tzx%I{p}iRh>2vFxu^6YnD+CvQs-`0`etxD2s-@%MmK@qWJ)veF@U zU-m;r4m5aP*+~&;_O6p+508~iZI_au1LT&tJz6M%x6ktW%b*Z^biUCOIIlXvkrTSA zUx?QL*V@u~0QOJMhP>d3Hvmmy>)W0i+eYkp>H!h25fqWZVALu0h3Z`wSYOftr_c0K z{8)fgV!tGYnNVuuglOtdMB-mWMYl9$9uX1*iBcG+WxIG26+A&T1dHv5N#^%{4O9$o z`F#aV=lvGm&I+=?uY@LV01#{d42nSdRvzJ4);+In|A++#9Q(@ZeZuS3Y2q{%8a}yw zU)`1#!`1SaHR;PrYt_F>|e{Wm}k1 z4_PF$3+S*urg!dyCi-G_c0d?P9C&#amNw7qP~2JPddaOrr8Pxd=?mM8w6K6srDIQX zM>_FJCr*Or_}jGtNBv+0>b!@sqibpxFH*1pYc}h4GuKWdQzT59`4uU+d}Ry5_BW6A8kr)wI}k#D0=h=Zu` zI{A6rCs_MlZnvPfF$UM=L?ucFeFjiW-80hf?wb@WCY zGtvV3B*2fqx}@GEb!WCHlYW-a75I{O7jvwv1rh+N_n+EHFkb?|bwIjG+bmQP)5U3y z&)`8a)u=pxmISBi(lGX`>(QFgu9i9RQ)gHO*^JN*!B%k;;%xgo>L zK>|K(?rpk#@VdRvxy{YxBcO48vY6Z_O2yr`JS*cb+*tY6&(GK0?2b59fDwTbnc~zZ zU(f}!4RK5L)S5`gH*1%T^)>?8JtFj-A6Oo7$*ItfyxCyE6M4WD%r(X?60{~$AH~p%biJ`E83*CP}8UOL+KOdm~Y+4)tN7H%|BaaMw=){>Bwb`&Q z?s8db*`Abf02NOK$s$ei>k+k$05Xj)r(lH!z6kbuy15-6P5tYBdAro` z)?a#AQpl;HTkUfmA99LtMtWfE8`Us|+B_^nk5!Gg$Hwv0Xy6kvoGtUt7taJpSqa!HWLR=(^h9eZbPB7~oh=F>=;X_6(oihfwLwqteOZpU21!Hpc+b^a1lO(R{u&;rJRgVsO|iYPKq_!6<$9^AoR%=L3REwjau5BTZ_Sgq zz$_wYV{bVwX5+{x6?zyDFWGpZ$&(Os4k_3Y3zH~gW6QiRT`hN2`|B6>!yP-^cgbIO z4ku_gBA~F)vv;OgvekY)l5p9*8BZ3kMb|&Dgb0LzOj;3bM)+z&sex)qpKTub(y70ca>KIhbeRnNtqeU%zBzC#5ySdj9-fU5 za~7jtB!B&7aZ$wb0OcRh15iGp+-<4t&~?MWdjbR5?)2GCl&|pd*Mfo3G}^)sfFgji zs7QRCy;)e8JJ;Y~R^4p-WMw-7GJ;ETr>(?#iWE~@cQjK@%@FvTXLJF;6nv}mcNz|sHU@q~jr-scWG_fJZ%Tkl)5?p&APR_8 z^gA%1RGuYBv>=i9o9B>1dn}%O_8g6&;!!wkpn*xnc~*gf>by*O-lbAib#|f5X~m9; z<4k2R6rr+-3i`;moxh$mGE>=xAr1O6M!OlGm24|~BiQ$jLIlqJRbie?uNzIk;FmbZ zwON9`JyhmU7#d?~{UnAx;V`L*8zyR;+>Y4baf!e(tSq$Yz4Qa7Zfqn`BH+Xlr1g#Y zAaj_v*5l!;5&lWt)B+>2J09R2-dT1zGO3850G4~1`tOn&SRMIPr;o*#;A^}{pBzKha%k_GtFefXY&|zazV`ZJLZtRI%JAe z5kky~n%?>zr(L{iw$oK_t_xd1h4+j)k+xE8k}Jyyp{Q=)z;&>V+nrW86F?n1JZxiY zCwFZOZ7HoJ(eI`%Z~H?lnbfW%M*Yh5fJIjB=@delaizRaM-!*8o;s7grynd+EB(61 zE=H8^IkSMZErKj&EYDjbRgvR{Q;e;Az*##8^%U2O;UOFxoT2H|llDfuHW&=k43+AR z?q(iRy2?{YlVnxyKBMx>yxDFl`T=jN3?+}tF8HesL+Qsg59)nhUF%b%3Cr%!Rf=9x zH_$G|5})`@jn47Dqj%B$GvnHqGlH60sz2SCwG3ys7thE42<{Ql(va^KDiiwy2LN!V z3IM?WfBr7`7d-F3d9q9G^QiR})GvI3>z+(dG{!T^^(Q@KI?mKlTQG4`Fvr!L6Rq9uPUdX_Me8`ukU&ogGJhZxcj!giIvGGZ_y7fP! z8k|8!oh)v5!K}kUOOe-0*Q-l>zdLSIj?iOaYD|p*?4&qMGejv7>G%x!sgYn(d*LQ) ztl7xV8qvMb?4f#=LnjgW(Rkq4PLtDK*XgX0^v9%(Q2$~MZr1EFuyez-0j4+`jAH`& zR8x(-mu{YnNrJAb-zVkkEYPwAj}876A;OCIDn^ChyD(Zru6hD@Lp zoV+Tx7VQ<@RdJzU&oQCL+HXO>NcKP6Yjpbl7~~2~4B-nlgL)q(oieAH(%(W|d?uiY zGnT&jj(uYy)i5Yf$X+cOVkf7NhaE8%1d8!_Q2hH?*x{TYLfs*20qAx6l1+`(nt6*P zm1R??auGZxB;^gtTPJ?3IUqr8D{v71Q5TaLQ(|gykXsxJ4%sw;Mycub!K#<#Hy#Kq6q9K)>(1Jh(_h4EzPgryd_7e&^mh zgFbP-QyjFLWUZX8;X?p>#)4varvw8zjtAI$m?322*M765_@E|ERpQ?v>B!QuQw5Q7k0v5$_`VG5@<;^{^e0B2Xm&Yz2YMkm@~c z?tUleMe@Q=xZg0wrTYkyeL9Okd@Lru)AfmP@EO!puhgWoJg7l%2%ykr+;(7g0Rf?k z8lF96O7Ca>&$N_#gc5& zr*ezLvbrdZRAVhgcz?L$*jM@rohMD#iCYxO*2###p2{Po=DQUDKA1Q)5&KPu71-=A zu)9~^TEkHRk8BAobOF%NVMcC%S7P14sfaDffQ^SjvxZ0*bKr=7x>=Lv$-YQ4rBDFw z_L$_sJW3qVONY-ZZ|w=4yZBnjeO*PY2d6@{;sbd;N7uEQLfvC7!YHCCsaup0g|#vs z6(qPJw50gdXqqKR4NrZx)b(0mgcgBlqN0BylqkK zq3UN=>{PB&X8}JYA}`?-O=_}S`ypoKR(a8!*L-k~>-$TOGDxk^@?V5FN<1O=>Art$ z{#KVf2-(#!Sn%)kPWa#jM_4rBIn?$Xo~>?!vOEO9EpOp?k#oExT|U8>ptNPdVzDt1 zN^*+7@nZk2!X63?6FdfWPD>n)bd|N8CG5AiQ{cN>WP;xDTcjKM1arhOmR2gYw+-aH z+m!1UNzIbshhMH;RGzqUH%MRV)Gq{a%;y-_z;CoKmh%W~F(O`vR1KJ=fA@ifV?IB~Jp zf&;hVn(AN-oF@}>t_xt9Z3NooyH120CJ~l`wM*6dsi?SxVGL@yN~6S!V zB$D^r62PpbQ|e_MbXvX{d+rr8KC7Ay8tbR#HkdNpfsEQ%J7(Ys$luRHJJUJv=8e5_ zGtG#SFUG{bfVx|vkiM8SK6hJaNo!70f zcKM>lm)_`2-ED|SN)vj;c?Of+CT7&WHpMVBytmL@X@EgVN`*6?rs+N>xV$%xws z0g}0Hy9k1txoOk{;6ZZmdunAy>W%xGlSsZG5yjnp6bGoap*a2C&n%=#+p)t874gKj zIY$_4MZ3_sNe<}#ws~(4sC_Go)(K7qu!fW(a3}6=laocW7+OV~#12w_ zpZoH!Bie6!D7FmRq&7qk;&JE_^XXs4$n={|-0E<@fA0^2;A+7P;+rdo?OQDU^TDc5 z6{PGMbppfv$@9Sk`k7O9(cch3{}46_YJ5cWr~L)JEhuEHY0rfjODirSJxho;1|I(6 z6KO_AfXlGQvD{rL%gsh?Zi*;8epa{oIcqvfB5v2ZLNxg0EpCUCIQSSDb^6Q((K%+N zpLt{Wbj)$cTN#pQ7{d*q%Nu^Y%WDuHQx`oGbL#bRF{eN)GXpL$98V~&a>Ge;;uwb0 zschnR);Mh?hdPr$vn_ZPv(b9tdKArR1}EXFht9M|WvN3eWP${cmkJe{ zi+WUSD?=&M{BQ4&o~zTAdGBKXOOsk%Gwp-23fEo&Y%}a7tOxVF=PRgA`1S+Dfg)2N zjgm15QgmXzO@uM@O}b=bir-b-AZs3bdZ1+7@2Bh75zmyu-ddAZLH9$ewd?q{8OjR0 zcTSZWa;{(dk=0Xk*PScRa%^ub$>C{)>6-LrjuvqxgXd@-wgzn|FGMPojCL#PVpWaE z-3mHc+k2jhG4LF35P+6DgwoTeg48?~agKAyr0U3ZBQTl%LbXeKF|I1*E(4b*)wj{d z##dd7w$(G%9G5hAsqI`_dG3R`J?M{6OAGeLcyARZ?;9O=>S0K_24OQTkzJ^nh2ncb zt5MbC$StLJ;u4F&?hbZvB9b#RwgG9$A!F#`#l;s+)EWl<4#QAF&xykYY8gcw(yUOc zzyFSSVWo3Jd8r4qh?4LDwHAtfYL-yKzn9^?+#!m$e}A5j*F{u~4aLYd3KiW;>Zp;< zeg*3owY(FnKmHQTLh}3`w|#@v-JBkywr8$aI4FI2#G$=orfCL}1{XWm1zHpsFby-0 zf%xn#MQ<(kk?fn=%!JPH@NdGftjK-L6~8;L&YYBG!rk#M?E19(_PKqU-ddl<=asU1 zuDbE$+~+E{%B55+?5drDIDJgVfH|^nzo}NHYGG>0sHnT9iQ8(KmmMt^X&v)i(=)>K z0dsE4W-7tEhx2wVay3|sX+KlX>N)hRQ}D*8JF(o;E|eX|w%>&1Hj$v%mHYuiZOG0`8_fK1M5VF*vt zc?)u3Xy9V-MPa;4lg*g>>!K1PW!%9=!}eqoetUCsCVO!ubDfbVf2UB7I%B~%zSs5D zw=>tL@eGj;+~{zZeqv1*|mON7_%gCSHPyTu#U+M&xy3#^f#Rkn$mmTX| z;1(o0iy379C9XGNYU>ICO~=*`ozW0zM7Y@t|Gdx&B<}NHXB2;+*oCL zR-1ZG@|{kqdd@n8k{NdM*Fu4A`#aW)6<4s~^z@^^7cdrCdP+EuS1&k%Ca75{CS%PD z%`4?;a^QM7Me3gpG=y3CMQSY+Jb4C-YQu<0=YB%6na6(k@BcBG2h|;w4yFYY#s2}# zXn!n0{%>x#{{;9HWGn;eF*c#M2?197f!D7LzB0nY;O(8XzdVfqm zlDP0F3EEo5J`7MadNpPoOX z<3h>N{Wg#n{iJ3s%Y%={l8|4R5>s>|4|i~b|b)i;GQxkW~tN}N^|Cnr(9`429Lejrh?zDU$S zkOXaQd%^|S+jXi=9Fl|rKFXdbY+nFhOUCX*$Fpg2JVn+-kEb%$e($S>>g%$5bee5u zHVL(8A|w9pGjr!5!}1h8k_J7BeVPU1?~a(!2l(3^H%)QYet5+R_dj#5;AFp8`S{)) ze88MqtDx;v(1xXXyR^37FdRUA1`>eqiV=rW%t5Kc{&*>(B;(^9V~NHn?=d}q>N?vU&f3;3_+l>C}SLw>VX^ZgvL+}e;|2s=>L?)^mySXc8)KB zmU0o(0j38KvCmip?${2=AF(8Dpp1?KzW^}-;U&D*a?>MckD1TSB*gCe0ZwjLoKERG zjA-_F*zS!JPbc4z(=7#~=1ero9<*67m+*1O{_6EdoS&FS(%>E~Qc6(1v`VQHH#oc% zI^Q{LAwXQ!w`8YAi-E|iDTF3RP3=nvp*&VA)3m6u%5a z5T-)Y94&FiM0dW&Fe1d4&?F)tBz{Y_)wed8W_YW`r5D8w%juECOteY@kp^?7ST&s~ zPUO9aUfH7K?(#fO^0g@tuAaL*3&37@vT>HNMb8lkE-`|leWhL~8eGgyVbUlNhLQX| zOX=a59r)dWZkbs|wXD)hwHGo!O^!y^vo$EPX(dH|Xu?LL(owPYd>`bJkNY)t9>o(e zK*S*Y694!3);Cu7?&$7r`RlNJ`Dr-36VHLjrhePD%S(Yo?U4w?!3V^jS-)K_sYuXr z4+Sy_ya={2{mPtS@MtM+#$hHRkSJxA7nKP53|i#Z*eqXO?-i<~5E+H%k%f%d9@)Bha=eq^44um_Ob`U_K`IqPAVQ1?#Ir3#hp0gKX zJI_lcRuWUuuL(~|+|8@j+`snc$YkZ4$)CSK-s`%x3T>VtOCfO+kOa@Q$`|l-ztkuj zYql~gM8j`nRdcEQIJ-ibdHFNpwM*tV1+s;Mw#7o)b*=yAoP~(dXylztyQJglQJY%v z+Dx)V*CYVt-@od431{a{_P@?&nuI@lU(Z_;uEcY%1h4W`COA z{01et7;Ns2my|Aq$iVT=k`|djn!17oW`&Ru9dm(V%4lzXz#aeVaLTgT)OrrfNqNu} z&(AsQi|Q4BKofWYv+wX5MAnAnYXIwzX5dY{69ykA)tUB`%<7exq_NlK1XHX$-_as_ zw%C8v_q_o1iaskxnT0b=#DR0tr#+ z%ye+(sm0zxDlx5bg#%#%*aD_(LPh#p6_w9t##M<_N4B_>!~y>t`!tJr5@cok(l$tr ztB{+zdy(GFDtOWUHVY@)0O=rM>UwtXj_SXkf&U;CI0&Y>;eN2jvY&Aq{lAe{|1-N- z*2Kxx&cRB^!Nl0a*2%)a`X5|lqtZV!8W6e`dV$(IX>I&y`du)?fFx@B=K_AhS?RUz z$SE`s4n|#8eqEwUev`P=HHi3~5@;2ST>pAKy}ss~5^oF~9a5hV{nUNO*vK_e>Mj-z zCPhc=3*r8zWU>gbFVo>((zv1q2ZoZxy3YSC=68Nwp&zz zt0!(!(eV|eS^O#KaDB4X)`jht>{`B>_`o!%Y{3Ypb?CG&0i?7wM3JRAFfV(7HATI; z1E5cSq`o#E!*=>)M{kbjJ$a2fl>owdgwT#@b&Y;p@!T7F{p#S(Pnz?|wn3rS6o?K_ z)EbkW>LOm&^;-kHPpn-F`b$2KlR1*Z))p*dB6UPX#Z1Q znK8Xk0rUvM*Z$u%Vx0%T7p0BzAn>5@Jk|EK&}o4KD}txp%ksH}>gJhJ1WflBOI)fn z){zvg#{vhLPcl`W`yr=#%4Q9w#npLO4TDKhs?i%UsAOf&>|u({)NJ+#%L+}yhLppx zeOVa-5>_DtvoulqNacPNeQ?GhQ@!(_C;VXGipCl~7;*#BP>fwy>aHGL(44pj=ukb!JEbL zFGWKD0D%1Pn9Tp?Z2tF|;s0Zlp*iV*&4$#~K6d?wJ|qTp=>%_eYfe!%k5Wuk;_k|T z3jBAG1kn%-DL^pe{$2cGjM$pm%Fy-)yoh4MWF-_lLT@0SXB63ytQ zsD=1+Heb1(C{r)TKw7A@2%i90+~6gln%y`Ty8Bmfp|ltiZJ06XT*r(XUz?Uf>bH^? zzr6)zU1!mT1>q@@ZGn0{h_REsW8?e5GQQzR4)9ovhe|LR7 z)14ryh2-Ipiu1Y8AWA5QN?1h+(GQmsBPbxKE@~J%r6C%Lci?x5I!i@>H@UaEyc9}DY8&^#Ost5=kxMnq_pP@|s%p`~7sYz;eiIt_s`TCbq>HW78igC}NkPS{9L^y%OV5 z@Jt8HDO0@oUSL)AAhgnbh(&v#CEQM2+NdK=SAZ#Qk6`U_H`!iCzQFD`wnO@0b4?FN z`6l9(tFrntfi_8jq0Ms~l30@WBuI^gTZhdn61%w@<$!8wz>Uns8(HABpp96ILd4GQ zk(WfgrEL08`s8F{<95r zq;K!jO!WI(oJRg^4|EKj|ch@^}bk6SVzY#UDN-U{H9q=z>wmc)r?zZ8Qu#-Zh0 z#zLHdj}@gv1TxB;CRwy?tm_u4{2G!D@u=J1Fu6^l*w(J0^UW3Caw=%@|4pyC1(DX$vO#hY+;90Cu#ADf&i{Uh7N1D` zS1LT9G1`lqjzH#=Bw73>P-h8TFr`X} z;L~yz0*m0?8IHNS_}{6jOo0h*)<1@$c+4?g#DqG(%@s@)FA2&P4wbGsmn`nx*o7wG zTl|M-vQg(Q_(Srq`- zkVJjtvoN$JQ{(!&K5Yhm>_F=on`OC{qqSi#uX<$c*>wp>MwToXc4-!i)z}<&VA5!h zuvJCm?eU`efJH@WDBt_1a@bC$Us$G$9{8L{R=SL+0Z%^DjhWT-e*>c!S zAEwtgpYK?KO!-R2_N2x94T;zfW}C6S)4_+&nw4rQ-=E#5RTd$X_HeW3||)Cow%* z?}@ebzoSZ{AtbZ`DLT0PU@KrLFgaY>H*UU9`D+O(=~&=-%g&lOfVa~5aQp^{S$xyN z+@V8>H%+%dBD!~`%pld^^GO4p7TVUb=)K7Ju)Ikx9>w6ikic#_585(|qRF=kE-DIY zg??;Y*~c18v_wgvg1LDWSyH+OvlXi-PB!u7 zNoP@nM$mbnXReyUv?^hjZrT}JB#o>du(6^Bx2eFyHIOPFe5#KiErI6r`xw%cZCnt9 z10UCqbMc*0KFyG`Jdp?0Jh|_#@FAJUd8<+Q%2Dx{O59EnuUHb-DQT?-|GDSm8{+E? zr}zc;-RMPTx_8SX8GlnvsSh;iT=Kl=-Xq%m6EoAH)E#<-+2HKgZhx5jmUNdL33a5R z`eax_TJyJUA&nhAw$y~G$Wxbsyym;#sTObWkcFQ#%oI44z&raMEBT?lO-Hcp1{dwh zeI%GOonVmXg>gfn>1T`+x7)+&a{7uxSG5i>0qSEpovB~CUBI4rSu$?o-FnBPytS$u z@Tvp$v1gb~TqVQMZ-tHjr?o4Ai>dqj(?$v@B}+<3lxEVNR$7#&jk2{*GgHk|(~Mct zq9PJKB*dd6OOh0dQfLvOlqHW)DO{OG!ZLoO7ew}4C@1JxRB&ZNCNOhH5IxF&7U4CQ~ugEMm zCgE|i(NXpxrvsYyb9;!7*N9OMb7mSmNb%kk9=&gYO!D82O>N>4bd62ay^Wtr0^hZr zj9->_zIfuCG3p;P(t6f=2|vi;FpB*$?5~GKdzywWZIP(RbY9vnyZCs^vbFjP>t7w+ zlw{LzvZ77WYhOlidvJ9JGj)E2vwV?L{34y#VtbPIitv<#A4qL?SyK{G@Fi`B=C^vw z2UhRe>6&}u9vt2D=UU^JQu2g*MY=bE^2U7Y+XxilP!xC7^th9_J|IF z;}VQQ9R6KbsA)GUIozCCD17ONYpKv&hf79q(qWI{ZJ8_nv#d&ju4MQqSE@?3i0D?G z(U|Z2hfiA9@`?Gg4#=t>A$u0oj&)k;n5O5Hws0?{N$;adjKB2O_gQHpI%Ck=jk)&tTT*5_hP>-4vK78O4Gd+TK|Q5SG8b4sElupZsfz04O^zh>zOypjS{Pp z$X8$QLL%l@T&7rv8pWtp__oxQKRhC0D)T_5V&_S=#d70Y%Kt8j3D_VTeD;cd_{KGn zH|{K^N8XB`^?GOenvE}9Wjt<75lwk*6C$U*TP94)^hEDu(N-Dn`0yPU&Zfcx4GtAC zY2Ke)ucs_o;HuNEx&BF;%+5>k^Q!MW*EQZ!cz>#4M1J`3lyalFBgS2jyl{zT5@WvT z!2Hm=Wp^65ikH7E7;PFdX0!4L)tZs38>%{y{)Sbn+rR)a~qo)-4jS}%XHcT&tt_nQ;;q^sS!b$DUiqq#;)EeuOo!5b76 zq@J%1soJN;%=c{jB(m%&K&o$zrFEjd;T_ z>K5)4E-MqVeWk;=x8Q*J81i(Z^~t-9K8%yIQ@55lnH#XBD}Mjh=bZ~e zo0M#war@Nx%H-wsE55!7(>^a#kuMvhc8+~9Gca^_#orwY9$d5Suj zjLuj&R+7Bgo%l{#IcuUs!kES;p?cdu(&)w`isc`J!;O`cl-Gn9o0^YZw({NQInrT^ zjf;$^#6vW4yy(@3=Hhj>3Sn}p#BlB%{ZMW-IAy%8gXHVl%Ugps&{520A?0Dy1%XMunIvx{O|5!_l zaH1f3te0c5P5@^(={h;w`OPPGrqLWQc^2cSk;;% zFEjb*aqo9}RtF z6jjG#r*sR+`;ErTpc$oio*7viOZ>^3y0)n9%{mryQchv%K9Rp~Z@4a3byn78-JPSl zpS2_WJV3%IV(Z4QF^iBtqI$5 zJp22a*Qh-u;d)`AcU&EaNrm&OLJr)|6sf*vEq)|hwrB5Y36s*hX&lkS&%A3pfB!>-S5^qJ0{$$+a+S_aYwctX&Jkex+Wc{JXlD6!iX+0k$ zdZ)Z{+b7&=d7*WCuIr-Gp0IV6V-q~DwnedxTkgJpQa<^cw)=+zp2<^cd&46>mkv2s zKF-L~pa51!%3+dz?(jJT4$gF-g6ZGUphexk_SXYsAEyR4L_leCp(e*2Z)Q=z365@R- zo8}kQT;ZiUPj+6y>2-GvXUC5^E7H3;Y34VJ7i)W#iC*1sKu%QDS=lOW_l@R^xW`V? zk7lZx=(HG(O87js5r8#$L;LpPajT z6<3=U{d4|edjRiK`-ds~8KgoNH>WXmA1Bs<;vHZHX*|5U!3qo)CLg_ALRmD92Qh>Z zn7;D{vdhA?Hk&Od_HIBVF<-Ksyv$N84pDXWlno^MVq#vBurC*~e@zdKj4?XbJJWQu6?>B3B-9@Vjb z+%MV}uRZ=!+hkWx==R1zJ5?f*usu`GOaoUa1W7CbNwCP20J0yA#G$c+=@c4=#GrA> zR5F)LqA=Ms4HlcpqOrMj_z$v{ubzRA?>t>YA3YuIdD@2By82W-ilz@$-&c#GrA1_M z7*q<2v9uxRsphx{FM4m;b?pbE)*nl^Rf`MGs-3XW5Ewk%1(BiL!YhgcK*3m-eiwiRI{DigO!YnVM1fYcC$G* z<6b>3P8#ns*^R>ZvQb-D*T$)7hp6hz$%Ts9+(!~&RPWT3i7OcmPxV?ucNsWTl-5?M z8?8G+Ih2wSS7xO<+g-{?!F8EXX5Q|*7n;hPf{%p1O3|8L=Imgi(nzIfZ=DcBm%eVe zPh0YCfZKVIeBTU5gL2`cn@%Peo+G=R!1OAG1 zQ&ZOASURUXZ~lYyq^IKLzv^Z%gD4~{OOOTaixUu=7@6(AQyFXmnZK8WZ_$?*n-<6+ z5s_Ey548@~tfRsY$?!u})UOu9w+mP-J352TC0YbBc~lD~gT>?0gZzlj{$w_5Av1&+ z#N)7Nl)<29joqA@b*PI>jp6rBA_O7|^dry&(tSulG_H;=X;^q_U?3LTVu5Yq)h@uc z7RO*mmm!4kY8s@>ZSMF<==h2+F{rUe`hjo}j*Y8&Ls zgd{TpQT_J?PrtITWx@?|3lP1v92? zhZDVPRS1M>gFpy`HQqyEoClBda2ge}Eqkp!G)e+Qjz+WeA3$R=7|bBj0uCTK`uqvw z#DRyqaM13HlE&Tyv?}Q06ja-8(TAe-7YSA&I6z(s-9B_EHI4&{G79Kkz|cbfXV}6@nq$eq{IuhsU6?2foBy^e z;#U|37Tp9Bc*$7guY;|xS1!bg<^4$k1hx!TV1N4sIA*r#wJuw*)pTiW0Ld#Ff(Z+$ zBx^1U`O9A+#0t7iNq_x02$iCMidLRk87?B?0ex;D7}KP>F%A(eQllex5b!VK%ea`( z(t%)ZPY!|_mhHfUjL&9au~c&7J7ij_PsGI_A+*0-_kM} zCSP&d{n~z!_7Z5bE`-RLDCxM=2*B?1Z+sJRU7O&zPrkI^24v@(P=TNkU)BymL<|{u zvK#*5BO+!o=|ITkXlSD$v=MCSUGCKLVL%1iuY`!mw9a9|D0aX!D=b1J1#yQ|!$=HR<7Z%_1QI+1wI#Z!2^u>X$eJEVLn5^cn;gXP z#TA=nz#rQ@p-mc4Zo%n)gRTl$ER=I72WA)rHEI%N#>FD|{WP#ebbaX!8HeAyfg#`m z3e4Sb1o_SS(bWzxN+W!MT5I~CM!^VRA|4nrTC#-+GPW<7f>-GaN8NBNfrc4EkW)tC zeVKxT2i1-*-H*p6BSwg$$+p=`(^3KM3WRpF)ILqcfrAtuOs5W!KQipSeq&!)K6Lae zO!A>sS*?VJ#PbQHQx-Vc;xvlk;G!i8jUs|N!L=Q^^#tLMTsm`E* zp&gWVt+cz(%G#n0SnYs?_R*f1{aBbg)=1DM(!gz70%L5;NXfsT+9CwNZhYwKgEwnf zR1Bdtxo-s(M-Il~>Pgcc6U<_Z!N-qZ>L8l~+m1yUfoyc3Cusa{q~F2E zlRe_y59Qt(-U2)Q049NKp~b(p%)`e=s_6c?0uBlJJZu2Ts~MBL7MgDdp$%QRX6gMJ z1rk~WMS}B(yp$$^0(Qz12*&)@!zbSS-$WlKdexUbVn>0A#y~_hMKQfH`&}mHnxH

}jm{nb|k24Y}HK86iL~&hSI6T)cd7cU!l^ap|=oAmEs~FUSt{6hB1kf-;kUARX zC*kd9kqd#Zw_j*=8d|>>DA2<}<3+y!k8u)=n2_75P4C}aEdk(mAS!c|n9OW`O3=Tb zQGYKmH2A)Vu-Rp=?HmK9CI(R$J%f`0<3}XvV4wyjn+6b9uwk@6BrZ>KtF*g%YMv}2 zCtv6a3Du3S4g!E}t({30@Pc8)k&)|3`(kl0qcjMuXb0(W{0Vr!gm5VEztSZ=Sz`Z5ukpLYy zB;bm;<3n4*yfs)7Y2bZe(2ZAYAWgqRA7lMD;IDw7i)^~ZziK>w5ud~4u_-iL%l_1d zttJr?UPgQDMIdnj`JgSe*bARzU{2#_(h+g5w9CpeQ_6uRlK~tp_^aff0uOJeHbxot zXQ7^+4DoJ0Un=ma%ttVEznLPlF)+%8vi*DYgKwpsV5Q%V-2RMM>2y#qn&%etzwijM z(a6IgsVTq{3_R$xXSo`mXP~07;IY{-3PV2tGI(YJ(Xh9>WBf#jdL^J?blxU%@X;}5 zijfyi;3ynK7nzpaqr0`!5tOOlIG+EZDBZo!_fE2Xg8N6>P418S!(Te&wV6 z4tFjGk=WA|dG2ed1;0fQ2n$j6DEeU`YWl&0Z{i3uHe&mI3Ia6xH<9#a;p0Zy_FP|<<6A{!qV|FIT~ zpCg94DSR$f4}z3E1YdM0F+VE+n#uHo!9J71Auamx5!CwQ@CdAnv)EN?K`^sqkD#2xq{_VcMCbyo}M;4;_`x^^ZP{|4F5%oGvZ^JscRJ@h3R zsL(zHI{uWDr(I8N0JGz%=tOTA#5b%vq8mhhg z8$6ikh_N=LLyK0~b}R>xAyc1d@7{FvH;4rJ$+NJjZeB8QR1bqdutD`hy<%AS{(QnW zX-seMV?ENTe>e_ahR`WkaKNy>um9g!@ghEA=pDU(daeK?7lH8zT4&qgz;e7m$?w|0 zj;{PKRx$zwrcUe;=zO`FJ;NvZe!2k(<+4A{@Iiem&?d3R2J>m7UkyXsugUMNz~~WU zm53;@N9yvaYF_^@RQM)?Fk;V@;xn3l`QI1?nG5!`B|c+v=P-=l1^Kg>h;#~j>JXoz zqwCix1f~(};V^vo6Ft8OFL=y^J-veuPVfCiaMW2I*c^!6te%fwFDi!Xt>2~o!)*p~ zg+PbFZs^RXm?VKu0V_bmZSOp|A9y>3@<~+2{W1y0Bk|n?Z|4?1n~2hXXTx(5ysb+3 zZ0n}|l&xQm!?_1`7XUu|MiqScfg%p(+J^24Fjz5o&w=yNAFJY{4`{#t4WkmAAc=jL znorO(8=nBb2F2Gl5%WC<;6C@k y$auF*^O2{|7sI=O8Vi8E)0hu1*GTOD|Gs0VKg6I?fcpsHPpul%lO`}iA^aasi^$^u diff --git a/.nuget/packages/AppLimit.CloudComputing.SharpBox.1.2.0.1.nupkg b/.nuget/packages/AppLimit.CloudComputing.SharpBox.1.2.0.1.nupkg new file mode 100644 index 0000000000000000000000000000000000000000..ae19042011252b05d2680e61e413400be56f45de GIT binary patch literal 176651 zcmaf*Ra9I}(5`U;1P_ED0|a+>4-+yk-xjJiY zTYb}2Z#~t!c6YC>APo!m1qur46O^d6hjPIhFEj--6cic^6coxwtG=VDjS~~&f7is= zX{mnZFCiE3o1|c0*%cRQx|0xXhEICqojoi9wt>>van57STw6h?m^YB$6Zco|8T0!U zG2oyHIyNccN>nj%~$&+YSo7>u1L;)GlBANkdX7y&*8W24X<4+9#6 zyG3krqG9$K{^{cbsljVz&mF zl8*5A4|uLic^y$h@~TeiXtI>1KXttxw-|BR>-xbc{6A_gtl_V=f2aus2L(m-aTEFd zTgKAX(wR}z#@@w5)ZX^Di?gMjIis?Lq2uo#_U?>!E>6EqjXRZP?dO;=I?1kx>9d9N zTO1GFdOXYvLZcMEgohY{6doi?+nvbq8^@MW*D*xl_J{T1YA^3-nH(J0X&T7?(o#Tvedtc^1Z|@F*qy$VQPJ= z23)b9%8#%)S*A3TvS&^+(m1&>ObE#3Og^!o#!3S`hzm7_#g5j@3suQS|1~*UHd-6+ zkesA>xmh+xQcgh;!!bFsl6@QgwoVnoYj14p!yVuhLx@iSx1AWlSH;vHze(&EDF-bY zQ&V+aR<^s7=pHpf(nHQ*u^#*hzfvd(E>SknQ$iR3^WX)H1U&s4CBjSnS8c#VZAxda z9s2zqX8=9R+P&tjs>j6T*5&ggh%kmoxd@F$a|?(6r?sb_AiVYEz~%ajT>OFdbORQ$ z_j1{$af*u__-zN!O>8{dkZR&}|zrh@??x*H=^COe%jWSUaR#|q;4ut^M4;Xd(Er++U&($-MToBBT>X1{#@ofWH=UTLpI2SM*))Vsn|;{&uIFqNVCeqcD=Ij<5nTQ~eAUmB+j0(koGZdXU=jqX+ZyS^dz82c}n9--pdu zmGkR_1h2?^v-kE@HJqnbNM%#n8hi%%`>(j7>u&#z?066TuB!}n+k(w|`?*(7)0J}> z%wi^c`NqjSb31~k=;*GIbC4UxYjcOs1f9`{#_y0UL$u+rGnEB^EG8y zj%JRbbSEAe@{ke{*boVK;FywVprH#_HCL0e#MGpt%Ql&<2?`6*N{Se0F zgT^~X!GYTxpoVdm-&|wex7T zW-}|jE4Yh>S+56aZt;6#6&^0El?FwHjLz;*f5}wtPNu1~MZ)*Ku&=X9G&H^O<=&c< z0aWMKVvXXZS*5`VXIR$m^m;$h%{+4=dcpVf>w`+>;^pkrK|05m;9(7A$!uw{tuRkP z!Cu{>uU6ac)sP?SZ)U2EBO_eZQnGi(_|h9z8bm@$9Ob#%CrDoZ5EB1^jZ((AC`*i0$Hgp`lJ{Zp8C49?3Y$`+_)Fmwtr@dyuYWtWqX_dp<9ok zMeYCX%{5=*`X!BhDxpeC`b&|KrTbMA4DT}W-RyV#f^%-Gk~CKZS-?+Is_M2lEe(R0 z#5slXP?ujOPLq$*c#ws3Uen*Y{2`q5xk4#Vu-j0Lwuq_UUt(d9a$TabtY&tG<;x4= zqiR@uS!H7|PtAW4rWrbUyX`e6Q?iX`M_F}udcmkwBCGE3m++;k1zuSYeNDy&$-q(Jg zyD#yet5^%pGc#py3FbKR=HJjFStMfILUn%sUgX_Lc^W4^n~)TpO;2QMF~$JD(cES+ zmW~|qk1%NpkDgxZknF~78p3nq_P$5@s$1Vd=t*{=C@c_*uRuUwN2EwFwZ)gZqVUeq zn5v&5W-Ar%r_k{rMbn#$6NDpvi=|t%$uW9Txvr9wC*zAn{CJjVIdUd}G}<3wVrG2A z(D$6uF^h%YjB}`DZ^nC5LXWCk$rcxK7;W?zRehXcEUDf9_WPhd3SrTNGvPZuW>#39 z(XXjvZCl&TuFk-lv)?ii^+PwdqMlA!dBbXL8J;^je}&FqOEAfr4U+vR&X%sYyRbm9 z{%dK7JkY>Z2uU%FqZa_IBfOE0*!l^`2O&v@h7fxIt)V^i0bn!Xjch~@bP{4vpf?l{ zeP2Wn4HOC?af5a=2Y?Cuy}8jLy25X4=nz<8!8k+@Lg4#IAU`Td{F=lM7HsSR{PBl` zcrq{n`tc{A00~5OO%eh9r~)v70m>hU+`)p`TCY)ollvmkh+roVU_A7rHvnAd?@fwc zhXiEW7pZ~*OM3vv{nrxt%As2M&>@Dx=fi>C03g%B1d(vpM~8U8>U|Mk1kfUcq#GK1 z(#i%0vu{}2X@74rG>CrG4F=nz3+z(k-o7Es)=6oo_x6u5>0Lcb;vf&q(r z0I8rK9RT2J{}=LPr6ssWOU6Pd@H9UNgGBq9B;Wsq9=(o=BxX+p4Gw(8532Ql;YY7S z1L}ikV1=_lANIVkqO-z+sv#uyaNyy!S43d(hpJEz=X1u7R;L4KlI{;jQGo9g>(Wq< zwg7Ol{|h5JL|oW68}X5f#19m>geLNqf`|nDs0=W{0}34|Aq&67A!@>d^qt&33ug_l zvp_v+|7X;N2k;u|Q5;}`4}2f~uV*%NM>3N4^mbz?Nc9>KEYQ{iI0;iHfDXYC-cSPl zSwjSw@x>@za(Mvtp&$$VpbO|$Qgn#Cu-#Z-f^NWC0^&C)@IF7tTX-W0Q4<-odQCzH zy&Q+gg8_5{i$ueNGd+OVP#)?46C#p}iNFd(uz&{;52y_xNr!6X_|NMz40TjT44j?4YFpx)n(Dr{6IkJ=Jj|CEqD7b=2sNj3izMB8&*&aStFJ)bfV~xWHzxh#dmB(gUddzeGI<)3T zLZOo=0>Eg(NgtX18C1s)qJp;E6Y2T{((?d%K`(#ggn%&GM<|d%PLK~X96bTxd;g3N zsi;WI4<@F+i%bj$KKOTVqR*g!a6EvNBehCy{IfTRu*3tO<6#hSauAy4V z{!5AKJ&_3*a*d3kYiOmhKp9x2k-!d2pyPi-&xa9q@Fto7u&BSc7ds-vL0FD>G(X%w zBN4GRM*L$qz!ui~FgcE-Kf=tuNCJ?a_<~M;Aw=iF7q>VjxB|fZ!hb!0mcq0iz(r`K@jx9E z33&kcm$2tG$puWo2Z&H4xBqGK?E?!~kOBZKD7+6A@r46*@PnA3Q$KQ(j06Qj@(yD{ zj;_RrZDI&8`2-YzkVr#mCL%5q0Ovh`qR_4N*nLeI0*SSIpP6(7tx%aX1&vU}M-R06 zrIWcTwoOMxt=)}02J!W})|x{W>%Zx@lhybz6>NU@BbHUjRg6*6LvEhuKvuPE*IN!1 zds(Wh4O>l6LjpE2N@CBhJ^)`h)zvCPS}+JfS;#6rAH|ZA@+i(~!5zqdnJB-a!mNX@ z@P>xNC0Dev)`sag*mOhMMk9J9bV_O8)j-o#2h05rBR=I20X&>aUVUUQ(^%ymDnO9DmW-tzfT}Z@FKRTd!X*+Unjx@5|ZO zS+ri6Lz)p?)eBVc!*we!m4db*Qya( zTlvgFH$R@bcguT_)|tcZd)#a5KS$niRs_w-NvH3s3)R~`yEf7jJxiILKJ)$CP((Rw zw))ovHyPvEI&aKTaWc`Ej}Kf^dSgwcTEsf`GF$c&L9EJZ?xK;^GdY1rj4_hldvG6)GGI$vMj5=%xTM<_|L{UaqJEEV( z#2)+ed1Xr%@M-*Np1ouusxAf2R~`#B>N1q=8$qqZioFUk$L?QO!S zQsXpBOyyL1n5TQtEfQvb@Qk61uDHPtDxc_C9}|x4bKSk+M@c{Cy5sy6%l)Y#r?MPk z@Os`rn(i@BD_1^30AR@?x`8n;nw|V8mbNc0{izyzY>TSPakWbH7LJ*&4Z4ho|4rQL z?A;P_%tD3lK}E^d)lhH3woNe8#Q0n#Z8^Z|4U|Dhb4%sWQ#ITZp{2|5z?h={6ya$`?Hw@azn!1T zdG$+ZL*C`V*m-uFN7p%JMDf;6hpDm3nz=5b&Uv)btU>5?5liVa{u*ypJk&+45%jBoyIag#)k=Q$TUG;A z)}+Rt?&MQVq`)xGgTfn)49_QQC4_9LzPEvA{@rf}(^XqdYRgL29U*U*DkV#E0&*z$ zSATAl_XH86j8=9B;1e90?E&XWV}0xXTNAErETOG!S^wI5O>1~e)5Z1%MrcaXxt z`_sX_#8nxpwM@Z&4wq^I&@b@jsP|RbiM&)?98|KheO8vJF{j@&A|)IU&y<@I!7bX3 zhl&&!whj2sh=F$+B81z$?d`azm<~ZyXOi;f6(rG@SempN_R1sQiW5eGGh2h$rOUbv zq4$TL{`E$2@|sQ)WgauZQ*1spCHnQskNpMM0Ve{G!;!FW)p3+aNXz&vKPh6&P~EQT z*rQa~`bU+P6Bi(3W9AboRwWGx?Ax>+sPc;inO2Ip{E-Oe)79f?avo7Z=^X48w`IDd zdeP<=VeUN2h^D3LnWf2_Vf*2;Rv{x(Sn2K^fg_*B^)iMg=S}uSt>*U^+Ayc9&NW3JLx!$XQTOSkrOx&$njQ zyUHD-5@x4CSj=qSahN|=PI^_q4Rc{j{ul7;ONS`v045_&{Pq61@{rjMX|%Ww0R|`W z+f{~|Q&Uo)!)6SccvtoeJ^f8Dad$;hh5upE+;=)&m>N@p6|NcfnCOD!(LdERWr}eEG(HNi~k7d7`~e03S{? z&M(*{x;%WKw3RKc*rv+?WL~4Ppg2IF^mBJ2{f8{cIi8}>* zE}}lk6^7;4;76N1ai6n?J%eB}9`FoWEG;CVZ7~IcEr@Y7W+}dOf$GeqSSOlzlOgn9 zo}v(rMf9wfr>75LO%scJ0R`N67uELHpL93oYI_D8N`OR+c?a9Im;%{ufGpA`Y`<{F zJ54pV6>L);O+7dE>0FcjjnSC;#*XQ{^WRi zOsGA0$FO;=V{>d!lmZ=n^B6R;cQddJJD0bmFj4iY@HB{=}JnNJi0e6<$ZjbmErxm9@F7 z9&13f;mQC#_!OBA!(79QaRz8+%u)c-rByqzEWg8onSGgh<21L`j#gp`On25 z)%CyGrKvD^=-0b(B#PX2hQ^Cj1=yVNDqb`cc-wOa1Vn=A(mKo4Xu3@0q>K5JX+yMW z*v7;=-_1oA&nwG^+nw9BmP%9mj51uj?R;i*7Y@~nlqvU!U*_4O;BU7xTB_2fpPh9n zC_d#+JQPVu3>Q1uyVplgK;brX){RS8?>WgS*>`JoG?zA4+zCo#vD_U#u$DIK8%oc> zhF!tx&+Yg)m!#(OMWBflMZY~FB%7+*r3msM3;x}Gc!U1l;FQXI-IC1DCy1?k?ZH@} zK-dB+L^jm4vI!gG#_7)QM+Q-;$kDATLD_Mdx;J)s8s9E;=|DDlYpKk)i0)8WD)Wso zN1M!hppd}cSoi1(VaNMYOMR;(!^Fhb`6Y4S`lO$|doB7=_4AH>y_Mobpv0zrmEMbK zs(AgCrwKwe=YDdULB}s9V`rI6I@mKlewf7Qa2$)b9Cg+GqTOFIUl%A7d#N2vH@A~k z&k_^9TeHU7NCN&5MEPTHrq9@pVHCQ`9|eyS%{fDLpn+oqG$L#AP*uh#8%hhBl;Pu5Zt%h?!KzJJy!htA6`%!z3Jx-XP%Svv- za@cba9LN=gV3#M*!|KKR@5=LOJ>6lDi1fP?>7;=DlLrVbn%b^@02ATr ziwX4J*WIYTf}!*pK$giH2Q%EzqJ@@}jx$$QPZvrN$;{~&b(vz^vPWx^Xjpsk?3|=D z9?{jfmC&mNSNAv8owmymMvQBx7dZ^G%SL+yeRb1IF>DJvTz&3Qewu4At$=)9=Ns`v zR9COFo%0=wo84S%3_Fe7TCiZ%iU@J%ScY(C!$rw_hMQGDECG|7d-)LL8#|v5(ZKX* zR`y`(_3KR*&U68!1sq4ER4Y{QHA~M47c9}(J%vH=+^W8!MU?;Rd3T|S=XOSI?_0;0 zFPM$Am5L}gj(Yoiq1wH~ZOiE6Zi!{y<|}T(O+FNsqz*)>S(49p+fmKYy3yg^?@U%K za|uX0BEM!jOq$Zp4-{YTDRX^~t(!Anm1FC|+H2Q)5doomRb;J2SFY}p`ku9AoPfvf zmEq}4Crv~|Coy{uJKqz|das_taBJZsm8~oGjI)opvV+uW17WmGi~CG8%^8zYA#hZ@ zLjeDW@X!uX)kS0(^6g%=91o+|@Cn8H&m!L^7X|Y(%}f`Yb;+aX)ku5EE*JN1?Ft9o zmC0t+H6o_XDEDycXiRF!O-GHgp3B6j?t(GC>8>}i;mGkfsT)g?_)X<2nwKqpsDTpPR zA}n4lQG)X?7*xgEvD?00tB`jp_wSNlWA96>uae90PTx^RM&>plorPGzk8IMwedQ&^gb5|O4^_e;P^HyJ`aCib7?S9IA zA2};W0_Y<{`==PvVuTjw9bA)DxGK34j{roLr%jQ2(FZ_sgZlAb^fQQYXLn-18S}fa z<8Z7*#uO3DdO4-OhPPTALUI|rs{{P~xo=tQMIpogN}(~XSy zPdP>88G3H5uJm)$B-hVC5i%%LyHXk3+*an}A+A8&n3XF%_VOLBD`|{wU8*XPo!ci< z4n5A3V015mbvKgEjn)kFxgp70St?Djl({j=5kh!@4BE77AXtC-5n3Odflh;iUqn;u0O{ahU7_lE**2%F;(@2xHH1ku!g+Q|*F z0V5+i`*VY+zZc?MaFsl_t=uE%563B^^rD{tELgq5nM+_cSoq=|RW%3qX&v?O)dhpB zjh032WiU)BHpwch_hcrynAw#|ymfE?Qc7-9Gw2f9seaY#FK;iQIis07khc>49po>*!rH|@T{_BzJBBts{!pxkQ zhG7f{Cm*|z#TWWo;Y8JtDM?zOBBs4e(TXT_7JDvGfez)Hn51>T9`%7c>3841^VGf4 zw#Qb~oXjj14vA{-;Exr82uNKVZE}ZJJf&_t<$CWm`RSLN-@7-L{LXkY$TGil ze{xzI^!FhAuBo}Ok*rX6b*9m#tgt}v-r9TVla4J&gkNx8#?yHCgGEc3n}=OSnOla9 zAk*hBXOR*VB4?2nR3wMTjugmi2qOP{6vRVm7%i7#jkSLqttcDH2p*d0Jed7=gvP&(X-(}~JtzsSV3y=V672&-o|$Fx`)w4y!DfdpTi++6bU zi212$0}-EvE00`e(DKNA>=aEpkF7qmF6!fr_5By&cz&Ohi`1ujGoltu<%H)`kHAhw= zi?brv+|s|Cr|u{*_AJhWRBpHBO}3kWxgU9Y+pLGS7@IqAS)mtD<0+n0>``68<4pyW z_CL*EAJ*x^5G$N3nkMM#W@4>A&1RyVj_{kNV_2!3Na%|eQjd&kLt!JwePU&5Q04f+ zRIl3t;8X7qPh&(L=W>zgm@Cpw`1v>QsNR#v0YwiLGgit*-f+V+we8_9q4^hF5_W5L zv!jsx>}yBnvq$00b!S_)TTb@nsJkVLa}%@uR4vQI@hPp4Ggsr5&+m5Ug*{XRZ#c~D zs`aUSm61zge1@F^Me^7ihPj}7)3!#^*~)=U;lej&BeG;1n~1&UHD7J{opHO#iv4*T z8^o23)-+qheAl535qDLlv7Su#F_RWa-L}8#9pBMXh%9#Q={V)3C8bXp2?itTZhtO& z@<&ho^3vsY!9>wn7?YgJUuC2Es=?>|*J55icqhsFwg`H=jKSlsh-a1^@)KU_zR9CH zudhhZKgmbI^c8_<>GzEFm2_Bylxwe^j%+Z{^K88{2t&s#xdD)7Zn!O(#DK zf0R){G%i3-6QAwq+Zh?koY)$QTr$9tMiCvP`R#o zCTnpq>M4XwQrKP@04_btVlPLA*iF=bcdXe&$*=Sq=|abhR@u%(5(K~aR%`5e&EW6u zM=OTh>x^y8`qf>ch(`2RHQ%h__kazs&kpIl+TB9M>ziHiYQ09=D)M~PPxANy(n0}3 zCl-db*LvX;Vvz=x$N4u05LG071%tdcG#7DVHabdD`2naibG)2acJYR8$)(LDSltD_ zOmab#Kz7s>{hWUT#O6&dFAA9!9las)9_lYhQ-ch9-oj4O6g7D~a972G!(J-7tWrC3 z#k`M;9%jA`7YL*vaHL@7kpebysA;H)djYJ>Gts43jXcb|giA`+--BQwc{()?keL#1 zJ;LXO8~oaWAMaLm4*1GsW~q~lV#m%!Rce_+i!S}eHi}x!CMLHk0(w>i*fF&z&&ahV za;cT-F2AT=Sm?CiIzz^@hq4t<@xH;46K%@sOTzQ>Xd*2fm(0lhGrV^j%r2CNt;*^e z^j#_Os7&5&n8aOS5_tcp3r=o)mqjzeHw{GL&uXax6|V;EdKs#FV{wR zsa-;RF&GR~G*|aaDh&pIgNIcNESXNj7Z+!p$c@}Q)60A$7;xI-^Yz4Nij9cdG}3A1 z`;&IVkUL7@7<`iM(3}-loGcfY_jQdH;D4Mqk<=|mro~N06?>!9b3=+0Yx?0ur?Qn~D!#sr4h%b$O&Fg5KJ;FR9(ksh@R1TUPFmHMdZti%;w? zl{lHz`#Tck#@5Lc!uWiXnyd$(zy1urSZMYj4|Z2ZoZcaBYYMeKcd#R{op}!wIJd3X zVS1iLoYt~;#17b&GUBxHyfTDmp>gBR2+vSb8tPgC(R|Zab`aLd$rC-Am2UInVFIlz zcf{D9&a+9c62ZEN6cIBLxBNv1ZnSK?i`h3I65*_XE)s4^xzha~|mb%NC--Z>9a)@h*uEf>a1#pnx>L%BtL zqK$;$zOFFt{h)e8f3_(+7G?>$FmfVx8?8M37LKb34@nEQpEOPzW2WtK36exqy*rE` zJnOijLpzlj-Pww_V|@Hfj+~dTlzYVSN$*;oV49bUBpDy~jZ;BF;GBc5KAN6uZR3x{ zkoY>lF}=0gW}U}DFyAEIZl&oc8SlU6PyGavfUEAPENi$X)8QLU*9?wZtKUKIf$N4T z2N!i5N$Xc~*as494$*ULM?92&i1iXYw>JZ!1tUhy2)1CGl$Z?DhQ8Qhzg$Qwn}1!Z zXu+l5r#mIYzc&S%@jW3^*Kypc4vH6v5=W0JG}u)3;eUAw@9p*>G%W8-dh5||Wxqvb z-%YRJM_RwLgwhlC%dNSXOSR+{<8mZpCpR>ChajFQP#~9dkV|z5jK`^4IFCT{T0}I@`US0VlfWv( zc?})0(ZgP(#b7P-v%;Ifb2;CWCmMA266SA4=+3w0vsCdTvijM*{KswY`rx^c{_QJF zoQVtK@UHTB+z^zZvtPF4gy8^TO$wX|f^32b>*^F;k~0W%5d-qqsMQ+v30Y)4LPh}7 z%yZ!1+rLf&midGeN7X;kX^`-`wOdx*H5L2r-5c;Gr(!C*%vSHt*roA54zr{LtNd;1 zZIs%<{wyv17+5nGdP0JWqd)X`cZ0<+ID#AL2nqQU6FuYrD7=tp7UJhJY-h4ZOza!-CK zNd!jV)Ehfpz3Sy58{3hlsH|aiCaNXr<*W+HVdb&hlU}H=3%+Qwtgoh-vMaZ~<(tt$ z65oabV#nvGot{B+WCntP7J;z`v`yx+<6oAd{+MJ8B=;J2UXC`LmjkL1Xl^qsf}<^` z^~3bwYgw2P%Aw31?3I3@$@26|kst{Lk8R=0Iz*-DgyDK0%Y%_{e*fjPfJm<9>^Gq$ zo8b36Aa*op-M%T^i1H_?ve2Kz(42{DFmx}itm>~Uq{OEk2h0i5cMO{~UK;;HT>B&R zY2rMH=uX85yK-UU5-mQfSODPEAn`>g%v1Ae_YKk4yF2mF&$}_$mn)1Aiseseal%&J zDGKAL;DtL5VBU%a2(2p(drV<`bkok7l-oaDGMF(eD0ZtgwX$kyW3VFIj+Z1nZcGpR z$0g7-E<5}IrKqn0J-T{Bn4-(um5*q4qT?>JGym>tKuQZSrWlDTk&J~{X7;Is2yN8j zyH;6|9nQa?LqLWm??D!~Ed;KCh{@CRk9B|zn%Jvis;5$Fhx%i5SjFa^l#YPAzp>+8 zSWz_?zc{!=mT&p$Ipi^tYa;;j zSt}bR4*iQ71^mp?mBp##{_VNRgteS$#beo`xd)aPkBs}d((KvEl5ynCR2xG^y=!^>ODs> zq%-!M8*w$;@DkeABQ}G)>qDDtJb6~sB8BXXgZv~bv9W@1ewxIsjInP;6XGk}Eni=# zq|19;HD95p%KYsbP_8cGlnM8t+)J+foW#nPY2oE=s{#W^DC-yE2zAf|>#+AdNSZxi z<2%UkCnq0m^T~)U&noT?Dcf_L)v%q_T(54A*@Zrzo;{d~^Wh%3(idOtl*6g1!Q%c& zd5tqrHajz{$SK+6PM0HKJ#CpD48D1Z6Xy?6N}p*g<6jK-i&icBN>k1M@Wb#bH5f6# zg}cy&frl$FUcnQ;ny!Xzp*q0=_DA#3Htt-9_@=%AI&%>{hEXJ!ohl}*C%?nDm@dHQ z(hDt4*?no|RQW#!&S>1oS|(4w^@>6Hnad6Rrd6F5gJ>2ph-_vPnLKw(uGM+ z;k=yus;046%*l_U_H9TSk6}R?It((F?W---%b7fcU&%gN^b8rmFAmT+Nxb7UZ+J($O@2liVq3-~K{g)7^a{o*<)B5klJWVp339rI;wcbNi)m7ls{c zUDdgfCGeMg>6nk#&g8AsFsvemmdk{?C6OKK1wbfjZKDSQkl&bN@ z6eFv6STq1O;x-ykTnY28ILi8J)9=S$uhuQrH^7G)?`}#+7u1)c_Uv5KE4e)nq!syM zJ5}7J4oh=jH;|!1eR@Nxn-}}eWmt`WQTw3o?tWEQuf+yKkMtRQLDc|_ge}Gtvtvrv z-7s0q>+?qN4l^u&$S>448gn~}ehCqM+>(0R8qp+v`OFlfsVPv{B%W<`qgL)x%^5Lq zghBUYFK}%RL^QUMP{q#QPYAP}twgc}=( zYcJ)Qa%CKz!G~W{KI-)<;@gWd{nMZf-w?S(dh#(RMe>0|r<>>uaoV(WRL{5@8grVr z^y}qcBrJXvhV1a338f?-y2)roq4mcfF9o|0PV+lpxqmB9`)fEE0;HV7&#S~m;mZFL zsVdo+q`aZDo-oWCzhShLzVBB+*8N%Sa0D+{F3$yeS@&oB+ZWf2II$n-4C-JtfY^Lm zekFqE28iuz<$-JtVz+KFr&u?aE|_oX4a$gNEArH|_VvO@Q&~IZLVh}JsgwCUImMxy zhwr?}6V(~5#!#=!$8y3SRheMx_tZWi_L|AUR((9p9`~H3xbO~E=&J=|pu=^HMZI)p zR|2P;xpN+m>yf7`QVL7a6J-U6b~$`9hq3WMvE(gN6Y*v6hCQH*w`!7qxfuUt<$ zf;X(}zYV_SyN>uhh$du@l`4p4#Qbt}sEH_mH=q%1-C-uaoA2&fYXQH?p*zyMsT@|D{@UIa)~K zbUsAL*d(-CE<~nol&4QKEVDSdqt7wN-sEYs2`V@ujGbP)aJ7v?^~&G7L7)Ps&n%ax zn0yL)UP5@%5&S+?ab0=jwtEdxUxvUa?zjIJe*e6l16TA&Ng@DP2V=%b0)`Qj9~2 z-=i07nR1zS-(&ywZM8XZ4ZJ5LKlo{?s2Z|-#`hn1XO*$VjDGnS6)OPWOv&`b? zLQ8KvwUzo;8-EZ_L3QBcO`@>wiG(@gzu}CjLaw-9Q?YrJ2;#+|tiC;7$hQ(0v96*E z78sL^o}5M}z!r1^hXj23iFzmGa+nX@qUMf@1lRg>XS>?GD7LJ?{fU1&zwGWKVHj|5 zM(9wUTDxlwa`#&`+|N;v)1ua@CIriwk=`(j{qR-q(-Lr}rtTWErAut|7ntj{%-|If z*yxTanga<{SdA5r9Y*5)4z%2Op+7B_3Fl8-BfpHh#|S1Kd88o?ooa}Fvt_H;s89@+ z=r^37VHroJO=Stj^=ME1gr{uN8qt^cnFc}YlrOPz@&d1)x3J?oMk!l@xm55j1hSip(23eMBc=-9aZH z45Up>;~kT>N%5{ z6sD@jK`GZ{T8voba#=5NoIg6ZFDZETq0Rm6o&n1|blSybk@r(PLAHLA8Q(=wSq5ZX9 zgcy+l3oZweV`|$eUj3fVm>Pf8l6|&#kMNW9w(JrGTs=@tUwDDy{nPk9gk96 z`CKEBtB6*?-kq4rx=Q$ppX9T6AfMjBSPB-8XNacuK0?{Zau{udL#sidh7pQ~N>U19}C_3$Di_ur=_Ckdhnl}4yNnNGo5No}-mp4mrDB<{^o zoa@#bVw25LXe%ElPxD}Q$il|KSyjRS+UeimlmS=BV9fMs^Tq>)V*Xe3X^?{!|EY-& zF2g=))qxN9tp>dZx(@$pFVOV>Vef}#Mmg3`lpGrio-~R*cU`7});F_#6nt$?P7R)o zHAh5Vl_rE{8yyrWJ5<|k(h`KhL#L%0W_|+n1B~X}e(+DHsOV}BL33dZacmw&B!#cM zeW*KHUR>ui7IIS*g5$Dz<%%As{UTeH0vBlM3)wu_GJ<4O~<2X_t5aX zJ>(a1K2F4S zqaS2y81~PLTh^pT>crxV`|lw}lLht=Phd(kn@!>*?NU+`&B-JkVX?hSp2JTs43ry& z4tab0nG<#I&{;8?i16mG<^SRe$JR{qiu4R*v&~e-iY+s*J>zTis50!Z9!SfV-|`l0 zHVZjc@4v7f6qkjs>BMxcJ|GUq3`>%e15SuoIrt zudej0+MAMmX`!NJ+d%BWm7f}2j$a($t}A;RXf_%D#YlAd`p0!%RUg9=m!nXl42#}f zo_(tArwm#B=Lrv@9kJ(d56hEIcYmoOMt6sJVuVijFyfF7?g7~QlC^m2!rBXl?bppXzI^DVnBF1q5_T!p5!{7?t&&=ox1u4vAQFbTisj zxL7KSJHv>m@-CG6D<7W! z&BHvXnoQH1N7(7_yytV-Z4116iuUn>0ybI-b*KTB6!&F!?fbJ0O~D5emKo>*`JV0G z={Mn?5D8KuQd$EdmqN9kI-5;CV!J_qDnZZDO`KQQ=rWfP7TgBn141N5SD>ub%y)=` zT6>Fd`br+6nAdNYkk4-^!}?y`ANyP&wB*#SNa5AGTj4k{ak9+vJJ6_X5KGSudJmX z84T0unrDb#DS1)s(j!N)EYmy2Vw;XI6EZ=IIS5A6??qT9Q;W|>s77R6)c1bZ69 zLwfNjaKAe`JBda{z#YJWTHmjuoQ^5$8Km`X6Lu8lM|h|WN>%K1ds3Y$`sN?Yne3y{ z@1Kka-c!SEZ>U7n2sjtIe~dsA-0>BO{{+7SXL6U##T27)ax-o!s9)Z;FYoMTU3G4l zAC1ErVIz7f`ao;G1OiQ)l74ylg!y=TAW;6)XkD^8C<~-jswfRglQALm56~{Pce>wEI(cu7@pN6S0{Z%7er2Eg5aovsK%r0NnzuGJ znr}aLVYj!#)`TF9umiZKnUJw6HVK53t`Ledb8TH=Fk%iBms>R~7@yC~ng4p7{i5lv zTM`?Mc?-a~P=ENvRV;cN>LvEmk32O2TcB`$p!_WiF#36vAS%r|l#$6m08saHmL@*! zB{GpI@%`2A(eJ=auztrOR1#&bxhgOq)gY@P?%?FHfGhW|4LvZ@QKl-ysXwRAWhsC% ztDO*+PW$=aXO>0$Jrdn_A+>p`@iae$C_&?_?ozbhQL;z%$4<(@d1L14mE`Q38zOWL zkA<^?rYhS#a$k4@Ux(jinY(7ifFhpD$&Js=>&!BZiuuZkIH%_7HLTL^F#%U~L$tnV zGhzk9#nyGa9l`S_)V{wlxc!hi_V4ETI(11uQA*eH|? zp8rKDcc|pd^90eTCzwXT8_jQ2C%5j}6rLEIHH9BroHKh14E!$>$63Bo*YHkT1mD86RS@4gKN}F{$%z82eD0Y_Za5j z6RZp5%Xe1cgmm;*)@0!3hdeV_wU|9DLhLU-)HY#<{n+2f<&Kx#@f72_f|IC%L9SrD z%o@RGWYeJP_S4nL9Vg99-Pv;^ZZ_q<&K974#5*1_zAjuk)!f|aXGy%{_os73qKE8< znCY?y&0*MSci*@wny>#Dh}QPmo7jBcx}sVSvbExRsC(8Rz29&CT2 z2OkidVZzgwoK!A}e#OeNg#SaS6RLeO%|6c;*4JmDOpHgcuaS)My3zE|y`jTR!$YOZ z-r;bg95ob@LcP-vQk*1a$9SRNjOjiXUK^jMr@7I*f~W^g+|9+$W>zV-UZ_nX&5>J% zvnG+6TTESUvP%5yMoa0e`48Px_3!G)(PE!*3q3X2=ggS~#DV`HaQp1G7;Bs7T3xmn zTPri6|I;=ZZHr0#)$-4C;9i^$>GK?H@xXZw#tzPNuvG--IoK*v4WwT|(k;s)ig*xD zD$5I74)v)Ig ztk)LLf&~TR8YYZf*>R`ptO(@O0DjlOfVjp7Uz%|r|E4zkj9$ixiI4Ool57b_(wvPa zQlEz88-0(Rr+#25^jy?;Jc4zfY|6GG@kJMB%kLnD1N__QJ(5wKL*1H8f_aD;*|ze%a&6^@P%acLwsM;bHIaT+n&+BhbBpA~4T?CX$2N$X z*E+#_V9(h_^JN0!+^mk8LXimH@oJN3F3_gPR1^wZysdoj=X-=Mdub(r?{tpwWDQ@)w?L7ga= zoT5x!eu*d<4UUlbh-r7hQ2ro$53}g$KgzaAPr2p`$z^ z-5J0AMIkYlBr5VrlX8EVPnu-Ug|tR?mV=McCcUX&@RSdSx0~)UTPE+3WEP zVD{*o&YjoUxi_Sp10?E0=MZ=$ItG#;_p3^kfh8j9&Xp++eu4^KxHNz0Xyb6&Q?qN2 zLKJ_+(vISLchJ)FJ6rn3w58|osHJ^`uO2>!qj#i|Y$&}NOMS`KicO-Iz`U*W86-we zf@*|enB+qQ_<2;$DDNfhH&HtY3gJR?c?t?J3E?48f>)0qSbN6_(OR8Wa(!76ryaKJ zC+T6(PR}x8UqC#Mk278BSW|h(i93;Vb&4E7qJHpN$EIg^T;6j&!SrJ(vbC>Ap*Pc? z`qVN^;}-a(l4oCcZgUTpc=y&o`}U4{?Wu9(n?OE}?Oyh$mP0u7f_Qf%`Vyj@ed3F9 zJ^?qNB);ob`@>7kQ3XpoE+NKp19`-cohJKw*@_cCluxpb-eS#g);5ip_Mc*XbSKG? zW|pjsk-6!0(Euyn0}bxDqimU}tNjDy{moLHm(Qf}O1Gx=q`y-uQiB@gtmBgJI!TGq zLbdl@roV3=PETA0$yV*nN%A|AWNCV8H=Uc5-AVndOe0L(L&=A;TWP-bAD6*g1oujF7b0|o4RAl=nC8CU;^pjAsdfo zQ&SU8_=p6J903O^sOq;}302bRLF=VBJ3>uNr9QO4+2_rIjvMcT?S)T9?^`gT8oWy<3*!H) z;e2u>fk&$Zs#WPG(??q!g#=+>_SZ0If3~HMvaM&0MW3>9HH8xmgA}XQL{dtrvnoC> zUFOUo;b9(Ur*0Bn%!Na%JvR4uT1q{$%CP|yY(7arY^k>t8fd)O~6eJkFl^9?^j^cpc~M%8&+O(ZOP-DY4~Rv zoQZqPtfbBFaq8U6wY=qu%=V{wCT_zQTkPlB{^~e>2itc|;CHC~?dABL(|&w+{LXE! z*c0&`uIu*X-CqlQ2Wf$Cjgj-NHyiNvdPjbS+I07!xdi>I7=;%#FH1 zH<MA3=fs&`Qk3*nY)r0;;uZ@jCYx4@Wc65 zdupX|7(AAx0IL}t&3J#k)xO=pX5QJ!V!goX@-6%_^W(=@S>M8E^W{6exFOe=zzJWI4Y^$&AXgrChIZV+={bZ$yVX!~V?3wq-_&wd3dW)p~wKMfrO6?3#f18eT zYqNiajyZuu)-$#3y=WNZojsWD%#MZ2=QzH~-z5H>V-jG$(Sa&P(XQz?esOK$DQ$??OE}k3tABuh^t|r<>2fm`}EJsYb7Vf9lO$+pSY1bPNmf= z-WxXeOrNKBahJCGdS|~(VJZ{6WTlwhT7lK@;xVOB8o-N%j(#cCgQ;=}Pfb+(mZqS1#{)Shqg`oEElQWjr|3BgpZU^4;# z2RuK%5VlMD-Lt?JoD42p#Mgp8TH>zYta@^x@es$yL&WV;+uutcax|<=a`A3-I;Kf` zh%)%opwl(CpEeQt$ZH=>iE>FL;`_>cmRMsZDwyy*BwMO`+17|YlpL2aT=r57dnty! z6vJK@hRqBOe4YX3>ROypDyrcgq`wkvutb@cTn`8*COy1rYo*qsnqiMzxfvFx&eDpy>G$eL zH}GFtpu$paGiQ*CfVDnCVBa$1qwpEE{8y@C?sq@R&}>FN|Isd@-PC&w)cdo08H)TD zW+gp7;~^=3vW=H%#{1Oxn>yT@Wk+RQP$endqlVLQ?bwf=%^$ThHb))o`UX?TYt^N9 zP%SQV$grK5FN?QfWQ zZca!apuAaiU3#HWo?XDovumL|JD8PcfWYKjnrZVfdafjTk<#uc69YPBtx3^`JYf=C zmWDo(Dc4u9+mBL%GNovnN??gwLfvOl>@z9GAwU`?m=>x*vEJkKZnowEwzS;&7=0Fjrc>t%I!c54^3bu3Q!zuhl;b^GPzY}hD~iYa+uZ%S@C z#)fLQm+YN{BFZujV2H|=f8B)bwS1pF=(lvlg-VW751LaKY(}u^CYsa|glFqwMRXo7 zT4$NyVM^w(#MufoNwjzvXuScXEfN+tHG8<8n&!&(@hj=eo~AF=Vp5QVz_l2rjTt0T zi(4}02tulSS8&F|A}U?KQJH)Ul3jb%-a2wriSL5Y7i{>=irgI}A(E*xhqLV5x~$)c zj$pqVnRGR|2hYqqEETJDQka=@d>1d|+98!Q*Jweun%#g$1b_{N+dP&y1>#26BdPk; zo3@)9U3W{vL|az;Ey}e)W-00kkacCb`@zp#WfSvt ztw)0<@-+~hz{?Wg<>s;ozBMB0i=&`U`5x39q;?8KszfM zr;bGdhb&rdFK88pg6J5qS>GabPMrh!gt>zSLN*X2>gAU3GqRX|sr5IBnMp#E`k`C2 zqjPzpgiic@&4+*yEVcF(mFIX}JYxyY5&ra9D5m7b1J%(UsE+m{MW0_njsL#zzs$sQ ze{sEge}Vg(tZhwvb=!4pYU%+<_n2-!e50zVp9)8DS6(qNjn}f*e^Q+fci@@#MaBaY z3)v3o?Pr*WyuC-)S}I8|x1a8upfWxivg0EW*$z<&mc1@Sd7X&D*cn9Wc;p<8JUvd! zd(+j__D94|^uEOrTu-%oFmb($kkSV4A+-! zb(0XhnpfOQ9XYN1QSHx4lU)5E(@yOuEBG#3(mEMLKe~4-7zsBvZDK5>6_)Q{+B zXLd!`WpxC%(|11-pPPd_d~OaFpPSqFfdG`KDU3VJx6@PGw`_z$d`rBQAH zr)Y7+=E^071(TXn3`n#(N95EKyax`>ala7<+|0K%KT|ZQSm>wv87im4{l+yiE6yPD zxk$$~^fz<$R>#?1eN9JMhitI@R>jxqheW-?K0P=~{S!^!-ogtOVnSJFM8(&B!1b)| zgBk9+Fx-BT4Lvo-C+f*Y9>i}GBMurA^^`Rs^)jxfFN}1@tt_|h?6L~@rP|4DPWuTT1(q_=4QjM}>n6=ZNKp9r?RMvh%$Qlkhh{ zRCc%D(^1*o%m#}6u1wnfk=`#WJpAk z*>OiWP}q*+ju=~19$p;w)!3Rj4_ax|?md=MJCet#qIN(q*bY%58n;6OP#&P4S-LdG3lvJjq)?gN<6J_0%xQwhjvikS^%dtnp z4)eItWnW`SV8yE~m7c$}IL6G+kq1J=o0x|kw~2kIm$$wU*QOsw^K#Q^314%dPTGj1 z_$5@`$Xmg+56w#6Trd|hWVE>|xWr$k`L2Sh)zu4ecPo4|U?=>AZil}1cBP3tR#ID+ zmz5pAP1r5VzeL&1`0ZN6pOu;Di+G8cdYXf3xw#`2cZWa0bUEc8?38B^UAFCb7Arf; ze(KXyP;*i}Ihr!zw>QxmA))+rlRDIcN@D2x=Il@-db3f|%z`yj7@vhD*e|=r>IH8NJzV>H3Q|#!ac5>fFChiyJ?G)AV zWLihblOV@ON`{VSlS!<-IiF(Z?<${cbjkT)`i_;67JCn zUp5K+aQ_ak4E<_{)mN;<@8fE^eCxi_;Wzh{T;IB{cI5f0Zpzn8Q@*Bs>po-sJHB;@ zRRwZ44eWfxd0OzX?0Il>AtvyEF~YWonzt` z6!_ko6XfT_0^dr;f%x6{=Pc^Dt~sM<6alVQ1h~Z6lW1|JmS)D+Q^D6LedCiIeKKV& zvvkGlNwdVQhVB?w&d2L270<@XN?sWK!%WtK8H{-J;caD8p#Q2VwvCrb6 z*BK+<;roPsx0Ch{O})5~%6JanFZTP~{OQGHEIVYnZxmVv2WvTUTt&aFD|FJ*S^gJ^ zpBns#{*d4z2iI646)TWQQgyrZCY_;allmNvJN;VSCq|^(jbn4h*v0+Y>W{LXTY5cv z2hn8z3Di~L^EKaWRfIa8cFU)ICr^S*pK##8SE9weZ0`u{;3P! z-I969OSh;_jPSxC;a4>9L3{`18+&{*A1C9HI zL`VLdJMMDhP$e_oI`Xb=!tp%58`0~4Q{t0YZfWh8Sp-Z8l25MTWwEi9esZ#WlEe}U zGPOb2?NdRfHq}}hWXc8=NiuNRnUZvTVCt^^1Tm=fQ9Gx|gEiqaHnhscbl~*K2B*ja zC}yIjHag{AV)S{v_uZwpyf2z9@9Sn4`MTa#zF`u+p%cDo622+>OncRS#_#FqWS?=^ zALB1<$Gh8-`>WH4V_kPv%e6C8Uj-j=vT$BiDbGIeF{v1vx%FdVJGRuJp}hDG0F zgiWJJyI;kpQTbye8F&&r9abnkj9EL$vZQSJ-nOREka#_Py~3`yD)MgQLa@~DsQ^Jj z{Z;mSRET?GGjY2Lyf-{Et6jKVtQ4O`f36_}&h0Ail1zDLc9jPn<8M;lw^M#!d=LEe zJ6n!>o?Vb|p41)LLt9Z6ZpY_{%d~WDBc`6ospYmksNBD^yky5O;kNX8@k&82y{^6U6bj|WNaV+z{YThr98P6|He4+8N!ME`3 z`(OSxi;AhIOcO83cy>MlxI6Ph{5Uf|K;QP^J-HtJI_-mpI}s3rCgg=c?Kps@svQpj z@6lf@W5W-IOWFr*`UhMwmphG0Y~0wl59@|n2hqopDgir83Ah&HSxQ)$=%QSXWx7rL z?Hm>bZa|#bQOfleJn+$wZQh?DN4=A(@l20^BNB$-PY(Qc*Awq$XV0YxKevOtcD%2x zvUeLDVRXw@gEk`FFLS-qe?s854~S@T4$=`#`|+RWWfCu zZp8sk_E8co#fjT|GOn>9K3y>Z;(=D9kmg?2umq@s2;C@X(p z!}6lK%4LK z%9KZ>=oB@p#7Qpkh)fy(O6#!H>aPC1n)m65e{g6G_>O1571d+{bHb&(OIsyx;!*5- zTg|?=Rd0kpGYLP_36GhC$8^HuCgE|N@N<*!bDi)Dlkf|j@PtWtLZz{KvA;TkgGbFa zY4!)VAp1kp?${q1ykNI#PT;3Jp%5>7=Fv{3Ah3lgaQL?ov2xwu?fISj#Ewd->r#5U zilO35y7-DMJVy@~e+YHtr!ttA6*A+rIO&goc~6=U23QgO&81CM zw!S;?R%uoG?QH{iEG9g+&9S$9{3Wepj8y^WvV;z_HyD6u`ut$r^J zQ`(gksC;yu0=;ycfzxO_L1mMz#Ftg%5(Hf3XxL&gZuUfXrD z5dlg)j`X*mq#*6VXyjtC5YQs#bP=k}y2H9!l&MpWUZu8O*f5J+AGIbMV4&nmePMDd zeP?wlJ%?Q4?^9I|k>8B6N5&9)Y zf!q2dVKM+?W)kyyf`5GlZMpQ9%GhofsrF?gM?SDFacy`F1jpAhe|0(XN$#u-1)EQm z%lVaAu8Wj)`LBfMv{M^PbTEu#f2Q&|XM9CgzMGYA)&9Gd@3LLY_my4D_v9|-n}5Kr z$s@?^vWPHk!yL4=JYaIC}n?@o350@RQK83bkGF(g^ zWt%$1?pTi5Sn+4rS_1uFg9d(Bpr)>d!m<1J2-;<4jL`9~RPL;#{Kdc0z0*;{xR8_S zzgQ7)NhhsYWp%hTmU(ZePQug0;wTl->sd}}KTYUp|8|YmFj2x^k?@a9Lg7G4a1Trq zsz?Yk2}4MjpGg=+LSPQhlq6&ZyyZ?+{{KhWnZVgt{g3}V%UxzQcXkXj_B#s(W88aZ zAA=bNgTY|zvW(#}gY3qVgtE&{LPAlg6qRowBuR=&mO_e3MXAL9d_Lzq&$-W&Z{OeF z>*dVzKA*Fn&sm;xo_h{_atzyj-rBIQqET!EzCWLWE}}}%*7>-kIRxA2f7pUun1vP# z^}%l4z{{^ySetCTjGxNE?@F=2nLThywh7V;UBpU+#<$ru>PHebxTf|px z9TqW=Vv6O&ru@tAGelfg8faJ>c37P65q^57zhRyCd0F_y1yiF(zPQQ8f$IAF4?k*X z?k~qBX@@$P*SGk6;v*Cy_Os!e)DYAac7lW+vA?!ODtqTict*$cC-85y?46Gf)hilj z;hw1MT>$$Mp)&4KvaQg7$e_f=G|+|g*>4Tx%)C;L-<_89Cggy$|4UMzd{|QbLy1pP+0r?`O-P%(K-nHlVRJ+*S-61Ezj6^Z zs_w&zz+!lM0AplDGf?d`YB;^nuq%3*_z~cdg4Y8-C-^e(4Z#hvDL2j5c?58!;3t7s z3w{Ckb-`zWFA5IIfg5#mjNDjoJCnSfN#4yQ?`D$s2Oc5#N#Lgi9|rzNuwO3auw0#I z06!skXRfilUIl(9*T_Edi@@Ir`2%3T);c$84fp&AZU@{&@L=FkCiXM&TEXuFe`Mls z1^*3f%QJFK6L&MQSMZ`ds9(Wv0KYHzr#xf+k^eBs8?`ZVHxo}2`~rQfcvbMRhw&#S z{!4K6ws8AVTb&jG(A_;cWIOswP^xsl*5`H+4SKQ8z!;P(apVB$Z3Z3RY-H*tT#OA3tofb5gs zwI+TJ_!Z&*^8#aed;|PLfsyOAH*$Buljvi`Y!kmA_-uP)c_2S-;>*H)%?^|sbTD#( z;8DPjn)nI9Z*(x`_dVbbh5RpIze1gx6&l^A6&lmm2Dr0u|45-RJa3_~{3ij=EHv^m z!OsJ~BKULQtAZzVG|H=h=XKQiP2fXJY}OH5oYcn*GfhP)H+}l__$ZLfBph%%6S(CjE@;GyWmR33*;Wqr6i;qdxTRXG|ZmPx^+K z+>ZvH*w4rt1RpZFKM8zJ$Zz*E=I_2KeaQCyqJ7iHic}N(xJT|GrC={CVAu|I;YUbicAytFtJZ~$UfnXd&HQYaVGcEf#-?v z_L}5xoA|gXyyGVM8I$}H@b@D8xS_`Q5=@+8;!NQDp<;Xjt`NLwsImOFBa8X(9^f~H zdvzGJ-(f=kP3!{pS#QV&ZZG7Ufp-Z$V&c=l=LLrjryMg}^yk1Og69l3_K)*{mkaq` z;I{>T4g90v`XiwK8KLt~;Bvw9fR~zhpWv^7Z<;u~Oq72al)vCPz)zX@fZ!iZ@*hp| zyTI~Dopb49MWNt1z>5X%1AbTV7r<8phnG{XU2fz;!DE0Y3tkSqLGWw92L)dMzAo54 z%BWAsk)w1@2X1TPiGp{af5FF1{2A~i!Oqdp|Blvq_-Nz!ggi#bR|0PmdQzLa9^#`INn47*QwOmU1@aR+QglKdsZ5GritGW{FTZ5x4=J}xZXG;w-G#Y zoKgNL@NB`)1HUBrBJg*DrSX*g#v9ouJ*|N|3i&{jeB{H}C%@x?XN))UD}w(3Rwn42 zMIS3V2(AR4B6#ZrWB9v)U!P#)PXzxBYkL=@qFtE@13G!GW-wM22 z@G;;|O#HjxdQ&Jjo}zO%6ZbXoFcXgjo+NlH@NU5$oA@hWpZz%GZ-rc$N;zn%&Mkm5 zOguyITHvjM&jMc-95BtuHGvyU)4A6)WBCmR9wp?@0>2>mC*Z#X*P0IPZMu=$nRuGu z7p5EizYcs%$nOAKX6W3MKKicDBm1n+yMbHJF!D&jj{`3e{NW5^|MSTVV|iW#{!X|L zs-hfKWn`cBodevV%E(^9%YZiuJ`DVk;9r3s2u_@7i2|jC*pEb$9nQ1Jq8^CvjJYp8**jYwyC-@Oyui$5aeb)b9nPtq+TfpxN_g?{j zD_EIrLU}>&s&vT)@ z3+@KoSMU^*{4wAqCVt(-9}2z!d`Gb3amqCx*SWQcJDRvB@Ib*&13x49EU?dh&(-+9dI|nlYnOneqo+5{MUj1Bji_se-i9?0@DA4Xy3pEf~NpKF8Ed8 z_XJDxjqwEmN6go`&BNHId^(%ty&lGc1upJ+;=v)?`(44+2p>L zNj@0Z`=rjdP2t@(`M+F^?+fsMMff*=9~iR47@zt3HS_m( z=I`Zdfd7;wI-9>|8x3-w`HT7cH}m&t=I^oa`>%;2eDn84=I_bO-xHa?*D`<4v8sz0zrrpBHlT_f~g7E-w}Oz7+Id@bIO^^1$zlCJMRv`yli8MCR{(%-<`SzrQhm z@3dp7v3$+nPwic5Y@g=uW&YRqHt&LepZyc__d6$+8pkK|_c7-0In3Xan7{A%U*8{? zzb7z%-(miq1HZ5MOw@<@dj<3N4Ce18?m_;n%XH4Aj}?W2&EF$T0C|;=|Sv znD@WU`|alabn||@d41Kq-;MjPt3>?f{pHt|8RZ9o&j>c}7uuIo4qYz#XVd;7?*I9$ zr)7eBpZy&3{%L!V7mM)B`$M?@I#0;AgZq~SoA-mv`@81-Df9m5B~$$7{kQK;a`S$c zdH>D4pO^AK_P5OYPv-ri+slpRYu?Yf4|4ko5g&c5$P{eeA1MR5&w7e^zhs=rzj-~| zykBG9KQXVrPlxd4iSW$p{Rd3(nfI5DfZS*Q$h@Cq-d{1V@0<6Ja6jafh)-T=j6cxC z=KY6AkjJgm*}Pw4USBt_mz&q8&Fkal^=R{YxOx59y#8xm&o!?nnb*@gL4Nv){FvA0 zaQ*5}2dlhBpDxr_SZ3Pbi zE)#5i-#5Qs;`{45;r^(}Kfb@87Obu|%FXYy!6tb$aF74~{$PGzs=wM8-jk;H!zL#A z?T5V&`moCszxn;x{C;G9UopRLTK zy^Yv;lr=SWQXU7tqEc8OB(WM7Dc&Taox`r+8GCUU&I9zfMAFWz3FB6kOW~(@%kbx# z+|hD8U~Pq@-9Q~~6s(j)_=9Npq41loaDO?r30?yi5NT@~B*&H_*zlKaa5N%Mp3U17 zemSV8AAgh%hf_o`2Ur>gv3@`*{6_Q${5}qU|Aq(Q(0tg!16=T{K<3^1G#rk<<@JDS;h-L*Pz`SGm z;O_)Y-koq-9A2gVDBO|~VTWIYvopiRdCcMbTs(G8NNw`uPWf#df6}HuG_Raa zE4a!c880dfpJtWtH{9$u-r@M0NN^ehsbt6+hO5-)cr^ze}&^3ccS-YT#kH;y17|o<&px z&tq^tO{4L5u2SGyzNbdm@F&70Wq8oRXrka@bb?vO|rpd&b#_!k4r#3No?$BoT!|oqxsc07@U=eL@5GBJM zyKn;o{7QBt`<1NoS^ROV19~M7{_d9FS>*Hk|B9V!v~mX5;-)*_da>XcMYSLe74K8` z&MlY_=T`b>{2=JvZ0u;WTEynv272P0L32qJ?Kv13RPQ{YZ#Fznr?$k;-iBig!=39C z{rqhO?h=FFzSI0}$7sCILx}9i;Q$%V?O(6Jan-7K2jmRvH&{EuBq<=+Y7e$}SHtmF zmCgRUor`0(UOGX3R_AUEU%!8c)$Ixr_zbaVb(;<2FzPbDjX-f8R`3)kx$;$oU7Bg# zsPJ1cqIsn&w8Cwhy{|)*TAj*xURoV2*T^++|BK`=N3jbn0xDNyiCMiX6*zaC=FsL{ zfs+ct&bEScVCzauZ{g01h%!{%C^%b%Cyd>Bh7E}2S@Av&K`726kkAW3fh0WK?pyhXQdA-R!B@Nc0szHhSKNyT(?5$48#dH1?Xq$SW*6f=(-o~+@|=PU1rC#`WtZn z;nYDW3AQ-5D?aDdGB-Y91Y4-`p&K3!oFPy9C(;nYq1xSr%DWJLCQSmIw2Ozv;m!7e zM7G$&!<~8TmI&WF!1*y_bm9hAxCD`4Rrd2fOCO8j9>m6Ycm--a6dmGgVngjG;zg=AvDEY$kT0c*t>*NZ|t}K?5>GAq#8eCO(sv1I{UPH zC)$&_r?e}~{m9u%q*WHS85{Q7^+o@I)x1Mg^FQk-oK=!@r?IYSA4D2y;Y89tV=cBk z?dIF43S})^L;*(*D9(d47wjBi*%}PBZVk54c%OrKgRQJSS_I%#Hxj$}3Cq0^u~kfl zTZk{A}Po+-UDqthzADK#Bx%xkcJiOsJsZF z(b5jK6|wS#+OJrtMT8f+vpenmE5D|H8nE6o%J~i92PMEwF;}1-!vbjb9Ra39C0gu0 zXEa#h>_52Y5$||}QB{o$XT^m*OMuPlT?Vbyrw$Z2ZwB)GkDuWGn;*m3b&$?~ zJNpjh|8n*n$_;1Vq0K|jzC%fM^3Sk*<%YBG0;PXD`wpf5a`qjQ8_&LDA+odY<`P+U z!mUb{vJzGgB1|@edJ0bKgkp6bLNPo3PG;4^?6i7-aU=oaO^l->)~k>tR`}LX$@>}{ zh)CyXdvE|zysxuk@&fIRKk+?Bg`H=KxBu|`F#AMyj>>(rJ$L`$A2X!MKAPx|@Fc`g z3C|P_)855=>d^5&binaHbinZs9dP_t9dP^)9dP_Z2OR&Z1CIZp1CEF2fI~8#f9NpO zIUFGzhHn{IWZum_HP6p)X+gtJV)OldFja&8(cc>BeFM+*RK0KF-w5wr!v5|@W&<|( zhxe?;=zn}$);mXPcV4a4uBc)n?|GjiWL^~d#?GSSXX93^fMLOTQ6WC}u=~o_{}1_A zle{MFU*YV$CcJ;$P30EezmDsGnN__9P{FLu3)He2n3XSR*-MY>y_P3EG$+LYii2&vsH+tev-N#q*e(>EkmWZysI*ybOX!V0BRjaQ0$z zR_{afddCZ7-V*f=iwU%3yzfZ-=wVUwkOny0xdpG6Ki2w;dldoe=uE#Y9D5|wuEuvl zXZtS`I_>a5wziW%s=2i@U~ME-({UOPTwX>eE$pp`YdgNuvB|g9dyu9z%KIOms>Cqj zXc#Ft-d<;~5EX-INES6z3YEPuL2Af`)Q7tr^E0@;O7tbH5;e8i#!feR~qb)9=@{Cu+P@(rd z=9<1(_+wGwIhb(pvNei#1uUfc5Rz+y2%q3e$J()s7&NwHAuyQa`i&4R zsB{P_Uk}RlCBBG^*VYZg`DL*I6@?qL+a?^UC@j1ku^hsPaqxrF!c-pgHfrrqS-8YC z%O_y^CaPWPZ3x)v6EJmS4%q4&u+>nun2onLm`eN<*j~|XNg^&Xo?~qQ-l?D+w&J?U zuA6i|k4gjQo?5L=MHP1gLc4>t1sCee6xyY!s?(;jJI}uJeUDPGkK*o{fjf)S?;j4s zct7A~@v}Lq>ij`d!N_10+oOn|y#wOf26YZf>I_hQ=KU72#>A(xCMN{$v!e<}JlH-Y zb_w?bcn7081o!-@R@M^KDT$EoDM_$CmSnf!<*1455IN*u1qWOF9MmZthgciCukgE) zLtwesQgIzBIE*hWSKNl`s72ryJgW5@QANTfhS?k8en^eVDY0}gD}Bi#g1*jGY}g2w zlHr}LCIKEz?>j0g2M`#moe4rMQngu=#^*Iyq7aYG;=WR`Y zWV}-@&F37$KvI#uNxQ*I!S(teKd^O%Yqq%Brr@F#t!D2bIEl{Y9Ec4E2NI~qPiVO5 z7;Z($CVtuiz zgu(5}`bE%CoZYEKY=*Zktc~24@pUYTTIu0ruyDfstPa1(H4eY1HI?h&Hpe5ZO+~?6pno&Oo(E=~9ii_!O+|P# zBb`3O5r?jwVDXH~bdG8wp0+q`Icy#{EK}gG^L?76#u19ca^Jcc&K5rk%Yn45B*N+6 zuq+m06A+J0@qP%SX0Q7y)wN(cmMAG&GoUlGsFDEg;=YOpA-~Am0>9D- z`;8EpkSvIo-|FSZN;^d1w=Xayo~o;MFuMqyU3o_;mJ5NM3D^bW6+1*@@ScQG&3I0y z^LOlQD~?cS6^r7jTcJVLim$0{u+3ogCsJ;#u*TS;CXKjkgf0tZvbg;c zzsQs7ns%Fc9U4clUlCh>L1$t1P&>OnZx=3hpci#71Hw?gi%xqjY^bhY696Oht1!{<@85Dew zYaw)OiYj8L*bABQ4-Rntg-bObLmAQruVTApzA;l=QycpQGfuSNVlRe^P1<`J-^8ud z>pd63dOB zR3!aZ`3cnR8Ht_j`6<9>(DR2%c;l~FTVivJTnytZb5Zddjof(-Hw@5WC%N)-Sow*U z^mE`7`qg0F^{I}~VSPcxRS6P5hgZS#fm^fwRK-CbTL9hxP8*f>h!=tLJ5wwKMw2|| z$HFEG>}+0{_QunR)jZ4u+NZCC4Y|fq4X3Z55BIHGp{xCprldg?NSu+h!=3)L(akn+ za~|D*Wcn(j_&Fg4iIaBii9T0(;(H!dSJDR4iSKnV`Gfbm|8hTtozM!vcN60|@a#R7 z=`v0Qm2juOD&BK#xHs9B#S_LhtKv4p)y-lGVy(Dbi+a>crpbC7*tihel|D!OXMCz5 zzCc1d0@6;Orv=(^f)-@Q=~|d-m;b-#X$l>B_%w}#O6F;rRZP)f%HfQL`-v~I$&<}M z6}g-LIkRFbs&LOPO%}=bDJOnJzI!PYx-~Y74Y7pIZi{j38x$%#yW>2C&hKg2(2>l0 z33}BmzFJ*}jeMbsvj>&ZKPAxbVUL9aU}7wFu=!sc5E?^o=mb_=s1_>UkEE@#rndgi zl23atEPZWIWV+Ibcvs?;oWy56EOJ_7$@db6X^CuE3~C@@jgNg3X5W-8mnFlWV0BtE zKFw^dO*5Nwix!$i3vQ93S)_1_mYPLNZjq{4q;iWKU3FL+&(r@FEu}z#V#SMVad(PC za0zZff)iW|v`~t>y9C$ZR@^NF_u}5-&dcw;KXztk=JuZFc5h|&_A@%^GAxdpa8^To z6RO^{$HH$7N9likq}y^wVTtkPme$0K`X%p$dv7Q2c-g=7?ASif6$UMoqH;Q!F+)!M zaKj$d2#{^bh`}9{k}S=|z3IfVY+Z4tggY>}cv4<_uXLEArFkqRs`Ezs=R7oEKJ16L z4duKI(o(@QgV_zumjBigqazMZeG7JwthVy{*clN{jZOtT8`;7BbMSiZw`FY(l?r5j zjKy|puY}lD(qDm<1iri`Yx0f|t)}o@K3%trPp0^mmCeHLmZ$9Tv zw>Ir3pBg8oNmv?V(^~Kxt{ApzzDi;Y)*=$$tiOHyG#c2KTC3D*)i)xW-Xdq$mx!cz z1hzbJ{?;IioGJ#IJ?-coTaY<#vtZC2T-~u$!}@OVKFfnqghE|x^~VL>JXd}e5hdxU zqhi>3lTH9jH$Z3H#!1?*zs!i7a$i`-NDWhvuLeyYWw;i5H?+*vULX)QRIC0teYn>S z2&wzhXBi+zS!pB5@NPa$XN%H`MEkB|`yt@qVl`~?zT^V!{qp4xlPJ7(*6xMfCF z7tuRK{zyvtc)`AIx}5uDlg2S0u4KAIr;`$c~llwocbyOOi-s0#?I2V*IY z(es_D3IG)=0K8X(*H0+eu&}br=g4|9=HD4l?1pc-ltV9tW%XWC0iUqpyJGe+oe7pB z=%<2Gh&ajc=#mkWzYg<c=;`X&v1g}k6j{#{%*V!;RNof zsvf+7@sqy%LcPmM{%0*rzmu%=#LrcFN&hl44)3CbeUPzB`%4{57ONL!gqd4I>ivF4 z3BAD3fn#K+K+|qKpSNjfhV{HzhIBk1tI4Ju2%9~6du`rUcMd4p85ciOqd~&U8l$g! zuRVn)OiRW2*+V|G$w#Sraz|N1(1a3EzRK94phe6Z?NGgBwWT`YkIizh*^)xrCaZ z8!<6&l7LfoGJoD-8xwrA0tMRVT|eNmZP0uaZY!N!em8*cnr9|$9whaR0+$_j4Xu~> zNO!+TGs2_0QLON76%%CI@})P!HkO8QxJr=z7cV?C!wiP`H^dzilb0=^LL39reV7(fP_=`!^r3v991|C(2qhB)SN8 z8ejdhGR6DkHFL1O1nMA}$w$14NukTb)dQl;N|lHIXUZ+k+JhUH_*3wl;ObxTzZy{y zdrRYy+X`LjLK?18rW5T^R7d_z4|Hk6I=jGrY3I*7qyqrv<8Q$q8=vnyIG?G^gR*^VkpMM5dKI?n^|Z1jDhc zxNp+YMwsC6o%+;>@?u!3suA1MFQkovQ?A0>0PE|cKIjX#o;dEMZ`~BHrD%>oNBG-B zx(%)b?i#@UIf0{j@88ekQ}$9XX2?gmyEpY@_e~7NEW>5r?JUwW7kHvO5&!rSh=(q( zo#v7ZXw*Z&D^%Z-tz%8LFqcG7u8o!SNMo*_gSMnlbco1Z^Gi+UTFk5+v^xS7oA;R` zghjBPU+!P7zHFt-eYu=u2WnSXpt~&S!5bjgLgl6 zR(<;=X#gV-Ka`By>^ZmnkZ*+fAPZ*{svtm>KC(TyD{^)J$i2017=i?V7x@Ck|m%1@i=BP5%ad-;=dh4n>TR z{x!MX(*`LH9R28fM^?Q%vrJydb${|DDjCwHT$hfMJ4u&R!M#|z*74ur>{)(aot$If z;^-%T62irVEMBVGiS*QNVhc38@b?4zB($41l?}X--#}*cZ5ucyqYoe0mfb9a84t)b z`l^SUb8JqYNh_|#vGOZ2SS&a>KP${Sh`{@Ph7qtRq{=V@rrYguevR-DyKm~b2&E)@0s(uzel(< zH)T_7^=_BN?!9Zf=X!XS8SwlOAZ9z*gGF~kvr$bjyeMHMZfH}QlBwYx8N$!z>X7#$-gBVqvDZMOA=Z|Q+!uDXs_v+bmVvXpV{eiHlL@u0}q zngF&wXEpNT?Qw9X{-R3Lv;Al&c6Px6#ed}cW*`!e=R-V$eoY>GA#r`0kj*|-ro}(0 z0hlCNVN>nA&wZig&KwV>8I_G_HgA-e)H~X-oNad6Q10bdzyB?~?j2XZS9`;q`<*^% z>tMai*nVk(8xNyv;KOaA$d721!;d&?hs)?)smwC{)4CUc8`k`WwZh-E_;IR+Ddo1{#;ki>v@OR)XOND*m zuT4I>)~GJLl*l$CH7>V5YNZ_&+UHG8F6sRO(vO`-bjPb692w;sS5tPI<{H8k?}s%v zf^E*YQjGyYEQOV8SI)fdQsOx&H%0b~?rGZ(!W7RFm|VS{F)IOS+mPxnW891U?#;7h z`Qg4kZo&AH4*a=nsQ~_KtxdEP$Hm9fa&cNlu2Zhpvnew6LBI6O7=K>>@}3c;)(SV2 zjw<0rgoT9~f~@N~PeXL3FMmNB!zFP8#9vW;f6cITKI?iXjPShWpZ?(Jfa>eM4{>d4 zfPOi~>vWP#Q<1lgUn!Jvjj(byW2xO}FW;w=I#glU{_m;Hi^u%d;80L#);xqB6~%_x{1_!QH+jbLD+6%eN*AT_`}T z$h&1L))XC$>5V&f9X!N`G3eA|{j;!)Sz#`Vyf5!hm74f}Sd*~E_yVlo_7RgYn-^9* zqy;MMVuMLKo(AFkRv=Xt1y2HC?nn_35N;mugJ@T+Hg_^eVF6*!CrYM{LO;n;|c zb|c8V@V)|>`nUpzNdd}~=QM-$+t8@mhd=OKNhtNn2a9w^bgj6W1f^@opmT(>}uGDA4-(#F)G- z<_6*7Of2Tsc=OP5LUlGCLAA4P9+hZ5vh&_Ku<*H9iskN|QiIGUr+!bwlDo}`YU`}{ z)L2d*zrfU(BKAP$uNYd?0-a@>ar4eO3){#(j;5sQLMAefPa5tdb{>Bu7=x_&TDW3SS4|VcTIYxeH+SkVI`F#zHo>mIyYiLppZt?AGHNW-*Zk;&qce(Y zGz|LsVQ$2W-A-E=E}kNWWx=@wq2g{{SMzCG71tE_CR z7J9ZJ7sbhAfD%L1e#wHTC@e{6bf92ATpup>he-O=2-w^ruYHz$zN_m6^oZcTugi@~#qX5yluqZO%S|GAK73Rs53a<4{w z2oE2o4c%~(WPLG6IXAuG7fc#xY8P9%r4{Z}vaS<}{U|KpQBoUkFOl0sh zoyA$J?b^K1kDuVyk=yEOk+Id&YboWLhg7}7fAXYwBq~yez~&!^@iOV5rR=0w z+h5qUBrvhDKrj>#FPg0u>JP|a+l1z6fx;fqz!KlAccDcsv>nU@OgRZd@%nagN*z-wdm$u_X#~Ne3G~= zu<3Msok|_<#CW9DHLXUW~mL=kk23_93291D{E+Ukpw&-3e>a z!UA=9=u6o3v(3tUT8L9ecC$TL$N<;Lfh(l+)7?)T{6h+r*rs+40;ooJKMK|~U2zO2 zX-w_#tN0q&Ix&L^D$qjVfDz5SDAE1erm&1{CDgL7w&it2RvagB!sSyYP>uYKQUs~A zdg(-qf}Cw+nOe&HzSsH>w|vVy_LtmhQj0=>2C7Cv_xHXb8I2-EQatbW)L4FbUE9nu z1O~OqB2}WX1kXpWQRf-47j?=*29%kWZ{noWbihFtm^a}gaQi`?RCP(z^z4UYQ^#!4x$ zHm1!dXu_QYw*V0H8=*!pOxa#>XwZ_0E@_~q9e*fU@hH~#_$|zE>F4XNBeCdK!UO@l zUQ1Tq4S#4FY}*!NpAqCC|D^=`+o?YIFz=Cm4=+2 z7i>=;$Y$S+@!13{bwSFKjv?*^yGSWv>66z-nM;u(B|xdC(ZD2UhdwDPd3ic8F7w4vJykIz_6|(NA?#I@8suhSgqTGX+L#$3Zs0Rc#2Nutbh^oR zfZ~Y;0Y!t_p@w@qhP81of&iX+9`W}ef8^W^vOGBNn#3k>*!T0D9(OJCx&SdehKz2aTio^|i< z3d&BAE3+1>h5<18xpA0sl)j4Hi&b?Cj>~#1%5)|lZI)d~+TnWQQlvvu_lJ~XTejhP zgr61~#A~cxt2zLTME4lJ1*FiGt}9;l0JBS7@Pe7Qzggq%%c5b0DUU5FXvXV9aIiKR z=cL40Yeg!ug!F_@!h-JftT}{p1^*kL)}yx;HYW#tyCY9tW@*&(LYy@DbL0lqyN)7A)gb-fP{8p)rWBM@Fzu8wThJ8dNle%ypNC{P z)^VCt5&6{d98E%c_d*y>_xbVHt#}(jBDJnFz2Eb0h{qPObaDMtM44O?7#YIo&tA3k zmA9rDyurb-PW)zfchhw3YrSxKOaVybWuDLrC}o)vq{i|1@GSjWxH({46W3;wT) znnVz3$pYGGN)fYlLO(kH8otP&xy_VyiRF{I>P|RKVE+~y*zkxX0 zP=a54+T`?SsY|0I_tsCht!G{5r}gLF4G?jldoma7h;*Q(F1JcDp$h#b`}GP6C>G&T zqcp2-t?4*N9W76cZ}*)&Uvpr#ms+lR2ZXSvcGo?)RWd%5lT+ZT$4?#nV^QKw@1I;1 z6d~trdz|3=4H&&UZ`A!*f}6Uv)lsQ2e0C_1URH6MP$C%I_?G3ajqj3~!FJSqa>Gi$ zT;5eDyQppWuv0D2*aVyXhDk}^rAy>`SAoTrwcscuEvX4_OJAv%M4C?>QultB33Uvo z6ieO{zDI0}gT+97;Go~=-z%B0j^mxPB!c_$hF2Bq;b(e=@YkuoPJBJMSaRQ|uevK( zd;h(*l(q#LAUKF5*vgO1%p@tMBNKmYj;8yuR5&%q^lih`E}xiFHCsV;!fmQhH-eW%b&L^ zTR&+-P-8VS*YfMAT#JHaXAQe)Yp4sryf@|4mL@b&Wi4de8Z?BxBUN!#99#@x(*B@zddVab7@rs4`7%ck>GmSNFAX|#?$W6;u;d=jdg`;?SfSBF+>?_12qyov1 zWp{a3@C;Y5Wst>F0ip>`hxhC1aln%l%7nNTzf?Jc5{@E|Ro$}wfK|?aecP3-{*n*T zfaBUKT-M9PY4MRAq^~c+m=()Yl^#x41{qH{Z++_`WG_nXvj7oB-rMvvT$N8VRgJWz zEJ{jkj-*C+wB;-j)L*6wKE`{Cr3VW(LRzv27vT)FKjCoKRvaawEn z7H+R`$mFgY-;ueJ&2@v~TKql9FGXl;H735*nFp zc;L3w5j(b(s9~35hr2j<;5e3YO1E7CSZ_{AXbbtCj_qH7v}}J` z-(1-fQFQqO36)Wtwsq=J!B>p`+#@OslEQ<>jFC3WO)8bW2+=d#2h;!2u;I+^^a� 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 diff --git a/.nuget/packages/Microsoft.Graph.Core.1.20.1.4.symbols.nupkg b/.nuget/packages/Microsoft.Graph.Core.1.20.1.4.symbols.nupkg deleted file mode 100644 index 383e2e18dcd6c1efb015d539ad50d1576c921563..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 209975 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 z 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 diff --git a/.nuget/packages/Microsoft.Graph.Core.1.6.0.nupkg b/.nuget/packages/Microsoft.Graph.Core.1.6.0.nupkg new file mode 100644 index 0000000000000000000000000000000000000000..abfaa5913a714029db66e2af421625e661e29238 GIT binary patch literal 121103 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 Jl=vnA{{@bL@lOB% literal 0 HcmV?d00001 diff --git a/.nuget/packages/Microsoft.OneDriveSDK.2.0.7.5.nupkg b/.nuget/packages/Microsoft.OneDriveSDK.2.0.7.5.nupkg deleted file mode 100644 index a54d431aaa29d96e06d46cd797f2fc63629034ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51507 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 i)^Y>g0Q9VeWWZ4xWV`@xRyL3otUx#w7|8k5 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

h}mb+bGX0k(K%xO$Nw8^ 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 z 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 Ju(Z5!Ra2IWBdm#jZ+_#qQqU zW#CnOZU@{xWb?XiR%DW-s%Tbh`ptZkyPx@?hsvFE>D{g1X}Ntf7Z3B4Z**5vE>KJ#_x?#Rq%WYZ&J1qL9tX)dKhRs$M>h-EMPeOUdMENrRQp4f(Yk=z!ZlDwQ5K zrr(#E>yoql&n`*NJsL5T1r@!~A@X@<*WaQ6YKG21VDmwVCM6vOWZ|AFeyK_?cDHL~tq9kDptB6w@8o7`1Z!Qf6T3^~E)Zl;x)-g-@sRzO zQuK8TC`D30DT)wc-+9*hyjYD~=`!1=y)j#h<}IR9-ZBCxUie7Un`8HUP;4WI6#0+e zFa(QUahEkYT*r+N)H*(tK2k-;Nt~z!Tb7k%MP*7cjR3Ak&}@qQFz_5GSJp$v054*Q zN92fv14F{(d*A6kd1bH+a@Gay4VXr@@TB5+r}&HJx;VDJpmXXS(LvhUQ<-U_T%ksK z$*r|nXH(9J>!8_gW~&+$?a}skqB<>ET|F{ zJ@6Tm2FsWMNAC-Qo`R5!3s%E!EyD~WeadIoPbU7>|INe?5BtFrGO#7^bc(>K0Lz+y$|zamH065xZ6#p1k~o04b*zAmzyXLCP5bNI6R`qEbH9 zlLU-ENjabZDF=j^?35X4JC*Ugn&F9*Q#omwt}6@!ka8r>6C!_*a-5B4v8ZFYF0%QQ zDS~7?v=SFsrg>^c76FCR0?M11M}Bhufoi2A8*ZDpWeCXS9YL${sOTJ3_`by(DDy z>9{pznooJ3yj`xTJ?$4$Hc>dAVDxJ|zaqm#FrX0&gOl?&VFz(BaY!ONldSe|Y>IpK z4tmE;iWAf#ieh%x&nzoG~?jmi~|~nb4|t432b+3N9EhL9(x2d1C@0WBbgZm+C0q=cVxr^*8P6 z6iV^Nphuk(RguUH$hxU5Ld5YBO9JpLoR-^!GIUqmzA+j`76fLC1K*W%R=Tw(s*@;` z&TjOy^Ck~`zgqYNp_j#Y!L9}jeSd84B&mymcOV(mmAY+cdz1#n7whU(-*$w3{y2C* zTc>C0(^|mysnIuy;z*X&Y|a)j+D-!6ZVLecEm#=&b-~Wr;%GwNp=sVe`2n#@JvR=G z%hl?wYfj z>NjC^z3CNM27wED2oYm3T?tS!-&)vedtm_B*vv`}`58VFq(Qh}4^r`3y7MQ7oTAH> zPuk!S32v}uvz+CCr^m%QUQauHInaJalrd}|nLo)!I+u2%iz!|DHQj8~G-bJjX-+hZ zE*-T+tIe8fk`ydRIs=UV*o`^StALf#VD0!Z{6 zAk^4!J8+7@IVfk|zHpBxSsTuGFS}~&;MxgB$`ld8>_rini*Bf`JcieABH|9YttDDl znms=4>358LwCS@$Hh>8)EH9b%}{*tPCj=hb9%VMi?qlzc2NJGkYP$>S#fC; zH(;m-HTB}dO5rq~s}a;_5jQMj&$?4=WXd>~5z1(wnPo%@HRzZ4qg{Fxl#BuiIiInM==s^`nvk$Moz>SC&3(qlN zfRUx_>8009tMA3KZ3p>SE=Y8Z7>))thEA%<*U((0(kx+8@=5C;K}GyyA-%SEfUB|l zH$;(EfJ5-uF5g$lBMBa>ipyD*jJtf##UEO?@fl{}U_j1g(Mm8`9vXTS3FA!<+7Avv z92t^qx-p+>`8e4dN?nEc>TLBBXppQtRj5)l#xG~w!glfCA=RzXMlQ=ov-+8p-H5#?4^P$;F7N5{AXn7|k zl_qx`NiFRQeEA)MKc5l|xurJ{>0WntJ4>$PjC3D8m{K=a8SPO8 zcU_6V8-Jl>!%GhM8@QC-Z{l?=WOEVSnIaRlGA$)Sz<=hJ? z%Lh-|b+7%g2OAr&!^i!J#;fhAg~;e3jt;{`hi_L6QpS(bx0(jpUz(9wIy!G{+ZL(r zm6GYEIEDZ+g0jTxW^;_bAfnFu?Rn)uj>=03d6C#$z4iRh1#6rcMkyI(C1{C>D zI)~kbgyQv%EEN5;uh}ScD`8i^D5K+XbJMASHw6;XziavaW4KYTA}8|%6^r`d#eM>2 zV!kpbZ;Iwm)y+9+MK?{bbiUxi?o^F0>wkU3#U-^EPp`f!REm>z#QVkBnRmbzF3XQs zL=F!WPEp&eLzE_+w8&-{wIV!KBX&>I)TlWY-@8hnD`T3`CJ5q!WK7hp7d0NjSauNG zteUdtz(zhyOgCIL%3Goefa^h`!m3lk+86MbZ<)p`!Me=})bfPF^nL{a1kuTkX}x27 z(EOg0yM^`G`|7s0n;_U3X{ zVW3%IdJ6HlREZVaT{n{($M3Qcr9M2);nSysp`|d+sJC6J9t)!+gRlc6ed}Ld9D@g9 zj_4@t>QZel;FlL@f4N?t03vlw(Hm1Qm=^>jrkzQP{B>2f+M6;p)GS)yhrh>7Tb&a= zo>bi}l??9UwVS5NLUMOvV zrGtcAd%4SpTpYtI@SSHdA#E)fQQmE?(iva8P52h-}yU-Lf69b-f|Mr+yFx0ivH~HJw+^;ikc;s$it#p3lP_Tugw|CsI)TW0UL8>f2 zp=}BWzp6h=t;homqG~;D6)^MzfI+k*J8AX>xTLNWrQ0gV8kO2*H5xV29Tc@Ac)j!M zNqU!UEXM5|?oG1NOL#6*Yo3D?bx4UD+y0%Ny%sD&rnx3V5v^Sf8)oH<$jO~sqZ>zN zUqf9`gN8TryU#dKLSHjJls<>w*Mv@$cg3L(D8vJ`r~r^`Hn`^+)+sT>0Fn(xumV2Q zq?zb`{Qx3Q$gPL~Z7`(ZhP1p){LaXYFHvX@J#SpymsGbLqqv>YsFJ!*?c}I+jDb`B zgABXX;-pdJo7iZ8$u?=|mv78%Ku*U1Fxd$47236jzP1a)d3bM9PQ5<@KL~ob>f!;6 zDy|2&NPtm=<}2x-9d^{`fwQ;{`}_L-uI&>=2k=<~f()1Lh|BwjW{V!4M#j5q{>rq0 zYK5wa)M&#pi96h@*ev@YLfNq1Q7=c!bSatn%oDiSan9B9I#IYn_jbRo4lM&W>h?N^ z#{WT{{_@zD;`+dA4%{)gfE~%d4IqD>j6ZG~e_4{8gc8%ZzNV00WC*NFEZKzp=}!55 zNz$+X*Ck18SCH_4P(10g#=`On^(};!QFh${!&nv?~2-+~aK~Waq zjVvBYNzHoTs4{ZElSr1NrZQ6Sje=XTDe&>c%{%fObRul4=`lhd>@zDGeTnJ|o!ya@ z^3PF>SU5DVWQtBIC}GCJK2Z|n372g)4;#2yHH%<+*;aBl7BvgSywCB9Ue1dcP>y$f z4U@Ki0nFBl=lqyez+jbLaKrc(jPtPrOA>x>rov}AOph}!>~oStkBgfq?7?dJ?@SWx zi#I4j>r_b+1lD8L6*pd%v0{zaD4nA{kMi$>RIHfmneA;S8+}KFw-A6xpK@$uL{UCM zcq|gIx!$|5X<(~1+8iwlvlmbChy}{qvnh=9m>Kw0M()~`=Y58wzz5hKN}MS9>H*63 zm_)*DmJkL0S1@1v^7auRSJPD2k@|NkOJ-}_KU*_!SR+Iwr>pwWttg4W(7dM*pvyN3 zXkj`Jz(#6}t7~f7UFtX%4a`Xco>T78{@F!(pkb9YsU9ELoL`GNX-CL;#4xnYf4$>- zYy265DS_gXfNj+3_!QfKFdDjDRXU%G%ya40APSAu0o(7=_AjB02&~uvB%ATcEBd#l zp>h+uv9*siP_f@f{3hnIT?qzk7q=K-)klwQ3M~lwc}*5cpRPl1(dUy|6(@zmqYu<yB+7FXh{)_SX@AA|){YMI2f6t2nZ`6+d`2UadFRkzjA~eae!?PjMtF zAdb8bG^m|Klz3gT8DgLi@n(h_uVW-Js+WlR)mI&WO{&bP>g+#tmVb&t>SE3!s&G0d zASYHU45`t_G={tnVJh*_ojC-lXAnn=1eUv)o#~p-%x@^y1C}9*L znxj3SONx9k+!O{bPF2d$0qO8aOEH=z;Yl$?5(&s734X{Uy^8e~q^C^P+^s*f0Stz0`2SH zv}lSZj+S~>x~3L?H<0`b<6!nr>~5={>~2|r-TnEA-K}D`&(1%!i3H-`^|{Z+s40~A z2fJGcxohcD1>5iJ?jzq}HJi6wRiD{$Kt0}LU}&j$gF~E~|6q5^cfsL&oB&ONO0um9 zD$osSdhL_bk#l(nu)9|{0+(E?gIdx{mjl}CuAJO+Q+0~H4RNu`hCprTd3di{Xrri9 znsi0peRH>AmTRw`+}Y*4Va4H>Pkq+>9tsyfn@<2*a$U7P+b5$on=)=$K;4XrzN=p} z`W0BCaOX)NKrM+U*3vC@3>8tYgzj{!?LT2`m2-wM5VE^rW>zYA$HnzR>~p}t&ROJ( z$UaSZ)=!~z?{yE$6Pn0o2wS>NK&8%uw~dXh4@C{2C%qSOw0MKB%7Z?hd+pZ({O)mO zwpXs78bf_$0Owj^&@9=h^eV0Qw%~l;HpRy0QLG=Qa%Ak5+G%QK48V1C5ZKQ(&yG?P?1Zy-k(^~zVxJm+T1 zx4Lpd(ROY{BG>FqFM>jp?CrT_5PV@w2BfhT0%@$BD7pS2;ASqX%-~F5%>ozMO$-zs z<7Z&QvfI=>sT*&Ah#Hk0nT>+0Y0wrB#-lyr;bbaEC8=c9eTYv>FyRBt$@G*pDLUMr zG+ta?xAR%vSIAeBBw#Y7z8OqmOfRUC^^cxKZcTT zs%z46iDC~;6|s9mnVQlh1-(>wT$DRx1c9++KW0%Oxd&(UPO36> z?=kkw9rCv^i+bBJy1Qt&ov3#y>%Whhas{=o>RNX(H`2)*Kt)(dYmZ^L%zrqrF@b7B z>9$Vt=R(1qXP=Wq7@og^-#glzw7tepWVhBpI~K8WE<%@pU8065?GR~$Xj4$@t=uEv zI~_vV9KolcK#CytMA!+to2a)xG|AgvIRJ5C!jDjHd&lXxyfKx+x$U%rmC_hhh{8jg zB)f&rx!)ECl>zdtZH70#<?7hQe3hQvdY z8P)uq5^HV$<-2o&_FwM(Rf1Kk=Rn~+0fv(N|1v@PhkwH#ZS!~R0o(sjy{G?5_0IT% z>b(#`lt<$Lj3lR*oxi-})I#e{Lex?Las-!BTF9Dd@HLIujwzTJXVD@L6me0caqvMr zlwg!Ik^<1x^YOPV9%z|nFzihnBEc%(xJa_Lr{K>9KOp2^BCw>q9KnlbK!6d<4zyoU z8UVzdA}a3a%*wko8E10>$}sIhulg};s@YyBl6uAWAcXJ3AfBzbD3 zl%UPF{R|bHh&l6fK}At2%@kObN&^%x6<1)sGn*_o3R88|9fM&-AnlQVP4iKYjuU7n zocF`~rd_zC(cXE{&M)F`gNLpKmxkODSeM~!SQNe%c&s*)1&>@66PN^`z47iv#>xwe zvs=F|hLpUkmGXEV&R=02iz0NDs4e{f%OS%Q#@%0J62rYyxGpV?K9-m{-nMmplDh|k_0kJ$J2*&KHVts>q3q6;TU&IS2zXhToiL1ja`CvEWGbz&*Nu<#q) z4_emz3#^ctDO&SK5%A(9il9D%#kBd_!$Yif;rpvdDM?Yq1z zI6b9NJ^{YrFd~ep2gZNj4J!WJ4gM)b!vc6yT5;zW^39tdVxx%bpbP+SN(;;9sVFxr z|NCx`4@l8q`>`8zzbNJd&=fB_XeAC(zY(ZyS-OPNT0-ZFjy$~CCKGwu4C)SlA>`~` zO(FH@wwq2(`$0cgEfh#Y$j!8Wn}WBUi4fF?ucB178POqy1mOM1rljUW7StYF4+wZf z)CQf(pa`)_6YbUwxAlU)ZBLWLZC{uDSPUw<0gFL0d(w={`!w;(O&^V$hX=VtA7C+P zeT?*DF^KD|tZhF8sUUVc>cvu7A%-xoeFB$T_4{J*8pzRT-8!EE7K5Juvl#qk#wR|V zg7)9i5B~Fv{Lz9x{fVgiSGr%wtyQ|83m8`|ev8R2ASLJXmMy6pEjEJor`tYiz4O3K zqwpP%h>Qc6-?U2FW(-m^b=TNSP?ZUCs1i;YuSUyd8Nqgpv6r~$K(9<>>F9~}5WD4Y zB(tLd?=EG}=nL(%AzlBbOrPi#qPg9dc2C1T)B>Wi%xQ}b((@dai~})QETSTMs)#;U zV`m)gQtG^>;AQ_g3OQJ9=Vl>X&qmg&#SKLgiNT4_(TW0rbE#iVUmjD|FE;jGmo5jg zqeGAr3%VFU6h=m=q1b7HfR-sjNRppZ`V>h@Pj=;H(_oPlgYSyalnY@)@W1$sCpE@y z#ehKO`AN@bjUoly3nUvE*>w)>jAPocs^&vU+;VOvyLY#DtSg^oIs`7q;fab*e;{tU z2_ywwe_cQ{lO^~tLcbp!eUlD^iUMMN_@{{70nb%)^VeeGRTOa~n5g`_90Ky=8m>Z6 zu-&Ux?4i9{GdpVYv}8S7s}4M`(ZO66>=`R^)MEXY2k&NPP3r;@``zuSpju&qm*%2M z!7qBC^^J`By_}a*Glwg!#fLO9=lg7Dp&td68s==tIgC=QPgF-`Y57|q+V$ejriXc= zinrgQ&^onKeTa(rj=B#H=?*} zy4+4z5>*nbU+kYD7@|_C&hWB9z6n`!QaRjF*pO%42;Cm~>JvcN|1ojP{^4Ml! z9Ce%`AS%S%q>J4n2)G|~A;sN;0!N&G>%6iN+ihhs&^+~5m6yaO6;oXE4zs%WQjKdB zB~)8|pDml-MC8}(;4$r@(2B7dqPYZ~-E4ol^yCe)ETKj#6<`;IZROSEoFzNp^JOXo zDY1X#9275cHd)J7okZ*2k3q;!7sQ9~Nn}|NHoB%>6W_ou;R#H;7dggyM%Ue}zc^c; zZ^fYAU`UrMNaf~Dx>nlp;&Vr?y{-BE=Uhj&iqh+kJxuAzt1OluuRtP5iwrK5RS|7- zZyA@ncONQ_U^$8WrzuYKR3&%Rr<^8}(isDBd-WO8Bapoc;bguKmr+~?g{z6F33_$T zF->2yrn%mR^JiE`p%}gvk}2|J%`6HD7`R3hC%tN^X!e_i*+?+1#ZrC95S7?;7BJ)e zdNU3cwwEHqa}b-N8ZNW|!K6e%kg{@g5LzS_xXL;4Wr zA143+K+@3Dtioshs7uzwB>f?R(yum=s1c!5P|NA!vFpV_`*LP+J)2@z?nU^;^>g;* z$gvEok9i-_tv<_Byt_*5r-H(bsH{x-B21#1*e_^BsN#6) z)Sb6BREb7a%+TPH-FD8GFGHHgSWdg}yeNzo4u+5GD`N_@H_kXWaKMm)ZT9DoVkqU3 zy$lXT5Ql)Ut9Fk(^XHHPgc%QVA!DJ$>#s+I;iV<>Q_NWUzsxB9w(SVIF_i21T}L!B=&t6e1mx)@ z{-CU``c+{y7Fz>0b?hzwScWcBxvq?>8k-=DQ4ppYPfWamJ~(jzpOHkO`L^H{z=uLo zAwgnfH_7vS;j9yHM=u8vddm~BCDDVVzWA9yr~us#ChsF>`c*^FNi!7;pqjGu2tkQh zl}f^OIAPm%n9kgSP!kSZNzKP#>|mDMZl}nfIaq<44zt3X(o{_j23gBgOLpVk5*@S! zhf9|Sr%#!z0?B0J5Q4q(6{i=Lg=M}HW)EVF%oVNSP#gHt5cr9VqW#_;Vvu!-+qtR9MMdiOD4v^(tX zhW#vf0F+J{eyoQ2@MU)y!*uqmh`R0!g||&glAE1wD&r=svgwRmwQDPgaS*(H3L0W7 zYIZ?7(r%OADaW=XfvB-uC|F)4rg(|?j=mR5n`b9Cks^#xS#X5yt`;A!%6cH@p^=sG*D&HofI&u(N#DM!(oeJFfwPd(ECiwLj-=EwT5`?JNE5!5n*)#>Tb8 zUAumFXpQ!+(HZO`${OfL`F*J+o_LRZFcySG9&kN5BN^!1eOg=OWUg84&f36d+7*#P zh$|hOX-z>&#%Rflas!mh9n35{x{hKAhZAy}5up><5zk?Y5pP+l*2XsP8^lS%zy1YmHMSu4fhsxnFN2AH z`3wB{e)KbL?2p03{~9;;b;0ySGrDjZFtN2_XE2IZ9a zizN)C>CBv9*X3yC?sTx`b)xRn^S4S{#WWZbyCBe-0mtc9B1~KsNCW7i2lzOQuNj>A z`q?SqeNr!7l@NjJQB>t6xk<&$&+AbZT`y!F+S&R(t7fintgA<8iUV{KX(|;0ckj*p zqS+mDR&PTrhIk1QLQhX6B4(uH5GT@VxZDeeZfi(avU3C)JW_yvoXj)jlmCMbl`Fg$ zjQ539)6|2sdB8^D9!48v_2v3fh~=KTY-2L@Vkzb%Q34MiXa7wUPeNn=7aH}ef`m{+ z8t&)9#=%l)CqsNBgJL-=i2M8yElHG7%174ktS7wxM<(@zhT0EjSVGHuv$29^8^PT6 zXnnN4N@!=qA_V_H`DI*kBh7LPRtIj^BTkXxw)MPa&w6l?S!|QxF%08doR#_ErlfAo zsRss0RcD-O2sfSomR2SxKx2F@@owr&IYlU}M0!z&R$|E6*yPrEm@15a^4zuuFS_!T zbH@8k?yd2ANehtJ8*nft{d|C_GjjIi0Mv~r z->O}hoWy=OBj_n^p!+Fq;QIHt0WKhJ;7iF@oCOsnt8%fZ`OM?+T&4qCIULf!e6M%U z#w*iYek6OMUQM_xOdrlj|Eg&>4thcG6gTkSw%lJ2$$!~$Nr9H@v}M1OTk`##u>y#{ zbb+NXi^K~L4zDi&$b0?`vWJBu9uT8#QyKPg(9|d=F1@S%3Agdyf-T@DZeyA|h-+KO zCdl8&2Qat~|6y>itXfYshbPGd10S9ngk#j8)|u;g2-U< zLaE2x7pIRkw>bv-_R+L+1xVrwhv65F)9Qt$35+3xKK^XEZOplYZay+4i|yF`n9H;u zQb>~r@z%7FB=NJVsW+QIo-6O+`HSBu@W(>42>H4XkztYSfTy~XR0yF%@MnL(D?x$= zS}wXLgB>uF$N=vIT}5Va&H8wwnQSJju|6_nH7SYL^~r3R0u{oPW)T08I>w?&IJ6-< z)NVw`UjXZs&?Cs3YcLgK-(%a|?!=Q8x$pDvo`yV93a45^fSH_FjbEk@#m!!xsYd{& z2_GOJ$h1bFkaKCX5s0CSZSWW)oAW`}$NnA_2nE6wUR@|E{M}Pw)GuS~sCu zAArLVe((H+4yOrCrbJ%Mc<8{7;~V(*BKEXbs6Xf%Yx+Eb^>4nRx~-9CMU6Xi=lXaV zHtme+D|Ea?=k1dAu8u=X+Px9i$}KPwgJI}<`1n4-9CS6{^P^+$3gdiNU$dfaCxnNF z`qA{Oph@e_e%ED!fwuu%To}Z3dlgo!UsmamSMsR)Rz55X4vC0Q=zA^MzwoDeCq7{; z69PNFL}b1+vkf|{*@{`cr)J%5JC$d?gpekZ(MgJock2KS_ z@ls2@yQiO+8tc-GP6?Sua4x#ILR>XWur060k=|}<%o)O7S5Lv=U#NZB$7#GIa)J7_ zQVt>%`~TlG-T&oJ@c+_up~EM?0g+d{8ML9R(?OL03F;YvF# zlNlWJ%Ls($ys!ReCIJg%@^P4jf!DS0fv6C17cEXc%_M9g<)3B}G9&oFHb7i3yU>2+ zlc0diK^}m5P=9rSzzQH42eucFyMJy2l7MXhL6#o5Ru5T7VJNT-;QijPz&pBKU6CIk zPv!4dUA@-}Yy)Z#eryA@j_3Hbq1y#vA%{YZpp2p%Ui>Cdo@6B+V1rh{Arw0VdSj<= z(lZyKbbw5(1-pw8XQ*)jYy)Z_MF*Os$e<2N+E^Z~CR^w#6g^DWpy@dUEf&J9A#nJm zVJy!PDvApDQHsak;0ftJKNt#d;lbfEB<6lc6J*gSvj!pj{K@_RH> z8Kv2d^^KlWXPVq2{BWEc3f`ddU2}k6J~pa$~TS z>B`Q~YNbIv2cXpSd|MGoWd>yx47@+l3!RU^Q9;u#d>w~`E6P^|J$wA!`!^^!{Knh%!=vxw-+aIyP@b^r48IR@!o5$X3O4brCWSE? zoQP&wDtz@Kn?uICdg5Lg>4X2MYoxk2Rydw6~^7<4rnAWznw0&j`4 zIrMPu(#(WjQBq4qY2LLY^GsKmrN~?AK1MebPhe~txMmS7Sk9pznSKacU4aTGuxKCylZupv?v!RI2vm``$ZUc<#&Hw1zam5|3xzSA1C~e z@>s7RB?Ev)+N(ZzVcGhM)*nTLC{hR{W|zd?`_%Jl5=m@GK@N|hc zu)MWiX~Qp~^thR@FPuVK?Qi5s(ZXNNZS#jzh&Aqne~NuX=x+sEO{=fQslien6m>8E z;BGDM5RUFpDc~zh!o8U`Nds;E0q|n$2}nF*CO#j$dj`Zl7X65Q%)gwR&Mb+H0Ae4v zy+`k7B&~kLKHA#_0kMx>MVrGnuRi80+KIGOY*X1~j(SqFHP&KVnEiz`GWnAHxXOqdY&j~Z%nnhc@v$Or`x1epb}DDs@vr5wd^tasrjhOZiJ3V?ok|mJ44n+ z?gTOsM}_zonqeqGkx0zmt^ghX5^jlsdBqbtSC<=Y&9P4}YtZl=L3<;#GwxUq7hLnH zv1SG$iscZgI^Jv`o;FA&3Wv+2@^(6Ek%XzMjwHr(>lqOD!1bM6{k6i8Cs9~D zJu9sK<@^6=l26Od|K}&gZxLp{A>N~o+@t}-JGI3!XB;M6o&j7Ex$W#?IJk4YV`@af zvsDSTo|?VW>NinVfMgbvoc}agK#-CXkjyfw!&ftZ_#N@y`5%b)#zoX8#QTQ|G`5NEGIR)4FK1 zRJ-$P7L-k#ZmS8)jGqR`SLgN8$D+WJQ^lAS>%+H*7KIlt?sCGa!?N>?)-#TEbbIB=(@`tMsi+ zXB_$}<^Bxrxro91&eVNhdjy~~N}GvwyL%geYd%HN@RB=kN77w09#9&6d;2uROHBJQ z#QVU*oOxNtRj=^b-J}=L9On}~0{jf1&EOUB0U=Y-O5>)iZ&7Y1QpYYyDZ--_ zuoC*$+iJPss}vLEG(TAN;Kmn&SG6wXz;~*p%^ld5#*i88Scp;)_2Q16)}daj`Mrp? zh$J`0Ys3md=`?xYWfMQ*`{nsWaB!P>=uP^!8WpwmnIS7V8N4o?c*Y3OkAoH;T)MKJ z63k?C@sr!IrXd8z>d>jVkl=ZHS7V1h8`fSGjF4&b}n&%+{I zqU7@2Uj5E@M?+i;&H(uCbW#`1mu)YzGsA3CzUwMoHN^843a`9v?SfjGk*6S`ry`X)!2ZiVAtw){o9CRa~<_hw**$MgT~5Adg6 z`CFdK@AJF=%rE;xSk!N-3J7(<`*$3!mYuT>Nx`1-%g~{p+ZZoq&J7#|OYI1=s-x#7qCsl2*S$2(6(zZQIGj#!PJ6wmq?J+qP{R6Jug0 z6YKlUx%=*Y@7dk6`~467Jk?#@RRxG!M)?!Bj0Vc2&yrYMsBuH}MK@Os8X$9AvBwot zNW_AwP5evd_}3iq<|3e*4Ujpyrz6*$TGRPxl~&4$fc4rTqxc!Vr4=Y$N~X4;q#P)2 z5{`z-C6d+z4PapUkU2E4#wCyGf5kZypeb`|8qynu1;LiD3Ks;YIe(8XWVzu{_xB#k zxqVRAiw53NXjyk3sIK`>`zY#^Y9}aB#nE^!j1w=q)5Ycc656+Q{mn;+M_ayXI-i5q>8KmXs>*%$l2T*49n>Zq0_Ea} z$xH6v1;Zs8{IC^x1G?%rTkVA6Hl*|^z7AKg;Ysb0o`KP4iM=I<6GA9)qy57Em)(t9 z>8#RaB1{hC>lsC{FGTr{EA*mRj|37-Oy=+sH|IRuzJKcv#0V3zb^+g`HQ;-s|G#~Y z|D&w`<39w%-@^Lv11x>V0Rz)DpIKp31#zKs9AJ2WNwve-VA_s@RDQl<`9@T9+AK33 zi^!Gp{(NVe!5Fw6!KX?L3yd$d8NY@BU%i0ira*;d=`i9_4kum`2@k}VLRQKB*ER!U z60prEDy1`-J%)wS^1Dv7g7~w|2tlE|z_Vc9W^x8>Ge+SAIk)c0PEkB8QmP*$SXEAV ztuz2&@EKS+>H9d>Y3QOdHskfnA%()a&4st9xnE z4-!EYS(VwQYa0^_el3=nWPtdaL@xIn)T`|t=Y~rf*CTQQBb(>6crR}w3A4uQc1XaV z?PQNHcLI4+1WmS>ri6xMH>>V*YjpKimV|8b%7oW3#!+YHByy^M=*sfL&}arB+nrMz zw8U|Y!XEkWQg6=KIi@#2v8V%zh4mkb1u$p)L!#8W4M3sijWO%H^O6Xf_TJqH++0Zs z+c*~c3j#GGtZ6)+Vs5OH_>+&?wM2X_1ewuF;nyxFD&ENTz2b$~AX9H|a+6|2W^PjR zu6oh52RB7@$2^z#I3U2WW0?_N9e?Oi3pVOv*x5?RT_a!13|i81q$_^s9_1M>?Mq z7voe?NZULl&56DrxH~Pahe`lwy-ENp7C&2I8!OE3S_PhJEPw5MO-|83a%3S)^;`zy za0k0HtDQDUc`2NnFha2) z=#61Vm72=CH`YWW^HDSBbwCgmcRu?f)*-(J3N7}QA#zlHl{R%cUVc&3DG2?LEbwTs zCG%M&u^S`a8|3FyTeGWvQn#NE4X09rQ z!J;des^|*`Tx7Xum&Z-%jpYc-BP)o}s;4XV=7rco^A=k5S>5jlxl!?)pq{0z*0v%iHRIqJIi%+A6TRr-Ssad0l z(3q6Q5MZ|c(z&KnagEr4D+B`BjawI&wB+&MlpK6KROXygYFy1M+ zTtFyMKTvouY`G(Ehs29G{jdjq*Q=z-ac|#tTEB}+$UahngqHh@|JGH)Omb$p@nm>q z?>zT0b*|-)GUESBZvo{=5aM!?+Rf9k(?O%0l0a^!HiQqv^ z-f|HNtD|fB2hLQ=*H(*LIpI!0?~MDu-Olsz59iKwhOS8_5veGj)%UH}d~43nAK|{{ zUc}TWnK!;o#5qMN)Iwkis6u0}qo3lVz2aLtezu~^?x}GLISY||HCw$F(hk7nLp#bXiujuo+*~HtY6LPHE>`?x&h-S$pSi)(rVN zhLROVY4>5xv7Y+C*E^?uUXKH>FCrqs>001LZEK&cO?1(Pyr1<+w4J0=W7<0RfP-6^ z;cF_{U0e&t)hDL2zUW%N?=&2fl(vQd*NiTX965Ax;k%%as8PArqyu1dj(m)+OW;_LNjh@H1JNFBmbX@{I4lG z2dh!0eJ}exkZ>j4FAIqF4a_l9TCJ9X@ol>T&{Mt8O8CIfOnow6-Zsp~k8&@NnGQ0@ zcdVs%L=z|8RBk~WEJQh1RroTn*o+cluF4}$r`M{zLaYN*a*B_I4Qh}+rehb1d>+UT zI3Dyg@jNnW0h{FChKGDwQEi z=3{>6K1zb6vDlPa=q4~<;*8#wIBr{{JVol5I&IeL&XUWP+A=lIx_d?$D7s(B#8DSp zBsbMaf<3_%VrhQn)T?BW&77od?tSvReEe-*2@`vc&j-va*nl*ue+usYr{DG8jb4|M zwCx@vV%N7*U(9>ZQ0dDc;-<#(*tIIq-%1yKq#!rdhNjjflp6OvSF1!sCm2en)kTmz zEI!%pwmkBn+ucrCfcOSRW%sgY@}}8{SGy6zsz?pZ`hPiE5cLKjGP=rxADYSW>kBPw z;uo-{3J{?z>L~_x{H*_~*!!h!3pj<35OCu@jNYh#zAO=7_SQI24&!a1tNrK3ou0jx zGHS?baWhzv4D>*>CF2^L%mP2=!4zAkAHeuHp+3Pv1YA8%ob>@EQ?b_`e$k(Rwc4;Z z2}@Cif~{2ek}7pfLnM>7k?TiCA*Kp>3U#jn@UjRNBvcP3H)U$3?@VzkdPWJ3_@xSn z^wU7vgRK_lAq>TEzLTlI{rX>_IeK?FH|mIet@ z^}5T>A=`^gmSOoNTgV3Svc{;=YVE#_F^F>id-Nx)3hJySo-bN+$`vPvka$kfP2{o` zK3T$_Twb0mhN4pMn&X#PhSm3nt@5EN=wyWh@=8NvA`#!;z7jUB3FGgfzVg+O%Tg1Ws=`@;xTrM*wBgU+3<0_F@Lhs~$@+8_ zEPz{^Zr^w3{femjwbVZ9m^k)@9V@I^$&<~K=foA&8cIFv=ldfr4}|aEe6Db+Gx>l4 z)!z!>#sA42@c&T#j&=Y&knx|>WW_$4KN29#asEgSB#{e*AB3Vm5S59N1iO?tX&*3c z>$jXuSLE3Dwz(~PNzts8sFG>dZB4d2*;%5ApMK}9NCSvZL&as%mbL&J2N;;N*gA5@ zNJaDS9Oh1tb~UJjd=o;E=;k3*1-eH5YX;CsGI6~JkP*)>u#WxEH*a7H>`-KB6jFbL zWX!_3YaG64BmPm1F)p4cN`Hi87H74PBVttO{RdVY!cn9NeFRkYMOn0rTK003&lO1Q zt&B~Z(59s0Z^}kto+E>NzklAQ<=QWv(Ge^!=pZb&HO7hu)ncGGcemSZj7Y=%<#{FZ zTun6gm*>?lC@aKCcyrl#&P9DhGx=OPQU97k9<$8Yjk?p{oNiD*YCk+ zhnYGBcRF=aEc3sE!_nC)3#D#4AFfz>p=q9rkgP%}XZ`*h5M1esWyp6&@)glJv0PWh4eyg2~~}ev($(Gj>$l zDYs~0J4m1Z_No4f_#=xH1G@QpC1>?9>oWGw< zNW6eZIiS`=XZ?hSlzzYBUiTCFLX*)hla3B$HvM?JvFUh34%O+obVMnE{3DM#C7pkg z)~Ug;buUdtOqy=Yxs0_zYYcNo3$oQw1N?F}H``EVeuhWboH0b=u(WR)v0CgzLJHb& za0$om1Wlr<+rZ>sIjN!doLy){xgig0fIHZv-CN*cNyEC(5DCaHLc3CcBUrW>54fZ` zX+?*quOCL%KNCw>qG%}uuQ;dMlW&Sl4k|I8D`jM=ZRaBrP`;aP^d`atg0WohATmle zWzqtebP1pYPVeN{sZ=ik5EvQlRKilu)1brU{Kx{z_(}oAdRTVit?zH8&$#>cADG@T zKe7Y;AElL1r5GSA#U<$KCXn?-%wdyAa~|M(l+CQ(tdgyMCM^Y~gOe>JvjC^*4WHMB zJYM@@Rc_1ZFRI*Je(BsqR8?SRQBlRZiSZj8(rLA14~$0Jv2a|T6WrH{nC>Oh>KS<7 zJZjBxo9gp9ld=m(rhN^@%rei+b{3;=)8~)`&mLba(P~{@0iFcjQa+(t#quhXV<75$ zVM%3?pX+6;j&ill$-A=8Yz$X@EZ45S)EMGb+JM_oDqX1BkJ2JaEU@<>s_=i}Mq2H; z!Gb-Nn!HKg^GuB`xfIJx>S_L9VaU!0p>zlnfmbCS2UADtQNm(aHlv}7y0W%jkyYt8 zLCR>rFPY@Q{@f(+-1ifn&MrJCi0vN9jC=XP0D%9m4_X6jI}l)0e%OUXPO zcH9dgoqy#?B`jBYS#dRq0C4DS%6)Ip?kAa!s)fKh@mM#ta1MPTb?~do(LK(^!}Elr z*dfU26b}hGKFQ8-O(s9gHKFDb+bgZE<u(vfg4yb)02?GS8gmixo$1Bkr{K7o&hDsjAZ&!9FXp7vD+SEL; z@8(18#=gA|Kb=%OIfGVyihRoHeqN3A2p-{ycqOoTtfD^OluapolhCNDq@^-MpqEb* zeWkChsI$d^NwawB7qW>twzb(SIi`B~8zvMCf>VP4Fu@lvbN-XP(Esvy|J?>vC2aqR zuo~GWxd*see;JH_6pB;%s{H+e8ter^iyqvjW5c)`hBE!7XQu6%B(eTJkAjSn@740v zFoE>?KygMI;3dPNxm>u6gW%2igMw^~tK-(HWIq0SJRNhVVmbD!4!nZXcg@M4FQuf| zA)HF&-@fz+x}`H9n7F7B;&%h+e>njCKM$gQrVpV1Z?b#Bzv@$7G<0-@QI||&qc`2D z4YE|QVf3G9z5M_eAG;Ylfh6`Jl(!j{r!?nyyXk!6!|it{z^zOaTZMu+BiC{Rk6R+_ z1>^*!X69i35=;G)6C{>Ov`RN0o%m?BM;RVTIh!P8K(XJ@XIt2e$#APXmjn4kn(4Jy zo{y*`8j6O}?gfu>Jci_tzRgdKw)yF~Nw}~t*yXOg7W=y2AB*A2Q+_j9 zX*C36Mcg7cY9gj!r9zCXA^{$5hM7qg7RltktE(5U(0W)sgYx0=gK%Wv^j>4t z^a=R0I!^1wAOR0k`99PAhlCnMkiQUAem9d>psc^$qYKo+mb=|zK39|#LzdlGRuzm5 zIWTrhiF~(zV?eWMGEdHH2#H&}mFL4$`9#2enXGX>sl~|8mzu5TsOpu;O|9MI?ptkU zaFlLyyWA&04?}e_`16%}*_W%|#^WbNHX8e{3qp1t z27!+QS|vqd7i7C<=*zyWhzkA_bNb`B{v$IF=_vZk$mHqOxEVd3;F@5qSc;k(i@7D0 z7O74%*+6=^G65|ceD~2F%qZJ~9n}UngKV2R@E*15z1HLXu>Cq+9Fn~(Z8fdhX{1Ar z>0y0y*EK>s<34)^HBB&RK0MWg6oSyI*o%3LZgM%uqE)Y&wWDI?7X(4o3eHoWm(-^k*1e%cK`I%yETyq-}eO~f_zTKB&)?M{?H|=C=c7>oIt?%6@*E!?5 z_BeSKzYAS852mcKI~${Z-g}D8T%;tXh2-GC2BsDQ?gKx$0x}R8e`=EwUCOFH>$0sC zchOKD^pj>6L^Fo@S~`X@xNdn2UC~CfR|lc6BEIOdU&#uuTzv`+(1LoYVz1$DeHVUx zCV9d5&wCjVOBzxkU#QeQDyU2R&$($pcPqe z1v+1^G!yow>U-E9>8Q$)gQ za&OJ^G;bLNUokjk%M3m$%xCMTZsC%^n2TXesYIw2UhrT!LSTyR4@)~l8xrg__3Mu@ zgGr8clFFxD<7RF$%hx%T$4YtfU5p5?!xl>yh=ax&U2)qn0)22eIKp}IGJoRJo-Jfk z6XGka!O$fkRN$=CH^)e(@k3<}K~Tz%%cp+H!zwAV)*1Dm-Bq9%EVHgg*2?)|)8n~2 znbiw5E2mt}n36KdxZ2S!FgX7k^!tmfU=^d_OBAlh(Wzzhcr}+!u1}Q-LAgx;1a)sI znT;I;SXY7J;n%EaqGuc9xk-yz1>O)i&J%VaO6{(4-WFKcsLmd-yNbPHT~t%0LEyz& z4oqK@c{1O{s9U(%2-p6>$@2MM?W;n~CSUs;TB8C>v8mI`n9meO#d}BZKV{|OVOlUx zk;;EuP{z&7WXPM?P+%+@RlwUgUdH}BP?V6(ln5eM6$)XIF9BuFb8-$wk}YM|bVrde zzF8ZP!1js--}ky@IQ z-kd&by=dWSou(!?QWB2oLMN5Yz54CLylK5A=!v;jw_0M*o^F6e@W$N{P#ko1CZ;M2 z4c`yl!3)YjA-a?m!2tGD(J&l6w5EZyi!?$YrpqdI(8*ai^ceB@H{W%LatjGkz*FM_ zKqJHdWm)w1WX691nJa z^hK=@w+ae%^u`x$8~LTz25O|3^-UqthdgUc0*0k~*g|;dBsTs!JAiQ?BOChS0N2dn zA9Sp0@Wue3BV8np1ILxM%z~eMxV^oT1cO{b(oplbT_ataP>PRp^;+@~OGLhr;1$!k zu=xTEO}RP)v^YM2Zq)7FzTv28ydE`LzXIZh2WY>&IRzY~-7qC$`!8LPA*o|cQZ)&3 zV}Ct#vlJDs5U!~F?-85|WYFg#xaMyL2VWNK-7z-5^^)xGc$Xgm%*+-b#HV4~iiI#8 zut8+{ciGFXY_3(Ke$9WIuo`GAr;s91>gS?DMU_Cja0MpVZ6MT|lF@5brQxu)zZ_{# zhEJt8J@bDo0*abAvg7yb3h=G5zs)^>_;zQs=5i6DOo1~YoU%nIlb?}>%qQr@i+yBos~SKvf-`)O zZKZ;uJcI^W!tg3CcEzHO_N##k=c{}Dnh!6hN8=|7p1?0K=Adr=O6FiFPj@t1V3WsB z-W=+ceQ;8YN~m8H5c)XCbqPPFRUP4OcE|T@aK}9dA zYzW|tZdkCxb9xY1oAKYT(z8aRd~(tfxr`=E7HVDzyJqtfq8vK3Tv}1A?;C*kSRH#t zgdQoK*y+My`N?NN;hEVJyWUW5l~x1|!k1>iAUS%gMnEDlD)HuWccl%UI`p>{KzMcMvT#PsGlKJEP$83A7=64TqXrYCVSI!d{r;*mIMhQZ) zW`gyY9FFjv^%S2!&Ph^AKC@n3b+ghQ*Mk7(;&Ix||KfSoAX5QeCXTG7uBr1EB(ieY zQhBjd7PUBmB`5XW!)b%uQ?8iw^C>%Q zKrh4+e5^w*6TgXUY?4JrW)7YC-?_K-LM;x`AeQV+b+Go101SuY%+>{iT;5P>PscWfD^j=^wr~<-0CqL~= zBY5D`k~D`}f#3sT9orV7sm@=f8`V5JKWO-~5tO>XMLHU4<~w~=H&jtH$G8NI<>RlP#E<6?CT#;>V@Rl^MyR5W5qd%hJhrJK{tAF%d081PwFWi*kt< z%QwZbluz5fzpbOAZ5f*=0C=SXz^i`>HT$2>=RcD>|AH)S0A!&8nnT~#@CQUh$u09T zP&|VqaAVdtAojK*@Rez+5^aF(cZZ9{cg6AXQuH!$Z*MRDY5QsbJ>XokA{a{wsGMvA6@Jo&s`E?vFa2-c1qbb~9=Pm{IuC^g|8Vnz;oGB1(SqE<1mLa?0~ zMK7=w8#o1D&g}k2+P|)333U$5St~$Z79X@P1m&>luk@9$A{wS&7%ypvC>g?S_9PQa z-Is%5gRLjn)}A_=MAx%r*y=N3vsP*u7Kc;P(H3NOB6zsk@ryCOeT$W8^Mk^GgfZFK zGVZ07=dS+oFM{u#dbC(-tH2M8LVg(1BHAObt%-7<5Rx<_k4h~#DpWXZn>ELfXPh9Q z1n5|T(rqQN0c%0|YA8NlsF@lOTuCe^yp}`AFf>W?O*y~r=4{W%+F3qYT%0nsITLCW zl*Ob1gTv3!#Ez3IxbJJOt2V`(%RG%`j#ESXq*(xSZ0nOGux7X!YQ44h?=MamLr!a_ z9+xr^`%Nc)74-}p5|WXv-z-0bl39>FXluD?HxAGIZe<-?!Fl|c+{3Z$RX)Dn$Xe3S zT~s_lUnm{eLGMo4B?f0ngWi059VwRa^smb&M1Gq{<-sJ|)~F}_rFDQt=N{t-5eBad zh+XHV^av{~I>VgE*O$`atH_pZe*O&8VO!6u9y|gC-v{R|VrLA_d;-NQsi1Y0uHB^E zOHBK=f3?{mM48Nha@7~b{4lA+9MNoEIhU-lT;7@gDrG_$h{2>BvxrxnG#E2}UE}P* z;tcW?WF<~!fYI^@C75@`wmn!TOlm)n5r;EGfBtSn#E@cebCBBxtU{e}tAq@{%<_vS zqYeSfr2Q8x6#wL!Tu*sp>F}s8dIjg&O1OEGU5_fOrSFG$J-eR)&^ZV0RW($M`zSIY z@E`OW<}9Z9BHs)X-Vk7}x)v4K-!F6bPN!52KgR^ZF!v7d-kY;Y>~=}MP54}(xV0F5 z(8{YiOMYcyLT&q4W)#~^X-~a&JGrQBke-@3)1URuIMDpv@vM zjsb09|C!9pmVCGC9Q~XmPb8uii9Lr+r*U)aQTtjL-m3x1`qa|OS+9ns{{k)z&9#-Z z`AG*d;U9H?1*Q6*1?3e}(dM`8T-H<}dc{S(7D(9z<1T3^m_ZLPt(QQl3f)w-SqXJD z8o2zh1q)U5-;3St>lK9TS~hy}m=PWNo>&0j8jL8SJU$th|tB->dw6j4F6eE5~nhy^zLBWr1jNd+(BKa6kP<){l*pI`O#2!T3e$0 z@aD69e;H^qf67BN!5r(B%j#g5Z|}X(CRczmq~9U*+a?U@q~B$y`nb42GSrlgt8U@l z{p0xkLP~e2U;dY@*N=F}==o&GlEAF|1ZMOh{1mGM=%YdC4X-a*irZOku$LHKKy=z_ z(vB`8v;kQ?%->THKZR6+TM6}A##{-s2cTq;&1RuS$BrV}8$YO8gY zn&EJs)f=tKCZZy!z?}%I5m)7(`9+HS>62dTXnsc9Z~%MtXPkx3L+JP$iCT7XL;JPt z5_($31@()#sbynZGCBT4F`1mD#fnktH5*ph5_{evMRf4O_+Y`)x z5*?_pDaA;DW)Kq`2#D#QpVj{%iuix@fq?m|uSNEU+UvXCcUP-)YHPbM9|C4@Mup-G z;=3LwpYNGMez(L-o3zT-TI(}+t2P6O_}z<_YM5VsJF8$a>2jD2O1q)d&8f`bK1&zF zxe8SP?Sv#U-pZqknax!)5?xh<%BxEeszPDg!34ILDYQ}PMXF^9lrj^tyxrK0P48+qqw3;*#Qgbb(Omz#RQ2J(HC$Pt{@sjU+?yYK{ zdW)(f|ML5;Iua&gvZ;C&znMq1Fb;(Zt6=R%t$s^=$x>rdc$m&h+LqWP*ZxZ7WEsaB zgbUS;a_IrZ&7*tH5hB%Y%D1;6x}b&OdG^da`=P2x6%EpFqO#*``8gW~y3k6|4@=u? zNsvu2_guq86>SVYtHQPh;8C{gm4x*HTH|@{GAydB^owT83THu`oG%cdCFKNRQ`j;s z)%MBf7q|`HOg0pc4si3c=ZiYlYVYO)U(f&6E%{j!eWwJ}mp7ok*#3XjmxA%Xx9fmx zP{4NGyCo6uWs4v-@dNo|g9Vm;DFeDM8Xqu-(Vy^G*~Amx4E4RL1Wq6?OhTqrf0#a) zf7uhO zz$H~652aCnCh*40ht;tc_Qvf?S;dcybOKBiHA6@RL%}5f zOrgEdB&|evS0R;gQ+xWlRTzh@{F*s;`TF5l%O}Nfz|#u(iu2`uPi3s8Kp)h?k+2Wt z1U2$h1zfz;1@k!V(Am_EcJJ@VXXiU4={!dk@Br>BrGKrTxF5}ucz#6mmt9-jg`6X zJLah&aP+pXtvE^q17T9gw-J?R*WdsScS*VUuH7h@{B0n$kzUsZtv(erz_ejxF_=BP zXx;C~zq2)UDrE%&qjtjq&N7>jhZzp%445TxO&K1*=|TZGU5XI1i9v8S9tUFMAVLB; zUTPcI@Yy?_K9^EObp5eCt(~Mj1Wfup&#MlI#up>6(uU3GGB2-0+AyMo5=cepeQ1<{ z%j+K4Ca4_tdzy}UD^XaX@x)zPKT3TKc9#K)r%Be!9KQa>>YZHZk+m%D?-YYE^6DZ% zqXIkoP~T)ZG#y&I891m{MZeOTmb*Q+NbsrX$Xc@|1Qsx&*1eZ?^`QTZhSVbC$Si3t z9VXnYxl~5w?+cIhkIw?pQT+5YF3IzNvYoRsh9$YStxoyaDZT#HMVzEqHCAdVvF06m z-}GFe>d|WSqSJA;%hENYeNVBzgK0CXb*d%OS5}LfE)_=HUT_;22AME86$X@B#FAEqtmkvH=$74Ms5%;xdQ-^S^e`Py*!~4Kk0iBf*Si2E|209f5kn^w zMVi85>{oV(DhmYFfU0#^hW%8{6m^FoSj!Nzkh+RPDL5-oib>c`71lNf)0*BI`fLWR z4FsL9nn7?W)Froe-Jl<~%+1UZCUIWr>gs5nuKfs_NXA{FOg7)ph|q3);By4(%zbpt z0u^}+DyNt3>Dk<82QS%Be7H{N!g8Q(X4pUAOg+z+9Sgd(l4LbxLSHOyTC>thf`6CA zWu;$D2_8X+%^`P73dC>Mcl*0e{R)y_X1AjaL>ag-P7iI0yyZBW94Dwr7rL9%cu z&f%XO;#VXELNCSMMzM_$R)VA^^)vTTku#UyDfk1+RYn-=kw@z7lGbGnMilWVP5kkV zf^~r9H0==S=UFvg<>S#MM45q7MW-hT}**xu{d>)$YNj%TxY z9)N-C5dUvI*8d`(srr+R{a+)C2vSBTMyQB+i;zXirVxKG2Z-C*YT3mPIl#yg=IEDL zzn+9$hW0RtJHyGBP3do|`=b5lrgHA*R_Oh(V2W;kz>b(^mqVj1vvn$pMi7u8)o$Hr z{dFk%6gtXU7Y1(95U4{^E^v#WTPppjwL~h(G7aK5g^}2&*k!NTgj#1>on*Z zEbA1Q6Rz`tR=s&o4Gat{b?@o(Lra;VhRF<03o~i@t;N&n5JFe|UBT>VWC2|Lrn};p zg4cHTKq7T=ukN9@}4xM^H5)?7u9 zzg^sr?r!kQ0FXKy;J-ri&q)0r;oN_5X_^42M)hHz{j>u>HL}sI=LM$ZCLzo0p!)em zR&uoUSJtw)L`60LR1=YLxBrx^*8}xJa=zSdyMMht>&Qmv@*&WxoY%TrS`)&|>1B&) zVzw|p(J7H&nMjHRq7VGR{mpm>9`2Nm-03xH1Ryz&9RoSzK0F9BuxM55CF4Y72);96(-4i)&)ekF0NAt*(ln_I zc6PIZ?JDVv$Gax6}giHZ-!cw|Zh^Z=3ta}gMv9D$d=!T;EN_(nPEtpgBuUm!) z$G^p&XN@x87M{&~SttUnvWcZeqnJ4LZ*Wo`U7uWohs9Gn=ye??sbe0}q9E?9$s|ar|z34hzMeu+T}VDvy7TSuXG| zbI_5=Iz_ouA#;T6xNv1a1bhV(jfx+f#_JUso=@z2L|gf^Njx{80=X3>TWstFRe`uz(#u^4Q1?mtPCWAWq~b~PoS7%z84U8Ez4h_;w9hO*ol znkv;l=Q7hg_j^M4Hf`l?TbbY|kD?tXR5<)Kjn9L|jB)Y()eEK?X@9sE0)80gVzm{%{5xV`+tna1W3q;40K~` z$f=0*xuqq4k=#UD1=%0s(5ELyWFZRm{6gIr%v!#!;~EPmfx2{dFd%{BmP41^f9q`D@$5f&1Ag|(x$U(@qz0aUGdz5Cttg zK6KQ%!3{aw;unX6H6E)8KJ$=cyHpD#O1q5jmtR3XP;4cBW+gPcho>K>mQ5|_+p{yv zZq{AXh^D_@4m_m#fk5tg3%1JayQ-!5Y+$2GGG2!NJbi&~91~x&38H3C_l1%np_)M` zl3g!=w;jilSCv1qvP;6PM%3YC8SOMMNJTa5{?%B$7qfU#rl~t(+`7dh(V1xArgYG) zM}OWNUT-jlDExjoDq?n(Z+D^`eDH9@MsTs|YgBq?(8mJbHft@JG{N{(bnQ>w6}vn- zH}3X%B_QX(HM5NuTE6iK6`H2{TrGgA(}^6q_geP%&2(E=3VkAga!i8#zcT&*@h$x~ zu2ES!W{(3Phe-dF+Y@P7{lzyMhiS4H!P-@sd(^lVAWprb4G=_xpkv?rZr9%lPL9Aw zkRwypRp@k4tMXDnz1=G2Ajro9O)9znAsiVe@ppnuHL#S`G**iK5RQgF$`EUvRKSz} z5RN5FHUION*9J-Fu;zjps}gaYVP_sE5im;q#r6xCBX06}U!4BPUYnVI5LUthNFulZ zjHCS%*32KqF+bx83&1#LrB7n)lSrtRJ*hW1a7XhTjf`xg*XZIC>^e`tp=uF!GW|t4 zDqDC1{q-Lm{^k~FHg*UA-{M$ui^_c{-DYh;H^vo>Gva0?o2bRGC%S1k^o&$}-ky7e zJQk;X8dU1QTL@{?bT9H>7Y<{j_;74HLQi>;4I1_-Z_QVhSK`P~{-6&&cO6J$lEhj_DEhw3Gg5xqAAzv9g-kLJ}N#do+@ZenE`~ zuG$YFo)tbXAz~F}JnN!&>C-&dGSpTBRzY>qcA}=+UHcom=MCu%{-<9&RP_8Zq<3+( zTZ0FKI4}LZ2)$MY+s+}llk04NlLkTSwt%GR!n~H3NpjzG4!n_6sN?A2Y>l$bDpq?4 z^ft-&BrLG?uEN}DPp7{37=Cd`&~!W7Ff4|wh;^H7S*vB6U@g1qxymQ8X)+$czQ8`1 zU~uD0#oHH$pS9wl!&9|z@lln5Zje%Z{pt1C4-PgC-09v3xU^#eokFjkp11>kGp>vl zx7A@hk-o=ENquejmJS`hJqq{*J$5WIYTx^zFl>g+%cX#()*;PeU!$+);Jz?g{nl7H5rjE+@-z5wQ&T>vNngwGy6@A4 z)HITj&Dg{t6Dz1{WIa)Gq4J}q)K*j|KxLg1+zOocz@^Zok!&GYn$_k{KXMby(l3s4 z)*Ud>@n|9x|0J%!({{G>O9+xBwoYjKO6*z8?IWu*IP(P|0JA&8!WheWXP*-201UQk zvt}}LYD<`7kqGax@xbOl_8`RVL@O-!oA>4YZJ}~~;{Za(=R3K{$a8m$&XI4Np{)D@ zimzdhldW`4`!mRN!+05x!3PD@Z3&k^9j@meSlt^q_YJLpNQ#ghitCytu*9r6 zgi|lP^-&;=i_q}_V;DcP2_JpgkMF!8#Rg@$$h&zBCoX|mGBx=Z(*+(tt-RAWX0?Rk z{32t@n!V}D#i&W;A2bhXDPAiQuz`-w%OL3A0#GYM`H~INm?wX)RoSTK^rxGx%DTmu zCJba`79BT=q`(>hsFjuV7^QUvD}b6~6CSB#6;Hh& zeQs~n>6ti~1L4AHa~unZ5~gX&h?e7zy{VtTrE`5@@tlY}9__LmX^!d(d0&3;C@g7V zzU{a1Os`X9r?2krT|RikLw!ugy;*bY^bs*gcP{vdeu^V}NJ5iT7b(;|>OL=f$9z*o z^STpAYCs~8!@yE*(tIDP8g(@AfyLs|^=bUwU;ZobcVso%du*U^)vR0h?&%8HZFRbR z1B|!Qp|OVW1<3PFHnk#*nfK3ZXkzRGjhIdE^yhkKzk*GUHPZ6QyLG5()2HpffvTXaU<8@QIcu~^gk7T*}FUc~ixS~!nZ6-bPcW0g$ z(`-|dJuKLrN0}Vgu0Eh0f#SA3RYwrPMz9hd1%-*UJm(~dA=ngR&{bLF*2WH2fQPel z08>@=HDu5*CNomO7nW}vyxFuV=70$FXP8zy9EJZ%ZX-t{J@9;Kx`yCdX^(!;W?0bD z>f=}e$Jq6gLUss@UI36;N<3@UR%w#}`U%}db(u_fH_qV1tL4aRbBm8V2iL}Sz-XFn z(#_v)f>ldh%Oo_<{J-cYUHG}oFq%cXiLiAl|4u>YidH7dew`YM)i2KD#Fd$D^w$l0 z?0~?{v#l8XS(e;^N2g{r#-y0>Bq${7Th!gu{#Jpe9gTGXRVLj%ek===Y5{PzMAnRW z##m-!4oO0Z)a97`zHd*1tmr*1J5Nj?u89bRyr~DL!0I?jqKUq+pj&)QwQzk&6R7x_ zKZ-n7RkyAqBwk1+wo@ihQgXhUmte_K8p9lS`89X~(MWJW@7q7Hq6rq-0cdYc7x=f1 zzR^5I`?XEUB`KZ6lYBog?=u3PTJjf*5difw=o9OG6 z<6U!j4j&Kxow9%8zqsSd7}$-9xgLIcmF)7^yi71N`p&T&t(avnXqsu^HH53&?un5E z+7D-HXU~_-!_TyUyiB3=j)^{kqac7nSCy)wB#f00gS0qq;uNxG_UHalW(rB%ma`9F7SkV%5Ds{zU z1e<08{#H->69YL(f5c=fe;nTlc!@U`Q-^#`pk;_>Lq*@2e?R7yq% z)q0Bk{a3PkBl%RaavXCV4TXvq4lb+Y5jaKEZkeL4WCXuWW}Eb(+g|BDoaircWa66l zs}9*W6;iw1qVOiPb4xsR2swgs?bkk#=rDXrpHg?!qc z^;{fvE6}z{m6tfLIP0~|-_6yv4!-898En1c?bh*;;7>2@60^D2Mt3#I%$cNHz`AWl zgSCJ_d`6psxtuLEJU`8dOprA^H@a<9PkM7>*r1C$t`Mu0VX66*N9DHauKECNm`Rxc zl*=qjoET;*sI1@=^_W5UL?v;F1JWlA`0|phq9`>4@-0?sXz0y|IzCtf9gFbvJ`z;N z3jEw1oT%H|6|J&nTSIXZ;bh-f?^_`}o(fp#aJdlt4Y(Ylc~-+WQI8-9T}j~2z5YMW z&N3FzV#J+tnAYAqJ* z+VZ{f=xD(|b^ppOy%bnk7&@64cg@pn6mzMP#dxrF!Qn&Vogaj{1D3s}yD`bYJaIZgjG9Y&9z#thHUzTr_-+sL9r1dwui0F!<+fc87^Zy#P2>QwLhK zh5z%=;9s8H|6|J5sitJV!i?+}JNM>rjF!vj$%GClQVW@@LilGO)ca$t;Bq z2UHjKL;cKn4?My#mE4Heut^dm+K6c&?kVK9Ut8(KPiDyqx1J*Bk%p)6+IL@0@(l5@ z^GcCm9vv{ZBLL&nzlXaYQk#ajy6TW0oZCj-4r8yBzklUGq;iA0VU=PV`lZ}~`SGcy z1qa@+b{kS%m)Iu#i!07|`l~wnKYal(zOY}p8LaNG1!(kvwIs%TE;KF`E*W?@sc2y-=sk%d>J?!&JdjO zuq|HDf-S;)htgN+n$H18X0sgVxrDX7jtvI-bsWPZ_3N<*mcsd5)@M@32Egtd2$1{^&xs zM;gI$G+)I$9eM-o<+jRa+7*q8wswjs%Ko%!?%z-iC$BAC{8uvy)z+6_BM*(In!j~u ziQt^+{Wm+Q_z03j(8Ar%7Vm*MY0R;%3CvpD9WA0NKs zGD|13mn9|Bdz-rf^UF7xXsg=lp}_=od` zP*3hbW-7|&0=awxRhdwlpqbA=U3oqk>0sC2LuVon*Zy5KddDaw-@H@X zZ~RQpR?vyH756mZL{*02%3J@uS33J;Po1SAnR1D&pHfwl$yZk`V5@RIfHJWp&?VAL z6yBQxO%r8_ptVy4h-4}lF;vhsl$k1B!^7omL|a&nJ){BE3ZFZ7PCqy-TcxZ^vV1Hf zPW!q5<``BFDM<9TTcDR-5&J%MZu(?#8!_OKOi<_S;ne1JMq2G-kwU zlE1dtQD6-8%mVMQ1LPne6#sc?{r$SSrwOcV;zsu6`r)638`%d_ckS)mYeqnZ5VuMx zvuSB*Qy)*kAkA}!`Rjoop|0W^!Jtxn1IiCj$}qMt=kIY7x6ha6<(pv&-AK$|cS5!j z0fE`gU|4Ht@lit&L*n=_#UALqAj85STq`nt2r}r-x0biSY?aI{35+j#ZZR~L`MFaX zyd6qvcR<(>G3GSaE&5k#W#$-g@MZqr8Os5X+NSb^F%=PCind~a^WTUmKT*C*7{Y0BpX^#@?fF+;^h#; z*B920IB_{Dd{BvVM>&6(uzAOkG>vCQa4oVoU z7x<&1!->8ov5U_en@8bHXvHa7GBbW|if!bR`ZY#TlR9}6dz}d;&9g!$cfbkhc2V@p zC~ZQju>b_A3Vx^ny?l)Z+bEGcX|c=e=O{uV=Q4pTnwoiU-vdDd+suXb_&ZxyWBxKJ*^r@y z5usceG3rTBYm2}moaqo|zGD*D zqamfLE{4);O?))&`ldH|38N~|dJ3%4;`r*y@bQG+Oc>t|B+~@* z5o3Hqh+Kf3_djBjewBSxum%q`Zr%EA&(g>*>ux=cZye^IG6MIRmiFxYHegf(RQ+Gk z<+5pBOg5Ry3A=~e)Qp}{3+Fl_vh_fQBGqg*?14=-BqX@$*(4w0#d;x)_Pte@*?YY# zKMoA6QB%$4*D@d-DJtI@AjaXHy1vyswtEAby%-znQ&otS;PQN7ul@XaA(|;Q%h5Go z&Qxm3ge{-zwJ46WU9}^6hF~Ec+o0^C^OC|Tq#4W38u!ueK1NE;AT#s(su3!%h;;TzsLC!MA=wu?U+>XIk zLZ`+~7gC;#jN%C4Ep@ymU)f`cDnpzpnP{FhjJYG{^EK`|RVd5^cs`*v;C9K8&2!F= z=lJ^1HB5~gAiO?_393sF;T;Ru=G$XF&y=banJa8IOdQaRxdZ!JGB$;_f@qL@P|qa3 zFhy%)c!3GbpL>A>buSsUL1!hj5$xA6tBl}3xR)Q-HRaw+{^5N4!mC`8 zla+sEa!`Bo7e`Gyr#y@s@UkI*_rKO!{Eyr9q?X>NU0$H=7FfSFR}$EA_IyopmV!|> zz^Ku1IzI2~Fl%`-=ookwKs(^yZE{c`Z33N#(iX3XH#Kqfn3+DnxXbb~e{x5G9s7aO zu};mWlZ6>Rc8dy0tztJh&oOx*0*7!MrH6}TYr)I1MLhZ#C6(Q4QZxxSHExW8mKiTN zXV#NN4v}qO2eHKkF+AeU&_Zai_KRADP?B3)WF3s2m2dUEY@8M2BJ-+M(6r@mH}>$5 z+ztyjfi{uwh_GkI*>AH1GNW^cGPR$baa%HDWBu1B{PBtUefl{nT7*1|aThS-aEz^L zAs{JZAedVVyBUIk;gQnqaI|5#;764C#GRpBdxC{;PlXrmh`I|CsN3ZoLr_94$u+k0o5 z&c_`0B#4UH){A%UpvU)7g+YQ!tV_vKN^MAJXSUq3JHd-bZnPen-t_=1d2o}4rKQGa zZC$^;t$whDSdn&c!o-Es&C%X4B(Q)T=X-kb4kAX?cPN$d0rL82CVbxTHuNS1Cx>;h zsc^e0N6ogSNNm6su!!1fe{3zT$X&JGKLmmYo^BhV`QUnW+*{R2%WBx5cdcDa$wHZ; zk<32lv2TI6q!00Dj;O)@{g@CnOO<3uwOj}{@9#_qbvg=#wE}WFj6Ox3#Y?=;P3}(C zT4DAYhV}#U{v+)9P}3q?%h9si`BFt8vz=!6>O5{7dysXk{4^|l*M1?zLv1=0MdLHK zTxDOa&$6eo#w%CK-Ec5!)}6aIg6BqTZ@a*XGVbZ%tEcDAPt;=}FVZ+?4|Ec8b-z0h z#M9n*P%S#doiam8a@^78oA%vNZQH8~9BB{}5bNWKrP~_RHOeoQEt?=-dIrjK-s&y; zj^qVmNI^j_Kq{H7rW)F!HNj%;gXB;x`6NofjkX--4ZCSVeO*9Q9&KgXUZJv0{+@)K zvt(OaDDVcwsS<5A9?<5V##AL$-duGpKeHjDHFprct*jIx1|*#UA`Qcyi>`ydT$6ae z@JTtoxT-Hz)$z17m%hsOsO@p2KKQzJ4?Y>5aETh3U#5iq-YZej;40y6DPs6+-l(_Z z8|fL|SJUt6!kk5MEA|EN1`cbp;B+BB8wT-*o#k}fxY_KvUNa6Wh8-dnWB*Y_CKG&} zaJ3uKy30*35pkxQLsqhPY5Bdsx0}KqS?3O)>;YVm%@3;ek8gny5XZa%&Dhrib!L-; z4+1$MzXdi?dohl248{Rx8yIM~0x=n?E?O;bu8#ya#t{~x0}BM33tgt}Of>s3@?_^d zyjV$LBlIdCzob`=8O-XJR9a58S_%5=2c~ZQW&c8X5(vBo4*Lm#H{1Uux}#!hYT^QP)Mkw1bwXHh!RZ-WASp4omhI#S7s+PzD$wF0OA|=}zUPj3}sm#(^_Mzj~}Dvvyx!65yg@Mjgr8E6moEsf)-c zI107B{Lvt~glDgiNuTwk>`ZOAo01Kx{VseZ^i!A}gZ#tCjq0GLp5u5kKW^k~#lDx3 zp6?1?1iY7#w%~z&64TG`iCY%QdcNU{jq~bovl=x4CqlHo`tpQUAObf3acztnYG0gj zTIu(+_Fv0LC;H0avUN2zfx~^JecL+6Mx?D_X>b*1;BeneJe){fJ9bS)NqN03aN4er z^GhJg8}xMRv1FAQ1eUG6-e;(G@-zMD)uYasUecA$p?Pig?ge9pzqaK)gA&$&0H55g zz*F!)VG@9|{lBiDfAlk}ti}K*P48zR^G>)p`!A5CWOZO&m=T?jFW}zDfpVbIq@)Q< zUgO1k_(NZlsg9bqtkOjKt=Av89td!wl-yw`;t0KQ5}&`|d6>GD&^t4H+OlZ?xFYbkaZQ_=n^AmM5uZd{Kj z!5n`HF&Flp>gm1Pa8#J1KEA_@J&~@4I??4FDI{Gg2(m4W$$oADeCWk10C64rgg`3U zNde_OinfHDMzGwOypb4FKs}=wN2l}yjxjc4Nc}v`?TN!HCg4-wU|1C`Y{Q+ewbl){ zj~?oT=4r%_h?g03LTAf9bS98dCg_~uL&whz&D1X{CbZ=UpU{Fu4qjnb#E1ZTCGJfM z;aoAP10n93IeELus3~>r-%qteVq1z7oRIib=fj`rUUU$gX*oZ)!`V@+373qX-Wn-O zeJqBj+xrNC_ROjp^Tg~d`=l;C4#u}^m!mzRf}~FYC0S#h-9`ki2gcnn`nHN9r-Bop zBs?bbTFFJf`;(6;cDI7KtDRh~A4>MBsjc`kJ>E4uyUWJ4+e5ES-$x%LB-`JRJD-%o z4v~L@|MMWzD{3p74!EA$(f(KW)1PbV?=S97jXz)9z>?%N*hmq?wrg+S-eNmW>)hJr z+O~@Z_NLZ&X$ENuwaAxeM4HvPBKe@np0xMVV^E9PIJ{tN>H`N<3kfKZJw&IvXJ;pxcoO;Q633)!|M6yp;LVOnV8 zT>y^ESse(Z#29*-)S-${hmmnKE+Kb(omkvMDIq!+r1+s{wxP}(PGqgIBjaV_&I2}M z0^dd0pGM`IvlM`_Pn_6591UwQO-QX!@y;Z}Yia@K_H31(txgJ4xX4HvYi1F0iQLJ( z+am~3`q28UcUluWzUI-Y#qO;!a(xh%H7H6JOlYLQ!QzL4dE>y}8ev6v*}LMQ7gglmbOFQwgdj z#TY0J*A5YL-o@0Pv!waXB-=Id2jS4e>p6f#@H4w9Ze&j@?*h}f&}Pjm*Q(2zKuEfj zV=JSjK@DX5if)20gMscBh=d_;HSGTT^4z1z`PmJl%hvO&ErhnSW*+I}Yi$!VpC)Q{ zk%x^=t+;1F@Ooj`z%HmWblcc8x45vT%o)!ir@mG8hsM*E3T85Wc?*-Pn8?>6&lytJ zhq}nR>`xI>vsY4BQ<9f5?Z6wPQnbm~Ou^k&ouV#xc5R%LLush>J`2uKc#0qR1kpT< z{}xkEa6es8(5jpMDFz>P(sf{WcY?pEF!t>9bl*ZClvm$m(JqW(QUg)FEY7Lo7~Z67 z$Hl$ZKE1PHt^Ircl^TIpHDL>45q_=O8kXEt)~N4%B3TCHcQ)sGU!E^?=B!J4FqwPi zNPA2?W%>Mfjkei6`l_^jJ#Up!fo(-%)|a|Gp#Is=(QYYjpTsa8 zB+S<99Y6%oAmS%LftKV^s)L7gBr>g-4q)l4bgsWv=xdeRcTwPHR9}wd+*S}w(GPHB z-CwX8`q7SCB~Wort4J!I56+S>`Tuki`XBGPN?`5_c!s?a0v!(nR1uojokHX??XUm>ydOk{nL!@Tr-ppQ zs=%JtYgn8bS7ovXIM>Q?BU!t2qmhEe z9#~XSuqUCZQ4HoJB3YdvT+Em;czVQ-fJhYm;_(%^4{!rd;360w0Mj4i`Ixo&G_Aqv zu4ZXy;4mwF?;E)E%|cdsb_@w9gYKtuENB2sWp=-*R4u^Y*go^(;Va=5zfkqhcx+(^ zJa}!IjSuT1rPS8cV4@mL(c3r)?SBQ#f57Ihq+rIFz5wAjvkJnz59HP-IV8yFMERa~ zEik1*FZ_))1{k7*C5l?g>;CHJX;1hAOYtgiG9fr!bV(B!{^u)%Ryp?nGI8kH4kbg>3ATHkrZ`!^0@IqQY8tQdg&~)`g+C21l+R zbUi;(I|MPZjfI-bM}d3E@2;S?Y9^LxJEO^DIjZJN$22iHrd59=BkiccGc=A%v2ZcbSE%mCLl zinvA$0`$|w<9&q(6jH?298!swDk7OO^Z4|XD<@5r%1Um^&n=)|BWm`y7A zMZyG3Mg|NH5RiItwK!&fD00HL>K-J=<_J@-i`NE=M1Ft`ZCBDy2ILrA+4Yla)5@Ug zE_FUI(xDU5Brv)|F)o~))myr>bRtYASZXpWY=>I3G_(vDhV1hoGb=k0llFGw?@*wm zs`exFuY9K&u`aViXA6yRV8({7%jczFsXXPzxUDx{WY46~2qFUOF%SNBab(j-N}H2J zhl08ClP1JOj!fIEyp=fW{cylG4_Vad8y73HrP-Xdaw1c=bu#VYPhE#W zYx4bQV{TA@W@k}{qd%5evM{%93fF%2RdUjqot0X=Jn2uALGls)s9`g6XdXIytugL- zil4jse(b}pUxI~(a@I^51NK-u{aO@mQmZ#!Dn+oh#Fh_lWcFryG3cpxSu2KgLQI#^ z+-Dh!gAn<{eZ_ga^##gFGYiBXX3{5__BU^}ui*?sUEjs==XT3p4vj}H5Mqi^i;i2s zpu`4}oFA0F{=`riVk@*Q?_1jPAJDLkU}xLJrp9a3icN+Zd^NC&!t^sDyQn&Mv<)(V ze9_Gq@jF(WA(v;7IN*CKE-IC3ci|~Aqd~K_g|X9A)p3#t`IMWjQ6S2->ucsrs8sOn zEHy#)sbQgljh z3h|dYDfw6YUqO71Z>H6orHE(lx3HH1aGnA{yCyhkLuhwy$~3zdL!ri4mN`-(YR zL5fWTivDK)`SK+#e+AGM#97b`=v;W@lT%{6tLNei+bQwJcz`KrUvjF{gcsZW>TMYK z(QPwC6iY~PElU?43DMYn@Me+BT?FxTy}1q>-v5Rt?_6L-{lIa5ufecKEt{F$a#n<1scC@2OyHKVyHogB=nz^>;JIP{@udgsoMMALnM*85+Q%ypc)vc zNTFHSl=*`&^Ye|aDW54-%%`D7aqYghVZRXNFrfIqT?t0<`bHH(k9l0GqCGFHCUt zUvwtxKA*P2N)}uom4BOtR)_f;(O}F0NHnPV)44x%O9!+?{z)_t1;2~>!xZE@mH2K7 zg4X%d6cqK!`nvb~DnMPRmFf>ukfn(h%DX9Oe{PC)bXJj-aiXgh;`RIVQh6p^!uX~r z1VIHok~G?%rXX;(Vk*i%OhFvky&VP(cFiU0=HiieYzAZb&iGmifRPnz^Z|!XKMu1CN(q)F{MWR^h8f1Q#?K7L# zVs<@bC#|I}v4gThoOZ{(JMsPzoE({EZKPstp0%qkpqR{mnwcXig&3s%_+?eU534#X zqe^C%yK?!IB1z(#wIzOD^~bs9Y`MMj;&C(3~T(nq8xqZ z^4Xt`og?>lN%^=MX^g|0y!(&8>0H~ezQ0nkf zk>i=t;0}MPXFz`d9-Xgm@7bMx?^QDz5+WxRD4@OcbbQzn>xclYU=35M>}MtAq^eU_ zgT?v|Kij z$9nyz8VViyjQ&$!B4Z=bPY80aFW1b%VJ4NCy+E=-ab1LGZk$xZz1|k(uGE!lhWSa9 z$CZahh5s`HuJ%U7fTG)Kdbipho*e)!+Ayq+*V02&vfNZyT&}qNXHy@8OU8r(QE`X^ z+E`7(c{^7vlc*&C$+AeouJJrP*G5W;`A(+$22{Wu`I1*yLYg4ip`7idY#Pb?#) zHTbOkTS3A922tz#bzeJO-<*F9ym{H8s+^c;#+T3q9Q|v-!=<+|vKH$TE@Y-bk0>D7 z;1%?rmljI0vcoe_Lisrb#~qqTM9^M7ucv(QfZy2{T|= z!pEFxlXFe+*`|>zpWJkxxoX5@g9j2ZzW|CVQe4tzrCB>@`)AjD;HU`7M3RK2bRZAz z&)S+pO9<4CM%=>DM*y*&6q{sV@{XfC$|>sl7|q4BmDF$Bd)JWr>Px)X;LI z2SyL3^9^INC!^jxpBN=Sp8*CmOrpPpBtO-$4iN3;^FnceO)$Qz?W?5VIi@IekUSS> zC4GyC=o93Xi|6ERD^;~<^JMeOV4)^fkoZDT6FkUYKxM~GZ-+yg;QP5Y$O+QR&F5;L-mZR~vJ{_Dpq(hk~*nYJ)2f7RVw1+OC zgYwvGeeL9|fb3q51fqnRY7E?|2OND{;)_1sZ+gcCtW3M|rJf)kHx!vUf`(FU)slYX z&Uqc4-}Grj!(mTqTM|&G9c+^GYWNP@sB5U(GVLKK*EEB#VBNT_Fqzcknw8vJG8~sQ zYDT{O^J2pK3(0CR+jAhQ1I4393dOTm%ap;v!a zkZqWhpyWu9$;bG0!Iw}+I$C$aZWRS11)?&1ao%-tE_VNX>0Lh6{_Z@EgcL;HPsFm`T{4NuqQn{pvE#yAJ82rpX-6Vw9RDKbsFC;x@DTF0rsKLNkj_wA}P9?0X_~Wj-cDaM(2=abZ zJF*i0%t@~fO0I1V6T(t_l`Lag=cDkL@|vhz^xHhx4}w(MycCvP%u2!-+9ZF@j`|Tg z=WG72Y7^{>h9(8wmr0%q4apXrarS{avYw-U!Cy9`0L`I_5m@-npeh%~Z!m!LwCgzM8%?r7D8&fWAH?iY%lylm79M&J!dCURa{R;uI%`MyNI^ zn`U81e%)#v_XSeq5Mv#A~{lLu-n@%~7Q!PoLvyLt@ zD~VbT*+0>61)P{nB7JRBxqkyJ^Ri241ERvR@9g`QhZ=4^q?{Y4E<3=J^@-vRyCp&G zARa7p%xuuHZ2Tg+);qfM3dOVS?(J>+<=*sD2X-|V&E{vLye}P}g87T)QIC&BO`}wT zA-$XZ+c(sXIpe_;y0TPK)&+^04Dr^&;-vNfy%$eY+o6ZP{I*=}`Ee=|xHf|$)g-`& zmqPBz*#}6JFiOpmWkCT=?B-eDYO-VI&bnsO0p$5W#%kN#1G^UPYOa2P__TJJU>-cA z0Dk1+Z`uV%vuADy0@*{3Dzj{|FKUK_rOI^2^{% z%j5*lKS?Hhwpa1Lxo#P;uxuoWk8IP0IQck3{KjVXIGKVg(2O|Ue3tBMJ5xxXLwQ$K zJI;wnS*;-_iT}=ZBLi~Xj${6T>-N?2g8rqpO@ff}+9&yNVs+r$R=FXo7#2*SnZ+D< z(|j#joq36Sf?@h8^}Bww!bSzX)_r!$;doBWJUIe<_o zMu?nnw~c^=Ild8S23?bS@ak5LjE z@_p4BJ#kFOJt7VzxxT_zkG2OY&dm75e@xppg}(&LE%Nzo?$=*hwXr6yApVb7w|~2} z{^KL)@6m`I7{$~MtcbapG7&2cm_z+y0bsz<9tb5;P+|v;`%mNIeqff)O)7&6Zu)jT zcio7i`23o4#S}y7(KHR&FK-O7TML3Bv^JfaNzgNYZ&`r8utJ}E2 zF6sq$O3#ICm+(lNpkw2iKrSb6I3i%J^fR`3ClUeJzdmKUS$gJuI;$@ifCEM&V9nm6 z5i6?>6~Vb3hi+&ad18~hy^O{C6dkklI!K^7M%{0Bj()2b{XUZj^sPcm%wTj_@6ibA zzeOV;2COMHEDZa@ry&BpBs!waBR^GSJy31}^EEC2~dYRxBi!Tq>$Qk6xvm&Rdv+Vmw6Gb_H}&eH4R9qSed#Jb7G zI$-~50n@DAqP7iZW4|RfjvgQXJ`m5rIF%nSS+XD%eA2|w;u+ngc92xo?i!~vpbeG| z%V$r(RriH;TmR$*6!Yf~+u&}e)zJsk%-q7M84n3M#!m^-1(S{J+24I>%IY;-2!6sf zb+0#DIhF?(0ecDP4ASAI@m_KgzO5Ise#{?pBZb#{oUW7D&)&LkIG^v=dqM(au8r{* zk+dLl``k)Lm;H>X@wruU|Hk{Ma2s0ZId!5VSUO1n#JcIcW8M5z=HIbyKyB?O`(9Sg zvtSkFCm9kdE)E+6H{JSzR>E0F`v5+|ZhNRdv2L)o)|-O9qNAPfShq3Oq)~Vcns81H zK8|+&qegUFXsw9l*9X)N@V{c+S~^tD-)$QI2dvw_TK)g<{Q1{dH;i1}e0)rA8~gVE zBi5~cO_-}IBqVB7Gm&=)z)qgY&I{h60jn-J-UrJtgNP>4J%Zgh2u_SmuMId6fbpEDxB za(R8t5g*s>me^HXaBdTH=&VaU(M-6kypT>H)(z#Kv2IcSjdlC!eTfQUfU-yk4F7@8 z7ho=5vg_)RPAs30CEQiExP%<&;(i>06Tc1JwIt2N#ygW5IB)$jW;MPWtO;YinP+<) zdtD{VU%>XWXjh>2^@z*D!n54C@3xAqTYtBFqz3r*p;C37dBZ}gjD^6|DLhZWgLVI5 zso?g+%ux8vkd}6jP+*`|l{(TlZ589Sl>BN=Vd>0S*7LakObtG0CBx*L2Bh;%Fu?CGLMfv zIrY@iAfYR&>hw-X2S*TV zU+1h!4zf=dEXF&3&_cGPpq>Y%26?+!Xr#s8eUnn+naEYe(Z1yAn@qEv{K+i7mMv7e z(yRF!Oa)2Z8p+vKxan;cU;%P6v(dI%U+N;lV5rR#E6zWstV{xau!BO|Kzer88b$S- zqH}wtaWUDIwTI~`5xRj`vd2oOWp}Nw>OOzAp|F%{ho#AgKM^9P=?oRA_~Bt-uL2ZN z-x@~O^cFxo*{Yoz)JTkB2JF>zvkiGD1>&O6nSS78?~o0I_f4r+FlU>MA8LT#l7nx! zn<_czAZWvlgWV9G@snfMchH+f!12s|R(&P(PdW7I7?0zZHX6e@f(q4PYT+kA^9jD9 zI(KlcNMyewsCKj_Ac@vbBRU8_BK5;fz>hxCxh+z&>v41Z{n^?^?|~u;$6Z#V`+1hv z3VEwquW05bQvX(e9yPopAa`GlYL8Z&=47?!7FDgpF8|_CqzLmsBWwGMBx~t~N+?1$&I;UQ`r;J@GAyv**haHkU{YB*z$6wwu13u(sfdwc3 zNs#(Ci}~+&gG!}ot3F_xInpE2gHlK%Q7-g1Jkkl66(XtCRZg7@7p`U*oY&`>wgni( zdqd*j%uMI=(M}_1BzHYBVC{jd4oBg2pi!orVF7C0ce7K+GJ0wC1#pm9?fxoLo~1ld zUc=*QjpSU%G!a{6@-0xK`WUYzwEM5W5t5FeF_zusut&5*Cr}T!*Eh$Udl^?c{rONE zRVc%Jcd^F}EgSEJeR!}ceh>pbyOAJ7L#vaQ8&kGP%^t0CdZjCn&?W`RSq2xl#5FsO zcA+?ep)P47%oO@@#i7sqJlrbvfinU37UE4XmOz6hiK3rYW9>D-Xok#@CSYDFA=;4* zFvw#Pba&N~vBCsH#jst)rm;p?P*O^*jhA7nghIHutd|)6)HvhE-#bX%;Z%0KGzCH{ zT~YYjZA2wF+)hv{$=7vK{K-6QOB-V&X%eQDau@TA%jBXr$cOOWK4_=9=F&Nasy@aW zmFns*|Gf4ObI%#TohS)dn8o-X27Leh&r>mV`TKi$J=Ngdy3qrq)A40`fHqKE1d+06 zH%eKj6aNOGY(EXd0i|>m6ap}-?&x5e%t2v%9 zR&Ai$k1tTT(2Smi4Z5Fok;QqOKZHty&WgzPp;JU)gla#9K#q`y#?>Q3lwxlion8!R z8-=#$d@z+DL3_c2gatoFAlA=!2fEMPdSd~>Un!xjNsYe1b7)Q4c6|VixB-WT zyR!!qmRka)r~pt3N^a4iTMH+P?DNL-OGU7feMliyz}|pv6LH66%*sJL9G~rdX8oi> z${Ex-5DPa3E3V3o{p^_i9Vc%TejdI1fI9iv_)Kl0-lysr90w&L;U@;uCCpGx_`ul= zca7)#K~rK3yGD^U(~y`M_K&Yt5_=$qmj1ri>t!FS7mN%JWRjW2bB@7IF=h z!*jX8_p?_YqJDjm{X&Z+hbvmHDX;YHO!YoAENJ&hjIrp~MXD{74!CHmk#Kv4y8M0C z2A`3(8xAt2VM3a3&w=dc1p|ZpZ34Y!2}#_Io>Ox1Xk)&SH{Tn<9wh()#kPlc?l@qH*!vvF!~vg~Zr(`j@cyd$0J55ZdkDpc_O z{g}J46&^tb@#Mpx{y3A%dYt+;cw>H_3_B2DeC)bvT}V;2G$mW_JAW^TS<6yH;BW}P zrG1?{$WX|0BsJ}}|Mb}`e;(D-Ej!ELpT|RHR}!kVz%42TIL7`@-NFB2mH&NvYE+T8 zUuA~xggf*n_Ob$-Xk*e3XrzM$I{_ts1*@AJSou6!PzXNs<4sT-ux>VD6s8_vdwFto z6`q1B*m=^5stmf;?nm5d54JYfd|7Div~dRd3~(ESe779k^E^ez2tst|=4UEXUn+(baj8cnVl<`bF^y zy+zjQd2X^ugR5R&Vk4;sHe&})J;v#lthQpqVsq&(Ms%Lf>Epgk_myAt$Yc-;yfZ&H z`PWD0NBF_4Ymakz2dcVMm`@u?7qfLgU`Y6?6R^ge`jAJT=DA%4v5gp~_Rzo4F;0i2 zUQC`T`w)LxPEC0C0BqJYw#q>vt)?EkS3kNbvuYtV9meK8n2rbpQ7s3rh6u`l(ryZ(TXSoMS}UM zC!2lt8F4_f1_2k4!4{?3HtmC>V-d>W@iR|df`NY7wG0T+l_uI!fKF>sgQT=jtY#)X z0Yg&4jUrA&v8rL|+x1Y-u+;%9B7S$(}fohvXQidos}Y zoWUDKIv0R%Qy^z8fdW|dXAE&Ay#qC=armj=VuK0E0vvpUO)G)96X%qHEq}~TIoN4f z3K*pSXai&AJ!7LFCNlQk-%p*!(tQ@f2%o+z&&yopeM2oSF^?T^?{ISh24i11DvQlv+ z89D^Y(aUU=xC!Sp!R9%zP+T(oQKOSjE{L@(fZ)S2N3nvE^M%{4n4Uvsih{2_2VE&9 zh;Ljva^=c3W0tU8hG$VR9ytA8R|5842D}<-#m4gmZkEaqjsVf0K{ibrC)5@SeSQo1 z78&0H+6xcOz8s7OA?1{+Jtk5BdJr?2Htcm5M*XWaOd2C=cROypfEyb0@7RM$pU+THC61q!!|~9_sP+ z8;s#+jP>ui6Ofig+~T6tFd8ox>!{_YLQ-}@lM(qSM2nD1{)|O4w=|LkTxu1b&pHd=2cw zG$Ioqw}@NqE5w5M!%T*DqaxIhKq-ubH+ZH^yu{HKts-UtN)jAT^CTzXk>F^28Kz1&6~o*?#%s4kZIsrN?8$oBR5y;TeiP_Ui_R!jL_ zk;^w6_=G&bJiHn8rP0yw%stUfWq5VeSWVtQ8qn%oeXt`d7-KB&ZYl6UoX{W^l^m3$ zGtj}v0qP}<6945QgVON>y2z5h+1^(HU1SrqD$gK57a5He4A4bZYWQ=wbph=m&P=u8 z-9?rspR4@UUe4kBQ%w1??s{v+1YU*mM#_)Q!)9}ToFJM;@92U`i&xu0^_PF(ivJSa zSNsH_3=0KZNZ)b(b} zs3=gtDbP~ZCl`_rgd|d3WRkq;CV05c)YcLb=*gRzOG+~9ut30JDz#9-ru?CqWP<`V z6Y$`$$5fFTQXgrIHMXB_3c_njo=kt!OgPk7U@VM=VM=l++6!LUJI;VneL-K~KrZ1f zW5gw?dLqsuYY3f_@IJ>&=2z%zqGHi+swh7ga)td;xB;MGLJCcqKgY=YhhVZ?ZPK!2 zWY6WKHD_L@MNlB7P5A+n=!3$=!+04#U$T<>=ay$LXG`l@^izH# zloV@7fkT~c^P0e~?}CYt5g2#^W9T|u|Kw8VNi01trVw^|##qlh;UQ@g#wx+@EXDK{ zV{M~oMF-I)`Sx~<33j*q*exO^sr z)4c;j0%JQGoe4m}ge*a0$vmjp_O0OOyI>+dOS4(3YVj+z=m>Yv5Q#asYKxs6;P%O> zTXJ^4{p5-rKDkJBklhe2^vqVe|Fvmc_&dQG3+Q6v(diz=k#W6RQcxnZ4K<3iD`{)p zzNWp~&lV+?L;DRih-yS0e~G!ux;Ll=_s^^XYBWgvSb@=iaEE3D%?0O=CywwYePFoa zZY@&-3<@oDh20v4T{mw(JLewf$(zl3FcH2RUQ3;_kmQ8rl+sr`&_w2AfW zEc2ua;-`=|G0A1RAB z2As1CA!sX=oCaRuugLQk3BphLrB$wSP)tj#@wamz-f*Y@@y-=FO%R!P2MQD*Y{n+Wqe1P#EnhBh12*2FV zW!?oqbM*RZacO!nDo`_l`#w<=@LRT;^>l^I_|DNFG^5|}4iMg=pFZrxcp6Qja27z^ zYGw#d8}sG#hhgyt=1PkO5Yv>ejQZjqs-5xe%i>2E>G?(g=zBE)pC-1lQA)h+J%+Jt zjOvMg_P!G&jaw4V2RQfik;8(_ujcwa@W2Z6CldiS*!p47}b3Q=cBs24O-(!w3t_D+*yNYnq zw8eKV$B2g8ZZ7c4&56y03)SVOG}%GIm5*PCAs^SWuQB21Dw9AZXU`{^sDxJn1{i9A zYAC{o52TGI32b!ZK3?JZj|d&pS1iW>D=zMDj0a*J>zpM?eAkO9`0lM7)&P#MD~rWf zn!e@W@fFBr*oVKAD;vkeZS&@YIb3|VyjghQ%aHq08w2r1KnNo}f|rD*buv{cnoGpv z>%!mPOtgT{TGIftCfk3vj{P6E>mRE}tCBPTkqDSU4}HS$K@uyf@xtEqQBX4J+EO`f50d*@U4^26olm}@fr8x%Dn7F6EUb0)`4x; z4PqMzY0+Di)O6TrT;Pifo#S>{2wbp58TxmS87+p`*y|#kT zcK&VC)}2n!cm@oq|A%4hKcD=I0923qKTthN z;<*yvehmLY^=uRQ$>Mrw3yTBORRQchCP&%nI^9b7W8bwszX9w$S3{}j8#0#3pUHyR z=^L?)4W4rp7^E=bNeIDxBiJub)X1+Msf~w`Sftr7j`YQ6hna&8 zhf?%838do~eWa4yB%TTizVkT?zPLec{wCpS{NUDD$TEoIsFgMHv%wyDBRWzVx_ncm zQ&`yaCCH;=(JN#>t$+4N_5Ic(#b!DK*LEdL0;rH0fEK$1-q8=i0iSdGTqt)(0FTAhW`xu(rrdadLVg>^>4M*njY*PB%=*I42{Ov{tR zIta^bHl>^GHMVd-ua$UdS$lbMeHETmLqk zk2$X=5GE;5pNjC2P|hr2IoThGIeTJJ0_*nz)$iaj))Fd<(MwWm-d!(ps$af!NG1H& z4yiVUY@CPxee+Q?(|0ibcbgC3$%GH)%`En}&F7^f2HgR$`Ecq2HXq-AZaxxEy)Vb( zd(eV+55+EoUGREl)m_EPTiGbJo9e=2vDzMe+9v6M)AVjb$1*ul9z>A{u=!wkD(aXd^2bVmEWIh5*tX>e9I~A3O{3j6GaxilPAE*#Sa}}{F zd9Dgjf9>35wjJ(NJ_voIu=qg;Sqd4L7dwl{PAVUSO5g3m8saHFy6pMdMq zbOiC>OR$P14k_G8p_aZK5d;>DdfNuOAW6Eaf?yu;WV6&cY${)S*ss+`L)nUS`+Gd5 zw~$W011`+V#R{70F_Nw14?A~P&)Pn~JGIyg%g6J!FG(azmTftYLXWR(`XlM_m=>=I z<4d=0`m?FySVc~CTkn@dL=qGM0UFYawB$;@;4w7Z6EACJF98pcc}(x5U#+@43zxHi zR^1h=YB+C&U1Jsgw^rSF2}Maj@ZK1d5OYsMc11AtKIg4fSIinQVfVfdWUu6`;p^yZ zdTDRVCdG+0^lG@h=XDahnnLsZT`kIJW%)vw?_b40QmQH&i2r@{`TJ@5^Xl`TN_C+> zo}9X}rYO)B0~$*4>bKh07i0uuNm{7XCxn5E$40hO_EJ(7auJ@^j(*zEVFG;w;GLcv z%<)3%HlU;9XB_m+wDT3{yYnG{ZsI^_$`XR1XNoH53ucjUvM105NeqOplBi zk_jq&fzZbAlrK3&z~bYz%#fyb+NP7%uv7!eIqoK~^N;9^e3$%N zbfy@HAm}YRJe~3r<2u=^V>cjEk%oE=(nI{PEN#fUtEUe z2eGvr!S+n27k^4BXbR~H6SIYHMRI^KCyviZd}@xPE&%O` zL9xNsF91~1saE2+8x1}lp#?Xf*B=gdsHn^K6n^TSglix^hg@G=Q*)O0#aSZNb1538 z9>4krBV!qM4t5`p!)XORu&p)-sg~AB9j41M+L)4zM6;6rCN4vrCF&>v?15YCY-ebz ziW{ZTtj(dU%Jvcgin0BVL%wf4zolk~amHr;k(v>gXO(6KYq9C~UzP-VMU#a-o+C8) zd7&Ge*sb@|JhVYOrP+mMB@%yh^^;z{i5_T4l;75bULlZOF1`AvI>`~G${fCVc7E1b zAyl+P(E)ShorY2`7DkJ~0`$#m>cb4!%C2sfu32*SvDsikY3`x-ir-97guUuS$osv4jlo?@XmAAOr+~p(d9#~ zjutIc3guzJv+qjw>q3defC!AbmrNx=c#QdLYJ8S0!GKf&5Ly8)IaxyOhfS%6P8_Re z1#wg^`l1Kb{IExouBQXw{aQu9k1%#r+|io-mYS*Y_8m2db3d_(p%7sne^f01dm`CN zTZG`*X=Ra$nPuecdu_*h0{`pv+K#juk@3IpKL6o*{qyegUt%*We~Ha3NT>2;3;!uL zlMC?{n}N)M(95}0_~~qsjdr`4(GQ5td^ehJ{Ef2^`y`y^X!sD^j?ER|r!RD0Ns(XgNgZvKQp~RO- z0qj1ozwbUGzwbW5B&PIrh&*B~b59uE?w6EzNbkx+9HOOSjHQyc`@ZA3Sf>!qApDFs zXKL;F5KrYBd(-EslIcYay4bbo1GKj>q@u%J7QH_|HkXwn zAr2lA6qp;~+5aq#T0dx_S6rtGkO&P|JlV#rWy4mz;u$+#8BqynA(F#1JVS^gK)VG} zp<iIzIRH22J+Z$&^YB;H0 z2c)^S@9;UiPrTCgq!K%)jhBSN=uJ-JQekCHVawAdI=mS(z7o4vBB&VLp^|>N;Hq;C zG-vO|MYT?KG@)6e$2LUo$_9Umd)s{T&@$R9Xaa6hEp7Xb(n-<}DAMJAOb_F&q?B#R zn?3?-4tG=Q>NfVxJIa6%J)FKd*%*v=HPXyIIc_ej3|x)t3wKZ1Ba36NV2riPc3HY{ zn>yQHj-8cUe)fRW@o)d@*^g;i%76Xex1WDK`~PYC`6Dg^_G|n3{~edXzmZ2Z_-^D9 z|NXjDOiW!OTY((Ij=j^M?H|QHa)4r=D>#Xd=;hc(OP{Qsy(ptVu`B=$Q4~dGa++$h zbMgpLweeDKhB1+_2r;nNz@7w$oGiOunfNVPK^r@Mi)NHDn8bU=@z|Rvzt*GhowO|Z znIr16`Dq=vxyGt5km6OM(3-l~*thnI@BODWfJ@>t0alyYM5<#@Sv|olSECydHc<)~ z;2_XNL?jQ99i7RZ5bSChUoZftG_>JnLPglG0H-#QS+;wYxRc#5As65C09v+b#^(## zmwnKoZV>FKS_d$s+lAyIi|+S9LOHc(z^oXeVU^Qdnx;MB{mll?%eIpfmnOu)-^;o%!sw(#ap01w(V*P)i zn*C8u{>O~EsxEH};0F1;y>yyiH~I4jl#;0ZsdFGb+}v8C3)@qmE41 z7wb;&TYA)IYNYHMz(hk^+bx|86XE04q@C)#&8X9`adtUhdYSB+E@>EMS{10$)9OCX zZEsh)Zf@M6cR|k6Z9#UWnWHZ^jK~ECL+k0~Nqq`Uexb%orK1;tb(CquCK$jdX#|ud z?$>EhcNV;HN6mXV{gT4Qs~*vL>)Yx-@c>c#6jUF?7YkRSLy}LL#1O3HraF|H$H4($ zW=l@0I-RMSsj$U}|71QWyWYY0(na3aBQ;d0HR$0iyLb&x@(`~TQ4vVc!?UdiIfJhm zB*RyeFF2*wC%Aekh&P)$o?qE*vzdglj<^Zb3=3w0j@inVk!_MiL^~FQ4#ad#x<4+R zwx8|!XN1cHMIa4Fay3*SXct7epR8qKlmrp>1~!OLTr!Tqm{1LhF+@;m9~Uw&yb6F^@yjGdikl| zJuAAg{hT6@AbaM>V9(zd(SVv9`QSmul!Ca_=-$o1=AeOTrQ%hs=`2`~S^MZ&2Q@K# z>zj>6a2Ri=oPAaj0sgLnYe#Puf^@=^po_@n@K4ntMcMC zEHK7`V|)hq;IE)5RqWe+&D=R5tBFIJt3!*2AE-tUa803Ttvbd+h?}IgfR}4GZdQK0 zZXD{^R}z&QupP<(S$DIlD$#pqanaCsdS4cihA}k4$ahVx?VMS2lZx)BD-|ZhpL{jP z8vU^&IHO(vt`Z9Crr?^dwj$4!y#XUI0QS1Ige8V}Q1eQ;bV=1LHJ&fmlt#alHn6J! z1BK~J5?o9NE!%>N>CLD>1Ev*c07S)>7;p4ZrjYHdh~Gg%wdwn*8zi&teZB(jeEV|C zCE%VD+$*6tQ-F8w;E$Me^X%x=rgE2)Ei4vit((&ZbH7PvM~l(63#M`{8F&(*jL*9qfn|lU(|M%Gd?)lkVH`~$0$C#Ay^E1mToZ~ zlF-F*1*>{YN4d0LQp`6ffxg~~9+Cd_Hk7RTgew3TFrNS*@c+^{^e?yJpGV3+QBXlb zq}QN@m%an|3^bGyyd8mWYWm{0_hE9c-lQMIyUS1BYe;@^qv>ZfmH8-qeo}Ho0)R8b zh&#rX)&{t&_?cqFeKR>&kSWIB%$vmTaLw1#5K>Rh5nkHxFPCKyv9npl01^mdC+`&)+UeiC*lL)-%8%|z@!$_r6{{!GU(}0L&to+|i@(}oMMWrRp=H{1 zi_VBK@O;3KeK;dyR@@{Eby|h<8eKgdA3V-p4qDXEPx#a`(=OO* zR3?RfeWO<&RwALYsJ=UHvNh`}oKPbDRMr?b=v_||5!`2(np1R>^4;Mq z*jLi4@~s=6)h$B|8;)ZsGjJ|YDtz@_vMTAzdubv!?naM)l+=&RxhuF?(}=?@y%@TR z#C1wMPheJMOTH5bR_4#7^SmLnX1%|m_!+E_+=|)A9rr6JV?rOTOLqT85vB?;FllZ$X}yohhj+w$65$pc_>^9 z$0j}_(pf3|z=w1G7<_&rc@i9i-=T(9i%>ekPkP!F9uir{;f%~0#Igce4*Ut6Tz|Kk zYGfj(m@a$2Iz(PwDoY6x(+;CcvW6{?TtCX~oUh1QK>v!e??VKjdDfZ_XMAwj6}CRt zn|T>aMI&2)AwjX@|BdA0Bd#;_mL z<`5es_KWey8Z@$lYO|cF5uAcdtOFg3fcuiS*q8kK-RA5|&I=Jxz<&OSY-PlQF20{g zbRU!E<`6-x;Z#L~_!)r}5#on!Np7A{Q>E(_)LG1CrG&N<>jDGHDq{!v?Z=#su;1AP z42<_w5!_^Ln055(kCSQK99gl$3q-lQ{zHGtZ=F{!6)3*J?UFU8G#_KQ%C;%i zrdtHABTiq>^#qXa7U5nvwM((Sum}sQLeFtx-9XC3Ao>(<(ViA*1IH~vGgZ7C&m^D@ zV(f!zkv?=%1Ge#|qyBvR`CkVXJ6OgdJHZ59xT48y!WEg&pl(G<~2-Wzp z?kYAEMl>ul)u2=M351S0ZJ6Cr#HbRy-S*mj{^0suFvBQH<|r)4B#zd(m^gJ|@Z3MJ z^K9Z9eH!Ks{E>$r3^lm-{)wHBva@H=p^58+_{I7HD~?^KV*s={(j`E}UUENc+#Vrdf!LOPMMi*F~%1@i9qt zJMP&VeY`Q4To(Q`B{=1J7zUqd(9bW10f@xlVHiJ^kzNq?HmxchZ-7(j_Dz;NcH;1- zev>67dMN!eQQEuhkk?&&oIYjXfbvc)8gwf0kg4{8)C?*HPNtD_y6Zq-DV%$7kR(K&8(T_Ag!X=SVTBxb;gUV*qEO5fL>2^mU}B$=`Vf z#uqR($I~N7Y~FR8bALlVLdnD>ZiBc$Nr_xVgNopJ}#cxgvhua zGhw0=+tkjR8MD0&i=F-Ix!P%T%T3z$*WjnFnA^B$;2Lv(ISVxzsgD|!(l&eg8nen# zC5&^VCFB=wJFWh?CK~9&sYBAL-8(@N0x3k+j{eKUf(7EvHdX$XpS(TI1y?3PlsUSc zdgRZM{wu`asJS{Nw2u~CYo?6~WaBZG$Dzf;^T~I%Zf*c$9|a<1y;fGrFhG{<0?3ky znAzu$k5A!a8lz_k(#~Nbtjnavj9%(TGs)&`cj699CTGm|SPM$rhP3i}<}KJKt*`~J zBILBY0qR+6BSV_KKl1{o;|kzUlXHfgDNf8A;Eprwr{jemMVHSoKnNsbSsK+wW$vjI z-K^5V6chI?To*pe;*;gJp9Ne43>onlLAt9Lh9_U`lF5Y~JAvzJN_^Lk?3fIf#v$v; zf2oqI!p&HZ1EI~z5MFi|X^!@u3li2E)i z`&%gkTQrw2Y>@@Wm6n;aVHzdmBCgy*-lm4C-@2hTPxi>X#7?d|*HNGvX2#dD($IKk zOMP_dzK4I2>{TF0?OP5WY(3zCB&BhaTanlLfCXOFEOKud!r2LSW23GP)6{PJ+@>$U6H||vtA;!(v&nFJ;f$7dg@?o5RmWQ=(73@MqxO}&z9(u z4DQ(;A4M(;Q*y$naNX>v@Z!{2UjnZ?ZWBC5{R)cce%bWsC!AeRil{kIp-iA0VG(Pa znUK1jj^t>Z>pN6W5ucNzO40l3d;A3b*Qv1u9Ln%bmV^OVGPwS;{rA7ztbd*#{|I0G zgYRYh#`i+}jqer0+COGhNS6Ar?h3GDNI)wcnm{7_&i4XHl3s@#(0`F6{r)ORTK+|n zbpJz=#3}gik|a;_a`nHHBtQI9l1%@HBw63zd-DHPl9YHV`I8-E2PcJKIGLtD(wP5v zi9s~EP-;6qm7%%dmnLZo&?HOjz>%Eo0Gg!7KQ&3xjZWefI)Ek#Ditl-RMu8n&shr4 zB>MoGBqTtSbaY5Mb6QF+R!!vZeVT2&xscUJi>BnrG`Y1ADecroB#SJ+g%FJi=lp~M z9W~{V7Ly$gZ>go7E=!q1@4X_7q@-8}%KEsksU3B@VkuA}98qgmTpm7VUy6FZ zbHllpR7Lbzhtg}@nsimt&OZ!L<8xP*!X-`=SvN7o#-Ix=pV>-sG;!5=(|)T$I(wO= zM8>LrLJ5uk(EU1X=NYSXnnBDf5tuD`o0d0FQd-Y#y?b>Y_`*m~SpR)se^C?aBX?75 zpR>6r8;?VZr#0*f3`YR&ayUspxN~FoA%0BySM{B_ zltNvs73?MZ-Y6AMvEJu*{XcF`2s`6&&-QCXmKR-Rb;%nud!gOZf0rb!vt2yiBuTTS z*s0;ge?=PM$~W_kc8ej8C4toiEfUnl@_q&e=%4auM7$Y1%@ z-{i==-{nYtfE-C^W2gVS99ca8kRwCA_p5gp`L~PaHF?SkTXD)9_3@En^`cSh+Bi5) zw+p~P3Tr`>$k&;P*h{F@7oambg8$y$BQ+H8*50G?TYC@X){>wm3TUdw41wjZ_8#S^ z;9aW)9gi+WKy}Yn5S~Zpxwrp^ehv13P*1C@?@Q-&_%UWDU-P7IY?O`Swq>{w#4k3 zna_=p2dcy{<41w3JazGfI4u+(qKss@oLJA>0CMC@JyLQ(7gA1OJa)_3^x4K0>siE( z;+hjHiPST+rd>hPt{wsAq!d%l*txS=Z(LWLg_r9KBaR=nsM#S7~QQ&=pBw+J0@?+o#P2jG-$1H-Inq z%EYTr{0}xC$-_Jx4^zGO1oA%wFY)uguYX?GeCBa}@czV)ObePhhJ4lXzF{*X#E410 zhGt8Qt~YlAhz$AjDAf#)ZTAMuiZSyPyo^kJ4t9I1%UMGx_EFx z#otq?a&uXsSA&DIJzLq_#BX4v(eHX6XiB1Aa?K!&fJy8352P2d8zZWWd>3QkLKH#{ zXqf3gkzTm~q!)X*E4~gy8=vV9US}VdPi;IHyfK4bV?CEtn)c)q78ryPCQ=*kU(&Ob z#vb_E?FeN0;z2@E{d ztluS5BMGs9W2a76RDt6PPjFAa@bGA1@tDU(BH_UID$J64JKBWP&~Bhpl%%wl1(;pNv0eoA)#K-Ci zPgxay553_$MgCshQzL^@Gx( zSE{t*Ta?+bf=gHASfcind^7 zn7v;2Psdn`Q5s@qZHf(?OT?ywP+8^pe<8gfDU);X?`L(0zvYAdXprI&|M?oq?wQ!+ zPLq^9U(E*v!5Xxx0fO|{64tdz_#4veWFa!6h;&tf=flfhG1-jQVz%B6&$tXwj-V;njr`2vUewYU)R0uk z6VE>A6cvd?T^SgvHwzGg>uj+PDh2w0lqc)vw9rPQ%JkPNzFqee{3fWzwy`_jhPHz< zVPwKp2Qp8Ltf|Esp6@5v7`}c$HhNx+8G9+YWCy9!Bzlj|;mBWzCF3%_B-s{1wr~>R zD%46FzM9N-&4rw*r5rIdqwBmOy{77vRRBmY9?)5`{-h#}EiY(RLO=7dTd4-@@9nhk zxLsFsr}?AVD_?h#x|@arzW|V4tY*2C#=2Xkc54md5@v^Iaj_FoWgZdF^=tBbryI*_ zwB)7jSFsW!2JeB@J5_LMQN>25`)ruV_uqbrbvU)JtwvsA%Vw$9eJ9(IE529NDtk2r zX!-gJJNC_&d-Jt)5_mC?LVd z{EANq6cZmveM}YcUC*rSdTKM2z9~W8B+GD*a4NfKznucFe&OiF1bZJRf9WtTJE4^q z9FZAmX`El@ta7|_K+TdFe9rI2v8qDAT(n7@`jFmLBcg)@!1VI4vI&%K#pUnF{PHs5 zHMkoO@C8&C+eAO=;8=WxMDEWgE+pH6oXaHC8$UW3g$P4Q?#?n$gEOq})D;glLzmON&q zMvt;DCMI2XSzF|}Z0Nikhv%Bq=)72D&G(;dua9rgcqegAp%{j+ph^!2U3<(G7dIPn&S``w6K^70skjff~=PN{4e8gAOx-)|a) zEquPM*)I&30~8aIQf(EXpWWU?zkzKd^Bcwzet~UQAb_A%jrIJ%t{F*?y*KD30w9qY z_+eRu-O|br=}@Q;jxg*FpdHG641cw^Qmcz{N@*KT%LUtSL-mJVov|$TW9)h(3c#-MfYl06@k=N4r z`6)}hi-Yu*2vH=dH`D7E-)4R*aGCy9-~tkBZ03M!XxZP`lbOnE*dX-+LJKqzV)pYJ z`S*wehbbWyq6TWmK;t1MnsZyfzBN=Wx#q6!rlM*(+PN8|ft25%w!6shv^uF7MLQ*v z)5)$I04xIb(Yh!Zao^JL(qPqtje~lC)0T2NAu}{#d92LYv}Y3^NWqvJ(xxk zx$lO`2vLOXr!a4C@tT_vuozr(4UMOxX{gf_XwqC9%q$3Fl%94!QN1g#Lj`ZXVzP?G zp~9zce~+`V{(#cXPwES5NwHs_*iPTvho}H!ukVSX(do-^btKZb=lNs&*VRFS z+*>nhJFeT0B+N;0Lfma%p6iCzRCwgrFTMj=Q=YZ)X1{PRMcup%$S{%4Md%p*bZ)P; zE#(dBXXkoag%+#76Eq$NR$^L5a0;(uJTs9M_i?E#L11-{Vo={=A-q>ToJjJCZk}!B zFXS(8`{>~~lRv+~eD^o+$&EW|qbs0>!1BYpcl7`HX#X?j`>(YcD!;P`%->i98qR~a z{4pFV&Ixl#30L6BKge{jIjgbZ-xj;N_d$VWR);L+8hOC%>qm~`>0gP?ZDy#yVt1m9*DzR^h*wDFr|d}dy)zZ)NS|h~`wVss>=^5_g&`5ZuEZb;>k?(> zoKn%7N5`+#4wR&mJ#z?c@@BO+kqB5wo7O}Cewfi|USgFuZt(gvIHX3k1u)tpX4{dp z@r9mCFUA)-MpBV>Zm^Gm4c`e=JMe^fDfjo!vmV@E(C0bY==Fhi z#bUVGVbDpc$*6X8P!N9_OnXuJGMeJW_g+#)JDH#e^e}&SE+-iDnWT{FKEO80yZ>5L_qRLXl+XiJs>~*epwE~9QrpreC4P^y0 z>|}3;egII~It;jyG?XhM*;Nsh7NuGjHS{kuGz_)eTzi8_v|Gn(p=XrBk z@JLT;4?=K(=MT58nOpi>%%ZDvDI3VdY9C;~aZR&vo@h5Q^lwsOxO*s`Ic)CBfzyGz zspc0N+g>#awECYPj_Y*_2@Cc#u&lLu%d~dePYG1nXoH(PM`n0J?3Vw4ERRdyF89aR zn_}ZlQs(c;fcm(0#~|X&zH26Wfh{$t=u0`aq^tRwf-gG>WYox)6WSeW!LQOo+qvC~ zX`P>c0b6krJgBiC^t};eD?UYZoA)!4Eti9OT1vvm!{ecytes4#Lw&Tx4t*R3)MmXw z36A+jWkOGGCoZ4f=hIOpjS*Yorrp$nzL zBT>RiU8-u6QES!G00-qz1ZIiYj`$47N88wj6GXZE*m);WY<_oE$Qh)&slnmDOyLyd zb1PVxC36Ll=s4rlJsrDx{PaF?qON~w7P4hb;Nf%Xqp$P}tjrny7VcsJFTI3Mu*LEW zecT)sr6k3&X>!p>jyD$B+(K(4z?jrlmA35oFsyk`?h)4Zw`^jO-UnS!zy~VlZO8b3 z{a^gYx2jz6&)=$F&wLgKfT(Ld;9F&v2DIXQe7pc8T>A1T^N}VSE%CF)kTs2|$ygc@ zdl6@`1dgy?&abtqL%L@U=6dFCeHBz7#w-9CRW}`h>_he z5^?x`x`bTPXr7GVBM7&;SyB*W18q51d~b$tVDIyvs%ywF<=(e7s`3W_!*`1V&QCXk z+Fg-`v~b)BJaTp}Fzk>JTruP$ePoTpTVHNkpPR=jHA--R{kyKKvqT0hBUFqM9zdJM zlj>6ib)X)B!nQ(DOeRxd zOZvcv{qL`o{hkzdixDJb%k=elyLpK@d{rq%!$YIt2O&k|3r2X5{aX+*^H9g0!J#WQ zW!SiwKj?YzP%2rH+vDtsRUL0{2fT11=19^uLy$ToQ>5R`s_J{W5#I5WOM zS5lK?G?qg(K|+X*{o)EHY0-<#%tqL+kQ6xlb*zTOZ0au7p3fB4s3~#w!-}`i`pMQ) z%c`K8cXBZ*Ni-9ief`6yg4wmuS4@Qu`*l;if!L}ysM=Mn6Zz0ur|KR$&iJpZ0oYub*7|t5In?C*;`QDO#*z)g_ys+D`KRRRy%i{^~dc zP(myL@Pz*|`~33*@z=JKzj^eQu+q+6E)RtF9 zoAk5)SZ$N*V{wcHO&^__S-`Z5Y#%+l!0^Eh3X~ET*D5?D36#3f`$EbSE`bxu-q=C& z?t2ffrxTieTwFoAO-vCkkz__#L(3$Lun9XLn230KI)uq};4Vlc!x_3gXFXeON6aJgQq`ahboB9SUwEXfp!;XKGK%n;T#&lxX(>>>%hL0dybV5 z3_=A4yw}XVw&~0xI|ukI19j z?nxnK2?pi<+^vn|A0Kya8si{l3ZoWb*lzN7PGJLKY4*)8LjCX&sq5pn!ErYFS42kOHTR$8-`W%>u3k@6WkFO$7A0-&WSg5JHBP+E*!!8Mw2hB6wrUXKFT{?ua#SF0%RIAmR6YE4}jb%5k`jymY(r zmD<$6UR=3R5nqHF`CfIL|ET$`%c^7znx1$uxSa3UKcPPkc?LmRuNi|rgBX(i#EBDN z_y*nGqo_9{Vfa23{|M`cp;$M16|`x>Phd6HaAENtIc9n8A=ov`%@b{I76G>qineTW zE~K{!;%KGaveWwW&g<48j6ta&nFrcYa>QfEUDaX~BkYYWG?P&cG51DD5Nbb5?t27gQON`N#HP;{*l( zo>hlnW-V#3`!IeW=x9@rdh#Bgp+#|c8GBuIdaFctE@eQ!H%6xd?m)KESmJaR`q`|z z__Cq~-wAIEC$4Bq|CC_g+xy6VtWw1%O>$KWw8*{Fl`Y2eOovO?{Tsz>wprDPh&Jiv zs~9409vyZ_q9F7PfAal4W86CIO0A*7|UY7cxWr) z9FaIY>)#W8#o(eV_k0~i2i2LPILh`v zoHn*D_ylV345roeNvhN}g32tPsw^IC^6r4T{DV`zcJne8Ei1sHEsq|4aDG0UNrqoU z!?Aid4p`rc9F0B((T1bc0_J z)hI!bTK?AA-@8-t%@|Pik9tyiL`PZoX}5US#@=fHVe>Hjil|}@$3oN#2iN>U8=#u) z-LZ-q;yTcnS-hBf*nP>|!H)!ED4;e4Cg_Z4{Pe#5yaNUY6P%7ab5mx~{nWlGXo({` zTP-qL`|#>E?qYd6%GupJfE2g^P2!8}7r}a69gGnx)MeL$XnEY8lz=>McH%jI8;+ zq6$C(KDzCJC#kFbvIV<qi@>QyG;mKQb+xu*bjZ)IOiT{OwdWOv@>#0v;}80Q(%_-%q7Dpt{*s-$>BR*~an@ ziMd>%QwEqGzSXvicLd>s%b9=}JQ6Vi{U=;GSMa*;%X2B@!RRlmrv!K)+>?@wXQN-+ z+E}DFcS}kW^2j?T?qO4m@h8SO=vXk=U7$nR+(k7as-g7c66wmD(F+E|EJp|*j2LPn z*g?J$`XNJXPkO=i2V-ot0ULe=lRd>|1agqk+k_Z;k6UWts%LnzRBuh}7pdK4)$Ol* zTceaa$bK1-gCqRJ&O-~~jN``($voe(b$T%%Sa#@FL)#Qn)g*Ak(XIQVXNx<9xkSB9 zq75;GGLrv_O(~&3`t{RDI0r4i#>{(>$~GqsT-45rf;)sS!q~kBp-Z3Z(b?Rj4V>;@ z;TwV`(@mbpv7X0fjZHnThp; zXjdJtIhq4YK}qgo-B!_0tC27SwAg&6oK-@tP833BTJB!2Oe);TUZIeZ#rvOSRb!F=He z(9&FL#!VL`k+CeDx*Ibj&S9c$(GzF>WfQj)NUR!`j?*1}jN{@eUt-Nez5fq! zfJbsrZkMPl?5E#mZ5H$u>BY+9wx18Mw2&;Sx=7D=U5&1u^L)7xDL?sS6x}f{7gc}UJpd-&(*_ocBBOR%V^6H zBr4GEI{ZAHGI3RmYK2GG-`3}ow}4io6iYekrwB2G2WSOmB^fLAR7u{K~GYYC${(ewK58 zkeX@JI8jDTW8hRfN$;;i*Zr9?o9ceylr%nxS6sq&3)A+)F;-jAu4@2Qo0QTk$&2MK zZU%a{^g;F}6$rOeLIQ0`Wy*&`XiggWIHnFk@0*Q4GrhtA@eAz!lb~EG_UJf-0mx(! z>@rw7QDxo>2;$SH-T)`ID3UV5(P5=Wvl7>X=lzf`(ak-*b+HrR3^tfMHt5}OC;9u1 zg8ks9b`Lu1$cwD8Mrg*+7wl&wb&^H!5p2##OkYTAE()Z3uGULv zU9LwqP+az*+0u4!z6;)f{q+_CTQd`%0~mmV0gD{L-*2IR`*CPR*LK`uIp=14hSQYA z9f@b0o+qS>BTQ+o8QcLATMt6m=o3pLBF$%i@l=g9rtW`FvG}=qS>k?zGbz5t`;+0$=O60gqyn3b;W+G1-C&QjwzAtw1txjhNmzmImKY zl7i2gY-N268y&$UzKK>WnSTND1uYT7`FZ4NZzL#GpGA9EI$t|Ko7#_qh z6#A6omp@bV49W+MvjT$(@*RmVkzR^#)d`Y4VR<7P0T5VPbl>wGL=7~SJoTZug2-Wj=jEKJoP&0)q-Vj1gG-CS2?8!tCl+ z)>s5bFM}b+iSHO$&r)EADxVx1VdJ>X#K0LMwn$x8w=wb$r)UD$zUXOAIWFGryasl> zrjO`hNLpJ=*;1g}q4z;js)*DTc_R>ys5)Mzu@vaM3%q;eLi#g$RzAt}6Hp_;FS*vX zp!)-`+Ncq=>upKlB|(aGEcm-zpv8V7$?gN!FZL{-K`|jwWaxgTT&f|Zk0V0N**Lb9 zEz`sGd)NelhUq9kTBqeMKd#-BkIk%v&lM*;V$77A83R>*49kaG7!K!lQ%*tW2}5U3*DJP1PJuSRUx|#8W)m)Y2fZZ7}r~L zcd#|Xh?v8#jT~Bi>NugOx5F8joOMVst0QU^nbt$}hkDn~)GveOF?3AuMkQV>q^#v9S$8Ydbp%AS14;uIL*G#-%$FuSKO7j|S~Y5PA$-0EBARB9*x5esF;e%; zv%%TSWj-50f*xeuOquwm+_Mn&cJ+mAe2Kae%*81}XMOMESxvpWcqOWAO8H!Ra*^Bn zY}R+{PuPmn(m>9yOR~JyVzmfQy$ukuZZZZM=O38QMIdOpewH}@OdNHx%dOP6aafHw zAfg`Y>l?*nKECF(ZxhY@Qfq>PUg74=xp#25_c`?EvL}Sy9HkWcj#N|n{(8Mz70bM{ z!5Vf|N@L9lS6wTKQ(rn^^;cqOtB&IR{Dy_c+3&vl1rg&kzG3_}Dxv$X8CCGSl25Ci zup|*aR*<7^&ywd&p;g;#E#OZ-3r~?h@K?^`tjs#&{G4#$tW})P&VQLLr!hb8Yq~cK zXhq(DJ!A9lLmpQH-WG)e9o;QLKR3&WRbT0rDCVD5p6XSn89}#L{nEd5w%k(wfjdn$ zD>`$*1{vY_)6vp2q25dg8hhi<4AFWnYwR2wv&*9Pgv@Vy9`LNo=}UXjA}XqBmg6_} z{UJ?=mT4m?Q!5hsL5{$>vkL~XjSR-mFb^l-k=+d#Mow4|YU@R)6i^mpW#o!&2Nevf zTct!SDlV=Wr>%#pJHFHVKm0TXrx8=WmFHcs3%Ay-7W53PUEmgfkFm^h-v~Bs-P;wL z|FnE*^Pc1LPP(rkcL_XN{!~z=d#%N(@)fx;lcWWOxtcrv!6O|0a~)cAJ+zw_c`-~| zx5ws)XUkOJ%!=~2d}{cvR%TVl`bUsiopyano!CCHHLe(0*KVzs?%wvleieZ=h>bk}^5!P`yLWhh|0@1{`~h^E zuBxrtY_TD?iKe=;33&47+cad>iD`#%BrF|RN#b@8ecmd5V-kMW9&JARhAkil3>&{% z!GadD(7$&Jc;UT$JU^1pluh@+)hn9H-yLU!ZoLCJbu0USTDuZ(sJHhoAze#^R$JDn zYYEwtEo6ygk4$4QS+j3NQe8<@BvL|@2&EKJskCS%kwS^2Y$+)bzxUg6=YD7AJ7fI+ z^Lw8Ao9Eug=kspoJ@0wXnR8ODtW7&LY>>;PSo~#JwA5r~&hfOASIDk+63$gVvRaC- z(ba=JRJHS&!fY+E;`SI*`Q$BMgzy}WG91=_CZUu6$Gev!Nj~aZ^v%UIS(K?%K?=)v_x@oV^Y| zdgQ5hUOgkF);^;uEi7-8IrmOi-e?%7h_V0_tRtS74TaMJCw$6b9Vck^0yZFcqSJA@X^^dds2vnWk*D7 zSHJ06I3Trfeq80oFX69BUS=LqA6Dj+lLNhC2(VnAMS?&84nl z;C;jASyQb!Lw=sw+I@Ord5G$>iq+hQe) z&doUGFKa_=zkm4TRXW~xp@vKGZqCp>1)1AXbp`txQre!HxOWdkIxL9+y@jXb^Qnhlh zF|U!~Oy6h4{wn0o2Ufzcjfs{zJRsw?v+4guoc|1>xHs-YM zKE%W$`8RK1-^gB$p7Y9*r^a7Qgyy<^ExOvp-ESsok+Ig1*IuC?ddQBxX%aVF7GA=& zh}YekwPA1B>_fqm#|~LfWD=`_K15hcnJqlI%R}|xm4^wF@x4FBkJ`#K9N(R9r)KML zlrR5CJKu!eQvX@wFLzw!6ft%6zdmv=GS{!GY3BMDL2>8DxAsJ*eA9`8U9jrzrF~|v zj<-7Tz26d~xhP3?V?=`H3cfcyl^0|`sJsvt`=0EatmT>XLGPPd^7iOqk=S=PzaH%$ z)m-qjIXC~|j@Aaj_wbtFysb+gaHO?A7myWJ8RQbk*6tWik@Dd-PV4MQ3l%yLdu+G( z`fh1Q(WUIdA>Ld~p_vY`s~2me**xPH?0x?IRdL-O<>YO3uUd^AV_)aaDK0+E)h3fP zncUecTO3_%<)hZzn!nOuyZhK z1wo!_+lA^PCXZ=oMI;!V)n|Y6E!e)Sp6QV3KO7d7PJNjv*YoG8NDSDvazwUg#lDs| z7D>_Bt9i$NSK4`x(6O=OZEpRxK732h_h(ElyQDNH#r;}ASk;q8|6`{|V>MVUfv=t+fYqMeu<2qhf$WrHJYWI_gLaZQSqiNKkrz$53(al7H|q2kKNaN}A@~ILR@n zRbO{HPx8vIvnVsF*k@SJC%Eoq`!YY@ZCmH;wL4|_R~T}IBz|0#Zs-2jktMd8uN!7{ zIsUkQE7rE|#(E;p#jFsA@DuX=?QN@&vnZ@(iRX@ddHK@wWZa3$o*=$NtDJ6=$zb@1%)@3YWSf0Eu zL1ses$@At|jyEebx^AECs^e*3d#WoRq^;4aO z*uf&Xx$9@njK5YAx;6XI3$yA{PQhc3g`B&ix707x*uUzOpNoWrItR04TMBQz=t#I- z>D(Ht7jr#ajpO5QzYX+wqqZ>hYFJNwvswO)#PB)37WH4NHhC%+b+(H6E^=)&EqUw9 z&qPRUJ3Kji9zV~@hIKc}zcddV4j+|Gls=a;n19dn!mh)&mBJouf2-->b$a`zN#Hj%HX@OwLTnS;AR8qk*mHK*iXFuT6WaifhNttB%SgaH=@5 z3r2m@*lnX7y=0`7mFuW?WPG@OL7fX%Yi3MjyTUHT7fqZ|z5BI{7X46LF7$EmgID{j z$~0}qG1-P=SL0um-Lu&-pWQjzr~hTUX-bxMf#7x1I$!JZFWcUA+|?;{ z$vfE^p>E9tyOUW887GtKrIpp^UOMh1DlVT7LhjqVdrzxcsztIxV{2zKbIVQjs15rH zZPKj?hg)LLXz5vY6gf2U#uQdGzmVDxRKgqV@=$wc?`j?8MQ7)y;|^9I_$>!HsvB{cH#eNuTvmEICrmW3e8&sZ$l@8_c6EEEa?gLcx9Oey%NLbza#qcE z$oEt~-S3jEo3v5mV4&HsSwf;+YSZT{CCtOKx9J|9bxt&_V#`PFM%BD+jf2IHGt9XU z+gok=)F5?CU&WTmrr^quK7mg;KT6_Uq`~5CA@kNvh7VPUrRpk-7-|x?Y)R98Usm-a z&OfBS-MVdnyY7?-i{)FD_l~n8;^YdSocZB0SP-NA*3WTZ-j5r3Z!V8K>v88#dQt6Y zR&Zu-<_UAtN89^naA}k))Vzzl@TvBklD0g%{LHl;`JC6rZ140OwR6l(YWL~w{iqxH z@?qj%JO9xM9g+H`R=&z&=0w89=%??`=FS-z3mXtldb9gt7XPkD@x)J;bk4hs<*`hd z-yBrS)2-@_|L`^M%YT0AB5U@}lB?zOf))r?3kY03_)DS8?kaX?$*74W9)dV?X-|oqb@=In)(1!4{ixE@xxoa^b<8jqDtmEV1V=7if-4bDK6- z9o?_QcVlpoV_&xo%UAF8DLG?sN6-6Sa*xL7SA5m; z`@6Lrq{2^i9$4!)$L5|(&8*J86>Gx;6-(=g_|W^OYsNolR6cCKYLfc7bf}muiH%u2 zg^T0tVXGbX@^PmZZwh!9s<&*0oqA?9z)kE%bhKg$(heq~Xr?&`ocPSa+GF7S}Jgyw`jR{#GLVo^*3rMdP7SeyOsfHO3>I zmll<`pO=s-J2+=3Ma{3WB5LS7^T9F?>wvA9d!vV+nDRvH?COJ%n!(J^#1G-a&V&S2 zQ`OGlMG#cTyVxA?Z_%Gk0R-F z50d+HIjXgRshn?cpg#fsU!mie^xIsk|<|D2XcP_Yo{-o(xFzJwrU6?sZb zIcqmBIX7=_a>P#F943dgAjTSYGDMYIQ$X~0+x%ZdA|23bb-YOUPYbDS}81>h7Jiq?h&^Op8Q2vjt3l=o;H9;v@M9HZ5+{9zczUSQ4?3!&}S|Zc;|paghT+}j3Iy!S5Sy+K=3Aj*i;0rxpY({ z9l&mYMbWzNmB0c+9#o+Mb#6)2#RUMh8@6lE1Hwfb18NP?n;Mexx7a7k0I3&{ke?)x zKBY1=kgUT4;W`ErUw3Mrv3PB6JO?nZg4NI=D6$+2)6biFz?8Ju`Y!^kLm(d-Yl|EP zmN72SlR#}~H2m}as61f#!S;tJNm2NAT>%5j#0}oEAqIt0@d3WD;})p%6AjsBCw=}_ zqJswKK&SwQ8eC3T41m#~Dmp$gRImVjU{a<6wY5l~#viO-0YQt7kB_QYP~cB0NbPsI z&)$c0atzRr8?;ED3@sW+`hNc5zYBXkKR0UTd&vJKxEz2lLY_iSoRdDqt7riS5Fjg< z5&u2CfB_#BeqQu8z&{0eLUnj4TS zOiX-3h(QF}wz+YxW`-n0f(-DgKML@eBo6?cwJx-Cl&skyNdHMxWOOIjM zb0ZMreS=eD=yARaS~*ljvkLYL!cmdx%JASNNH$FN;MFB4ExGM5pw<)cME~C}bdlE% zQMG)N`~deB2D=0bF2(@^&YFM=zksksDt!tAL79w&9ITc-F zvm&ar0V)D?Mb8BOF0`PiO~nb-9J$_r6%S&`pcH-YN&}1N>+y5=VhxLpO3u!<0E=|v z&{3R;rwxlFG%7j^$aJ`|17IaM1f2+!+_3Lud5*I-Sk`3iHY^Z`;5q7NN1+dVNL z|K0zfB4IddMi>#`20>+X$S`@)gQGrjM_$R`k>PQlBKZ~iU?8IUNmPWCO2>akE}NNFVZawl>hlW*`N6%n26L(r2k4q`LlacI#Pfo z5&)b`TN2US>XdBDS6@LK9Mo>;2Me7odO>4rac6H50BkMuC zdho|u6wUbvdNltR^|pJ)wJZVN`xL(Oj-tVfQ~(SZ@v*xLuOhIF0Smf{>4aCXk*R`O zgqZwU+x>cqk!EyQv~NE+0d_+UCZgTs9!-Pc=dBkEC?dUcSSSt@SpbPl73HQYF*Ilx zvsPITt6VF9BaN%oD3X*|8YKUwH^@f}sN}^`Ul@-tfl5j!wnsa$*%)w5G|StRl|Vq5 z9!~GVCScSv`EKeVwmvAno~)E3+y4oya&C>(Bi*B&L6!SQeWs+!<`t=5cZYC}I6!dZku*s_t2{Kn4iz z!Jfc!l9}L}Ah?P?3AVSF$9S-v1aBKbVS=_#(g_BTv7Ma6BIsHI#{as6yuA~bOOIe{ zFd>jyO>eW|njq4{+r&k_@C_}P5+=5|dG~!~$H1M|6!CJch&J@kwC6@QUh4L3HbI`S zIRw{4qML>sC5#bZJN5A%+s16L4?k>uYNNu% zhOj~~nLGSq1wC4x++~aa+b!n3@}<3?L2p^?uwc-(RuGo(1Xs!6fL!q}j?K1GZ=XYJHK|4{B7y2tX$$?N`$y!8X}puARz=$-=;7 z$Ti8N&+ZO-TvJTu5>n&+Fj+H2fX&dk?@TA76xc@N_e$PB0E*Or(a`IPqV8$3U|UV} zbNSgPRIT>42b&DZvHG6Spn;w|-NGc}ez0yaRjV!PWt0NjYP0ffJ|Lk+X*J=2X|hna zno?S~<=xHVTu3r}4HGUp8G3%CL+0lT?XEwIaVo1MRYj%l&7g-CMKTon%m_4f;gL=F zc;@G|1|WPmROYgzQ3BuUhv_l=yY)py@-Kl8;xj?=M<6+Rx$XRQ%DC8KpAj6kSOa25 zf!GQtv5iORz>{yRQ4#*s?uqT$ApC0(9-R(bp+ki=SN|Lb z23U2_CqZxCQPD!PqTeDd#Q^3d05^mcyaY(6A4+cUDzJAu!fw2v9VN8H%`P;+j z^|an>+W4r=Q!1|2QdAo*0Lh<&o#Y z;3-C%;NRXc{ef`Uz42)|*k=Ob1)Zu7Nia@=ZLJdhlGnqa%n*zNT~u6VOHZ2z+jind za+s-C;lnbt>1^S!9kL5ZH?r0~P3NaxPcB%_Cq$ZRX|iBjZ7ye1 zHUY@aLP*P?oaCv9O@`iZp)zM2Nz>|n1h6|P0+}DqP-4{^e_(&^9?*A(qO^}P(n-mr z2xL_i#!0Xpvel`37IG-*5k(+ts7;#(+a>2X{T?Ccx+vRP4LqbQ)@hm#eCyHTu#||{9()(0k)M+;XJ=0 zYhlX98kDNY6*ShR( z3?O40X!cj(4MY(AAExanRgja)I7BdV8hEpD}Q31@`+XT!a#q$psWZujQqK zrochBnexMKMhA3FnDRge^!cIVaGy9kbLxJeF*_Q~K zG*fG(?C@D)ycFQK0X(`bZnKRMcp8TYX4uc0qYD$VI6rwq@kum&3hW$Luho_h8$e7i zpX%aj^|A(b1}d?3e9@DrFgJ07UGe2~V$8Q&F8JmjZ#4%1IuWy!)mOj4k-z z^?6;$7VK8YXXy3mmn53_qza3EX_~z`v1twvD8gl0==G`bLB>h2o%}{##umF5WjOox(oz9RhyQILa>rcUcw_)!0MQUeGkPv_E@zkn+f-ak_R{&FiX#l>)u_RI`^t1_unqUY zLHrU@*Ifs%qf=Df)oHL{4rSid_5R4wEy`0uk~K7WrrgpxF3+XL2JWF;G6dXYkOA8{ z^B)YcQeQGWygNM-Y}Z__vK~MNJmr$%{(XkIFdbuHJ}UtZi!&{RUoN3X<`US1#H!H# z9X1p%y^wEK7z(z$b_d79qM2;e3_M{NAKO@0W;BjvfEp=Kh@h9dg$+}nA)W1}Qi!_` zt=Vu6R5%P{1YQ0ec*YPmw)o3+4r@h%_`NXbm!bxJDxB!S@WP)aM9P63}8|L7+d&7aUgROKuTnZ_bZ8oEm<|dzva_j$VgCc78@;}<9LD4! zEfKKIWm*~|q6g+0hb1LC?YfLk9T?j{@khK4AdyA+TZfNhnD`8|U|)Sr{s$TMl)rV* zm|zGW+eG9qOOw)K%1jolz@?_bZwhQXAQNUJkw5NA;&{bQn*-ZI4Fp{HIIkUvF5g4YpX6dqd%@>hvSEE)6Zv(T&w$lvxLL6-V|bix*fkp4wr!`e(V($yHj~C^oMt9A8ndw*I}_V%Y&15%^m*T7e|sOt{xO$+ z+~=ATYn|6UqpAoEg98BpfdFw8q^<5hVsavK?nri{_JHwNFicaAbd^SK{# zE9|~yt3`DqtTN6vJ>cx+_RJ2yu6ZHLkGawm%LlIud3b(NH>%p=E8TQ|5LQ%Xk0Hd zT@x`8c9Ni#Lpr#-pPGoS`v39Zf%7q`!iV=z;2|JLK0K&wXW;^H1z5YWsW@3lyV!YH zX-F%wIk~$!TUqpLjXTY8V02Pnq7B#(0gx+^T+~@tp_ru&)Z&_3?;Oq3mr_LR_}hu# z$-;F^>uB1L5!BFFi^tCSb8P7Dn%GVDrt30DpO0fujv?X05aSG#89U&1V*9{yWCYqT3@}u_e!wO!?I2FZA^xIm&5w{t&{1n7V zM=WlIQedk_v9~1;RJIb&%jwxx9JpIym~eBxen+?QS^*Q0%5H@7Dm2HXoxZ{ zTx!pS*|5!0L4zeCtny(6czwSH&lohY+g}4P78ZlDhlo^B1Ay^zKyU}3o?|vXpnSE7 zh&xZ5Oco{%QDCDeqo`%3p;(*W;G_p(W81+J#aEXy-4v7;LAyCxLezYD$#me?ws1Uq z8duHT7%yn8Ji%Xn%~YmpHOIKQYT#G1a#B=f04)$Oy|N;lEvV(0!x+Y0Yeuc@_BJvB zd6)Jcq|qhP)u6^EYd@Qm+fos5d{I3SQ!4R^R&Ij5HFV1In%h5;(-FIk_JYy$F8!@p zC^$fF)SkGR`UZLG>+dD2`X$&KE3q-QjD;e4hxtNZR$nf|@q7In;gtdNfPg;^%dyXi zHryaveC^r!CqRGE!z?*v|H3LYun8&R{Qs4McG?=*-$lZ(z#qxS1O-9;F*(|qvpZS2 z@qA+c|IbECN5={ycrQ(v#cW>s zDVRzImIXwSU&4Tgp@xY;%wQ^qBLnNm@Zvg=+2VQT^f2`FA}r{+!ELiY)_(n^#Zyu6 z-emZNz~b%YotU3sb+E6?+~Xvm{^5NqCbf6jwMS0l&MqtF*A0otFBWg}QU}eWxXWR1 zm{QtidZw_Ow9AnZcEX^NywiM<+?Chj1*U+R8Re0t+~Pu%`^?MIUcE!rkWZIs>2RJt znZ>Z6g+wpjYG&7$u&^Tt>Wc=p71VdAXxSrMV1U2l#dqf)?_=fPdIGBXXZ7&uj#;m4 z*5BbyDv-KP0cG{s$(wx-sg69P@37zgeCZKLlvBzYs}4sy)~Q=|DvIb4D6%R7KR7rN z-;fHX+q<*uzwaf*5FBjBXqAlXrp}I57uj%E^I?ll@nyl*WWy5@`^66Mm^_ue)Sv8e ze%IvEa2Mn6+zhjyQH@5aoxxBH`(y{AmU6c%`4Z-32cnlkwJVVbgRzsRkjm$wNuc~p zlrUtP3MWKJGP6k`RmVeZffa~O-=5S3ITk7Z(B49^^;R1D8<9HkDC%Wxb$s<&kc zGpf6YK^nX3)o_D25z>&V3emB5N+rANaiRmXovnD7q#eTzivf*m#85*X56_L;R~lEM zcn1qRTct3%nW?^dM~$DeNroAprMS6xQp5Hw8B)T`Cr9*$GVyj-)QR*Hw)pItqfsgg z^O9|>Dy_aaxO}BX{W>(IO|;^Z@|E6oCEmf;&Q>K1ZDuM6g|sM-no#hw6g`()O4x*D zvk{RKy%fIPp=4Oq%v3VUT45eBVXLB)C)Z6}7@6fEBdW;olp>K&d|ngJ4V@IZ-Jw)i z=gd?p%4=aBIpLF{Q~(!BTo|NfvlP)81z6mUKt4>UXzL3XN@X+$S{XAQCOT= z2@eV_n8S`hHf(E#BNK(X&^k7H zAC)MuBkVdbnt7@D3S*Zh4wuT)Jj9_!M3-Qez@v0k(V@AYl;H4_Cn?^njz@_OY&7GX zYGb8gIf3KSVL)_72iCMpQwZ}d+#;JC`8Awrx2w;Fx@VcjhB`aEnF7a8Xe|#$;TlT{ z`z}DfLs2X=3%5Z|9my=(ZB-&li8pKEVW9^j+38AzCC}6(pp+My;Sy>qg6+Bf z(4*eRZ~5CbZ`A*Au$&-rF=9q_7`_PP5f%?~pQ(vKIVmy|AtXx*gBJmd(72LOEuzlU z1fifSg29$c45-8+%&Ro6eDNpNc1x#6tup(Ll#6nOTbNwJOsKI5Tby=YQ78sQX3T_* zpTT^S&Z|}v9xi@{L}c_}?3oJ~!tA6ldJ$${g?(X}DxpF%c*5+Iu;Sqw4I+<(EkZl5 zFqF1Jvs9brH7*NM&>&yiS;hh55m`j))b<#J?S7N_iyz05oET{M+ zlIuXm#qTqbF%@`p=7NjxP7&dw!W$`#SCRq?>@!wv`d9~7Ht`EwbP@X z$8VMJkTlFq=(=T3U|n`%JQ?PgkyAlx@g$8^{6Z)aJUERb2Y?K-Lxbl<; zLVN)X*gN0l1vyJ5W3~9NG8>}GUPiFoT@pE8WyVxv(-wKryCAmk54Lm;^-OXv%8$01 z=Icc>F3Qih@Xz(gzs;OjXL>zEmlSS@B;T|(CHSL1b6HF9Sg$e@)Ki>uIuyC6uYry{ z6FnX|{Yh+kHg^toJ!>gz7kNbkX3|U;P4wtnGkbCDN2^8@9{5{w@ zG4Y5!VmXP%>h$zp5L%oZ;eU`#Bopz=sp#gHeds(rdQR+L9PlD5r~58;?FbNW4)79R zc9lge$*6WpG#NwC*-@2%o7*}% zV$rgxYfvz5vjdIf;+_1jLZQd2p+p;2$7+M|LjQf${oeWycKN~KmvorDmt^;_#qD!^ z4UeCL&Tmw*5de3A=J{~SQQk##u_?YW_)|HuNoV7EoY&`i6zVQFIus2fhKf|DpvA|B zl}WLMVIaNYu?n!IpCn&QcaJ>f?YKe!Y||NUWg?CaopsxsD5P)k7S!5CWcc!j)46m% zCV13sD54kGt_Ssr_ySAvgdDjsVVzZsDwwhGGvD|Tj_|6C(@ zO73T39E`nJZXm21+r&A|vsdpy>y5(WTom$T{)QnblQ&|&y-svVCW49n#Y7Z6)4`h# zNA#+1`x>?8 zb`IpuLl(gx9!Css|0luzPYc13T|!bQHiBq2%AA>RpE4mbo5BYa{(i#!OQVyUwSsXS z3|dAqU%=#al^G+!WFZcj$LxXPAb_wTo{0fzDTXKOOH?R<+|`auIzd`0eM}g&$L1B^ zF_|$e_A*Gz<3`;8Z1ofiw6xk%nC0Q1d3on$-B6a^I8+&V;xI?sE%n2JNnp#C&Txy` zKvSXPoQ6k9PNX85lSTdSrMT`e=uHtZG5ifFsCw$Y`T3Moe6$zC^(+?|0UenN*!{6Nye0Dr?o(K-!+s6G%ew1SrJ#sv8_lM~8_=h3^Kb~4Ml|%Dk;85V{dXkAm z_Y($(DohkgA85K!J_D1Rfr~*XB_c8{o^Mfv%a$5xWr*6=*^&e9tLC>UN(`RR;4n~b zUVNVH6jQtwhb=X?ZHYr3LPYru&_(U7hReZ^uj01BePfbI;OTu-uPBDA>xwDOL?HKZ zBXnOVaNhu7e1nc>LmkvX(7!GT5<%!k>s}tNL*ISMy)`7i3o<0X|M>jR{Kw!78aGGF z<)~t&Cb(Xq>Ryj_HTBtrZ6vmkK`g%jJUDYvd!V<6{qy>>)eSN_LMH25i2v%#`ys!| zG@uRX#sL4uSL?8e`>-iw3P7K?5hL04SFMXQ)IUV63nJ5qe4aE8OPLQ#fm0gndBxe1Dr;&gBB9xmswxnfYpgzH2O-(VkGaJ@qQ|A#=pWmH z?BmzL+ZwV;Ao#U?5$v;mVn!~uu>hVdWD%o=4PZmfF~D(D%bqjx)1a=MxYS;wC3qZ( zP9+1kEroO?=wAbEtSW-?H*{@E@@4W&;!Rq6C+E&j{3HCKGqo%Uk;4;;B~`M`M+Qa~ zI?Y<-G=KdNG#WA92a+6ErbTy86An)k3Z(XDGOF*7B_ZuANu_Zw_GKO+y6&TK+MMCI zTIZ&5Lpx9Wq1;y@m>`6hSWgj{9Rdo>9;UeNW9C+($2*6*5@Q^o07E*BeG&?s-#^)C z1ya2s7~5Ek`sp*?XeYVd?cn(b#vB|Bb2-sDz_6dK0MC^{g45LsaofNJ`3qmfSE!@E zG-3x3hdz6<$65rXgf4{>Bk_XFcg~z&l#{-t?_C;x8W>v4a>_oyKZ#1%PWaAu8}$6E zY1f~|IRM`Mgv*)yBucg}56w86x87OA$Qddf$+}sR>mVphtM?x$2cF{eB2KFcKI3u{9nB;BfA_G}jNZ2q)f{R_# zhPOIC9<>t)UKIXB0!iYc&>5HXU7~zHb>@PWurnd7n(48^1Nrp6{G~nH-H(;|Dc$}} zmZ*~w+%$8+O4ykgcFy$3qZDJOXr)A6Y5LlVx=?*${z`;+vdB^EAB}Oc=v?cM+JEbi zrrTAv^90*1uu|vWaAo#l@K|%uboFf#um{fG7#S4lnrFR#5y)NTjLQCz+zqE?e=YYI zlWakq9of)$h(DJvj8Cld*z!s?_Sa_jk0^VQi*eTKR~*vQDslL)67c#E$EL!ErkPVc zFnJ-DlJFL4@H?Ss+iLI-nQW10E04J$9?3I7VkiB5VicZKmzaNJksVNLO9fucG$P&U z5Om2YCY5n&;Jd}4zd+eWy3&P{s{RJ5$StgM(Wyl8!a@lsj`Ld4S25Z)fYV*l?!N}+-TzwYF5VHItQf5qf4&y|74AcR9dYlclxVO%DDAyASfVeg z8k8awnDUCtxZ?$C-R~`dM_}YVtYl%lbX1Me@BYyS$NcuJQ%E-NV+xJ!i`E0e0D0 z-}Tu4MJy9&9@hK^aHIo*K1 zxkrU0o7m-k9dloVs(B?|?E*;m9+B-FpHjzcXr9WF6{TgRw`j@|gnrFdm~egO8(r%( z;0r%ldQM7$e@-g=d2)X1xE1K#?s-8Yss(hZvO~!!1E0(+za@K}Z+_nE2(h^0d_zNO z+MzoqJ>`II)PU;TG-F@&ik(wj#h!Xylm@cb!J-pa-d(0yS@jUs1mY4U^i zT|HR0xr?mJkNi#WN`or)`10oFX?5nW)AQ#qvM7t z^06&x@^6|m=szg(cQc-O>6lb}ZJ$)s5&oDz*7(T>ovDRP?9*7ckvuQkj}!gXTnCFI z?daM+nidI9&!3<1w%&}NUfItLP*iV-x6Ug!gv7fpCA*L%x`aagJ&|5TS#O?kjy-`> z0J>xRK-XHCE@;20b8+$;p((jnv>SE8C;VQ^WT!zdD=z9oE#l^o*Z6G^PP&uur5gPs%t? zMo3R<%s0=$Q|H%`U6XnK_eihS1^$qqQVni|mf&B}?j*WGK8%`vU|9OV@D}?-s`SKK zCEgX%aci(yx}ghrtVf?e=pGm z{xI+SVgApD`S%a=U!MLN1o;DfPnrsf{pGw)?%VS>#EKZ+Xtlz3!sa zCroKhhTpS3XgVleBaGrP_Us+`^tKH`GLH_^{ZbS_9-&JfsY@QNOV7Cgq|!Euy3Jdz zMQS108{Z=UB^UKQDfJ05oBOdf3sXyG55(n!$2}5UaU-WnCen(IwIvu#2vuQ~hoYjU z#)S>)u~+*90L+tGLU*MT2%?8&|t`NH_6XjR6}`%bGsHV=P2 z)Ohs$f?;Xw!GeQ&>Z0x1iyYBg%WbROay7osh~e;Bge@4I02*%$V0QzO^DxJ4Bm4HZ>c z3=tDx340Amq9Atfs@rm{1HJK0hP|5tpHqk?OEc{DU5`&cUmQ6`i@sE9-9UQQL`V+E zbv2Cmcax5c0Oz7#4;k(IH^ytee>wF(@p;@3qu0K9e-o(XntUYHXw+|9uwX8EAp9e> zE|`uYXwUTVw%HAb#tnzb4Tr@Ihsw<|Wb}q2uOO_18e$@vKPM4ghePEoIIVvqGYW&V zKzd9bvw{gO5~GL6j01wqwXO-vK?An-mw3f$(wi1`?Y4AsKhXa6bMjZ;*SAFV(^>FN!vCCo|4jDEx(xCT>$%z!$UHfW+e0ZH1?p;XfsvuDECC z!U*s-BEsE|DPEX|--(SGo^nc~ex8V&b7sI=Ea*fnQ*&6e_NO)AS$Jd+)N7lM>2Ibr z;8-j`4Wa~Ib85V2d1uw@3n#y_^Sm@rB*7Oi!O= zqq@9CT~vZDMWjD^_LCNUoMM0{M71)yvs95Ff6lhGCq|(2`_lJ!=l6<0OD~hPuh0ep zg~JyHl+YH{&zF9p3CT`-4Jqq^WuD^mKVL}~N6&VD&&{ntdu`Z1wLG6}cQBHQg2(ou z00l4r6=;AIEC386BNdmQ7o5KuM+GrqlMAkk@m7@!uWL*K2@XUE4w#bC=ErF3%7xdK z$SaQl1qYaOaGk>i75X2Pad!AW&wYY)a0@3gu!x43n=}ff!xPWK#?2bXai73-pTKgT zz)Me~F3S$mn#9p+>M65faK-y7~lzX^hq|XoU`=oT0^agLI z-0lvRrq{*@qPN{SYv`IfpN32vY~tYFQ)Jg3Z!*_d!k$c^KbQ_C-!QKt&+dC`#*QaL zI0;Q$^)xIS@^(LXoBj^Ki`=y+k?9tZM>!Ef^PiErS?#GtzU*AA-biciqzsaz_4W_a zyTRIMASrup*}s&{;LgZV;n{NyI`((@70m08>1^;T>QTMtuS?HguAY4?OV?7(1afJ;~Jn{aRN&)%pi6kO0dhY_DF@I$}G}ojqv6Z zM7CQ6nQ9wg^b!+9wuf#)HD+!jdtKuru&X^?4BqqM?L4G8oVQdnULBq4_>}p&=lpH3 z_k_TV2|g~1sfx{C3@gn@8Imjq$L;~jHu&n*ydb;GM08HuU#C*UG_(#Xh-ZT*m>H7vW55Zi~3 zR#4v45WlYvfhcZjh~mJXA%L&8*D>Q7m*fDiMuO{QjrtCP-oGHMD*$4nU>Yi!JOoOl zee^mdpA1V$CiS)C(-^k!1kj`G?D+%7Femk53bYQ*PR-_Dv zmu28JX`*`dhp&tzaL@R=4@=o~6=Z~6j+b3_IahbTA;hAK1*@)bOck=~C{>W44+Jur zvhXNiN8wUisAf319`w*E;fv+uescl9_fFi+jZ)HU=49JgF%!SWRp{F|IG1^!*DzI3 z&TM$&_>x*Q2yG0a4R^AcfbBKsbo?1Cm}{!tD}CifCPfOf4;>`-=G1gb3RKIeP09)) z^J66QVk8{`PRfpLDdr&YJ^eTfZF~0*_il0B=XB=PbRNtxQJl1ZI*ppZ-IH-orUkgM z+TST&Jgn+@Bc zZw2#En$ir?X&irypR~cB0^83?`IeIjdLQQ{PD#nlNukey2Md8J^5{9~KDO*P zQ_9DAA#+kHGU#ieWUYB*PN_b&Dn6g0o3!!?JKadc%g zk)a43F$sr;u-*!)}~YZk#9Pq&)M0Z|dkGg=9X=K8GLh z$Z!846gt{coRT8XgKuv3jYKObZRbCNtg4Flm;4`00l#xn{{vNB2J8$YdotVg|G%L8 zywXtp{6v^)a7 zI6}w|jLmH_8Xu(UQma0G%Vz(;14=PTmw0+Bu^z}vM+s9$rzq#4D#@7bOVCmfCmvr6 zphJwhbiTqP<2vDwEB2Pa$WFx_q8IZQj^|r++??kZw}AzU94X$>j%@_atT5_Gzi)!C zk}PiuEN|9zpEPT9sq{$b^iF{+P6J1pRJtq@c+U!WK&(iez<#x0ndW9WB)n-&(S%>v zhDIm0f`r|!f<{=gaM%1kD)a@J+f?VcsL;v{)5n^`UzHj-m&!PCm3fQqF!!fI%1^X* zjKBRp{6kRSYVS)Hde_b}VV3PpOmU7ID?G|W!c->vK`R|p8JmJJc3A{ z$geK|*-d!v+}g)n1Go#w|-zayK^{1*7vMx5-;OyLU)&aR**$F}lwW zyxg~2SucH7NYrFh<|@>Th*xAzNWlC#gryFp^W>8Jk%si0jj&u9G=e(zZfHiVE`-d6 zad0$AqkH5UY{tuc2VK7>N_>__OE&@{{azsFMlHwdV!^3etj`Z?=37721!R=)BGTA! zzolySkw#GcLY7f#VluBQ1NnwhZEj~XwMPBADlG(+mQkTNW6LOYgj1miF`B2Bf$YMl zMmSx(Wg7Nkh^&>Lr%;us;1s8jW?j<9s>!8NW*MeAEWY>Vovk`7tT~*e zPV1x|KH=axiOUoe=^K*^sXRx=A)xZSwSrO|{cO{8(N9H?3E1{e?t$2;V)Crd6IS}&rGzdS}Exk%m~ zMOHYD%Zbs9dddFKGDBUk(fqXrw9!O2H^DbpjI+vi7~+~~6og|u z)@7WlomtuFFB8;8aPRK#FhpTYuMzNhbdez))pBRS>(qq_F__;0lE`wH$P=2#a@xocT)ag@yhS}d!=k!}KvPV%vxfW=+kC!C zEnUO%x@Lv{2;OR%x$VjoWnE=CP`GN5#by^((i)voLMkp7LKy?61Tol=PT~Ys!hl8s zT_HG($zUE&VhWGO7DQt!f{B9y^A!iCr~kF)5I22<{8uGXRh8>+Ey7K@rp&bWqI+$t zBn`t|!ex#rzQ)gM1AK(5OV*C_`w@+tMR%zCd~j7&=h56(bki;4vIjMxR^|Ha|K%G% zi*i5L_k(2e(kIW0yO;OONZ#ctPA0S`-EGP-`YaegW(*)E8W0DTbTcDJE|elWDcDOB z$-J2gME`>#TUr7oHP|Z@1`{%Pi46u4|DJE!f+rb3Ezv!{cfbx~266j*6hXlu*1e&K z5taq_`ptccoFDELG3rHHCdBEVSmcH<`=$P4KvcZwpX$vy;?hh|+$pY9`T_@#%iiUiaZREd-`&inLoEZdpaWxXAeYSFh-s#}PCl z3?Hy`>STSqKD(V+{ikDK*IrmVamf}k_5YEK()Z8k8EWX&y0;hU#bn0VKA!@82ts`1 zsYMDC$?Vg8ykriy;^x5R>gco)WN3&YB$6UgsXksmZsH()yrd2x+0GVafYX|9g8zWM z{R5VXdklGfez6c@MlaG{dc379g$~bo)e~`J`Vqx=vhMk0B_CD8D6gs&b#y?)1SyXo zunJG4o$9|VJE5GN3;C$=pL_LWnsoy69$9!k1X>c_x9Z|9n074k{uTXmA{yZ*VrL{J zdS@slis$!Q;&S|naqgQ!*^X3c%w>e&7ex2Wy1Vo(|4)=_(R~*^b}#GP&!5&@&!3FO%FF+0Mzr0zz4@6@YXABz{9hxT-E}Rd z`zo=%^;=+a<*50&{jW2lf5BaAQ)>Hoz|PX5Sf#@d?xD%ojm=n5!1e$Xj_W;+Y$i_1sMC`f%TDA^&`A`L(?VF~W3JiZu7IW8GnzlFJS z2MZ2vBDbY{MA!WsD#W z6hPV*LuZejMiY6lVtN^ti}-&-+`W@HME#vmYm0L(QI4m?L5rbLfM6D5cRF0Sk%G(s zE4JyUuq4WaMe{DF{FPT~uSN4FCy95v+!aX1`)g&;AV&|i;U6uS*bhf)^#D!~$jVEE zS3=qGdNwl$`RHQ*Rf_@nLRoR#U|FkGWib1e{sI%n#Ut@i4*|z~!a~{VSOb#%j)T^3 zwgsk_C$9EmAz#(a3}g!D}K7=9;PN1Zln6JCQywPTHo@k zYm+tPqEzKjOJnTSNj;g5ODN~mz$+UBTBMhfq|V=T@TDT?9Ak`)jnw4awMaStOYL&O z_PQcLrh07=)=mTSEDN+rf&B}^a+0NO8l}I<7Mv0_X-s-bNi1Kqbd_*fe(CEesp=_V zvh?<6v9x|TQKPQcyl%ui_+P608Ohi2*gGJ&XvX$ds=rFx8ny^K&`%EfS`}``x_e2s zTT6Pml)Zj5~htC}EIiyB7OG*WdyHze&T5?7tqy1Xoy4 zi){9P{WtNS{|^3&R%lI9#Of_HFb%p^j$mFl#lTy1Yx?S!3#K_}rRbKs*j0|CIB2yP zmb>)pCp%a%D^_S_%{3sE_Yze&B-`DaRj&N-8?Ud@g3Wf&3OaP~-O zbdw=P4HYEPea8h)mn(#)q_7EG$mRUDISP!Yw zH8qS0NMRk-`mOxOtm)2hUf<%}3H%$E%RkBUD!|{Z>4#~1fn|G<<##x)w_LO!L8Jkp z#lFnM|$Y`Gb%8LDB&s?N3_`9;OO6v0je9y;jqK36n?986Qo z$ZpfJv59$9&}&&N<0w(RCN=xx?NPC!6#Rm!IqQh40<$X<;x+(PS>Nd|&CRRf9{Hbt z=c9Xm7VR}i#xBrb-^Y7N5fx1zvauR`*;>6G;ulSJzWtGMKfCz|Gk2WcpRpIVfrKD3 zi{+yiPF0_W0H)INjz6Rj;Tl?l{KP`s-C?gCixz{_NjB=9w*$;J@(jGOPl*XEX5QO8 zBtoz+gZB0`H(sV-(Di{3;TSSi+sAfdd_^2zQhfMy8)g1hW}Vfn&n;fJ2>tAQn}QmI zWG#XA^5CB0O29+J{0$Yz^UD#YOX+!owS5da9524@A(Kx!Y|_pG`OtNRl4IPt+xg>f zqM6j+p0w&$zD}s|oa?(`O`4%OJn$ngn`}G628@C>raAA3-g9!sm*fN!$#?m(S2GV9 za~|J$ov!}uTNp2rWrk#pHvlm3iCzLjU3ubAV=KyFdSY3AH-1xqzbJG>af{D%E>u*# z*4O2D_zT&^t}Nz4g1mhi#0@6~_mZ5pEuxxnp@?g>6ZmvV^u>*%jV`d~2UIW9df%Yw z!&Thl6-R&`AEqJwYiyldv(9HdM%~d+1J6iRE5%kbB(a}c=@r8OSZR1^*v%EJzmqmJ zY-~+i+cSj4m0fN4UkpUKFFko5`hJ+m9moZ`bdc1UO5WF1vFax1ggGdxpsZly+doAG zpwE0kml5KdLcr_$6wh~nqapNbZNJ^XzL@OInE1ts$aU&KN0_$`F?-FfOjbdjx;3Yb zU-LKQ-!WV(iO>oJlwy*- z!ofHicZ3&WBu620?s#ynbXh~zb^|zMR1@$8?%%@5nPTAmFMx;-oxZU6rPJG997Kp& z?8O%tf@EWme$iM`i1_yiwSN=r2pSA3imQt=u1M4x3LhCIhqhI3e!#F#OvQAGr7{YnL+ zL$rv(O#GT^h0{reZj5PjUs1cQNXSpj$5}EAj}yXuEln>?GcR!gZ9n1Inn-Jy*9y?m zff&`kX!Oz+_d#W1!Frjq;cMiR{4`-G?t@9kQp}~FmDbtX?v&$*`sD&qb?@!`QrXWy z$FjH0(?UEs##t9?%X5d|k6WUKW%4*N5GjIHQ6pzwwM6Y5-c}3awtO3(dEFeeQwWG2 z{OV|98R*-((hsrT?^_#b)Q>OO9KXSM9k|iy(h<%db-#01`tu@E zw{{mAD6>dJlGotI{tls>=)T|ag2JhcckDLGr%^04D)UJc?3~Slb8Sh9Hi=c5M|gN_ zh+fHXRWR%n%I1slYx29%AEVykGQsR#cJa|owup@kmculPl$V;!+!U69KZ)--^Xt{E-&-_nM6xOazas1uJuTF17rx)kR{C*^Fs~ulk>?_o& zSKv4l_2~>Jn-eO0NBeTzU|yu_@b_F{MJ*3}#%nJ3Bl6k5b|Sx&B=9l{P&6a~kAh1m z4&U8}R3A;9^Ko=9`WO{kn<66kdyYVK+f3`>UE_FfcMoS5ckU}f3dF;j2i^^$g&j9R z##r@9`sJbsU24ovjxv=n1R)B{OESb$I(DQb6Z$OfEsWix6F z<5Xj^QWAV6KF7D5`l#XOtSVlaNvP$yUBEG1okQB2shC<5)Ac0$Gt^+pocNvA)x=8B zDFD6@cNwO34#RsMATKOG}o*m##od7QrC3J;ET2_V);a&D!=x?7GKjoEk; zeCC36kcDCkVQx5`Z-U4(f@#m6LfnK@pdU%a=9~#nj>yG+Q)IF_!nEA;1^7ur0b(Q47aQq6{Q@vI+pR?ljY; zbZGm2(f(wTwRzife9;Q$!#9%`b|?6I_e+J1(tg=W?J4&e_7IGaZcjYxG)D^sm)1RvE4f@?n{hFq4Z7Ze%xf07(OIJpEad^)S zBOogPjw4G+&q{H~fr8@!`487R`x@w4krP+Z>%bfVZ&fL=b$uaXjIQ*i8acY8j&2c) z<-zta-IScV+Cb(!$T^ZwwQUWG@&3iGc{poXp{{|9^N_UOKk4K2>t#MtGsILgo3q-r zQZpG>EfAlAaV;f`QS$oTA?GcRK#mfDT)JPSLo^`ftpz}2Ao!$7eTs;F{o{HM(E2#6 zW?w7CaXNYl=1I7%2e-KLGKBQ?3ABAr81;%asj34XnOXsg_}?Wj*o$#wBL4m?#xp$LX%zFfvr3u%p-`Pn4_>5v-M`ep^ljx89TR0l#; z@*BgEb8X0ZREXbU{nYw%5K~eWc=_dyET^)67SK z9`&LiG^!Bxnk~?k1C&pEv;+^{>LXJ|lr3%JHAV*Tuv$r6Zi!1;t{zy}w*2f!n*Y}w zW~Jim){0l`0cT9M`0vUX@ivS->=j_i?;vpirM~pudXFG-$;3UU$|$b44M%lUs=>fC zJ_t|4pi`>w0O^K0-%Io?h{Q;8u6;P}rg)jtgK#>65upxr~_A)8*8g;EyRB%q(tl$Ih!=0Tl46@(g>;)xMHm*I&N#;4Sm_r9lGk@j}L z=#Xo0lypnczeC9HoU8i^@-Jxr3mRbnz~{%7&Z|(jrm#-cgN`-YML;X6?S7!sxjKBz zesxxO4CO+7G-{vFt998MmOWA#H=){R(R{nPi5-w=(YH_04CyN_aB7|r{$$e`I9pKp z?2NtDpiYX(4Ghi*XIM4G2xn3?(FU1oXr4gfR}2xwY%|b$t{^o=RzZpkWtl{FIEai| z1wLPAm>YH)&}V~}&z6)LVvDnfOWFFRfk!e)lGP!&By_t$!VR5sdL3PdKNG!J4_!Lr zo9Op}Cknxc>qBln95tg%(wy3O(8QLK2zY?%7|0AJ5vd8>rD2>YTB+v+niCDz1fBgKe>@ z>q86ZF+~)-Rj60M*K{@K%dVi-i)ogoY>P|?_lNUn=whU1CW19ti=!7El< zkS#A)K=|Y1&T;g~K{X4)G^y?xzc%bN8OuF!q|^;|%5yaB1Gt%7QrXaK7!=_Gt04C~ z=*r%_h2MMB+Bqrm5c%}4mUbWmB(%>wuWee?Wz4T#N^uxteTs26wy4KcmfC-} zLdNKrm(HCB5#{T@TcK0($)-d?)eWe@@tlj?|jc&~FdUp2fnwiP%@o?A|3dp@$EsMm$IL2Mx+hmzr9Ns^ znXNvWqo1cfidq@3KJklRYeZ{4GTDXm>8nBuOh>djn=+s}X>>-2aqsGeO`%1y!#B_b zDMt=6F#V*1gB|u-|IiO}Tw+d-JkXX(_ONmzoPm%UWm=9>Fwi#2IGBNCRC#KUx)pmG z#gB?AEL-u0OFDo`zf=YBP~;@M)v7iqDD`F3+;@@)kkLEanASKJdUJmqCrB(oM1*|g zJ=UR$KC>E3@ip*>)Yc^2F2hUyndgTap3j2ZGrjQqQcKya*GeT(Au8v1C6pNG?K@99 zTV&A9R<5_O2oCg-5N4b~2htL{j#2NdhZcnh2~>)RS-JO<3QFMtHRWHt-`T}BoI34f z2L0bV=n;(HiIMCMN?3)naDFEj7Zl`d+GTBK33uQ%0*e8_^rE-umBHC+Tt%}(|7=-v zCKi*vv}x+z?rTrlGu&zYsCCAtT(d6rZ3GohI4MElV~`nnU_%1}_sJhj*QDan@~@ov zVj;Kma*{qJ5qj@1H<(|sh1+qz2N$2j9}M>P)KOhgXQ#mDY#*J)aSOZ4_z%x6l>oeb ze1~AO&mtZW6@I*q%%)OTU%A=McjcmTzEkPwXAZdJLwT+6bA=Y46qH}NS-YXA7|8Su zN~rw(rcF10Cmeals5RTH;w25WA)!Z8IG@kmOe;zkSxi0k9!XK}sb^;W^;Yt#Sp`)I z03*e8N-hnRC{w}%|4T>Bh(3S34mkv=CBV%&Pp!;u7ms>`A2Yh;^|EYg!m~GhP@-J_ z@`hu-Ohi9~bxcybSVn@F&uoPsWhQ|7k~gNGPbO!thXXHX3xwb|5DGrQw8aF3NfV`m89PHg{zTNW4V6#3$w7tv6aWs!|1H(*YSKa&tf zV1wNBs%O0I(YOfLe7-!@OTUHrxtKHEF>dvu_ zvq*aj^UIs;bg*{+ya?sEEc>)IWph}oc6WuCVYY;GriT$Rfl0vwac9)W)^*()fwdW>8q61jTjquCSxM z61DoiqsBd&SU;S8VbQ^fY)5*is(FU?*LKe1w#J7i~Foms)NL-RJ6SeRYxWtdHO&@%pb)%er?L1J@UI!+_8n*Y=7RH<~yqGVEkR5 zcEw-|vzQvvVw%d;ncC7I)N(|oZrN~1ga!+9$Z}--xO!A|pPOjY23Gh-1pB*o9S!mr zaDkBe!2*BKduiX;MB7U2nAl=>cBWePl#mihTFh0G2HZlZlF3(yNkPP;dvl?LR8Q72Ge;g7Y_;kmKZ$T02O^YB zRw^pkt|%R0$8mVNU)y12Hy^G$4=79il`LzTmi$i-sZ~nfQPr!wWz~@`zYP;5Nk6c| zt_cr92d-6226Ko-qz5KDAd;F*IHn#c6L)Fhv?&qmDnYfCR6DiS42tJVCbj;r6@G%ZS-C1GDr)*Y5U(_#|iH_ z=0ookFs_ZPo*X+dsFT{4_y?#&9Lds3|Gw<5C?P*(`rbi1*FQl-llc_3z5hcdW9e(V z^k!$%R!160|9dm!q_1aMNx7Q3>&hrFzT?_7^h2TN70J=$dT3^$_f_sA((~-~%}=Q= zHW=W-OZ`xAasNHuT!?D!72|4q@5E?5882u-&5Llf zF!YT$7i-eDqstjV@T}xW+z_kIn+CQ&HLT(3bF{B6YAy(Hr;PG&D~ZD`wqIBNQ=dF) zdpvFTZpR+{XiL8G<%wkOyLWZuPCL3_W*kpZaTWI3B0cZ}2|4z5_J}-ng4a{^Y;V&ds$`P4&kf&!n=mgI!g&R|UAjnlwC$odddB%bXylWpl(=i_l|2UK3 znWSJRdr89+gP=fDF?N14pa*A7%&~^ooQI$1`>qvH=b~Qkk{4GChX+u3uN4vHLJfEQ z=FT6W;Zs6)78@uxNN++2;++I@KfmP(iFZnPIkn)8WB4#b&cDTP;L8jhnC}qwx{6ha zXKY6b0kf-@L9%{yMzprW% zxjS-^pg|$U>Av%CE=g;J`;G|M)+6GXj%_G~c(z0|bBE5m@qfG7x@t7Q-v26zMKe9r zelC=IPnpGvFSS!s6+Gc|L_rSNbmXio>hN+S3_C>-RZ5*(v$-wmpv?ZcK_JRIp3F}o z(O^j@kwAhr(}CaI)>2FL8C_}8A!OB6$*s~pTzpNsd(0S&lmzn|f!R~!6>Ltq$h2ne zv4RQbieD*MOR!Q1!BrHjM+W?aPTaiSj@JEQqZpqtpT&cxbjNa$jn5V;g?pVK$a9}v zygTG?^l_|S!Wa69F6%4jsGGD1vA5x=Ge{P!f!)`G9#&7F?5Me;H1cZk;rgF69*%fc z(}79g7^@HVHsR>fCh}Zi$HG^=RtxO=mQpzUhSK1r1M1Ix9}Ci=2Q66b3!c^(Q0Fck znJM<#tcCwexVUNuCvPJ$Dt=Y*9mPK8IV0D9F?ps2CYh7l2)w=WVoIXAPy_P@zSB_K zc@b;MjCI_TAn@OD28Uu8RyM-P+ju1quPO%GwZ33#7&-Peu6zj2cKCBwj$1siq`B_) zyX1taq2kA&GA?=ZO`Gt5Qb;p(E<;UW2H954{eB6}KbqiRnDT~-`P9aFY2+xXK$e20 z(gGZq9=lu=+>9Inqj(l3ZJD{KL@)9?+R;FVB418lmcjAYg!o+Z_4`o6q5wt?xR0gDZ+)l(bL4&0_lL)zkQ$ zXmm`QDn1HjvvW7MzN(NSy+%9yUKf02gA2T(wL|b@2M_Fv5RBxtNg;4XHpw}5mXo11 z1>V=axfa4LPCIr6E9-sGa6ysM=`v#@c2mOV49J-}a!Tl?p4N(9L%>`x*^PET*i1Af zd2>8<>P)&2+rwnaeoYDRwg)rCTxEGU!w__>D0{F3o;m{~X2L2QKa5qvzuFvBaOzPa zz?~dkFUr&i2newV6i(4tz=C68k&z*&1=BFglP5J8Pbag?xRohN+xwtPgqd zc#gaU8_*B(?*)sAzqCu=9qt<5d7gV#Z%#+gRx9AEvb?*a4X#*g7ClcGjZ4!eZ>b~- zFSk*{Y>0q)h}NI^Scg(`)_@fi$g-p@@Rj-Y6%c?CWjmKQ5l z+&H~tqX?`N2eQb=ee&&t9Wjuh0Jsu^wQ?rtRN4K}{NQ~W`qMEbzO93+a6Sx)T69w3 z(JpHTl7qgrZUw)HjxB$Rzj8f7CF}$@%LFm@4e_04Bk! zvhyB29J$^R>JQ{)*54jke1_HQrj5FqB>>YqU>tB}IKViL9r2=XgJ0X(T>t8l_W;7-8o1P7i9d7n#}}?pxI1 zXZPPkJY(KSaT=t zwX>cqWxDQu{MX%C7(4M?MvD! zW<6)Gk2{pJX-j+_-6m%TM!krQeSp?;gbJr;J;6Ga;REBd$!rYN+^i4cxA^#kId|_; zDkt3Jnc&AIyrjxde8f>})!-6`h9z7Cwlt7h#?VtNHl!D4=~Fnnb-*2VNQe`nNH^j^ zAVk{OuSv#~v76FGP@1X{v&$*Jn~cwkQjPj)ag~fuG5fq<0wyMC{*)G|!gco8XA-Ka zDi#x21<#&UorPCBnkjQz4_Mte)Z}Z`!!&wgCJ3yZm~-DzV?@zvcBTPu#;s27=UQ*- zilQ+|0#C#@c~8={hu_`>%n3+uuqKp{*?7GOq3-JC?>t-n^cflS*zEDb=J3TS%Ihj4WSn3%iFr$%{;tkKM3J*A_=~mH` zvwOJo-L`r&oaV5l)X<3`m+F$&k;+JK&dNgANc$G&3;-mV;{`dNgp2J^gF|G9zhEkZ z!#@_FDb|5Tp{nBi0F*+{boW&EXodjnW+oJu^$aW93^JFxh#pPG-WxlbjSD9^@FSYB z?n@|>!Tx&^H@+44Bc&2Kby{7iQZq$l$h|guW@yP&L*a4w0@BNm-=^gYt&dzI7dIAp z&tvgAeAvyJ$L2?P$j^SrAlf#4ETzCmjAK_1LOE9YNv=xxCP#K^K;joUgg83L=? zRN@(!2|~cuocs&KR$w)CY3ngY!@^|m>0NQ>)bM>;DVp_AUFEs6sgLk6Gw=QNol6)Dn8%}H9a=)uT z@DG$G8sRsp|M1NV?pmjaMlyCX zSPWP*`n3`}@i0N-u=nZ}?T^1ZwTy2?%<>|pXlFkq;rVLO&TfbQRC8bsjChc#BwR&` z--)!MxQKa>StL?ZLC_f$Akd<)!2oagV8SGrjd3==A@9eW2ZiHW5lwzZ=$4frfK4|A5_6>Y&7IcTfH1 zW<|knaSd4uuk9%JmsSQxJuiSv=Ro9+0W`nXZSIk(vMCs>&P0(qIHFUBMHD8%|H*!}3@eBU0kdQ~#y z7>4h0{eu!vr%DQASuAP7qL7BxS7iR_eJ`=&Px*QG0?;8+o59TA=sENg)gS>@IhDSL6|BK_IXGwO z3$LE1-&=00o7H_-;Me_dvNE(wE)o3XoBR&XE3h(S4Jq+)smgiMl|@b+-H*xF5AWZH zTd3mS2`ij&1AqO=QJ?hSxnDF$9{;iA52;5AIQP`6a)REy?VKoOV0!c0Q#cj)_#+SN z-TQFs3*abB%(d>2LVai+7RaKq_gik*HugvNjYE5iAEl}U^*|mfJfoQ%soA@yG|gAp z`Ay()_phf$3itrdy}reZNp{}~pZH+yC)BmGq1_cUL=U?)n_qT5=S(qR1CK^1J6V8g zU}iI3dBz{FMCylMdW)jf*9d9rs?BWh$c9L^W{Pm+d-^;z@_J+|Dnnz$ac1|qbJ}0w z94{HF1pjJ!nv-$Gr#JEvXE(HFEPNTKXZFUNh}cU+o{{`8F3a^MrRv_l-sRFuAz9WG zH0gDdAz=$LMV5;}5))M9G`&ZH&WyPNs+c+N-I9AYOINjKG`)-U)p&NggsjbRd9C2S z*)RDo%h;Z_3lyrTR@(bM@F*DkX?K@thU|UqLHHYa{PBW^>8*Di1&CO)Gj==CZ66Xw zoaxHA1<&q}op7xc+eO4|sLz$y$wM~+Mb8bQ7z(mBA?WNcyW(AeQJq*RSC`>f8yYUyd1m&gF%c*1-1wWORTXl? zf~S3E(zXf)aXK6a4{*YQ)D#_ZDIY3JVI&}Ss?n-aMhhc+(L(*dLCY&`yo7B3gv25VIW&%iGu>y!W~@FYkN} zkIHFidnh=%IQO!${zP|lf$)7@UG}>-&bzKp42R??{yCp;Hn5Fq%<9;19>PAH!L#31D(SDBPOV{YwvZE*hhR^o}KQi=MU{NKgA z+M|TFu9ubQ7+sCemYMI~)TQO*i1yq?Wc87xyyvI9TQc+t7gy{V2Z+-v=@wM#b6Dbv+Vy8905 zjp|VRY8=rF5M;G4N635ffYMzz$Noi-D$^5DV&A@hv&gFzP&-v+usKH3LynL)qE}94 z*mdL9zFjo7$1wB|Dof}apqu)~ho_VVOugnSa>57??2q}?Qo+}e5$KEH2&PR8FMV?UrC8$pSUcpq>Nogl)&{>;Bd%U13%PcADZ z1NB1lWyIGYB8S{3H!}=r-O3&Q{QqGY$ldn5x!9=DI2{(U;wOPc;OB^b9>zdrcvqko{uxEJi3jDr6|~%`-?w zyWE_0PR}@V>N^ePmSlHPSgX8DA>}ILMew1g(m=cVV#&su*5OgKX}fxe%-8^8HTW;7 zxjDAq$H&2ahG;GQO>QMK1{kiYI9pN6%DP60P_ykXAyhurCNT(b{6cvLs%@&TF_$Eg z)Q*Rs)6uAXe`y(5NT>DXRLx)6+~Q5Y`G=@Q4p%Of`zm5zo{vea(IcjgwHI|q-6#&y z1US|})}$LY(#1LNPEL6wzPS&?x*2bNpG=}4OwY@~X&oj7(lSJAb2mCEE@`n`;mp9? z*SVjK%12uBsgr^jB4?LynNwY?tXOhQ;6vvXM>f~}Jyo&1=Z(^9rQd!oCV3Ltx)ucG z*6#TAr@&xocCWwoaF}szWF)yJ`IxGnsmX93OCY9xAu-8%5rrWYwat(%+}pV{ds#W` zJa2sK_v9y~lDNXl{G2WwoCEiYtn-@jyw*u&7=ww{`RLGfi8bjT)lJ#3NiM>_vE)VS zCo^~}UYTra?oUc5-ICh3WXE69Xj9g7O9+=(kZkYH4LgWa@oIWJFEm-|(y}8r=Lz0* z>yu-1i{kPe4!u zQ2CzWIobZ0C6ehLrxRAkq39O@h<_~l?E=!AFo0DEaAzQrjPU#wkT6RHa-5)ZVIif> zu^*Fm2>GRgZgx;}Dnxa2tN~f2#0Up&%8pu7957VXZpkgS1VLkNLrZh-{nRr}8TZ&% z2-1~=#D{{fW$^s6d~oYj-Pzmf5Qx9PxqDw-Sz99}O7w`sev@i_g0j42){(<52PA86 zC!-wM6PEm=OhrDGL28Aod3*bK#2tI$hl1c+$sYi->J&De?IFyF282EIIIzpa>UpMg#zqZU zXV!0!y($*r6+vJuGIh$~8$EP9O)RM4TF%I({x|}5&UcT-u0vJ5L-mO$vL0nbz41hz zi=b1n4%(jmAX8^|V4}G+qg20RT9()zcKCBX zpyj&$UHi|CFE9UC<$huqqZv4m*Z}SzY)uh}X)~;`e|Ru2<2&I<@m^xXB_PBxF=a2n z5{G!QWqTJguz0YNwn2g~VHhl}%x`P*w{$Jwoz)pr3Zv;yB= zwt3Tz;fb=5q9hu~Rxr~}dgwQ86}=NbCnaBHJLFK&(K|7x?Z->%+#sM z&1KMTZIn;_Lh3o4Yug`k!%ij^6n=SmbHC|Y| zW8AcUt*urpV1nce4>Qf4I=rx9^NH0Qhj?ztV< z4c?)jt$;s#bhtk3_4*tbhk)w)3Pe5S}N3FVo6;E)o7@k^G8< zDY8N5CgwqxQE})*=j0{-l?9&m(EzqY3A=Xg4a6d4drCqnnNisk@rZguQ2t0<$)`iz zv~2%e`egC0cr@n?!;Oxvtu5Qzewf;~*qw~GpDXd>gX3SzjI*eYlPAn9m}`2GcjCI! z=@>uOqzGefe9zy{9xF3SX8n*dg<6y12d=fT&3wv1=u(-!NmhPdNY7c;5eRcLrQNXK zvfr-nT5(!To4Q=Phi5;XChx6~B`BORRDfpk9&cQv%az^AcMmfxx*hWTcXfQj=dJeO zTiE^{YLN^+^}4{dWiqpZ1&emEDIQFj&4B$depRckO$0EWN#*scWUR+Tz1rWk(PNpU z$}K4n^Zp%BG6b5)6!&YJ8?>Crlt@kUO-(G6u(nV1j}r^}Xab9n!(tb0ZHCo;A@f8PJmog>3?`~$WnXqZidt=eE#NeD z$VNQ39-k+5bJ84ss=j`nrbfT+&-lW3*z4T9>QuAFr(lBgs5NlNrY1AQU?+Hx4m7YC z;lC)IF<>Lc+A{Aqe}7xldu!I#B|7|&4DB3|Fp0zv9Mu^P&zMZxcNj_xP`A)N5SJRD z55pTAv1zjW{r1dxHRRwh-+stvZcVXf%IP9xi{-dh>Jf7I-}zICB{iI|1)FYJYQ*InbUw zFEb(DX6KWR!CnwArVI}Ht96|A2KCWBAK5)+UjO3na%KAB>L^ohE9ES@$pSv1B>Mm& zHsXxXANqu8D#I50`|I#lq-8wK{ju)24m!#_cRp@+VTcajd$o~&rUt#<06D0o?oZ;| z3e|YE3xM791h_B~%y(^tjz)uQ!*`BR=}RNNa=nag-ZrPW!wdVpesh_vU(@LG_?Lum zh<0WV`Xa9uPltZ~TbN7|DE*wz;AgFt+B)9)Ij##Ay@)Tb+xBVvSs|{=l@x!r>CeFk z{Z_^d##i8;*6h?QD)G9jZx?A|;^Yayr6U)WxVLNkwYujixNwhW{Kgu^>$SDWd*!Kp zb=BarV3Z$f9xFA63~a&j4SlJ}YHm=t>2Lkz>@FjMjFw!lr9vnQHXUNvb2?` z$sXrF46K;~i2YX^tDf^AH=S7c%E<+(&A#Wn&sl@mAG$&iUjEG-oj2fj z0f8H5HPIgD5yqkMBCn5i&&VN8G$=}pD355#ryeu|0nZ+}Ej_5mzSDe@0CgI#Aq-A@ zB(^KY3>qWk^FVvb1tjP;_1cg4Z}y>ny;chxR#2~MH(Ra6v|?@amVVqWxVj7(?bO>6 zafMb*yvG?gGF*!vwFs~0@j-jaVo<1wPgy7PlSrtyU)2O$IKPq?)hM#Z=Ofcu#%j(@3zY3sNieT| z8B`hYESCT`8{!Om8-0oJhOpGm?3k-cO;b}W`EEK>%7LRyE)0xDyt`l7p943Z6*j=( zgu%VuI} zopRQRWW3aDvn_H0zF&YbsNnXVJ5t%6(JI8RF)IaAKcK?(kbp_BGlxc5GR)Pobi2LcLR9r_3e29L7Je`BSQXHH_aRF<~3L?kl{e|2xq-S0D$&l>Hgc8gr2$ly zx6{lO01JDO7_EI6#_!uPwlUG%i21yTZ=$^s`#Bi}1N(-T*1E*a^O@=y`pSbp4e6rb zU|W?PrH-!wJmdk~SKt!Yuk=ih47HhaNlf^vT?j?XywtjXpx_FfFg5$dQE6ROuJxvy`!Is%#4iBT{ zdnLl;TcgMKO7;m0xjzZG))vTtKj1IaenVbxEoPJg=c5<(0mUP~F>KP4!lN0oORfHu~H0)RX@J@1LhSzPu1T zBGnw}P#4&b3E&S+ZUS*)!)P6@zx`fyk@y1ND0^`DsKq%rg_73?;;X*ihFVwHKn`A% zl14AoFWR%h8lBrF(v5&Kdr4YLsT&Jc7%ar9w!POE^tBio4(|^drP11|JuXZOt@TjO zs=pRIV%qN4DlCie4VgK2MoO5T7#JyM9USVeGXuWfzzdH>=d$R|$@7eysicV3+xB$H zm)YFcWl`IorFP%-GB#<6jS4Iq7T;U($!giHYu;{r#4r9#%I0{&ozco`?<7^oxJTDI z-}j=%d;LtUubKIw)DrI}hXovz%||%c?o|U@8R?EO3n;{+p~GNSM&gkxICO>D(P6ZY z)f8SeJnmi+2%p%cc61+Yzc`0Wg~K*S<&t&`x#$%2XLb#_URW*d|6sCSfH#bLDJ(Y> z(AOVQH>>PzQdg1~&3W6I%v&#z7%e$scHKq11IHi_^m4sD+|P$y_>8h6cD2lQg1^jh z=}q%vLezZ&hiJ?rUO}U#%W&g?daN*-^TIEzsm%k5YC|m?Qtst=@zI-H*1J1lLbqtb zK?2*$kw~^xjrMN0KQ`VKU#b;1`3YdNF@V(prnh*==auDZIKb;CBEe4d$d$(I& zwat4o)WjyY3|pe`QW^n_0MNH=gwH)|A7WPF3*b^YfQE2o6HFj#b-)=XA7M~nYw|&`rF4=z;e{7v}6wcb=nZ;H*s9Js$J{6p}eHkJ2<`#9mptl$V;0z@A z7CD=Ty+fQR%`oE9ReFCBWx5a0yzPtLAr2<)ZQGZV&0BKQXGrg3X!()Ab^)yk#ukO* zxutJ08+Gy*K_V@~p^(#s9q}9M-KJUZ>w)v2lI_cs<2AkMvnJMt#^I4Nw2?6egPH?q zzETQJ0Q4G~02IE(GPS>mn_PaRyuG6|eU_YTwkh6GP3Y>IWoC$3*c0!l|JKklTv3(< znC-az4lo;9*MRm8v2%%8!;%xV}xpjAh4v zF}=kE6NjSkZBaX*Yo7CtW;-yQ=76sTniT$0Y@7C`fsf>6e#8v07jDL17#D8#wJv$5 z!}m^G)CL58hD_W0NBcCZ4QLOJ3bB>Z{%LSw7{QfT@M0Jd|AY_JaWCies<>!wA=RKYU zn9i-h1je-gL>djh8u5>79A;7fLpGhZ+7~?O{mdUGwI!YidL7q1Y&8-u-2xz2s81FL z91YM9N5a{RZKE+U01Au@{>8Uf6F2D?exYv9D6(xp1lL$BUDhECbmu4_EcdR5*Thh#1Uha+m~i z&ZFfyLTU0nt|vb0m2vwMhr}hFI81S>Nl9^ti=<5R^@U=1K@+r#h+wnixyknEjww}y zZZsmfyUZLjYS1vJ%~2qU{%UDIGQ&WUetBVZA9`b`KHD}rLv8YLMoe_=Y-DdSOEN%G zaQJL!m;_aUgg)}|8I95W*ppE-aP#rxavzq9Y`ILA%-bfJ@|f=h>(i;=5b9B-i_oBUIO#2Dg8P@{MT7A# z`aIV|>UrJbwTPs(1t=vn1Qa#g~1}y(GVoi(8bxO0YfW>#M0;2^A1&S);|A zOo09z1s^z`NK^S_+zg2qUlm7OOo{I4uQZvdj$TX^Y+X+*7T?wgwz9DWIGSm8KR(}E6I!V&)Vrp)JBNPY0e>E2bk z?e?_l{AuX-NQa;vv!mAb)0tHfKPGGSZ6HJ<*5`w6csKeMdV;Fb$FqF2yNVTiOB20ncc z?5*!7>KOO&--nz3z8_}$Sdw{_#qzFXp(2}Qe*P~sYaYwQW88gqKlx4x!bQ#Bu`qbI zT~{`5Ftm#QAN}f>54PNrmcOWp)M58uRR5%oXmpRH{U02RvVpVlgP&rf074q)+{#!i zazMtIWZC0EN?4;nuKEWl%5$9!?OTHQ+-qYZS3N?C9v}pAM!pH*O&d0@uR!pZz<;Q| zH9Tnl@H>k*n5^6rK~(N#yx@M^T_<}rr@Q>U?%^iv>4H|iQM8vbSqk1l+~V)N4_9_F z9Wh;XvM?FG@Qw%9;6Fsa_`(_9wi|c0$RZ*04aW_msPNpsmE4eH2HUgN&|nS2AY zLioi@wwEvb68sk;Nts<(%XRs`8GQJo8)^1GnBqqpK>r9OSR1obxa}s?@y|9Y6fC1z z9}TFii10s}=H*DimHWW`txlOY-q zrrmbG#qbF3cVgPKtQOf2jXr%>jXuS$mIpUD)zd-$WRogE&z{sSczm6Q{$@VRmyCAg zEAMf;qMlx9ba31Ketft5s*&WU){XRsZN5f$xA%i-fkyZbZ~SR9cTmK(zj1_n+uQBe z#(clT-)~zRSNsg1oOW!y{l1p*2VY2LXa6rJ{Gqv>Gz#J~_RWuvZ#3MNA`gBCZ6l9HP8Z{UtI~G!N zsQ6vbSZ7l()-cfab&8qc9s}@LGQ7HzFN0R&5K>cN&KhK4YAKZx()J>Jn8}MI*Arh^ z=#{DJz8!J=zT9c0+=M_nd<;t$UOgkOu=XVBZ8E2RrWZ5tWk0{%TnCYN$Y-Mh?~nyr zX-d|d$xS-zT4^N%Vxo)NhOdIjj$conMqizl`7mg#&vK#O;`}VTXG~PCmrn(RET5;yiqGTR=0)@v`SF~e6FP4PH>Oy1xbyQh@W&5IMA zROB}mN}J*_rxIX$z51zldb|lXT;~OUaN!!M!z$L9kojgfTL5#6ttboyUFmB!MfdCK zv=@2`91l&3XrH>VxFXt<&-J$Eik$MQq5UA8y<88{6D_fqYyTwU z=#XfNy;}RcFsu`+AqGaM^$UnUL-pw&TtUSRvu2lOEQ-$>KdhXvU&(Ju1FE5x?_$uH z%rHuu%gPK2-&EWzSM^JVs#7+G6}!_^Gvd00^fW^paTY}S0>uSwn%qFw;!nfxa$zZ_ z4B1KcnPNK>H56HCz?VWj@w0+A>A;u=;t``_>B9tGu?3$0X60?tsbXZd@Q`%jWXcw! zdqZ;OwLak=In3-nejZYSmikO7_TGzVJkGTUNjFPA)kiO57#@^IkByb=cF zwJ3`b7T?eH6v=AYoL9xTi2F`7r3~YEF?{jl%Zk{TSH_r%$Bp_Y4O@CKUJX0$M=E$e_J!Lj&@; zEQquLDWFS~9akILv!IJ4+4Rqa|5$RXQ{->owyZO;YxU96GWy{XfzJSxK8xtosgR%M9RUL6$qi1yFN@;3y_q26N9ysLDE zk+WocvIg5@DiZJP*`7~Htt?EaD=*jv2?=sdMWhLF2z}MLleLvMfkBXT1wTT-?+vP# zB`#Slk{DC?>Tl_g>T?3ydQ+r-`HQLYQnkgXKpt;F&>8mOnD(oYv-O5eyqHS)vae~_ zQiLrCw7seLFwV&(g?-41s4{t;P$k*nr(!kzjN%?{K_AXn75~RG+9&^B^h^Ex$DdI2 z6&yGA^Y^b~hy2f#uSqhWxZYtKc=1z5Y_S~bSH|j|TBMnM8>X?tFt@}%p*}h?LC+R@*fRbZdzU_Mtkkg zs-u-c-^izt41PZQDy4->$1U+LZ&mZBpZ_D#*J^uHq!{}T7zN)qaI4dP3%FRX=i-d< z;Xi*0s@7Jw-MV!jC!9vOyuDtXQQDfCEJ5zSy%f(K<7gy90@3@yr`!Op#*^ zQ{p=kZ}KpvN_}S!d%$qF9c_*MBsbgo%=1OAHGvobiAVy~dT4LptEVqU%C`^ab z4;Fr`x)9_X2?;ov@O*HVy5dM}RH*5{n2Vtgi2W|$*J{9t8ZDB9{gA4jk*7L$38JxV zqe*59gWnGb==7ZX!`*G;r~^O1#uT#Mew`k}j{=T9+~~_=`$zp-xJS31oz=x?O)Q-F zcMR~V${9|m6JtQ}=IkAHH!!i~sr~JT+3+xwx5r30q#JcRKOzD3#E%G2bbW`7^`0D5 z21`0Ij>w(4gKs6zN@1-=CkDP(bQsp-)UC!Cb4o##NV||^MrsIf-nhuiB@a2u zWKK?rR8QjMV4B&E)_s{jW|DcZfGj!6K)Qe$Me>nDM=PG?ssI(GyydB0NLLNjK`HLB z6SNdp>jT@G)`7hhPtJa-4#1E3BC^T(jZ5-miD0$wvhM!QOSH(dDUGGV_^W^0c2^;s z-ZZm(D&CT&P&&moP{C?b0$S_-)mufC_HnP4`wVreg{tL7PgRBy%M-mctd(X(I(Aka zFK_HTSd%%SrONM%H)HP#={XkdPxMMDX;@38=AgPETL4@6(ZKb`QKhe+I>t9@!q)ON z4;-xaSv;m*@4zy-w`OU?YRF}^M7i;*M%GrffS^HhGHhYm$Wefe6(f^RSK42tO?zvG ze$z*qgxC-t1I~~iZOAs1N5uBKv%6+;D3*SbL$TZE)xs0xew4%uDG~Nreh*~wIZV10 zFe@<{D!`f&tVs){V5EY@-od%;|V8^hv2+4uv1EEXYX7nEbLj zeREBRQ_v%ywdA-pK`B`dCT&>uOGxL$-pY){(@PUM(R;{NGRDfm-6VIS&@V?V!2^xf zXAWdxWf3Yo20OVygpZxJ#5DF&Vah)WRP70`nCV?QtJ-@lliKh2egFW$E zzwg@*H``f3(}uJ!%k8%M zn;F`p>KP`=2sP&Iaqy6geILYbb>1Gi{)UFKLT)$<78FoF&(2y?Q)KIC#aOpWLR~dy ziviMZbtdfkWXFS(lE#pEBh8Z&sbjw?`5rD^__1FKa1+)DGpPlHulc1kzdWRhipDTS zj}|dCe_J6t@p|((8hxl<273gYxk)F8#xP`KW&LjsPcJ;Ll;#+Zo9(v_{Wjp{D9?Br7a)_%+PMG-Tafc5fXZTy<+&c zf%&2^MYF_6i&jP^Sw(tCmt|jnC=|IZ2Yw!q4 z=ejGbbbR?YC|N$n6KEuT2o=x|0m@y~v9SO1ZGHw%#E61}Ysb zbIx_!HtHq))$peH zq&ygpB@bWX#j+j}Lg|!Ole;*fnft33f`=UHrpl!gz5X)1Y*ni?V$8ij)A8u7iJYY| z^f)yBqRy?+RLA)PY$&8!BwFyAhkRWAr@J3uuNk$n>3;7)m8gf6>_j_$xo?e z@oQ`&tmi-vSp|~Z<50;0=uerg9~=KNx|NiWIKaVO11w5HH>QA@EgA5f)#_@~tL)ny zGYxV94nP_wBu+m7T0~!;W0W_*$YZmY-w~KiM4AG=@TYoV1SkePCx5%gK-B*el^yh# z4a}f0o|P>{?f+j8D_aGN-d`D!K=xd&)^&+Pxuu)DZ2E+NnPQgv7sFJ!J>Z=8`E+Zr zjnNgW7bz#=P)s_ff_Tl6iH4L`3Z4? zG9)u33qL7k{Oz??=-c8?HdQo5#Iv#hCBVv3F;VnzvtP?zM~S9MKToT=w?^oYLw0YX zD9Hne6*s9r;J}Dmt0DmY1Bu-)6bGNKli%8$M(fX`*{bsTzj<;OjRlY9_$HeTndkT{ zdi9-G{d0fTs*eUhO1|N=P`jrckN360%s{RE&gJiKN{95+^HZOv$ypQ69t2t!(^dC+ z@;9A6;bU`ovqQ24JjW0<4rT73m6!6baG}GjHg$O{-wWXE^BvVz=3PjW^<96tSR zcYi0=Gm>r;zzc!rxzS128+iPN~|lWo#i3>Wd1Jw`_smk1JdgP9gb+0z%rC)q9 zP%C9YJ#dT~&sMXbdXe&fQ~tLh|Hq;KWkBRD!@p`I$#{|1ME;*vf#O$|y*)Yu?@jA0 z_wrW!NYb8BN${1QrqV~^!N>J z8|brss;rhIP`rj*^!PaL3HMVN~6|DyVeU`+P^1-Oi! zwj7oh?D3u3mfG6_tyF8fRGVD7ue9D8T~$(SZMuZkb;1Y@zPq)iz(j+m;Z(Kdtus+b ztt+oTtKWq-SD#~NVHU(Okk^U?@PDbb6yX0Mv_sS7WP06B&nIf-q$Qn_(79{i|E*=7 zJpiKg3YnA5o}!~X5aK~_wY~9Q1X9NRsn4t*H(WXJSq4|U365F=%;wP){_I)=+X|_p z-B?nPz748MOEE-hFU5y);Ge~c&|4MUZ%|$r;W?7wW>~x zx0%2`tuv5w$Afs_pLUCvRQ_UrPVTI&3jM6Zap?7|D1pV|cve_ae|8O3S0(g2pBQy$wnwX|t$f0ppp(3j6(P$0>eP~_G`mJK=s zePII)&qIIXhH>?+ojSd+INQ%op1Y@i-41F0x0>o_&Bw{l)9>|3kgO>gC2Nebt2XQR zq^#{&{dxWn%R2Ve)bHt6y=r=G&Rg{{)u-f}hXv1zZQ=7MEk$WyLGQJ66)e?T;_4N$ zAlpDzWlrwz9PjGY8O{|@rRXdyg=3WA+$LK;$vIF5wJjTLHVdk3x79M(9D$6y^(OI) zUDK^Dhy^Mb`(Q2P!zXBR_kIjdw{tgr_d+-CiunrVO3^|6!MlU1gQkNrCY#pTK_^SN zmCuWdjJ!c7&jhidOiNzDPlMZoMnjk(K9wr8p-E{cY7@on*sg0~VrSyA;0&??Ikvr_ zual~2Z-4}f``z=e1|>=!NwUHRKt_^tpi}=7Kazl0zeOzF>E&D^c?!+6kc)z`+Wci> zYvW5}#rfvOr5_7)zP9su?&?3<81x^Vn$P>X9~{uHv>kw!@qVaBXVZ6xNy<{6Y4QP{t+kTv>2-_%DJL3LBO|b%V?YDvhGQ`on3-i^&<3 z{GR@53h!W>eAb%s+YlUNG2J!TH9^kyY*@)S^N-u|VpkzW91A?HmUXi(pD}?yv5@6P z>dfyjGAWBgx{T44|3I}YrK7@Y`T6Cb$*88aE`4zXdtd_E8W#~KCKJRbJlclXxZGAv{1;bjrUVb zzH84HHLjb$Vv>N!$X6h>f7XU*!meb?IB>`!k_Qt})o(-B3(7K~{~t})03AuwwPSB= zZfx83#heXu4@fP7 z-4n1}lTs$e8>AEy<%d$$B!6=!!@8|2}vij>kkY zPV*9;!$dNP`yw$iVU@)AmEv;S{v(0eyZ}JUdz<Z{wi<-61BLV(~e)iJh*fyIzqG_v|0$zUzspeL~HMlq`DJ`Fpy(U=9!jZTylh8&K z@oWTW3S&0e)}9N^@la%2Tz-rYkS#wv=Vyz9)4kMRhe5O}Z=EzZ)Jf_ zG?4qRkn^x8;Epo5@Bn^xL~m7T?!`Z%R$#Ewv=~a61p*R!c)SDMZy8Ac{){<)kTeK9 zZYIOb#PTTJ3lQ*>bq@qS3G)|_;8nPGejl8=>)e$OLIG~^yFMkJ(6z$)(dT4c9tJ`R zdaj;(YX21?0d#6LA51&S;cZZZfuKpxy^%)4`H`4Uh@5e6XAY zfObLRz8hfo$eo!E8t?+MU&uS$-B&Qv1YL;UYdhh=gT6u~uDD&jqViJd)bX!l#IXIq zlN-X>W%=EZ5JRXf?gNb-KWf^U$s5OuG^u9WPGpdwwyCX4vp&OlQek{i_KKt2Lx`)X z7vE^w-`~=(koV?Js;Ke08iQ-ES`M!e5LD+yF%K9}^#=#U)X#C^592qRR5}vmOBx|xlm`$x9XG%rOO3G8e!3<+t^HX#8`WK@r!-KEt3ryklCzemB(5M@ zdjhc>sG)Ec&IcDS0Er8(NBR+>J86-IX$e;gt>TT+J-2o~1s9%4*2H7KR6vE9%ltKS zbe3iQZ_gWfynP=b*ixHz<1-~htAk&SyeqkVV+ArkmcHQmS%Py}W?Cmgq+`1~fbvfgiw|jJdKqKzVY%5=2%T-%lU}hl>Zy6%;7f2xJc_ z3(RVxf3$;n1Oqbi~`qTxrE?>j?H5-J0rKgzUokYqno3cPAm!Ajs+D@2Du zN|0G>kXgb0dzjZcvq+iWI4qba#!G&ROU>=jLf|;ed zNRJ>C&eL&zg5aj#VoWuU_Qe@*4>qd-i3yVblyWE384n#S%az!3EsOy+YsK!ZKRUQ1 zrO(dz$N(%rs4EOAxa8L^XAzS1gj^AeIo52-qKt`gx*1;|fIz%s=^~A7@ z)tFOINecOIQY;aSw^z=cv!IeHX+Ci#=qy*fzj&^^vqL>!+&ct8CHj%>VfIj2Jcw10 zUyQRunLDc2+u#bTzud$0Avyl(7O@}jjbGC+Ila?Mex}{M2#+SI)N^awiVR&q6o%OLU#WZSQbzG21Ee$Y{~_wW5{{5 zUZlT+ljX3U1wm@Jh}xw%pZFzc5HL=VR-rlmffunmSFD2f5S$>rkk1a`9T{E=il}{p zx+DOaAD64ppSZIrqo8K+^Jta6|0aD4E+G`L70(5o1<42eJF|#dZklvI3WP#VCNX3n zw)KQjVlwFrxPp)2V=xI!)-A*>`+;QMxG=TjwNg~}Edof@$cwyN620yPSIFtig$^XL zo=`ck9U~CIu&O`mw+k*=Ak`Azg302^?uH6Lvfhh%WPA1lom`VvzcA?QBJ?5PssH%) z4t2}rZ0kq#Zg$|XbMOfBT-p{AjIp7&^EE((l(To**V_legDvnc1TPlnEI*ugf{p&Z z$V>l+*7O}@7)6iK{=mNS*}m~h@@o0Qz9-QwKJC4A)PCu9e7Z~ z-OPQ-K~&QKqu^37g`Bq@a3^FAm&Z5cF3Q=Vmn+!oB~Z`%Ncz`7V*q_aq8JipvL|32 z>;-0e!MWF`1P%i9K7R1}M|{~GgCK3cSj=mV?@NY-yxx4XL#y?a-c+b|wR8Agu%OXz zVDx6ri)^I0jGQ;;zwVKOy#}q@=@jY^6tFC|*<&~%xQOQEW6<*Y4-kY1L2&&O2s5;1 zXC4#}0cxou7>Eqg?!ib9{{sjT*#7_=2nK>2gaDY?6j`aj>v55)?h%n|+W)}#+D1gJ zgh0IC3QQr>1_>4j&0*2vg8xkTeaXZO^I1nk?U0}}gc&TWVHOuLfD84kF7aCrT!hC8 zE=m9sNMl74zyWC)P(7F+7z2NTtVbqL>q87Gfr_!&IRaPEar^_u3;`Nd%V3=Uj=A;8 z2WyANHui;xR1J;*uG#);i1e8b1p9>7oc~ptUL*eZ63QJMaL^lsuD8KJ;Io4TIu=IK zwnY=S5Xz+fcouUC^#I_qqGz3L&&Dsvx%<^%1JLm|z{*xxiWp2!i=z zwjr)q17N$B+hDwbE=10yE`rYKF67tXW{B6K`<6S7``9~!BMyDiv2;6zh4{}IF0j`# zBftAbV~P4oV>*MC2%aEN@Sh-ote)UswgPZ~xo7fg%6*d^w0)(%^jPWOTHJW>Z5CqK zCH%Lb(U^}uuUP8fE4)DP0Bb_10CNGv_n~L{wOzIEnkaWZc{V*@!w^|}tVR&=kfvXj zJxIB5L%CA`tS&rMKdw8c9mb%H(!(>`TnN_EKiolue_HcmV6t30bYO*GtmV>PkiEH3 zJJIe8t}{i>rT#qiTS2lS{@L#(Ss@)+jN*`+V@lQFa zGiPj*kxlsn}L{F@n8YdKfs7NlY_3ZY5M}Rrh-kIq3|{B zjO8mC>rxkySB6i)&TW4q2`rw%5!!gbD7JJ!9BCUK2;*2sZBKwPHFk*7f|<^kJ%FKr zDZuM=_YI1uF*Upf86in@SE=>af|-69Kfw0FM0|vBqQ3Kee?(a_2H8g3?j_}GMv-&8 zs@)xKNY>oy`93FbQvtogJ|{?1fNp8XHp&oB)5y;&aMKu_)jq4L8|7S(Egs8)`_#&# zt<_8Ji-)*LX>#qe3-*@NRqNY^**mdRfc!#=P&vp1ZxU$C^A6%c^gaw4g}gz!&4JE^ z;`oO-12C4J5u^DX(&Eil=S1F1m!SOvxmZu;*g1&m(cNW2}&dV)_04j_nnx2y&O7-&Rj z+irw;1qWGLUnZzP1duTmzzG)qCPCmEP$-pB3gNxdi2RBu@<(%~xUbkH9rA9UE2DIr zWoBCtXEOZ;ac_c>msI!?{Uk{l-z$?rrj73sfmZI>e}nVZa$p@q%Z(vhYAWsVyWT>7S|6;uBoO}zAc?~8>0^bQtB?Tpuotbf&>mzfH=es26 z&#o9qVFs%plC<$1<}vka9CNh`rx1hAD~_XBTSdw zx1BJx<23ed$7$lKe>>M(EqK`Yp}Q{CFuOP?i``ASEZZd)K7MSIbbM;^_eE`ssl4`sp9d^V==e zGwdzbGpsApdm8{OKwk*zOIZj8l%SvXIQUVir|ol|v3JfjBN~_Ja-z6)nr{7aXf6k8 zzLaojVdvIrO-f%$J*&6n2_J1Q>cdZiKmJIVW!6stW@1`L^!%z@8Rr|v?jlR|^mR7! z+bnO$_wM$bqQ2C+GG}>7nRlmu)uLxnJ}Oqpbn&^k?fs72rfQM7rAno$mhGT8=`WH_ zex&{l1c=^t9Aq7Yj5AZU{@|q@&TbH(%yCtb15xtQIu!#F{#NVsS`KD!&Ymw>23m8q zzU&=NW@&NvmcEc=e>J{%O$jnKfSh?u6{8gxTbkOshvgX!oo*DP?Q}nTEIcO38RJ_7 zmR!Xv?esP`Y!%NWt3RrH9RxcS472Ii>}}ceF0xuQ9IkBbN=k}KN`!J=j~1xagylX2 zIExK>JqOo)CKk7`tRvpGjRa4f*xkSM<{edYUaM*i=H>k>xJfPpdoRlBbW&VpJ4gN2 zZKfhx>NhF<@sGIkrOhrp4_m`anFRLDSv14Yw9%wYDRCpQj43QXW&S+R9rrT)xn9gz z#W9gGipm7=T3)ASwD%6jd@%YxwPxh^4*%Ksh37+Lxfh!e+-iEb8HeWu`}&Fer;eYc z@v<*wEiTiY+j4d@gV)PUeIqv0mBez)KLhW{L~0{E^BT;u4-)qU#dt_K$fZ#))-&dI z6So(|WJBoJ%`582i8wA0#YA5yCh>vgEEVGkMbj9T z%#cty_3yT=9cJcC-Xf}5<@Q=9ta6}3CAY~jF}})H_NEI@7=LRnx07;bl@e#aW9UvA z@QDV5R=x1Se0R}@*1iL#90|g@2r?V_Z6yS_T#d=-o4F_qdDI{I|Js&*{$KV@hwv#< zAG1}3Yv;~aU&BRsY|;h#R#M-s&;X!E&fgb?qYy2vfn@o?OA{Rr!pX>D~3^ z?&(cTVp%v&t5bp5yK~p)h4)XE@{_`ooYka`VO!c?OG$7zm+)p50*-i4n4q^<&Bu5~ zM#uI?5?I*pQL|CHB(ajb!&}c#Vvij~T8zvVLGUu${H|v`!QWpkVjL-bK2HF|vQo8+ zq)~61=!ecoCy{U%j#{Zs2o-uwAdXnjLc8+(^WSF&cse|WCWcnFWm;@4+ehD^3-u2F zb1?iqpWVtOf9)fGS91uw@hNyIx4grKW~&&caYGZTwP&5`>KsPDE5z1|(PT`|7sTXPD_Ik$og$g4oD$ADW_B~mj{n2!zDF^4`_?}@bTWiqv8J!8TMXTqa&I88a5SgIe7w&@9%k$~(_;60HfyhoRi23Q z7$r3gGyL5wOe}HEqF9o$=e@l@NVq7~BG{NFhY0yiDiH{^&NxhbeJjYMfvwUk_al)8lCgf5i z6`l5(!eya1$^!v@a0hCc2Po388tF(4o#qK;6-UTP?YNi$0L7cy%p-bG@Pm8kpKymi z;Oa~O{hV;T!>x@1(Sz0i6P+8^O;b{jkl+xVvDFHu{MApa9ub$kHjz|^I(~E&N-c)R zTI9-I7Ea|ZeR&^?{=8nEKR-rZ5Fv(1iO`T=Z_~`is;BSLD}M66LIoxYSNsjR&_!;s zi;QpN3G$enf`1#Z$dB;ZL0tqN3|pZz2|a`YJspZvZp7ER5^>FMF}vI4%V=WvEsC=! zzzC^c9`A;isB(g%ssCFT-<@X*KWq5XjAPmFO=S(ef9~`=47M7xyJateRRLTR`sfun zQts};GfUmamOT1WGdDQ=7L}!ilao>2z+FXdi9P8RN==f|@najx7YoTxRyM{290vGQ z2E?aqm2)5}Qq}!TCX3`@6}-vu!(=9@n?*5xfqa^?h~?1{ERRGq)$@Q5JZIW$o5;W4 z8kz-+Txx58FIBozH%XdW3T;g7sM`ZxZuj&|WwlB_o-#OH&T&M|Kttr$uqLrb>5Qp zXk8+zxyO^A5vM+mkO}g7#HxH$cDTGh8p4wQcu_1}1IIFZn$2nG66h5s!5{5g=!*jL z9Zc7PlN6-U+*{R9lF&*`fxdgk%?-8y@3@_v#dseE4^+1(I33`y=3T4{&ND;6#5}^3 zE>bp&v_LxHNotJpx7ZbHviq(i7+{fAYmIda3=&PmS)x5>EztYnCR8;|hy0TEVucyc ztfOK(dESyhkZXvZtEQ=|i@p$iUQm#vkq7Rs|G~>)`I1DrUw3QT_Z#gRhcq{KSz9WE z8+$Rsrt0v3d+A;|A~94D8(*5ojMP$PHd$1*83 zSRl~Mza(BsP?-4eb5A>rmoPH+p`3CUI@nn!po+$*Ei&W0pv2Tw(-@mOt<+>?G$!K| zaU^Nn?E$GE_=!8C8?RhL2$BvK8RR0o7-FYyezZF z8ObByyKovnN;bFFx}vx3yXJvDL$Ks){(6d)RC%v=z;{dA=G0hL=S3!-C9=X?jrxMa zl-Ij!Z-)YJh3SbTKw!*Rr1cr@Z4MSs8E6NQI$Ucgf`=Y=ok*<}ILTV*8g;|-sDuO~ z|MQepzv(Z@{2?Ls;thR?2^p~^EBPVxd!?z=PP-1~n4S<$^cw=9`|=PIF;@gDti9|C z2|rV5+iWeD>x&hXys3oQkRsg3Y-4m<+HgYY%N=CB`v+Vj(5fB=2D=%zS~~*LiA6KU z4>_8Z<37LGB~xBLdZCBX@?asU#FQ!pIy++5*qTi7!ZMI9;HPj34pPcUT+Xi$?hbQD z*{Ykwt9a7N(sX~JFLpyo!<6(8!?p-q4q2C76rdd|$R6bwY2mnSnW>MO z4@J_$D%zOh87A5&N+tc4A4JERuh|hmy@*4G5k6dU#i>)=TY*59p;ok5zhx4AG&oMQ z@|HbH>qYI*p{U|%(-m~nZsgJXvf;BMfyTM{V(@c1v!O*G$74OK)Y&T}bm*>jD6;`Z z3=t>nAV;lpi~ntWvu?Ob-pbLh8%m4%f;KKrv{6_a443(bb%LX6*?kinBv{i^+hGSf zHeWYhM}_3|32l`}3`WZZ`=?Z+LKzQtw`mN4Q%MNXaO1S+>wGDiX;-T}LHp7VRW%^i zV!JpAmTW4&>sDTAKi;pyFkPDvmYG(8td%3(^te$j@tkEIK%ANhL>xB^b@*VhiYOKV z?;HyO_YjYNQGHDg}-9!%@+?ZKjq{q0nY|qUsBkc`_N{ zvTp`+2-5S^vVuYmH88<J%O2#TAM9yS5rvF!k6~5u{658ol=lk|T!gr;?-kU8hG| z&L6IbCB4?p)PBO*uh29736~%r%13^$@grjwF_Y2>G|D{u0r(WDiA5f0OEHOs);g;q zH$~yYi34znrqlm zXnD>02Ya9Uj9}1p&^5XnI*DDJau`F)%r^9#av+@u85UB0a=_NbKqab}ee6naA?rYD zb+r;eV5`#VUf3W``W00yY1eixHTi9clpA3%oH}Kte!BYb*GI7%VhE)Lbg1Kj{zD8r zFrRC`Qtsy@L1`A@*vu0SH`8xl#bdNh9@XD0A@qR9$!98qOZtS2IhCXBGSmFI(wD_K z){e4Cz)CZBiiVYB&z0kfijdzY=8I->=;^j-HE<H)rIQhxUHX759}KBleCNSU=)Z zW;do;GsDD_IU@pT-PzW)MNJ9V`-AEFUTn3f+#MCU1&Vmpi%4Qh38hEva^BrSGBJeb zcid!~CH3Fvn+r z!C{)U>M3IA#-M}ZO%+?9b8K8A5RinCb%?m%*e|~PVi;E&{@dyBJgEC)t~UH(WE6fC zHqSX<=pm@AG}1S4QluOrV7zP#dwrWF$pvGCyRXRP;czja8n3t^wqQWzD(zy^R6Qd* zTdFqwqFT|6mvFs&)LT7mdfnR3?EtE70eX=1Z{tt!x2@A34%=~6H(s|OJ{M-Mo}l0R zPwV;~1>HC55`ls?y=|U7c?+a(v=SAb5XW;e=*{^pZ!=pxE1RW0MJ5|95nGFut!s1Z zzG1vwi_s2#fMm%>s>k08Zw_xl=B$A~XPJ9xUJV=N-IaTxy?UIfj=b7M_0)iCM>1I* z+j7!fiKxJto&?`rM)?P7;8eX~eAXu2y=i&5J;ef=R}X6$zBdI(3D;}`14=grR@Sq=1s z-o$~EV3KK+)N@M}31*|$?yklFUeV^7Q%bp3mjylF&6c^S+m+f=yz=r72veI?gpWs2 z*0;LKt-oiDOyzuPQo1a+1%~N#y6|iH3CL?9BuC7G&sc-^lXp0)Gsb%3c16D;&t1EZ zTHn^-B{Zpf_Pw^5SNrqi7bSqf)a=KZl;HgNhF<5Zh3%i^p~$OloA8To)yeDv!<+Te zf(z$V9=+0a1YC6Xy|htLF;H!f#}^6R_dLO46t4RPKRsg$wxds@I1^5x9}uvs7Xr7Y z`gc^6c3u-dEnd(2y+~fY@ZhF*gcNS^5diNtHQ2J9XN3XZw61W7YAUwv+Qd9XMQ4qI zgj^{7Dh)9I!DFYCk5>ic>oC)|lFhlcxyWhNlRzaBl?7n7?VfyUn4A+V`c`sfJ(jP% zsE!EfNQ*s^C0#Mk$w!z@%i}!JXO5kv(?o{I`m>s$kh8rtsTIs{duAAot?+|2@J>P& zuOz4N70%&>?g@xsE&KCrl3bSZ&BO2KdkW2$J2_?~mu^8 zj?t@(<{g-K3PY>bt`lz7X#Aluvgeh?sxy4`6*F=F{&;0uxK+0FRbLf=wW_Q>Qy->_ z+$kwok3Vv}?_<_^*DSV=>%~3C!gZSypIUWNb$`1syHlgein#f#p}?TFpU%cAhqk}@ z!(VH@*4p$9zqi~5u)M--Z|d$`sKw5D`of-DvbD$I$$7(88W}L6x-b+pvG0YgK;bqW zQvbSFS|GQ~F)JMp&#cp+4twq(WHVN*;l=`VC zT-8;n*|=G#`B3+}n=ia*uoWIy`)lzTjk+IVa^B`%d|$lic468t+zny5-^EsZNH%2R zAEkQ;YK}Y?%YZ9iLr!jQlHGMAL~jd!Yj2pBN5A_#m)^xsv<9~u<;K33wR)dv4EZdO z`ckFM zD$8E;GV7CrJRG~tSQ6##fJoWgdPIQj`I@2Ec*E0><18=KFS1fFJvVG3zpovr=N&H` z{I?qtEJp#d3<_KM5(EM7@uVbG?zFdX<-u-I;Wz(eNB611-jArI`@MY`g_z^<&jMBI z(Xn`hd!K1|Uh4MOUfc-6Q~a@M9mX*JFoD)0bI_k5c@rT;dY|0{T}v?&`_=oEh<7+q zpbHsjrLEpk`fTupEkt4Nqhv(9Zqawjd<)^k(1I1?)2Sk18(L{OgG>3wPsSVKq`~SS zHce;wm4EXeSCsLNQ*~fn_cbn8(;N8N&h!kw zUdsTccb<+#w#MiCCy!G8o&?}(I8#k8moz2~qZ_1)NVe+cZ?KU0PRprQ5Fy^zz+` z)q%6oE(3OZ*3`VQ)CT)*hTV{cA5SW*4Nkc8$2m{)uw6jj)sH`kmsT^b!hR8NtMlth z#OndKU61Ur%|PM&VS%h+zGO}x+-MI3JNI|jEq3_>B?Z@uy(a|UqV?XxM($Ov*sl86 zxG03ma5q`JL!)uII6!~Zyn>0-m?g}|Bt#{_2aMH5mQ-&~uzBE3Q6L`fM>GKu&Wnxl z!a4ve3vKB4b!6tbUP!g3C(E%O#s`GqhbhbL?Fu}%p#)L$jhL2jNk1KSWQ?wLN(lnX zaIaJf@#>q5qD(hS4q8rI0Vb&|f*kxON^}uaP=F38Y2qR2=TwyFuRBTIup&%`_`n&{ zM44rDP#wne6HN`&nY0ygjcKZyJjr`8`gps8kkDOGOdx%sYZ-sa9x`2vMCjT-QY$Qi z0|R+yoDFIV=)b?5;|0(vg(udGm{P!96UzcS)%@cmyuS+c(7g)hZV@RVv>^V@7QIIVw#j{mU7j25W8K^vBReOS)Jfc8vN80&3<}5 z$te+IO#4Uno8av>Skn2$^ieFr2TF2LneX))Z|UF3sDM_~=IE(Wp~l6sQ7f?MK-$%1 zkBpJO0@Y@K#s@C{^V#UZ5sY{3X~s!;5X-qrVl7}yQui@F3WfDNj^S7LqYSMn$s1Ym z1@jSU+C>J3nq{8D5t#$k->R^6SV7<}s@~v?ube8Cv>L5hT0PF-xu2}c^gm_PWj>7D z@9V4)pHfZ~KawKpz6>v)XMR!x|J~#h4|h4OT%-%lHSSw=l`aAKh4+b9)csQG;sW$b z3jij9x|$J*ENauL#Y%Kb>@po`rw!#Q`~<*BZ7nQKcJ-uzG7s8JH8WWi#@3D8Z?uo^ zW?xN+rwx_F@qad_yH0v!)>PX zspskak~RTwBbvi(NWtutp1*DSS=2=P7Ilm-_2;V1&PDh0*Zs-QrsAwQ@AHAr4A>uy z#N^7^IB(E9!K-H{BiLefqi4qFevcHZ89vg#NUGKwb;q4digUb<= zWpbQTW>refh2R!%Ot)`VrpnHj36WU!JQTyYN;>*GAc~PrB%mD9Kq4gl?Z`r^UeaJM z@2^uZ66NK-O)?QDiUF5+jcy;Kf56w`&9d6j=fQpJ*G}u@TX#I*D_(N;F9*3Xd^I9^ zJ#KDRMhvhZhWQuDF#Y!Io{hj~xj+wH{Ps_?V`Tsy3S2p0J7^PQY9sYOqI%5zxNw>S%y6y(rFQAWc~v?d8)P!3e3e&?J6Jlz?FL0W{Cu;r*MxnM>gkCU{T@CZVIj3UOP;0 zRC<1;VoNsjf};&QImJ)DzefBR>vdw+!9%bT_wqVkAIV&DA4 z%uh6$7@C#}(E>-jaMM(hJsWN$y^M2=-INlWxxoVZpGf9jB0-xf@wsrnL$uWUBm3)0 z+-vAw+=0%v$EElI6}HFJKEjl=kiIh91I7m=qOU>Fbt9GunRdb`>^!0?z~u3OACy@2$ zBLALvFPANEpYyJ677}f0NC+7BE9B;sf9LOFIjZ}GiOCxNK|=5c$2<}8P~{bb)`+8& z1repzib>QvogSvYL_l*6j=!+)98^XdE{iDrGDdx zenoTRnEznl=o_j%ig|`*N?BWJ*xcC~lh**bMyjkE>nmS@OznGhf?!|9GjVfm&r_orcTfrNlkKbzZu9yhFpyOFoc7}C)qBjDU6R@{pwejok zcNds^T;3(Ufpx{eR0+a!LJ`9^tt!^lZ`7avNds|az_=uR>>x};7B zwKjQu$;e6?7c9z}z5nXh6M4^){D`Q$(%U>J9StJ&wDy*WT;K|bCtalCeBA0#opY$E zfBz>|PZYPj)B8cx8=J95)n^&@#@Y5$T#J2N!!T`3>vfFlt>5gB)4^L(|6hn8zem_I zx=CgFOxDS{AStK8(8xokI5;HCum(X2?Q7!A=q)EYfEW%eCpqi>p^s^nPbStdN%!+r zMFy$G#(8k^EiE2vdr3(jbkAsRFq$q2b|{bJHe(U!MEfOWJ6ziB@QfzuBh zp4fjgUSyj3MUZ`|9WRZV_E$oKJOVzwe`dz1#C#PV%r%C~Z~Y|3{tB8u$r&09mZ9T9a<=KejWY=D=Al&z zfmJ#?A{50&O!JDb6xvwUTny}Oz(5SvYd=IxWA#C7l$W>I(fl&N2o`+kK}Ubi83b$0 zBdmDF)c_zlK`^kcP!AXvixS2^?=I<$4!}CV?%NFpWkW4VAltjvBR^=6Ecj?cxl^E5 z_NYK{e=5M{|?@R?j@-ke?T)CRui7}+BU~!)!*0V@qF#D2AH8{7^eNND#smc+s zD#kr=cy<}5qHIA$B(mt^H0V_xgaH`W*+WRS_^0fl@R&N)sBjl0pi{g5gN&&Bq&vYR>2E8$$dh77pz%$9U8X6}flzi$im&*Xj`O@; zcZ6wbrTAE=nIWmmAgfir9>=aodzh(urFf;06SOPBWS}*1m`ClZ-I9+vy()6Pg}>P4 z0NILFht$WGW}wp70HHa%Y0KYYbLI(da|784rliF@EUw_z1d5}H<_y_c$YKuIgQYjO8(%csgF#1dFxcFG$>~>b=6I z8ryHSN5W9%qS7{nU)AJL>(=))+u`Du8F#{1c zg`@e57*)2Yad0_g z%4c-Rj7JxpQ7%28|F*v#hv9rC1-u@2z-P@B=ymgP=4aRn1! zvvm8P>im6?wNELH1RQibeuvT0kU63C?fYSAEe1iTHiUazu*yUew_-yw_ysAxz7%aI zE?vo-Y5j%KUISYw+Rwz4Zh$?AthOb~tO64xZ2^gLayz6Pa!$6bI znCQU2j${mtSa}UKs5(;Sn{jMs+LTw?l(pX*?NfDHCP3Vzw6NZD;D+jb=*t?e%gv^p zN=&(po76`fHlcQUyK9>I%RUdVB@WR|4v6cX>E4)S{Cw5CeAToye9^Uy&mDTrlX2}k zFs;NI6LXnIVjIp-PBi`LI@cE1fK@?I9BE6vxF7}Tz7q7hVTcy(zBIkYO61$9kgkFR zEj*vpM6FE?C)dSg`^xlvi}lllM-IEu#O^|KBeT_I?MKd5d!37H{o(AP>k_>Sg1F-| zkjUMC8`(CVz$%*HXdm(fSluUFRzfc;AWx7v>NJW71zl=JluA z@##I%rR4|~n4;v3!Mg6(z>3zeHa1MthdvM=n{?fT z2Xq)qFe)%<758Nbs^GI7g~$<))o$XRtDb4%JO0iV01(-oNKv3eGaJ9Ciip5XHF3;0 z|0u<7pua=sO~AC|p-pcCAdMF?#fK%TpRG_-nI-=5q;$EEbC0k_Xbg`n($6y`u96N` z?T4nAmno2eNzeve6evLEef;}4**Li@IW1|s3{7VQXoTZ;Ghq&ya3}_d zDd@ZL?-5;!K6d#O%>R;_Qsn=3Fv^~B{aG#8bC6PxV4LTe(~3|; zr?-0$EO0{Ilw@HlxGoAm2Ac~gkS!>bD4*Mxp-M;?I%S|LrD^|JVEmew%r`e#w1#Zu zfYvRilbz!ct5|b8{;j~Lz)qqTbyHNA{^tkTbzu+m%z%J<(yx5@ke}+rOTVbtUSV?$ zb7%!G*D9gC7y&M_kUcEJNP^XoD6s+obY@O^JPf_gKF^_ZiL9t5kL!{(Q&#&C_2=k_ z-UqZsuXmb)c{>e;3qa3W}fz7r=3Pik)t1(OCp~HWFZe&XX^fAbvUEX;&9_Q$0 zuo)gsl08gcn>e|R4C@SawGErfUNMUQhKC-5oJWXw*^=;O;crM42yKZ(egH%;SoQFC zY2R2Ca#r>sJGr(cH@8v86$Gjx>pe{cw)sF>vXVeiq}jzB`ZDHbt(&zP3*QaA0^tSR z>C$6cJYDu)cchZtp4vs8AHZbU{8CmS$DZCQ-s$EY&h~B5FgZ`jcF&t%boxl`i5q5R z9p&sZta039jmL1F+jxpIP!l7*S+w=7p>`Ys#n!Ybwu(z=gSKHcG~RAchI$MJ<+5qO z5?0Y}55plLmsvae^^`q}XAx%O5K*x=pz{bPTjA&0anp=d>6PRHF|ds*(pMb=<$|>0 zWPd}G_#evlxt2pmJ9JNf=ex$%xtT*r5!dz&*J|Aq%f+Yt`cctd=xdgG*X%Sk7IV$2 zAvH(+GR!wdAtA>_!~a4we?(9b-;?L(KLlV|bBR$SG}Q+&d%NAO|1qAr4r_UZ)b1#n-WKla=N^B%Q|A! zX#cR;o4?YS8*v`q!&AudnOWLk)^zFgk&|_5Hj_x+4(z({ZRw_Y;sje_Kej~rG@Km@ z9f3oy<}xE_!Ea}28LBJ3+=`1K9OW-CIXiHz@9NU__U@_E2Td_#z4^%*tfT3GmTPne zR;r&(I5lPVb_KUqu>*RNf?ozCtqN^6W9(1bM-#x1lG@GYl4`QmE+q{I ztcv5@%2y&~kF!gV^8Mta^lS?}`?9^JHl(I@tGb49Kzq|ovKKL`C!jUz5>NOvx6*HU zf6*|%=d!&-OJv?@ySl_L6x=V+P_J4%ru&Owo#}n;O>12<|6nU{dD(FH3;WDD0l7;yhxpg zjf0l7mV=*p(gt}e`eslJz^F7Oq`gI?yv^Y}0$u|!zld0gR8MSSL=RWaC$MM4*co4P{mb~z6vRyVguC1<5kJ|Bdl%I^0m?yT&9`Q;NmV7>o>Aby&&Ss;*nzktZeK5$2z z$#^Eo?&|R)ALuUXm%hO&31~qBmU_)f&Gha3y1eBt?EMbJc>eV}HU1J{2=lwzR3M=} zj&IuRWB4b1TJ}ukG=a$hu=>QeqMVYiMeU( z0x5={|s31b?rW@Yv3sl!HJ#^N}>o+U@78wEk?_uR{&M{m1V8)4$$Vbp>l+ z_K3f94?^!Tc}Ik9hjQR^eAMgCQ}*>6Rf_+-Mp&TA?uXyrcqFwEj_}8@-L}sSyHUW! zCM?O<@Cr$OOk{XNUDZ@BO)ATVu?eeQ?r*(6>6X}&%oaF@h)o%R#u+w|51^0a~ znUvZG7U|eCmKR#7FU%GubyI(-NmpOQeB8|$*7>G=X}@y3U2lix%tk}i4=cYIQiEa6 z5|_h1caKA!KBg7|-)~92?$Am_gQZCwBGUp}NnE(!SCbK=F^2b!j3F?OOc)FpFlpL4 z-{1a8i~cCI`6PL_SvUqKrHg$zhAhW^dFF=iW#8{;!*iB%gpmpsqRhpdfe@t34O+0d z@wws20m8iE7TsI{e||k6rq#+v0s!h0{RZw`#E3mI0HwJR&iL`E9hVTVjS-@+Fu2tG zY6s`Z#{zyKs1Jj0B{$jpv*kbdPj=GIrjlSHs4c#@>2k#hB5;PB#6M-DB=@3t#ARZ< zT;gn*=11})&9mfG7<^~PaTE=-gdJB}s#JC232%XZvX6kQZrN-E;> zlWVg@Uro?&Uj^5p=P@l!tcPdvvyEoC=jSb?JFD?*JeU58?@IHd57r-JlE>(0o`i%& zP#^P4-vkVs@~%MR{e8t>w%b_=vJo37;R3DFq@&%{+-)~%F62^xh61Cdg^{AFPil!m z(%Le%3|7gQs`6*{Rex8E z!v5kAADtMHH-^G`_Uy)ybwnnUQZov)`<>4s77A2CH zd%}&0@WiQDdp^omGDjk+Mi&)*p;^OGlW2fL%a0C5Z2Us_HE1@A=YDn*Gx_$>$Mn3{ zdUiU?=P=jxg6RBrPx22Eiw`f5ne5mziyalNuGf6M7Zbu~1ubZpyT!-nmiV#c&Nvdi zbiv+bp>Fs2wPEUz(Kt6{MRF0|M)$?`;iO$W1J9Fhf~$JmEy^}Fs>~}cUM!=+cKn_u z<6UD`Jthr%!=`KzjnFdYFlOk?2le=mF<*uIIsI#6Jb6AD|M*04b~!eRG>feBU~e3U z{^nDdH7m@e=2VD%{lcSQP5si;RXN+K6^y^DQncXyg5U3AfC$bKbe{y-DA3%p{BUSf zAL$n^wJMc*uy-K4*-S9esEr?aamrmRw8+`u3SE!&u=0Iqb#^-NnhC{jQL^rJc*>65 zS6RwDP%=pqL-q9`pp2Wg+&ma0-cVHnR3%aC1BxbaXfe4gE7&ntCRC*Wc8P^$yvy&T zYIui}sg(gc<2W3ce=Ow^!x6aGN(Yp~LAG-5q>_2xB~j}Ek|z>rFqtgn(vW<&x1>Wz z_DUvlcwKAhFk3f_maLS8JA_LHw$Hy&yK#^6X#lt<66rAYEXUKs9k`d%!`Ceub`cZKdmKOAGl(*X5+h?wcH~lnq=R88FYr=ZyjDDTN`t?o?8; zwnuW|?@PCpxi{!Ay^;!3c-?7+HW!@*NQD)othwEj!r3j4m@$ne=9K`QNrhd!?hI1c zwnq}-ekI#L?wi!`Jj)|G%%1Ui1AuQzVFK?BwUmkNkwSRj;(P+eYiXevDU#d}3^zt< z_*YAkcbFmLoVoy{6thTP3~H$;-qb|13SJDlAx>KonegpJ&KwNd60^`QSDDG2q&;DI zfJ{eMe7aEarrYRv4%fMmh1INXRbUQpENzGeAjgt~19NwRQvyKW4mM_Xlb>i+1Wb-| zssZSe&60T)UF#j1rn405?&XJiEHh~^HO4u$0A-yz{7MIkv|v|TMus7G+e~Go4Bqx} zPAR~rM6)v9X?JV^g@b7t@Q+2#Yz(&&vv|KB9o*LJ9kVInK9-prnAZ~x;sByVvqE0h zcSAI`x}fmqlI;)Ntm)w+mbx^U3L6IPng^_OV5`N3Sd6JsGa*uKxgj*}n$++#OIT%~j-VK#-a9>hIowAQZrFfez_qeZV^UI?9K>3 zxdRbU9k#;IkmVX9CfP*8M*x3^&RyiRPpF}Q!q6u#u&mW=@!nG=uZT?eSn0M4w+J<+ zN}^c|uLJdvw(Z&p@N@R`51Bd@`Jog`Z+So-&5)<<+PU!$tgPk`LofLuW$xd};YOC; z`hYz8A)duc3{p>pq0jfubCWsN6SqRP-tic73PabHYqXetOK)j_u}l4H!L*QET|~(?4ykckxX#4o zH{NcUaB%6i2X{9$rc>g!l5Ib6k`Vu>O0}~I!L0ijYuuysW&`4*6$4I_w2g8gIt~F| zBE|3v!=DUan{EfS>8q6Kx4<%Sr;kGhmVz{xvg4QEdArY)N7S<1QVk(V;a=mH zb-djQ;rAumqTJ*u;TGxZq%qSAdgQ-|CG!=0z+>+pl#~+Bx4gI|tS+vNk*&D;riD9a z#*&c2bm>!3t(-3~t%}I8KQbWQ{?PTHol~2tnA9dJPsxrwrZq?E8tE?Vczi_bBN6CB zI=_8H?c){bV}tIX_rVGD0nTr688)3pqOu|L@=r>&C6Gb+C&!yl({MJ^-?cbD!RA@4 zs_&O{;x2i{c>W*}i*FXcY!rGha!|d&jGN0R_{6f$a_U_!l^OinT;24~vjSS}4bwrD zB*Tcw#AP&Qi(j^^^)8W>$n-dFp6!Mq^2x0!_zl75?l2&mEMg#dsQ5E^@BTS4%}cNQ zb<7`^YjnauosAcyF0hXD!=s&cU-o&RR+vdmHpDx7&_`tG&}b{e>b5zJt+}%Mp#cFY z{^;k_47l8(>X`L34yQL|<22X4`3kc)sYKQbaz^+dg9^#m35X^5D<-cdDHOmEf{|Puwfoj-U(&gA$ouNs{`{K3*MT$>alxF4%_nj= z1b3gw$6}mf^x^ZVc+H`(^Z233I-%@Yr=dg_v?ffe#Ola>-LC7!AaThqC@?O<^~0p_ zko})V=`^ElE^@J$q#Xkzij9-u#1%d;Hkl3C0sI|pgtcJc62a?pgF9`XJ3WSmC}T~= z2ef*(Kdmzi>jM!C3MVQN9V5Ss#B}#5)89^NNQbO@x2;UXv0up_Hzo)xxhTapwJ_QF zbqEZrI%u}IDVk_9LlmeLU}OV~C2c4UT$3YLU|L$>dG)o&O@ztJuTwjIpFdyqJ(P7m z_D%&<+r`Upj-S&pS3O&BoV<-rbp};7oJZe>zgr|<66MR-tL$L;M(jrc-hWolxLBe- z4du;o78}e>6=E0>x`y16js84EnXi4?Z}q3LfBXU$$cRa)4~L~)QkSvrpkAjQQm|kt zU5wjVE~6n{d>AGZBVOzxS&S-K{7165v0_26P>sxv;7f6obaADz4ettiT$yj>f+h#= zx06yC4X5gbyMl!>b5--LLg&O~S_niJke3xY>8}ba5jdbXO&JbW!;UA|?J z&{^fkZ9RBA=8PvE7UYj`QN-Up!r9eDXa7*=&XrRsm}6@$7~m`jD|n6*m$vT0xc;$5 z{5>ho^?TfAy5Sn=r#wQg(@4`FBsJer4q^si1ocxqK{fjFHG630R+2Re@_${WYy5Fp zREDb}8eOGX#bVR!J!>S{HAFk6gLTE28UfL|andz1!BaH2O*m5g1k|rwmQv^$k^=l3 z2jR8JZpE=qAAeMo|2EA|QNa3a(cE%G)fXPHB`TJkazP#V!))ioVQ2~+IgtHAvZE-c znQfWPI0NZA?49Ho*_J+RitzRG-n@8+xAar!v!zaPQ)e+n8F5YNd7(v}UfPGP5 zL!b+JUk9fF2JR{9%XT0M*P_@4B$5;dm`I^CB0cRKbf`(%f{W<5N5so1f)m1DC3r-I z0su*bfabR@O=)f9yit%GEs-!i+3_S?(%$6QCn>c))y{>tevrCL0yQ_FOH1wzGESsgL%=-9c|o%#{VxWWw>$WfZUfPN4Rq=7GOH>FXdo zob2`A0?6uLv8Nfeo1fp|URU8?-{^1r7>?R{=AC2;{#J*_sX#)n*TRoC8J-FFEY!sQsT$P-o zDaL``kLr66+wZXm82b`jnCraB+sz>7r8Pc92NqKdL=%MtN(UaJWmiJL4$*-D6x7f< zHJ}L%c(1&oNtvnD-p?Jfnq^_aMw7+|5#(c_%((sqy@>@qp^T9nCe4HCJ+uQwsZ1X5 zaLzH3TUmW3&+ACeF~fl(We!ikNDk_V5Pbi_5VtkJhszqU={YNc)Uqk{j(mUPX;iSP zU&4grHL;la16O<%jPy%k_bkm+fIicWcB=c$4pHEjx`DYAs}03fPUDRZY)CeaN0Eu# zmz{`J=Sx4-*O#NSciF|*?me!w9yl^~;pCNWt|hZkqK2^=7FIc*Iz)-%xvxATVh(>4 zQQ)c@CeP6uJ45!qZ(zfpKdZOZ!Q3yz%e^Bz+1l6BKnE`&g13&3oLtW&u@(S`bn28(;3m&pNCnxe7Fmu@#BnNGFvfq$7 zZZ~v<4Iu_gUAkj%2^mI`7x5tp`me+d74}n?@1cL3Aa1b>WWqE_-e$3Vs7ju*qYQfu zOq!8nb9 zn1rV~QM!FdyH9+e8j}^5SGX+_Grc~2U#>T{p0#TO-;Tq!8}m7*Q zDQtQxLK+J~xFnHxL$OBDC}L$OVsV8lz6!%^IqSR6ucA}5)TUzj>w3|@!F23KCCE`4 z>e@#u&78w$>$lKq_rrY=5m4^0gL%aege?e)r8WNjrAX9YxXCk}>j&K`0m>dDA2_w# zMM=UQu7*k02D?Q;Y#ly~QvYnrEA<7f#Kfim`m{lpejzWN@?!o7R=%EmBG@U12t)XJ z@3Zo3RN&^kMmmX)Ge!nqRn72i_n)=V4t~nl*>enaexK6?)BEJn&pc<2o&-#@VL-8zf)@w`DpWKT)00WIfbt_l@gks0&PWFHwMtzye`d^PPg{I ztq#QS)rMr)>qq>ovn7O#xfeWVwz0G)9B=thUN=1lD9X+~XD&?o6+=E61c0s|4!$lmGi7wsnNaFAnB|Un8$h+4ov0(VAJy4NDkn+NkZ|a@Pxr-E@+NDK`QaUOl z!q+*I&NDzly?s9fbS>68rw7xw#9S|7lUtHWv1aTh$4M9G%SFf8!drw|foeA1Rr4DFT%KEKMo-qF6UR*3h#irkXz zymd_e>~{K7O|V~I0%2Kn9x>|Ev6@|(JQBdZ$+kH1n&>+QUkPQwsl2Xs)&@3<-260I za0)@Yc@J|##&FX`aPu5Ie_>n*Ar1??F9zDqzZyq8%R@ zKQbKObJ#wd-pv`X&OIVMdW+DKAOSuS0eoP1T|qW`6opA`BRs*ylFUwpn(-)=%hHuF z&NXIj{=c|O;Eb>$GHoZX)9NIeH_k@s=A*OEMJHNw?pf1VvWLG+ z50=jFF>)MiR{WfOUx!y6iiw#(gPBk(K2$8iCnD|xd}vGEg`{<7aD;g*JyOor;{Oer ztt}>;QLGc^u`ne+jdU?Y-}N?*-4!q=?<0bLAjI18;riku_yvail8`)`PhdRexEx3feexdU*f%nDj*<-26gySmbbXT1A zHdKcHiwrwz+{-)OtK!Vlj4;hW@k&EH^$A%hibUK`J=|OnJKZIx{jD~;na$tW&EM#Q zjRs70q#NKcJXhQ{$xDXyp$PheQ~h7_Fi=Cp4?E`!vS9ccrGnei5cyo2KhRlWsH|O>9UXnb=6etsLsW+M8^(tv=l> z$Z=q~;dr=LJBOjM+}pGDqpfnj&9lfUK^&qk4OxUPAc$R5Z>j0O?l)5ij3BV5RryYv z({qXHldLqu51LODxXs^D(!&Otq#MdeSbu6TXr$^svqWlPH3~DmRx_CIN9lIZXk(35 zF_`lW>E2GqYmM(uS&tAK-bt2iKa|nh&DXd8gm?9#; zv@s~8yvJPTmOQKDW58imbi%HZpx~*NKePrjabSGgc39EhtjSCv$TfMh9_J^~kxWq} zi&e>3Xer@OUXfh(OrteI@p|Z7pJrU9VFdFyZ-W@>-T0S($S!vvo^&fL|Bzn(@p0Kl zz|h|)S9-r8@jAwL6<#IYzT1~XW?$> zVpMFw7Awu<2-dAPULVFxLK|TG&TI~^U#u!jOm9NsMV)=4eQ@TqTmKO1Ho+C(4DF zV~@<}4aODz$#~T3k={j<)GD{VWE{C~1 z=Rl%4zINzg_3`vu7v_~(V_NwNy`X&ceJ3rIYK+wTIMad8ZUW1iN3Ybr$V_iq%SHb=w;PJpoCXU*Lhr5a-;qW#t$ zjLnzO_X@$*9cogzp9FsDMe(kZWQj?;$&l9PyG1~t zL-&UsVN;R6*K07B?EPBsv{H5(SPv1;*5%xEO;l)37@;fyuPZy|!}#|U{zGnB#JSaG z?pQ`4bEVRV`3lh(^JfAGTx!8c?|4P}U0^2jW2!lSLjIt|ySc5AkiPxGH5iM6lx$xB z`|qJx-Ipy8$G*7gF0F{r)xh5LKE)rt9NWG0>faWV8w!dH|EuNpU@>x8KE9ohG6Yd0 zeIwv{VG?|)-MbuHyzIjo%4ZCxWM}U>qWm^_#{1&zfwXiGH{P>8WH`jUIHt3vV6OuxCt8-zW5RP27YW-|yr;)BHZ?C$+S49z_)%aBrT_#iqqsm=dH zt=BrcvZs0d%S=mGsy$(sy%|jBvI-|L5EZgT$TeW>%RsvT36juIJ)?c*^Yl;nP z)05_flja%&G=_#sAn*dn(XOar4P@1*`e2TW@!4vDw(H6bW0t)-am>8>{M;G5Ng?UQ zme@EuzV1_RMK&RDI!}a{F)_Gv zB3&&+x%L)N@@C7{X>^tVB&zoM1Xxz}(?UtQ5o}F7KlEA=;i)a^?hclcEsVgbh^VWG zP?5vVPX;Py`Pwkt%BNfzy|(!i=X?O0tAYSx;LH_c69+e(M8>*MBQaYbLd2KXhmPE4gTO0}*4Ik`9D31&|NMa+khXFZ*LT99~ z!*g%uHCCilq_BH9UF)N7OhH7TBw&BqhnCMSTsv95M@+)tsUz}E{hN7d=$ivMM(~hM z8BnC@mEy2x6~1rIvRVy0KvDGOu=XMWGi3Rz$q4&JgyaLj+c{85vRz!V-3F~N0cVhk zDYRD|<-P#NllpH+C=OB~PUlDJ8)|BQWJwanP#9t`%z}ou5*uwV_0XfJ2=*gvvVsWH zGcH!-;nhpgsSIkwZyZ;G1leJ%*v1AI|I?n>67A;oR|>HSQ30&HVvz}M56JfEej|nE!f8}7qAtR(imY#vtZ#Nrg1QZY@=b!ErZsfEjo2mIR8tp% zrZtTw!A#w5G*&EP*1-`z#fHeTD>L5QtrJ6)r3skxrDFyzN6w&>LwD{;CiW}i1~I8F!6X?zOLE#Z|&TSSnOFD2<{p{cUC+6r(b^@-=s>mp4VNo>c{t-TDcT%N& z4LMv5O=&1sL}$99OOtr25A$l_E@4QgTI^j#6PW!YKblD`X-0M#5e?iW(BWjVm9{~Zw(%~Fb4E2v zp~5mxa{{9>IdJmzTBI=9ry}`HosL~SYS!LuhLHJdjD36otkN<)r_k)0vtu}T^tD;x zpMaVbaLN9}`A=+LE-?R%wS&JBG-(zo4(9lwpQpx#cWqZlX3@W_v8)dEePfmBL z;LlZDo_1WuR`vPIAPNmU-8|Pg%*cwpoAQ@-j)6^*xN`#RZPjs1<8yD9hBUQrMC?5SIt%ev(-x`E`A6=y>rnEd*iT+n2 zt7Sp2MQu`DxTE)py9d7jmJFCuIjC>e}IEz$dpYFq^$ z;V~P*5eGq(8P!)6k5~6c6_5}?G#UXj8Nrb%818FG-({Oi7(jjCAJ=ok@NGNkoUpjum_Y^7SYE&qD|NNOiMLaXeSFMQ+(~ zvu3DM+N~1*5i*df;Ax5b@jEXBd*i-*IO~i!!M6lGa4y^V=`0a-B!eoBlqwGOyGT_$ z5TOQ60;+^VMzFUbS`i3$*poW)j4=|ADRN8>$BZh{Q4+`5#gR}{M&nT%55X{(pOP8{ zz5=44gYfmS_X9AP^EdC2N>#{P3D=Q|*t3bM4>NZ8Z=JKGK(S(!+?3Rq8J&zYZAVZu%f?-e80q;k}jC>rin@UeyQe3zcKw{ zz>1Sec~OZxr0nahy)x7s4$K=GpVKNq{s-3$Ox?{X=0CU|6;JdQIu8E>754kke`b!W zsHVW}78I(AKlwM8C?{qKTG)P@w+%W++tSNJ&dX9ti&Ct|)U(S&z`_W4b!-kf(OA_R zXYr$Lm=m*9=+Q+fxFfSZs3B_j42oFL>`l0V(XmEf41vw@tfdh(4OCDOCGlJwQm6tN zbKhKY%LH~n)msvxS_!hVM|cX5016h58m(yeJK_7L1tIs=BeR76ux(w$yZ`F3y}u<9 zGZhg!HNM^}M%eXeqU4gIu^`csM4#egY6r7@nAxZ2AHEfGgpfR;* zNeIxG_#d+P{voSR28yg^f#U%fW!W&Oa_#-Eauv`N?KV5wR*HT3B<;%y)%kMRTPKR| zME{8r>+t0vvLZJpK(3Td40IfJIe5Iu+eF)w0jSbX+HVDJk-ssXY_rX{VnuMk>(6f6 zs9tzL2Fm*4j@2|jB2>N0)>MZsW115Nl5}Gw^6_$MvD&lSW1;f#h4P~cY2DJy6R>IB z44QDn;I3dKD3A5;k%UoR41-67Uf8x{q&Wq}BEUSPw@{ki^L43zCb{x9Y~DB>BqnFO z?_?P17Tz8u;Y5rb==n@>ZA@&pd*4ZEvtvx$2SB~=wAkxm-@EkN#IA9m_$N`?!6Q`- zNT)q@w)Z>6_b$IrBUbtL@b97P0yu%CJRUntSXsft|J@3gw%V9&Vf@nSTVO@iZmKG> zp<#YBwKP7zEXQ9>wXyOl;SU7=SETrDG9=dXtO|L~2E-Ri^ngVp*xtyOn$s-)#G3w5 znAovS9&tg7F+n5hYiaV9vHFAl&%XPMZzPM~dK`on^H)Ppg!cc+6hAQ0-XE6g^IjM5 zrhufE39W^)C2Io76vHT*;%F~kYyb!8dk#{v`rk$n^wD0~(aCofCb$-uUm9cn8F=iG zPy_D;?##&9W45ek3Z_l)r}>-VT+dX)(y!avPOkua=>N?2E|}N5{+UwiUHxgjwx9l4 zE!IDsqz`*}gd^(Gdrn!lgL$b6{V$k#wf-@O%|rfOU2R1KWzF>o!@bXK%JZ*VeEwHi zMUG;5yJC4jl6k|Q}eP2nl-UZ0^ z0~0oSy-N6jwK}}gCVNh?p*=IJ)9AIXZ&iN8+F^XU_v}mOBfoCn-QB930$gVTFgxg< zp1k&#|CFnaVOGZXl?YD~cx>RDw40q>Z32lpubJD;RS<#4OL5h%%0I$u z*P;OaP^U=y(Um9J`&s{x#9s}}JvLu(Ze%(?#izI()17Aer_6Lgg@Nb~OJDyEC`@{M zpLI~$>`&QbQGGnrAlfgs_-DeyrC8z`ld=9H;5GYS8{T^(USu#sl@J)R=>rPs)JC+( zhUdbR1X7G!xa~9|{_&2OpdH>4(M}RUk^?d@iD=`BXiEUg<$;Zi^|geTB+TBSF)X0V zNP0UBlt2a9OOm3iBaVMGbSUzj-{%I(E-in|YuCSuXs@u@6Z$`{HfewTItOw7he@Iq z?M<^fEmbm;Y8sD5?QO89S?xDgF!T7fy;ezDZPihPC8<5(KV*WLp~%#4#eW8r8muxu zT-96EM&ky=)#V5mg$morduXPdwCkwJDiy|mTYZzMJgJ~wh9%bV_@DXE3ZaLl&I;~- z)n(NXtwB{WxBUwHpG~||^5(8NuC8Z&t1+T6i*KqzL>7oK6Uifj6FUT9BbEc^O=%;# zX(R9c@kvzCsZ9|-9WyI^73(OP32X2`Evv7kPGCH2f(mT{lQwW^(?`+EqO}dPF?o6!wgsl9nO>5#-jZvn!>~*96!5g2p zu0sv<;QyaLK0^+4bDMy@(@xqvMz5ljFF@pQ0Jw09 zdRqr#-%LURx2xWd=MR@`etTs3=7P@>Q_uF7BJg$|vj;P-15RiN`HrwXHJ~IR)ljpf zs%|;UFF@dy9X5G7=ANoD;~+=;(pk_K+%3L*JIe8=!#|KRZBGy`Ct5a6Kts zV0wIg^i?7}n)||4;W)!))Oc{L-kRWA$F|kNZU!a_a@iG8-Qz%_$%*c|6;a#A@+(Bft!2c)uJtyqwSnofoJw;H$Cz_*||rt zc$~$U!!!}!yb<4As|Vv&x8)HI&OV;TOZeLbu)obw|9Qq*$pnUMd;0{9B1iYwq+;Ji zv_ed3Ua7+_DG8*JbI*`ijQJd3g9LUcL#oeAHr+$OE`Yt&VlRr>6#AhY` z#Fqaf3Dv-G3PG1YqrTyL9Pht!NoV|^y5+fieg{8%g$j(vzjPo9Gz~7=y~s+vS4WE> zn2bFk<*6c03xV`A`XSqqG1`U1oC3WmLNMb|g$1PfjoVg(F~ejB;92Z;F-4!kiSXtt z24=Da1}Z7Bgzx;Y=04wy#bpa1J}YKAY3^3*JGjqxz-)5COy@r@Shokr!TaY91i^;N z;vQ>$08FC84swi&GjQ9d&xB>M6+XnF_W~PxMl;u)_O!rL5`jQh}X)izx79j4RboH|WA#&}sKWO;BQoc3Qs;R+0ss@eZeSS^&~Z230_! z@Z5sw&ZBAXC&g`v9e++qV(Yho9dFZ+zqDU2x^!HU0Mzlww2AYNu4vdry91K*U}vsW zXBSMupX^!Treyu}fw0yj=4N5w9R`U1vL8y({`?ev5PmQ;a;fK%8)m^J zj*d5Lc>jWAiCc%CmF9fswNdWluRWXKMp?D}*TQJ~gKO~5``xwJ<&|fy9{(00{^HHi zPZ*wP>W7E(+avB`--o*?L)IT&*L2(ay&~PaDVHeFon0s@GNiEjD9K{rmvB9Y?KER` zS_s}dVuu+g5WEg&>Z(v}rjBT5p87a_s;_|9zP!7{yYnNz>q?7@LL{cwq57Lan9SFH zDKu+nM$CErQI#G9+lLS;X4&jO(p}DrTVpjPE|+3ji1%wyi6ep%%{34qN2$yMvrhTF z1I7JGOYw258Xw6 zZ#$~PP%+BYGB7CzZ`Ve?CNxhG-zY4`zRHwDX&)wM%*XjfX%vN?b$^HVku@)?*b;TY z<~Qz3v}&<;?^SL!^_^V9ATX-KJYkRSd<1Xa>Tt|;b{8o=rI!%c)|k89TX$?n9NfM2D2#iqdDN~_Sd*oqduSJ7Alz83C5^az{>&P9n{H+cx=VRoRL0y>Rk@qJV~H8 zVScOfqX5S>EqK%?6tdJ{^3aD$m?QgP3#;7Y<*W=1{7wABnDMBPfK(0NrIZ3!kNL^7 zH6kJVei&N97CYtcw}{*@$k-r+Z)s{AK5|5n41KaT7Xb?1Fh zX4!teGo`{6>LUSqQuf$VR=Kc|pOE_Jf+vv}kl?)3m?Q)E2|WUsu;e?O?d z1!$|y;7pp(&d$w89;t!ocyU(?&J}s(EZ;+WMW>7V%XTO4%zgh z*`kMt*yyu3Mv2%oe3pFzRG<+Ooa`k7)+(-fH1M~V;$X=}gu&7qXo%V&Q6z-~QNDOn ze~*)@x2h-BJ>qtM&~q9EHp}ZNF<)y)g(< z$X`nA3*5(AbU)-gT5ab9#>1br=eXN?r?5Fr3hqaT#%dPm%+ znUt0`F{|@4RWis=o%x+`v3jIA{fr6g+QgyzJ3_%7mi$X{ z_OH2zy}EvJJf3kSVr`j{%Ec->chotz{ z>N2hBKX)a2Y92M%$E~St@949w>hY$*bLOGe5eOZ9DI5$qh9S{0`|h&GlZX1DOXUN; zWsNlWj*T}w!CMyv3T~gWa&C7{w=d?v4tn3k?0Vp&?j*+O-KD)U&s*o|@M6+#c*fT; z@B(0J+qy9I+(=KeXp{;hpM8F&e`U|S^^3fXWS3m=+n)OJd7~H+If|IvQ(HRnUuskk z<;35BxxX%fYXl~Nzq9gDXcRcIWHDmj3;4Jj&F;O< zM)EH3i19OU(Sj2m=_P#msD;~@?K9Bt((WiG0)MOT7?1`|BQK?7mYF_%qgZsyJjeI+ zfAx`oxQltd>v8hM@nikN^f?RgQ=kXUAOk-cZ$O42@Z0g2GJ|*xpGZ0-dV^GFzC>l7 zFEWHmds;!dAp~{nN8^Vz`;z?oKfBkBA@!7}qZV3bPL>fRnr8JDPbvX&j#-JuW8gQN zi`e57j73M0+g-00Xgb3hYt=ZXtEz@jn(;V};0*-_=RC?7_nYmW&z2EH>1IETD^csk zOi4Q~xUI1`n_2MnOKnYqqMtR*qyfv9w#>_0le1gtTpM^Z%2pzoT4D9dmrt7~m!0&! z{ELO*p297lsjt3I7LLt4`IC{KOkC!^LEoi)7xMKSyPKAoS_7C)RNN~W$9``EwpcT- za<k1DpeXAjMa^og5wD3!(B+Es$vUZ)b;SUGn%m5f9MA^v1 zXQD{x=uj8=AA}k4AF}oZ6WX?%%+Yup`11pg5sNj*Xco74d=`o|vM)(t1!FzNgS#0e zFTn7rqw2Vum&mEJ+&FRaaH#_)RO_1Xs_gdh6T?M6mvPLi=I*s>#!?#sT&H1$McQ3Gb~am34U+n)B__FWc*L*p7gwr3g zqiPa>xxuLpv*EZq!qp~T*w7VEXz~X&C(b$62o-s(rBl?17SUFI$+NnrdLkm-n3Bh7 zRe^8Gj{`xYZ9*JqiWnMmBYud0w%2Bh9Y~8znq7rI&z*K0&4TmSIPS&)l{ z9p$dWoyjbR%zpsEVj}pWFR(M;W3BpNulSH(FP=nqH)j%$PqFPDp@UR34A`lvv~&t6 zap_{gt*`IHW@N`tNpSz<+l-s8_-5NYjx zBG^4fhEdxM?5^xpAQi<+({Ny^HIr2A(;kT55FZ3gH{m*tyah}HsGjg-uPwAgZD`V; zQweFK2ek9e*>Fe9-m5?>7GIjzV|C}4ZQ=wh>lOeb^Qvj*#XlEZK{sDyV3|G;_pBsc zG~7nG)q$l*I<*aRL3r|Klnq9!L&jFvi9gmtSL(e;ky>gSwi=+-L98e@4na87#^bxV zEDoVMn-lF*=v6%RiSlotEP>3K$k!uEKm<=@UXlJR&585^2O7u+LVZ>0`4)Ucza;2H9sF7aF1R4z#eB z6?Q~+B1HjGsK5@3^slsv!#OL}2hi#(x3HrqJq|?Af0ffG?S&>ysYPiVfy;s#pyM1C_1h3IO4_DQw{R^b+ujXz^OhqOv8Q%t>12vbE5xCS)=BYiD8 z#Ppm%#EGSSKVU!#Y92w&3z07kZ9&a0k#}afU|psnLfu2Y1IZh-RiC&S8G>*RV{3Gr zTSuDt*T|oJe-h-xY8aH1hL?E9h-`>PtEhq!#TY$qb2Ho*O! zuSZH7&sm=23mkep*JRvA@4p_66lFbj=@#5&&5;KlbY(#7_$(#Xi6d{^#RtfL2>MPU zF$Sx4V=-#KPhccbtMY_h5WbmS?Uh=_IFHvAJ;N(&UNcl>Bp6UR~T z$=7RIVX*taR|VvTvsrmB!3(6lnSZ90A4Une+5RU+D-$F&5vSso#%N9wY{EYPai!+y zd`XMMaBG(tp6@a}I(`k>i1p?B6Up3XYVGu8mMd|c<)R+no+JVt5NtubC5N0|pI}SQ z;HhfkY;{^VhHqv8iEnENi#9jYGS4n~cE2T8_C?P0Fc-}gPa|q}j#-i}ef0@SR1j(o zkNwmV^DsgT{ZQ5tbOJ)lb0w)nfe7gtl5JPZ+L((|+lhA=Ya>feoI2g?m|P}(1Tggh z^NVadhPn=)1Gh>jqQWPYR8)-6d3dcLym;R1@BUIA&NwU(DC zx^`GzZW8>4;M`Dp%*Gf&S4EIORjCElbmgev^Q;zEKKgWXkc;_YW5Jx|&v zv1pGnMz$Z(=@ijc0{X6mQ|LA>jOiKku5yjtQR&6e`-1vNB~$xMLA}$Hl>;*=BA4D*|Rjz#z2R;RD8j=2OPPM+ZN=HA?w68v3Y!eudji zkKzf}-n<0)VBQAmlEBP6jxQ~qA4<>{=f}K!S}1>J^?Q-tDQ-@Qn19<<$-A5GnmtXh z_VtLfI!y%5zxu5IMCx|@B(XB1t_EFER|+-J(&hg!?yJi0Ux6W`#7tJ%E_2Vixanc; zJoc3jkmCc}nHi5Mwk|8tEg=Z)`4@vHFEx>8^9!WHL#l*dm>TkNI`T?G>O76hiV5o1 zsp?Z{>Zgs%GOVo&#mg4*mAUej84b(StR5)OS7kdshJQrD@~juGYh+(dm`d51N(0B# z!41oN3FO@r*Rxf>@t+|fXvx-!6ABZxf zHIU-4$*wl)btk{4VX_+K_Vp$*z)Ca~oX(ihZImv`^`WY#Iu+=USneGxtvmB#z;aRO z=pVPWFLT)APtDNMBaBCggDU~D)|FLd+5W-JxQiUPd;0t1I@6nHcNZ^-POkFl&=og! zCw}S}r!x<}&l=jsDPvC$*A@k?&rPnLgVBq!8|B?dlyo(kp!3}XS`d6{-JWGuPIO7X z>G*KTSFBk~IPB!c{PYQ!$+ci$m_warIU;ukWFS+btC6k=9NhoPZjvN;yvXnJyCKglFc8OIWd0uVtb_dQMP7i!uEh4 zS+CICGU*G;>$^)=J(gwD2uxg+j77`jZl@D9YQ^X;8EFUV6G`E~En z@b>_cxu<>cAHQaEk_j4`u!7hyY613V4+d>I39kn2U$W8VT5$=+C8!8cX7vpds*g$0 z&;#p-3qCYq8P2`i zWP9I6M65=%$CCN1od04Dq29)U%V;yX*H1rDJ7iI|cQMgYj?*}lZC`JJs-~)B-xc(! zS=5%DXoWIZqM1fY-NECSGO_EKumq=Ab7nBu!A=`BXyq3*aos3l?CqB@k>hAtC4p6@ zC{v!ZsN*#xM2H%RiB~A>)OUlI8F=6mYu91)v(FkU6lPn~t4I*{oaH@d{?$~|A8XaG zqCt%ENGZA!F_h&{p|V%hca&Dt5+P)eB8Y}5o#=bu|AqI(Y2R@06mkH|ZL>bk(;C&1 zKuDHXZ!FfeMSjqh@@Pz($gdjrJXml76*fRT8k049j=MY==BYrg)Au}&g%JaLq)C`T z^XJDwR|515&xF5r3m1WHK0+KFhdY3-Qj1X-Y9Ba<1CE=F-1l66OfQFhgv zI|iT_>|Ec<81S5}=c+>DpIb2wimMc!FrycU;D9BG56@ENrvoZ#$Y z1Qb9BX^#GgE=I&}2_EZw(Z zLcOH6{z)HDx4cCxwv4}?yHS7GTHSbaXt(YoH2Qr>|XTe{qqdR>-=POz~UGJdzJbV7h|KMVfC0r;SL z2-AxXSFd+i;bPfm;;u3xYND%jqwz`knDSU6m-e2S z-rJ&inj`TQzlvRG5H&Pf^wq4w&#$~(XqMz1w9hFMo5_sf#zG;_0;4C|XXF!4BsL!? zCFS*+`wr4*W)v1i!oRy_jcSlDjx7mh!I%M+cJ6b7J{2sFglJ}Feyib7Q?tAQz_yF# zSuLN1R!O}kNd4@CAoLBzhwY1b;q;efRBHnzbv~d0ubN(HLwjV`P+PwiGXf*+F3HXI z*yxB>@qn;1*Nb($Pm%&6wneMhNdy~jCl`x&nC@$5w&5U*=n5ju8yY4|*a-ZiCJNKE z2X0@)jj_zct=W!QjR>l-*)@pJuu(KDlsrg{m2jckGCM&gz;jy*^fjcVynDINjC-IIHz) zmY&Y_99dXY^X})hPkI#2;C^RGfza~KsrsZjB}oC;ss=mb%g#DS4D~l z{A2WvfDYuqA$Q;Xtu(C0z%XLk2z@>B~8xw)BdpT1j~e**GIYDrLk zcKrBgEp_zGj?rp2`@$`d$IayU&DN=IKNQIYM%O&>zD%~D27eZTLBf$kTW z^vU(qP#9CLgt2rl-uk+{JXY~2ysa&#Z$>omn5O}SNAE`};3M)IdOH23{Wa{Ua=?-I z-8}nKa>)^zAo$ob;kYC2!1-q&%%86MnCfRUsmPzy^LIfSvON}WlQ>-il3fiJe?JQ} zkL}7}$p6en6l{H3FGkd)%CWwaxo@H1wl8}k!2GViEeTfX>SQI?p}92E8*-xK>b-1X z6*?&U(=#--PSS3~-0mOW(e%#IZSOMf1I?dUg|%; z?kzkac(~=j3N^_vpbdS0z#{xLlvojXT2X@#y-LhCG(ol-_WWGPpBjs6F<&`cL`kkvR!|sB<>7; z{mfz~??ZGp$K&(UlCbKyx37fAj*;>cauly_MkNu7T*l?^KuIKY z0j!*K8*AFqdGF;4@(#P7HuZEK z#g~M=bGN3@O)Gj=Gg6uQQ-JOjaTp;}L(3x~-r#Stf-_k8OU{#qux9VadZb!UU%bl} zVY~eRZpRw=@7HM;PUg2ig_m9vK9L@irB))i3vT$=H0wO<`rpu8se+N~6g#yx0^EKDN2qv)>GI&K&oHcsuL)cUoaNPT26 z*CQ3j+9+uM`qnfRS#~d0*p&%UUqVOjb5MD1x8}Nt#M!zBvi)&le)xQ`UrU2vXrcd| zTs2+6oqrOGitDb8EkJF`r{2P8kM;@wSu_NFV!7>*N|*Whwv!N@lI-d8+fMg^(&C4M zEkP>s>A}C+eDYE*@}a!U@=|v4a^EJ|RgN_ea>-?MJIZFX-&`!bygKGGRzhr^zXP?V zyXH^Rt8=D}TByCBfOGsUXEYwGZ1c0rJ60iVuB|{Kyd6O41!|mY`CE@~&PNOd`5sux zyVs8bk4ZZ@)jmDf=614a7;tnK5niIH4VG{Ed`M>po~fbb7>5@%xjvupTuW|#>>3$X zzt6b{SEyG!JJCI0ArRT(s8h>L%1_mB70#mRejX6tWajDci)2HV3uurzpOOaGM|X3%~)4w zVGnT<#Jl!#Ln3=tx7jkh~S(PIn){Qm(3zUlD#QdT-qpDdBJhD>f zd=^I4e_+jv+sJVa=u1I9JlQthuu4TzEfN-+s=9`Uo$vBC8TGN{xi*g7&Hgx{e^F*A z*t)Q4-rw%KhZDzx15;+>RnFVI)NQ+o8H}5dJED=!jPZu%JwaF_GPhRAo*P* zh_I3)`d1_~v1IjA!wi))eG9cG6^-X!-(D2YQd2~8S9j&U06F6NB9m35Tu-#_hR@nH zhX2^NesH^TO#F`R!T{&NXp0o(jP9#Xi}41UxxwL6g(N3}#fl936tkW!;QCKC&z_tH z@@~k!iIts^Re!ZXdAm}fjodvtxc%Grpbk|_kLciNfVxnN`bu+RwAZ?1&+Ski>#>aH zkTIs0LroR7r8ADT96N+f#f+W2Aznm0&1^a4lv#AQs5;)!yC5%Cyf{#WGIlyylpH&B z2e(a)vz$<-`_+7E^oAD0PPuud>@2GDb%==*yWUz9He$nrtEa}94lrqc=AUW}7S9TU zBc{ghpWLIYNt$QUp3tLU#*%WoPLhjpaTva0xU`~R()!5a*`RH&^R3C=cDC|*J3pAaC|^A8yi9Gi*wxaU zQYBUuQ0C|U+-s!S4#;>vg@nVEQwj2>yuowoRaCK`atu$W$mFdx4dq(SGVl~eWo;v6 zRrhaSMOFs=f+Q7)s-+cZiYzKtOWB=UO-)I%;)MX$E<^F`LCUJ$#AAwISweEVGPa~o zvOUyZQIIE^_(f8rGpt8W#S|;EQ#R_q;{PovrwMo^DRfm9eq1-TG>|H*HNOHsE{|FP zLkT-lHMn9+y==2RSaFlr4;n z?F#O6t+B%lCi0XmxDOtvb>-)C0NP+@x6n0) zNYf969T$dsr9A8%=v4k~Tdc^N8d`t6!(L4Knj)lb|DSHIV28XQy;m!gkLhKepNSPK zzD@}>fCzJ)k>bf4T|CMEm()L>5IoBcXUil!{Wmm?aZIVpZFkbQnAGwlhxkLoM$!Xq zrfpUXu`vQLZo-f#0gQh>bKg4O(Fpwfg4V%yyx&HSK>bI+^<6IKb^(bmFDtW0l}L0a zNGN_W9;lKCPSh#Hq(nRf?FRA6sb=$Drl2@t{{K6;WrpQ5XbK(_>*~ zW-~`4EtTkSV{Q)db`zD7sdP6^BVuf@c0J{GNAZk)=25D)qGgwfidpgO9Zm^iT9;?J z47kTNr0i&}9UCur)Y+~3*gGmrxtuskVLiN4&~X6mh^eJJoC#^Gd`g^Cr5`#5Uy6r{ z&e^ksORd{-Cg6&FVu(z|CQmv2Ka3rt9?XE z;7rsf4^MS+zWNWdczN?=H|uTo*=7hX{nl6ICfI7qqg>r33 zI|qSZRX)g)etnCvm49$#Sh$3-GU52Q)18ion@aoTdG59+h@@+8?uEUA-Vlhcvk*wG zQ_p$wo;W+{%#jOkzx(UySxy2R{24ex2m3VmLs+}_v7PhK<ys= zA!$k44lmhPYy&SR%Q5w%;Bs{*VLs2akWUE<>67PRR~wUIih zau}o_jLKVgS-;uV?8m!cHIs#O-&l&{CRf|YE6(Ec!{&P4+ETTt8z=5XuTSeGy@9@+ zu!VF^h`H24Iz#LhL<=Y>IJY1peuC$Ry}OXuF!OB({K3LlzF_~qrO>gELL^w*ZqHz8 zs5vNEP|7sX3=D}4W{YHy_aGfUE(@FL~xBG%`Kj-G}mZ0sD|a`*g{HQDT|_Yml9nyPJ5%Jyy$d7y>=B7H^#RJS-hjFEqTS-que~MiLBM zS{Ty6FsioAefF=PZXeLD5hBHWlyr0vO!YnhN>lg531;7^c^4{t>+)Ok98BUzx-pqH zjoM5au`GV{V&{~njn$qgw%W8MS1iZ1M4V-_b%qw($~jKxn4@J~qUPBzkD4(VtMVwL z2GNy@`#woGc9s?HEAROMJ$q^TG#fg-+}wpUl^d(>3EoavEFYH$6=oui8P^E@a%q!2{o723 zNiek(*npgHEaz}$eR(Ek%y$|lnVWOYj$Pf=kmCUd_g3W{9{04B2aavh{B4*S&0WVg zBxR}EiL-W$zFU5>GQK^PDGLxVOv9&^)iSbdAvCI={iFq9yC~Y4i zFzr~JP=0T$xgmGLE|O~JqondxpD6{&x{n=W_Ew|4>mD<9o~KmCS;s;+5MAePJMh*W z)tzmI7QYG!+*=!sm(b`=8vm0c;gPs>#GiG4yA>DMcRc24?C8?ZybF?}mHX5-$!9vI&hbo^8o9Ws9)25N&p)WLo zmSnkYFVX<10nYpNg-FMJ6$TMA5bV6EeI&h->kDu;dKp-z-G5XWo4X*uyCE|zhlDd^ z#0QDs2!viB9gA7YnX=Ji-o}oW{W06^0dCw3B1bUv+>teQJ;3csRx~dQJsOjwEhi-R zAf`+GYI$FBt=tu~m|+ACd1N<;Ocg(kFl`Y6d) z#;#drY4`Ms5a!XmbR?HqZr^cr9TD(*!L8%cfka}-5&6qeF1rxvx7hGvL(%(k!7cFG zCS9>esA_h70yO=JPj1Qam4$y&lMub}s!YFRQ1f)SeMz6uz&FGKp}O(+M*COBpDF)a zhU;~HaYTAglUKjKpL@ciq*|ZI>#>MkZnUXf1C`3$pE5S`rfNS}SmZL@Y5e}Dv02|% zypbMpa36NEJXa0FjRKuo{9WA{jCUH*2ZUF_h$T2fRdkeFpx-B1;x&GK$%;^Z(0*r2 zGC7UP2g34TGxU4SqqL202xkqGY)>okn8cZd@Vq>!SsM5Pj3KVTjb2gsY z`ogspN=dR3rfJzF1Y%T?nr)X}qhS`ai~m$J?yoR7M(8@^uQ;ew$+pJ8%u>D7a~cQL zn!7sF2$Y434nv-=IU*To3ahTp+yWDuyWT7LXXDI(v?o3G;Z&0MkFIh0tQ0*&fOpCG zyRY#lS=ZbqHv01?2>Z_(fgu^9tLMX*Mzp^pMO9j#Mxe^Myz{wLsZ6)3RF84PydzjS z&t|1<-Vu2;6qSv$GNsUZXIeZ;ebndx9gQ3m9CnN)>rf(8g#H#wLVH?xf1#VAoiJ- z0ebH43CH5$v^KyseC~km`6_jGWZDOllfYn-Ys0@K4i;FiTq=9fjrJMGcjoM(XRuf6 z@lkVBc}ps2L#bemj-q0;$wd!hl`we6TVe2%Pc1GANyD6E|E}6v)<2S`8N%;rQZPaL z7|03ttE*VYGk^N>*Z;=ntGH@))!g$04LQ^z1&O-6(uZ8ytz!PBb4m%3VO9RRZAuB2 z&HZEBxh=rdL#q5uCy)O&2x63yg+RZjMyluf+*UG7%;I2+B&wvwEZ-1)guEPn3WLpV z%$4=?G|VjRdWIR)QYo4fJ8b3cmRB>3xwGbH_AX!Etyki?`sf+)qzq;3*pnmo$;shjsLu7Umc)nQHZ>zB!m1XU``>@968VU>k@l>`MMX2oW?f4XZO}d z!pIPQZe@Ct?+<1C-$+jiJ^Br8cMa~KecNv~SK_gQi|?o=Rb6TBLVe)(UOG4D zt{rt;NfPoiL3r#~5Y9p+ssmqa$X3eI1EYsHJG4#D!Qr8ezeixrC&xiw+6$H)F+(ei zjWs;}10%2J=Dh4bKgV6B;xxr)NDw`K9Is|i$VM7+cYmNy4#{ucLq|3BE>l(`9-)*6 zg6GRsFU>1+5--}P^WlNjsXoheyJkO_n$9t;p+%&7XJx(>i5)D#?~nskLK#x5bWxfP zZDv%n?HASR4`dQF-H@eKqz9&(k@I(CZd`2d?VBbh(Y=*+-nyG>YPDtL+??3a?r(&& zua5fX;?N|};Ax|LOpW3YyaBZC;|!6t@4I(!K}l+|hP}i6CZ;R4O>FC<-rz)WeRs>6 zq|Lz$0&5krhW+TPCe_lrynwBp`}~o(8agt4avHR7{@f1Bt2% z^HZA1eAkoBNP;`~72Tv4;~}Xk;d1aWhVd1LTa?L?SatIQktY^;B_IZa1;lH14Nw`D z3w4$btsnO7vh|fl;!zer*Wz5Xb zY9G;Wk5)RbE@6 z0(v26E7TbrH`G^ymBGaM;*kksioTeu1L$CGj5g}5OXLK=Flb;o=JJcNZySJ;M_#Bi zMAPA4#1aFv?*(NIwyX(Fj0%{6I22-}ovs`UF_(jj-IKl{Hous(C&!a`@YAe=20<~G z^XJdMoz>xtBC!;hB|#z9DW=HU6+X6K()4456rwasDb_u~q<8-92Cy#s+KOCA%9%T|EIhr=@i~)Nt0s z6fIYEAl3%Zp{+x{*VFB%Y^XCZCbhZ-n>S}~Vum)DYx#@LOQryg0T4Bp>{^3$=Dwyv zoi#z<#bGWpW`6-RzcZu>phX%MeM78y%lu$#hG#A97yueFLj8 zm*a~%7|HGiQCEN1no&+m$M{k8-a!pE(D{Z0b=HYH2GAWdfk*B$Q2;A(z^IwNmw1G> zO#3o6Z14NP-XL<7ehWl7Rk85 z1>i!bz|~>!DkF*p!x`|!w;!Uyf_Ov2QjYtozPLaT+>S-PdU zzu=WO_?q`Yo~eDfJqC~H-->wr0APXrG)s-pr1!o6Dr@KL7l50wq07E(0V;40m@<5d z-PFF`ewLy}XhG;m8I`p!fl)>+4D0+U!nkZ1HLdwpgd#@1uQ}pE|NMQ;3o?qY%Yg%L zl=mziN?+^togt|dwCn~tq_vIUZrrX*w1O=e-@DF=ge9&}D3PduM!gR6i1|$Q!QyoCmDrsp|-}ax4yT zo|h0t9h~TX-~@~OGF%jD$%rU9-mh*=xG@4RbWSYO7)9?$h;N-C4`rjJy`+VaXFYBt zqBZ3S89v?<@!HV@yfa&&uLH0BM<%AsLR8_&zOkcN)K>oXr2Em=8ET&p(hm~_aR=>|##M3q7T8TGMAi2!@5Ej8OmX|n-2Gmt$wjZf|BFS##mTX+7PP82 z(~=4$t}@GCZx}W{6-$rkFkBvOwROcsbn_xLRv6;vl1_cSUl_}Y9wp!CV>(yIzB&{e zFW~Wf?Ppr-iRtwg?ztd(TVh^9jNiTp8$&TuNfO9}bB6DBvmx(yT#9)&m~;iW$*o0n zFH`H!i2OvA$TXdXHY7TA@2!W3L|@Dr9vW;Mhcp$p-(0@G;>DbViduz^5Z`Ro?hM@M zuPxX|-c;#@j!CO(RC)Krid)wbb+gGpC*}sQY!hjxFxtv$sTMJIt z{IaQ{-5c%!=5l{-_G8aX6mUc?OQHCAkf40MKW(jOsnqV+UhqnEPlCWAM`O(DhrnW$ zn^Y`peuQ-?AJ@}guv#^WFT2`HwRE5OdB<%9W-j5zP1onXR&?*DxJal=+=#rCaMtji zqW#=4)FC#`>vh#qcinr_{XIUuWEi#*s=k<7WUP_GwOf3B{U!(=Us-GV?CNL0hy7kX z)+^74AEi9ao)K)Bo9YQFF@DoyjbL4U!V~+)C81KLX1vvJ=#jE$^q?CJmutfM6q!N6 z17HUO>7I2O5+tF->XUzvO1n<+DUVG%Y#VK!6s|KoJaTxa*ds-|s{@&_h&8%5)a_?2O{;~Y2sSz-qZaLJSW&+=KUx5)-e&DAPgq2q3_*#%6HFG;{C|0 z<*W&B1R8{lg5Q^+K7YKUg8)a#Ql9JLi3nK&6pv|x=UATzQ`gzgu^4yRAndx-{qtL< zXeEDB$5xGRzJfJo^@Q!zD61lqc%QgrH-RZ$1=D|gm>@kdR40f#!GgpnKBHTmPp+;0 z#I5s3cRy)oI@hL&aOSCrZ5{7zQdP;%DCjK~q(IS|+OIFN+D>F_5a(M&4D!Ff4F3zj z&dqLjYM_GO3}gR5-&_0-^imwFK$lKKg5w##TKz_7vusSCwgE(x;7Y5~Z;yj?6<~Ry zpVCWOekeyyGSY6df#%F58hv-cZTUaQg!<_}GO{f{ z7TNr1mnKgB@B4eF(V>W&!X-LIvqI1h~1jZca z%5&WfKFK~^$A**(9mO;&p5z2Rx=xj5P5QnVhtV=kT8GdZFw_LM!oT32*Ic#E+<-UE zg=*|Q7{1_mYS11Gy*O%96Rm-m9({*UqsWer$!1On75d3`t?85D{ecr}1Gs-`NIe<)aAY;Nwbry-C~zx# zL|^2030&fEs_RNtq5?7E5#o6}tmmN+N9>!85DrsD!b42zA!L*xF8|DudZ%?&b3|l4wf& zr_bFtZH*z6K=aD)T^GVh3|`(pEd($3D&N!|Do#;kO;UmLp@-_q;df@JH7#BS*UpEo z?fnR&gm(%$eh>M%Ku~+ffOQ!;e-*#-KZuBz8Jzzg@NRYz2LMitVLi97y8k&+7oM51 z!-Hsu94D6~oes#9V6q#J$--KmCw(dfsZm;0JkqhPuNGX;zduO*u@>R|5azsdDZk&% ztPS0yNZ+K`zNGXvSG z?qQJ^J5)yZM03IVlmyzTd!oGnwv~fk>5^zmC~f7hjO&^g{3eIqZF|%{!hsF+6Tzn-M==8h-eh9ej>+eeC#F`lEx7}CxzY|u-Civ zD)wx!jor~AZ)sSTkKI(jzvusO-OKM);hWwTIJi~907l{g{jP+!2+-CeSesAu&Sv{f+o zL2B}T3p?*y+54GD#_dlT68rWfQKOb}-W){@AIBqny5!N8lBzq>YW74hRv$(LmO`?- zKu8{K01WwHG9X?fsW^8i-Kz#6w4jY29??)(oNKl_n(xg3qrpqV0K3}xJ0Uv!gjis7 zyYygtKD>wpLdY&DG>�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%0DMiW_sID>rxfgBmV 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 SHjou;K==$89Ugr^f&l=%R1S;) literal 0 HcmV?d00001 diff --git a/.nuget/packages/Openstack.net.1.8.0.nupkg b/.nuget/packages/Openstack.net.1.8.0.nupkg new file mode 100644 index 0000000000000000000000000000000000000000..70b055d1a026e2878c0f8a2acec0487e4ca5cb68 GIT binary patch literal 738345 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%0AZoDO2 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

;5XBPAlv)RKC97s7m#7^0UYtdO*#B>X)hhcxO9 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%0Drf%d+gb% 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

{_ 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

Yuutx%W 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%0AV-n}^Kf15;SVxn^&TZQX~u`_5%wZF;7Gn4<86J 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⪚f*p$M&wPO1UPN9~wm;?E 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�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

7ua;fcGYnOTVO^j%q5-)m7Do9|t? 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 literal 0 HcmV?d00001 diff --git a/.nuget/packages/twitterizer.2.4.2.2.nupkg b/.nuget/packages/twitterizer.2.4.2.2.nupkg deleted file mode 100644 index 1ccb92341a4630b25bf3fe16829bc3697c0e57dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 98550 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
    AJPvOIYia<_S#{tZDcs+=}el(zB2jV0+ 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�d#xjF5Ap6n1omAU-)w+_Nn3XOEqP4)N*6t- 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

    e`O=NFY~DpFRfCZ)Kf4fLriv0B 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

    Y-60oVd<>jw4yoT4~xy~rZj?2wF#710356IZfX z@q&^ANn6LAkNG4%(H&hReV4?t6Nh@|bF8L^GF1E4hS$G0`h=B&?b<-2kNe@j8+|Dg zJ9|1&1N*n{f2qGJ|1W>|miGrl`%Gh02#R72RuHfhi3OG-^#Rn=A+50ov2U>y+VlT| z`_d<0a*H609o|Y|3hKjpyK332sch`oId8Voe}B-U30>g=zs}Vs&tOmh5Gc_qO6vPE zWL(jgrZMx!aT6p;x!>1&oj&~5_JXugDsG!7dGEVh84Uq?(IOTA!VBpzM5u-)!64WE z4P-=*Oui=YEJHqwc;Rb>a-?hWo46|zaQyYrv{}yk5ome6wj$!7F~=cGCwQxZOwPIz$a zdwDU-GY3Y(NuR{?&tC3FdH#WGJuUqbhyy`NRnQKd+!><1YNVIW@0Y@B1tY;s`v$`* z!X9Ci3obcH5_R=@N5!Q5WjwxsxQl+&Ir2`jgq|_s<2U&9iHDBNd};guQBMDEIa`M6PLXlp1;8VU`V0e|J~jsOTBYt_BQ<>vJvKu5 z1Yj0l<>y3Gfh$*T2F&7P(#bo3S^TO+{V;xUH$}5riX7ur3G*goEa)l zl0)_s-&%dCkAGWza$aGvn-4Dh0^+NX*l@oNgCTZ+U7)Cg;2k@W($LHaf~Y_&~~0_(oO*_$eEUSd3Hd1;- zJwR%a4MWb0?aR0LLux@|0nls93cqs9m#&roB7~QJA$gAHr%LPiBdh zG*8y^NY&wQHW-<&A9IxJ_9gw$SCsvnnw9AaAiI3mT$6Y-qnl$N5 zgH{$vfXJ~EJ2_M9Y&3EZ$q#$iVq;IfSYw!1wAT0$QBXZiioSe6_WP{*67rD&Ll6eT zEd!Q^hgikjWKGH5cL(KK?I{>4Q-℞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 diff --git a/3rd-Party.txt b/3rd-Party.txt index 19fc904d4..a599e205f 100644 --- a/3rd-Party.txt +++ b/3rd-Party.txt @@ -4,11 +4,7 @@ Community Server uses code from the following 3rd party projects: nuget packages ==================================================================================================== -AjaxMin - JavaScript and CSS minification Library for use in .NET applications that want to provide minification or parsing functionality. (http://www.apache.org/licenses/LICENSE-2.0) -License: Apache License 2.0 -License File: AjaxMin.license - -AjaxPro.2 - .NET Library that provides AJAX related methods to simplify the communication between server and client. (https://github.com/michaelschwarz/Ajax.NET-Professional/blob/master/LICENS) +AjaxPro.2 - .NET Library that provides AJAX related methods to simplify the communication between server and client. (https://github.com/michaelschwarz/Ajax.NET-Professional/blob/master/LICENSE) License: MIT License License File: AjaxPro.2.license @@ -108,10 +104,6 @@ DotNetOpenAuth.Ultimate - A single assembly that adds OpenID 1.1/2.0, OAuth 1.0( License: MS-PL License File: DotNetOpenAuth.Ultimate.license -DotNetZip - A fork of the DotNetZip project without signing with a solution that compiles cleanly. This project aims to follow semver to avoid versioning conflicts. DotNetZip is a FAST, FREE class library and toolset for manipulating zip files. Use VB, C# or any .NET language to easily create, extract, or update zip files. (https://raw.githubusercontent.com/haf/DotNetZip.Semverd/master/LICENSE) -License: Ms-PL , BSD-style (3 clause), Apache 2 license ??? -License File: DotNetZip.license - Dropbox.Api - Portable class library for accessing the Dropbox v2 API (https://github.com/dropbox/dropbox-sdk-dotnet/blob/master/LICENSE) License: MIT License License File: Dropbox.Api.license @@ -132,6 +124,10 @@ Flurl.Signed - A fluent, portable URL builder. To make HTTP calls off the fluent License: MIT License License File: Flurl.Signed.license +FolkerKinzel.VCards -.NET library to read, write and convert VCF files that match the vCard standards 2.1, 3.0 and 4.0. (https://github.com/FolkerKinzel/VCards/blob/master/LICENSE) +License: MIT License +License File: FolkerKinzel.VCards.license + FredCK.FCKeditorV2 - FCKeditor is a text editor to be used inside web pages. In 2009 it was renamed CKEditor. (http://ckeditor.com/what-is-ckeditor) (https://download.cksource.com/FCKeditor/FCKeditor/2.6.3/) License: GPL 2 (or later) or LGPL 2.1 (or later) or MPL 1.1 (or later) License File: FredCK.FCKeditorV2.license @@ -340,6 +336,10 @@ Microsoft.IdentityModel.Tokens - Includes types that provide support for Securit License: MIT License License File: Microsoft.IdentityModel.Tokens.license +Microsoft.IdentityModel.Tokens.Jwt - Includes types that provide support for creating, serializing and validating JSON Web Tokens. (https://licenses.nuget.org/MIT) +License: MIT License +License File: Microsoft.IdentityModel.Tokens.Jwt.license + Microsoft.Net.Http - This package includes HttpClient for sending requests over HTTP, as well as HttpRequestMessage and HttpResponseMessage for processing HTTP messages. (https://dotnet.microsoft.com/en/dotnet_library_license.htm) License: MICROSOFT .NET LIBRARY LICENSE License File: Microsoft.Net.Http.license @@ -424,6 +424,10 @@ Novell.Directory.LDAP - LDAP client library (https://github.com/dsbenghe/Novell. License: MIT License License File: Novell.Directory.LDAP.license +NUglify - NUglify provides minify and compression methods for CSS, JavaScript and HTML files. (https://github.com/trullock/NUglify/blob/master/license.txt) +License: BSD-2-Clause License +License File: NUglify.license + NUnit - NUnit features a fluent assert syntax, parameterized, generic and theory tests and is user-extensible. (https://github.com/nunit/nunit/blob/master/LICENSE.txt) License: MIT License License File: NUnit.license @@ -776,6 +780,10 @@ npm packages License: MIT License License File: @authenio-samlify-node-xmllint.license +axios - Promise based HTTP client for the browser and node.js (https://github.com/axios/axios/blob/master/LICENSE) +License: MIT License +License File: axios.license + body-parser - Node.js body parsing middleware. (https://github.com/expressjs/body-parser/blob/master/LICENSE) License: MIT License License File: body-parser.license @@ -828,10 +836,18 @@ filenamify-url - Convert a URL to a valid filename (https://github.com/sindresor License: MIT License License File: filenamify-url.license +form-data - A library to create readable "multipart/form-data" streams. Can be used to submit forms and file uploads to other web applications.The API of this library is inspired by the XMLHttpRequest-2 FormData Interface. (https://github.com/form-data/form-data/blob/master/License) +License: MIT License +License File: form-data.license + formidable - A Node.js module for parsing form data, especially file uploads. (https://github.com/node-formidable/formidable/blob/master/LICENSE) License: MIT License License File: formidable.license +get-byte - A Universal way of getting a Byte at a Given Index from a variety of in-memory data formats, including Uint8Array, Buffer, DataView, and Array Buffer (https://github.com/DanielJDufour/get-byte/blob/main/LICENSE) +License: CC0-1.0 License +License File: get-byte.license + graceful-fs - graceful-fs functions as a drop-in replacement for the fs module, making various improvements. (https://github.com/isaacs/node-graceful-fs) License: ISC License License File: graceful-fs.license @@ -848,6 +864,14 @@ gulp-just-replace - The gulp-replace is fine. But it's painful to install it o License: MIT License License File: gulp-just-replace.license +help - substack way of --help. v3 of help requires at least Node.js v4. To use help with an older version, please use help@2.x. (https://github.com/evanlucas/help/blob/master/LICENSE) +License: MIT License +License File: help.license + +ipaddr - an IPv6 and IPv4 address manipulation library. (https://github.com/whitequark/ipaddr.js/blob/master/LICENSE) +License: MIT License +License File: ipaddr.license + lodash - The modern build of lodash exported as Node.js/io.js modules. (https://github.com/lodash/lodash/blob/master/LICENSE) License: MIT License License File: lodash.license @@ -928,6 +952,10 @@ tmp - A simple temporary file and directory creator for node.js. (https://githu License: MIT License License File: tmp.license +to-byte-array - Convert a string or buffer into a plain array of bytes. (https://github.com/finwo/to-byte-array/blob/master/LICENSE.md) +License: MIT License +License File: to-byte-array.license + ua-parser-js - A JavaScript-based User-Agent string parser. Can be used either in browser (client-side) or in node.js (server-side) environment. Also available as jQuery/Zepto plugin, Bower/Meteor package, & RequireJS/AMD module.  (https://github.com/faisalman/ua-parser-js/blob/master/license.md) License: MIT License License File: ua-parser-js.license @@ -936,6 +964,10 @@ url - This module has utilities for URL resolution and parsing meant to have fea License: MIT License License File: url.license +webdav-server - WebDav server. (https://github.com/OpenMarshal/npm-WebDAV-Server/blob/master/LICENSE) +License: Unlicense License +License File: webdav-server.license + winston - A logger for just about everything. (https://github.com/winstonjs/winston/blob/master/LICENSE) License: MIT License License File: winston.license @@ -1132,11 +1164,15 @@ jstree.min.js - jsTree is jquery plugin, that provides interactive trees (https: License: MIT License License File: jstree.license -linkify.min.js - Linkify is a zero-dependency JavaScript plugin for finding links in plain-text and converting them to HTML tags (https://github.com/Soapbox/linkifyjs/blob/master/LICENSE) +linkify.min.js - Linkify is a JavaScript plugin for finding links in plain-text and converting them to HTML tags. (https://github.com/Soapbox/linkifyjs/blob/master/LICENSE) License: MIT License License File: linkify.license -linkify-string.min.js - Linkify is a zero-dependency JavaScript plugin for finding links in plain-text and converting them to HTML tags (https://github.com/Soapbox/linkifyjs/blob/master/LICENSE) +linkify-html.min.js - Linkify is a JavaScript plugin for finding links in plain-text and converting them to HTML tags. (https://github.com/Soapbox/linkifyjs/blob/master/LICENSE) +License: MIT License +License File: linkify-html.license + +linkify-string.min.js - Linkify is a JavaScript plugin for finding links in plain-text and converting them to HTML tags. (https://github.com/Soapbox/linkifyjs/blob/master/LICENSE) License: MIT License License File: linkify-string.license diff --git a/README.md b/README.md index de6d79dc4..85e26bc06 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@

    http://www.apache.org/licenses/LICENSE-2.0 - https://github.com/ONLYOFFICE/portals/releases + https://github.com/ONLYOFFICE/portals/releases

    ## Overview diff --git a/build/Build.bat b/build/Build.bat index f66a23d3c..16cdc10a8 100644 --- a/build/Build.bat +++ b/build/Build.bat @@ -1,3 +1,3 @@ set Constants=DEBUGINFO -"%ProgramFiles(x86)%\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\MSBuild.exe" msbuild\build.proj /fl1 /flp1:LogFile=Build.log;Verbosity=Normal /m -tv:Current -v:m +"%ProgramFiles%\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe" msbuild\build.proj /fl1 /flp1:LogFile=Build.log;Verbosity=Normal /m -tv:Current -v:m pause \ No newline at end of file diff --git a/build/BuildAndDeploy.bat b/build/BuildAndDeploy.bat index 55faf38e3..538018370 100644 --- a/build/BuildAndDeploy.bat +++ b/build/BuildAndDeploy.bat @@ -1,7 +1,7 @@ set Cultures=fr,de,es,ru,lv,pt-BR,pt,it,tr,el,zh-CN,pl,cs,uk,vi,fi,az-Latn-AZ,ko,ja,sl,sk,nl,bg - -set BuildTargets=ReBuild -"%ProgramFiles(x86)%\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\MSBuild.exe" msbuild\build.proj /fl1 /flp1:LogFile=Build.log;Verbosity=Normal /m -v:m -set BuildTargets=Build -if %errorlevel% == 0 "%ProgramFiles(x86)%\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\MSBuild.exe" msbuild\deploy.proj /fl1 /flp1:LogFile=Deploy.log;Verbosity=Normal /m -v:m +set YarnBuild=false +"%ProgramFiles%\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe" msbuild\build.proj /fl1 /flp1:LogFile=Build.log;Verbosity=Normal /m -v:m +set BuildTargets=PrepareResourceNames;ResolveAssemblyReferences;CopyFilesToOutputDirectory +set ProjectReferenceBuildTargets=PrepareResourceNames;ResolveAssemblyReferences;CopyFilesToOutputDirectory +if %errorlevel% == 0 "%ProgramFiles%\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe" msbuild\deploy.proj /fl1 /flp1:LogFile=Deploy.log;Verbosity=Normal /m -v:m pause diff --git a/build/ManageServices.bat b/build/ManageServices.bat index 05247533d..0c3874e17 100644 --- a/build/ManageServices.bat +++ b/build/ManageServices.bat @@ -21,6 +21,7 @@ if "%~1" == "--install-all" ( sc create OnlyOfficeStorageMigrate%version% start= delayed-auto binPath= "\"%basepath%\TeamLabSvc.exe\" --service \"ASC.Data.Storage.Migration.Launcher,ASC.Data.Storage.Migration\" --log StorageMigrate" sc create OnlyOfficeStorageEncryption%version% start= delayed-auto binPath= "\"%basepath%\TeamLabSvc.exe\" --service \"ASC.Data.Storage.Encryption.Launcher,ASC.Data.Storage.Encryption\" --log StorageEncryption" sc create OnlyofficeFeed%version% start= delayed-auto binPath= "\"%basepath%\TeamLabSvc.exe\" --service \"ASC.Feed.Aggregator.FeedAggregatorLauncher, ASC.Feed.Aggregator\" --log Feed" + sc create OnlyOfficeAutoCleanUp%version% start= delayed-auto binPath= "\"%basepath%\TeamLabSvc.exe\" --service \"ASC.Files.AutoCleanUp.Launcher, ASC.Files.AutoCleanUp\" --log AutoCleanUp" sc create OnlyofficeBackup%version% start= delayed-auto binPath= "\"%basepath%\TeamLabSvc.exe\" --service \"ASC.Data.Backup.Service.BackupServiceLauncher, ASC.Data.Backup\" --log Backup" sc create OnlyOfficeSocketIO%version% start= delayed-auto binPath= "\"%basepath%\TeamLabSvc.exe\" --service \"ASC.Socket.IO.Svc.Launcher, ASC.Socket.IO.Svc\" --log SocketIO" sc create OnlyOfficeTelegram%version% start= delayed-auto binPath= "\"%basepath%\TeamLabSvc.exe\" --service \"ASC.TelegramService.Launcher, ASC.TelegramService\" --log Telegram" @@ -28,6 +29,7 @@ if "%~1" == "--install-all" ( sc create OnlyOfficeThumb%version% start= delayed-auto binPath= "\"%basepath%\TeamLabSvc.exe\" --service \"ASC.Thumbnails.Svc.Launcher,ASC.Thumbnails.Svc\" --log Thumb" sc create OnlyOfficeSsoAuth%version% start= delayed-auto binPath= "\"%basepath%\TeamLabSvc.exe\" --service \"ASC.SsoAuth.Svc.Launcher,ASC.SsoAuth.Svc\" --log SsoAuth" sc create OnlyOfficeUrlShortener%version% start= delayed-auto binPath= "\"%basepath%\TeamLabSvc.exe\" --service \"ASC.UrlShortener.Svc.Launcher,ASC.UrlShortener.Svc\" --log UrlShortener" + sc create OnlyOfficeWebDav%version% start= delayed-auto binPath= "\"%basepath%\TeamLabSvc.exe\" --service \"ASC.WebDav.Svc.Launcher,ASC.WebDav.Svc\" --log WebDav" sc create OnlyOfficeMailAggregator%version% start= delayed-auto binPath= "\"%grandparent%\MailAggregator\ASC.Mail.Aggregator.CollectionService.exe\"" sc create OnlyOfficeMailWatchdog%version% start= delayed-auto binPath= "\"%grandparent%\MailWatchdog\ASC.Mail.Watchdog.Service.exe\"" sc create OnlyOfficeMailCleaner%version% start= delayed-auto binPath= "\"%grandparent%\MailCleaner\ASC.Mail.StorageCleaner.exe\"" @@ -38,6 +40,7 @@ if "%~1" == "--install-all" ( sc failure OnlyofficeRadicale%version% reset= 60 actions= restart/60000/restart/60000/restart/60000 sc failure OnlyOfficeStorageMigrate%version% reset= 60 actions= restart/60000/restart/60000/restart/60000 sc failure OnlyOfficeStorageEncryption%version% reset= 60 actions= restart/60000/restart/60000/restart/60000 + sc failure OnlyofficeAutoCleanUp%version% reset= 60 actions= restart/60000/restart/60000/restart/60000 sc failure OnlyofficeFeed%version% reset= 60 actions= restart/60000/restart/60000/restart/60000 sc failure OnlyofficeBackup%version% reset= 60 actions= restart/60000/restart/60000/restart/60000 sc failure OnlyOfficeSocketIO%version% reset= 60 actions= restart/60000/restart/60000/restart/60000 @@ -46,6 +49,7 @@ if "%~1" == "--install-all" ( sc failure OnlyOfficeThumb%version% reset= 60 actions= restart/60000/restart/60000/restart/60000 sc failure OnlyOfficeSsoAuth%version% reset= 60 actions= restart/60000/restart/60000/restart/60000 sc failure OnlyOfficeUrlShortener%version% reset= 60 actions= restart/60000/restart/60000/restart/60000 + sc failure OnlyOfficeWebDav%version% reset= 60 actions= restart/60000/restart/60000/restart/60000 sc failure OnlyOfficeMailAggregator%version% reset= 60 actions= restart/60000/restart/60000/restart/60000 sc failure OnlyOfficeMailWatchdog%version% reset= 60 actions= restart/60000/restart/60000/restart/60000 sc failure OnlyOfficeMailCleaner%version% reset= 60 actions= restart/60000/restart/60000/restart/60000 @@ -71,6 +75,8 @@ if "%~1" == "--uninstall-all" ( sc delete OnlyOfficeStorageMigrate%version% net stop OnlyOfficeStorageEncryption%version% sc delete OnlyOfficeStorageEncryption%version% + net stop OnlyofficeAutoCleanUp%version% + sc delete OnlyofficeAutoCleanUp%version% net stop OnlyofficeFeed%version% sc delete OnlyofficeFeed%version% net stop OnlyofficeBackup%version% @@ -85,6 +91,8 @@ if "%~1" == "--uninstall-all" ( sc delete OnlyOfficeSsoAuth%version% net stop OnlyOfficeUrlShortener%version% sc delete OnlyOfficeUrlShortener%version% + net stop OnlyOfficeWebDav%version% + sc delete OnlyOfficeWebDav%version% net stop OnlyofficeThumbnailBuilder%version% sc delete OnlyofficeThumbnailBuilder%version% net stop OnlyOfficeMailAggregator%version% diff --git a/build/config/TeamLabSvc.exe.config b/build/config/TeamLabSvc.exe.config index cba73b363..4715eebfc 100644 --- a/build/config/TeamLabSvc.exe.config +++ b/build/config/TeamLabSvc.exe.config @@ -7,124 +7,115 @@
    -
    +
    +
    - - + + - - - - + + - - - - - - - - - - - - - - - + - - - + - - - + + + + + + + + + - - - + - - - - - - - - - + + + + + + + + + + + + + - - - - - + - - - - - - - - - - - - - - - - + + + + + + + + - - + + + + + + + + - + + + + + + + + + - - @@ -140,6 +131,7 @@ + @@ -152,6 +144,8 @@ + + @@ -167,22 +161,23 @@ - + + - + @@ -190,7 +185,7 @@ - + @@ -199,11 +194,12 @@ - + + @@ -407,18 +403,15 @@ - - - - + - - - + + + @@ -428,7 +421,7 @@ - + diff --git a/build/install/deb/Files/Tools/default-onlyoffice-ssl.sh b/build/install/deb/Files/Tools/default-onlyoffice-ssl.sh index 688016aa3..dea629e24 100644 --- a/build/install/deb/Files/Tools/default-onlyoffice-ssl.sh +++ b/build/install/deb/Files/Tools/default-onlyoffice-ssl.sh @@ -3,7 +3,7 @@ ROOT_DIR="/var/www/onlyoffice/Data/certs"; NGINX_CONF_DIR="/etc/nginx/sites-enabled"; NGINX_ROOT_DIR="/etc/nginx"; -APP_SERVICES_ROOT_DIR="/var/www/onlyoffice/Services" +APP_CONFIG_DIR="/etc/onlyoffice/communityserver" SSL_OCSP_CERTIFICATE_PATH="${ROOT_DIR}/stapling.trusted.crt"; SSL_CERTIFICATE_PATH="${ROOT_DIR}/onlyoffice.crt"; SSL_KEY_PATH="${ROOT_DIR}/onlyoffice.key"; @@ -54,7 +54,7 @@ else sed '/resolver_timeout/d' -i default-onlyoffice-ssl.conf; fi -sed '/mail\.default-api-scheme/s/\(value\s*=\s*\"\).*\"/\1https\"/' -i ${APP_SERVICES_ROOT_DIR}/MailAggregator/ASC.Mail.Aggregator.CollectionService.exe.config; +sed 's_\(\"DefaultApiSchema":\).*,_\1 "https",_' -i ${APP_CONFIG_DIR}/mail.production.json mv -f default-onlyoffice-ssl.conf ${NGINX_CONF_DIR}/onlyoffice diff --git a/build/install/deb/Files/Tools/default-onlyoffice.sh b/build/install/deb/Files/Tools/default-onlyoffice.sh index 0314417c4..7295092eb 100644 --- a/build/install/deb/Files/Tools/default-onlyoffice.sh +++ b/build/install/deb/Files/Tools/default-onlyoffice.sh @@ -1,13 +1,14 @@ #!/bin/bash APP_SERVICES_ROOT_DIR="/var/www/onlyoffice/Services" +APP_CONFIG_DIR="/etc/onlyoffice/communityserver" NGINX_ROOT_DIR="/etc/nginx"; NGINX_CONF_DIR="/etc/nginx/sites-enabled"; cp $NGINX_ROOT_DIR/includes/onlyoffice-communityserver-common.conf.template default-onlyoffice.conf; sed 's/{{APP_NIGNX_KEEPLIVE}}/64/g' -i default-onlyoffice.conf; -sed '/mail\.default-api-scheme/s/\(value\s*=\s*\"\).*\"/\1http\"/' -i ${APP_SERVICES_ROOT_DIR}/MailAggregator/ASC.Mail.Aggregator.CollectionService.exe.config; +sed 's_\(\"DefaultApiSchema":\).*,_\1 "http",_' -i ${APP_CONFIG_DIR}/mail.production.json mv -f default-onlyoffice.conf ${NGINX_CONF_DIR}/onlyoffice diff --git a/build/install/deb/Files/nginx/includes/onlyoffice-communityserver-services.conf b/build/install/deb/Files/nginx/includes/onlyoffice-communityserver-services.conf index 408e1d7b9..48604ac58 100644 --- a/build/install/deb/Files/nginx/includes/onlyoffice-communityserver-services.conf +++ b/build/install/deb/Files/nginx/includes/onlyoffice-communityserver-services.conf @@ -44,11 +44,25 @@ location /sh { location /caldav/ { proxy_pass http://localhost:5232/; - proxy_set_header X-Script-Name /caldav; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Script-Name /caldav; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-REWRITER-URL $X_REWRITER_URL; } +location /carddav/ { + proxy_pass http://localhost:5232/; + proxy_set_header X-Script-Name /carddav; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-REWRITER-URL $X_REWRITER_URL; +} + +location /webdav/ { + proxy_pass http://localhost:9889/; + proxy_set_header X-Script-Name /webdav; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-REWRITER-URL $X_REWRITER_URL; +} + location /sso/ { proxy_pass http://localhost:9834/; diff --git a/build/install/deb/debian/monoserveApiSystem.service b/build/install/deb/debian/monoserveApiSystem.service index 4607b7ba8..b9f34575c 100644 --- a/build/install/deb/debian/monoserveApiSystem.service +++ b/build/install/deb/debian/monoserveApiSystem.service @@ -5,7 +5,7 @@ Wants=nginx.service mysqld.service redis-server.service [Service] PermissionsStartOnly=true -ExecStartPre=/bin/bash -c 'test -e /var/run/onlyoffice || install -m 755 -o onlyoffice -g {{NGINX_USER}} -d /var/run/onlyoffice; rm -f /var/run/onlyoffice/onlyofficeApiSystem.socket /var/www/onlyoffice/ApiSystem/mono_crash* /var/www/onlyoffice/ApiSystem/core.*;' +ExecStartPre=/bin/bash -c 'test -e /var/run/onlyoffice || install -m 755 -o onlyoffice -g nginx -d /var/run/onlyoffice; rm -f /var/run/onlyoffice/onlyofficeApiSystem.socket /var/www/onlyoffice/ApiSystem/mono_crash* /var/www/onlyoffice/ApiSystem/core.*;' ExecStart=/usr/bin/hyperfastcgi4 /config=/etc/hyperfastcgi/onlyofficeApiSystem /logfile=/var/log/onlyoffice/onlyofficeApiSystem.log /loglevels=Error Environment=LD_LIBRARY_PATH=/usr/lib MONO_PATH=/var/www/onlyoffice/ApiSystem/bin/ MONO_IOMAP=all MONO_ASPNET_WEBCONFIG_CACHESIZE=2000 MONO_THREADS_PER_CPU=2000 MONO_GC_PARAMS=nursery-size=128m,soft-heap-limit=512m,bridge-implementation=tarjan MONO_LOG_LEVEL=error MONO_THREADS_SUSPEND=preemptive diff --git a/build/install/deb/debian/onlyoffice-communityserver.install b/build/install/deb/debian/onlyoffice-communityserver.install index 0d147b98c..b026d98ea 100644 --- a/build/install/deb/debian/onlyoffice-communityserver.install +++ b/build/install/deb/debian/onlyoffice-communityserver.install @@ -8,3 +8,4 @@ Files/god/* etc/god/ Files/Tools/* var/www/{{package_sysname}}/Tools/ Files/licenses/* usr/share/doc/{{package_sysname}}-communityserver/licenses/ Files/ApiSystem/* var/www/{{package_sysname}}/ApiSystem/ +Files/mail-config/* etc/{{package_sysname}}/communityserver/ diff --git a/build/install/deb/debian/onlyofficeAutoCleanUp.service b/build/install/deb/debian/onlyofficeAutoCleanUp.service new file mode 100644 index 000000000..9ebdb16fb --- /dev/null +++ b/build/install/deb/debian/onlyofficeAutoCleanUp.service @@ -0,0 +1,20 @@ +[Unit] +Description=ONLYOFFICE AutoCleanUp Service +After=network.target syslog.target mysqld.service +Wants=mysqld.service + +[Service] +Type=forking +ExecStartPre=/bin/bash -c 'rm -f /tmp/onlyofficeAutoCleanUp' +ExecStart=/usr/bin/mono-service -d:/var/www/onlyoffice/Services/TeamLabSvc -l:/tmp/onlyofficeAutoCleanUp /var/www/onlyoffice/Services/TeamLabSvc/TeamLabSvc.exe --service \"ASC.Files.AutoCleanUp.Launcher,ASC.Files.AutoCleanUp\" --log AutoCleanUp +Environment=MONO_IOMAP=all MONO_PATH=/var/www/onlyoffice/Services/TeamLabSvc/ +User=onlyoffice +Group=onlyoffice +# Give up if ping don't get an answer +TimeoutSec=600 + +Restart=no +PrivateTmp=false + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/build/install/deb/debian/onlyofficeMailAggregator.service b/build/install/deb/debian/onlyofficeMailAggregator.service index caf1feab6..39e478e01 100644 --- a/build/install/deb/debian/onlyofficeMailAggregator.service +++ b/build/install/deb/debian/onlyofficeMailAggregator.service @@ -1,21 +1,19 @@ [Unit] Description=ONLYOFFICE MailAggregator Service -After=network.target syslog.target mysqld.service redis-server.service -Wants=mysqld.service redis-server.service +After=network.target syslog.target mysqld.service +Wants=mysqld.service [Service] -Type=forking -ExecStartPre=/bin/bash -c 'rm -f /tmp/onlyofficeMailAggregator' -ExecStart=/usr/bin/mono-service -d:/var/www/onlyoffice/Services/MailAggregator -l:/tmp/onlyofficeMailAggregator /var/www/onlyoffice/Services/MailAggregator/ASC.Mail.Aggregator.CollectionService.exe -Environment=MONO_IOMAP=all MONO_PATH=/var/www/onlyoffice/Services/MailAggregator/ +Type=notify User=onlyoffice Group=onlyoffice - -# Give up if ping don't get an answer +WorkingDirectory=/var/www/onlyoffice/Services/MailAggregator +ExecStart=/usr/share/dotnet/dotnet /var/www/onlyoffice/Services/MailAggregator/ASC.Mail.Aggregator.Service.dll --urls=http://0.0.0.0:5025 --pathToConf=/etc/onlyoffice/communityserver --pathToNlogConf=/etc/onlyoffice/communityserver --'$STORAGE_ROOT'=/var/www/onlyoffice/Data --log:dir=/var/log/onlyoffice/mail --log:name=mail-aggregator --ENVIRONMENT=production +Environment=OPENSSL_CONF=/etc/onlyoffice/communityserver/openssl.cnf TimeoutSec=600 Restart=no PrivateTmp=false [Install] -WantedBy=multi-user.target \ No newline at end of file +WantedBy=multi-user.target diff --git a/build/install/deb/debian/onlyofficeMailCleaner.service b/build/install/deb/debian/onlyofficeMailCleaner.service index 51e16648f..bd00f9db0 100644 --- a/build/install/deb/debian/onlyofficeMailCleaner.service +++ b/build/install/deb/debian/onlyofficeMailCleaner.service @@ -1,21 +1,18 @@ [Unit] Description=ONLYOFFICE MailCleaner Service -After=network.target syslog.target mysqld.service redis-server.service -Wants=mysqld.service redis-server.service +After=network.target syslog.target mysqld.service +Wants=mysqld.service [Service] -Type=forking -ExecStartPre=/bin/bash -c 'rm -f /tmp/MailCleaner' -ExecStart=/usr/bin/mono-service -d:/var/www/onlyoffice/Services/MailCleaner -l:/tmp/MailCleaner /var/www/onlyoffice/Services/MailCleaner/ASC.Mail.StorageCleaner.exe -Environment=MONO_IOMAP=all MONO_PATH=/var/www/onlyoffice/Services/MailCleaner/ +Type=notify User=onlyoffice Group=onlyoffice - -# Give up if ping don't get an answer +WorkingDirectory=/var/www/onlyoffice/Services/MailCleaner/ +ExecStart=/usr/share/dotnet/dotnet /var/www/onlyoffice/Services/MailCleaner/ASC.Mail.StorageCleaner.Service.dll --urls=http://0.0.0.0:5032 --pathToConf=/etc/onlyoffice/communityserver --pathToNlogConf=/etc/onlyoffice/communityserver --'$STORAGE_ROOT'=/var/www/onlyoffice/Data --log:dir=/var/log/onlyoffice/mail --log:name=mail-storagecleaner --ENVIRONMENT=production TimeoutSec=600 Restart=no PrivateTmp=false [Install] -WantedBy=multi-user.target \ No newline at end of file +WantedBy=multi-user.target diff --git a/build/install/deb/debian/onlyofficeMailImap.service b/build/install/deb/debian/onlyofficeMailImap.service new file mode 100644 index 000000000..fe54df5bc --- /dev/null +++ b/build/install/deb/debian/onlyofficeMailImap.service @@ -0,0 +1,20 @@ +[Unit] +Description=ONLYOFFICE MailImap Service +After=network.target syslog.target mysqld.service redis-server.service +Wants=mysqld.service redis-server.service + +[Service] +Type=notify +User=onlyoffice +Group=onlyoffice +WorkingDirectory=/var/www/onlyoffice/Services/MailImap/ +ExecStart=/usr/share/dotnet/dotnet /var/www/onlyoffice/Services/MailImap/ASC.Mail.ImapSync.dll --urls=http://0.0.0.0:5026 --pathToConf=/etc/onlyoffice/communityserver --pathToNlogConf=/etc/onlyoffice/communityserver --'$STORAGE_ROOT'=/var/www/onlyoffice/Data --log:dir=/var/log/onlyoffice/mail --log:name=mail-imapsync --ENVIRONMENT=production +Environment=OPENSSL_CONF=/etc/onlyoffice/communityserver/openssl.cnf +TimeoutSec=600 + +Restart=no +PrivateTmp=false + +[Install] +WantedBy=multi-user.target + diff --git a/build/install/deb/debian/onlyofficeMailWatchdog.service b/build/install/deb/debian/onlyofficeMailWatchdog.service index 75260e7b9..e93dd55b5 100644 --- a/build/install/deb/debian/onlyofficeMailWatchdog.service +++ b/build/install/deb/debian/onlyofficeMailWatchdog.service @@ -1,20 +1,18 @@ [Unit] -Description=ONLYOFFICE MailWatchdog Service -After=network.target syslog.target mysqld.service redis-server.service -Wants=mysqld.service redis-server.service +Description=ONLYOFFICE MailImap Service +After=network.target syslog.target mysqld.service +Wants=mysqld.service [Service] -Type=forking -ExecStartPre=/bin/bash -c 'rm -f /tmp/onlyofficeMailWatchdog' -ExecStart=/usr/bin/mono-service -d:/var/www/onlyoffice/Services/MailWatchdog -l:/tmp/onlyofficeMailWatchdog /var/www/onlyoffice/Services/MailWatchdog/ASC.Mail.Watchdog.Service.exe -Environment=MONO_IOMAP=all MONO_PATH=/var/www/onlyoffice/Services/MailWatchdog/ +Type=notify User=onlyoffice Group=onlyoffice -# Give up if ping don't get an answer +WorkingDirectory=/var/www/onlyoffice/Services/MailWatchdog/ +ExecStart=/usr/share/dotnet/dotnet /var/www/onlyoffice/Services/MailWatchdog/ASC.Mail.Watchdog.Service.dll --urls=http://0.0.0.0:5031 --pathToConf=/etc/onlyoffice/communityserver --pathToNlogConf=/etc/onlyoffice/communityserver --'$STORAGE_ROOT'=/var/www/onlyoffice/Data --log:dir=/var/log/onlyoffice/mail --log:name=mail-watchdog --ENVIRONMENT=production TimeoutSec=600 Restart=no PrivateTmp=false [Install] -WantedBy=multi-user.target \ No newline at end of file +WantedBy=multi-user.target diff --git a/build/install/deb/debian/onlyofficeWebDav.service b/build/install/deb/debian/onlyofficeWebDav.service new file mode 100644 index 000000000..c8e8bfd60 --- /dev/null +++ b/build/install/deb/debian/onlyofficeWebDav.service @@ -0,0 +1,20 @@ +[Unit] +Description=ONLYOFFICE WebDav Service +After=network.target syslog.target mysqld.service +Wants=mysqld.service + +[Service] +Type=forking +ExecStartPre=/bin/bash -c 'rm -f /tmp/onlyofficeWebDav' +ExecStart=/usr/bin/mono-service -d:/var/www/onlyoffice/Services/TeamLabSvc -l:/tmp/onlyofficeWebDav /var/www/onlyoffice/Services/TeamLabSvc/TeamLabSvc.exe --service \"ASC.WebDav.Svc.Launcher,ASC.WebDav.Svc\" --log WebDav +Environment=MONO_IOMAP=all MONO_PATH=/var/www/onlyoffice/Services/TeamLabSvc/ +User=onlyoffice +Group=onlyoffice +# Give up if ping don't get an answer +TimeoutSec=600 + +Restart=no +PrivateTmp=false + +[Install] +WantedBy=multi-user.target diff --git a/build/install/deb/debian/postinst b/build/install/deb/debian/postinst index c23e65744..4b9384aeb 100644 --- a/build/install/deb/debian/postinst +++ b/build/install/deb/debian/postinst @@ -34,7 +34,7 @@ DB_PWD="" DB_NAME="" apply_connection_string(){ - binDirs=("WebStudio" "ApiSystem" "Services/TeamLabSvc" "Services/MailAggregator" "Services/MailCleaner" "Services/MailWatchdog") + binDirs=("WebStudio" "ApiSystem" "Services/TeamLabSvc") if [ "$DB_HOST" != "localhost" ] || [ "$DB_NAME" != "{{package_sysname}}" ] || [ "$DB_USER" != "root" ] || [ "$DB_PWD" != "" ]; then if [ -d /var/www/{{package_sysname}}/ ]; then @@ -50,10 +50,11 @@ apply_connection_string(){ sed "s!\"database\":.*!\"database\":\"${DB_NAME}\"!" -i ${APP_SERVICES_DIR}/ASC.UrlShortener/config/config.json fi + sed -i "s/Server=.*/Server=$DB_HOST;Port=3306;Database=$DB_NAME;User ID=$DB_USER;Password=$DB_PWD;Pooling=true;Character Set=utf8;AutoEnlist=false;SSL Mode=none;AllowPublicKeyRetrieval=True;ConnectionReset=false\",/g" /etc/onlyoffice/communityserver/appsettings.production.json } apply_core_machinekey(){ - binDirs=("WebStudio" "ApiSystem" "Services/TeamLabSvc" "Services/MailAggregator" "Services/MailCleaner" "Services/MailWatchdog") + binDirs=("WebStudio" "ApiSystem" "Services/TeamLabSvc") if [ -d /var/www/{{package_sysname}}/ ]; then for i in "${!binDirs[@]}"; @@ -66,6 +67,7 @@ apply_core_machinekey(){ if [ ! -z $CORE_MACHINEKEY ]; then sed "s!\"core\.machinekey\":.*!\"core\.machinekey\":\"${CORE_MACHINEKEY}\",!" -i ${APP_SERVICES_DIR}/ASC.UrlShortener/config/config.json + sed "s!\"machinekey\":.*!\"machinekey\":\"${CORE_MACHINEKEY}\",!" -i /etc/onlyoffice/communityserver/appsettings.production.json fi } @@ -418,6 +420,34 @@ EOF fi # end + # setup mail services + db_get {{package_sysname}}-communityserver/imapsync-start-date || true + MAIL_IMAPSYNC_START_DATE="${RET:-$(date +"%Y-%m-%dT%H:%M:%S")}"; + [ -z "$RET" ] && db_set {{package_sysname}}-communityserver/imapsync-start-date ${MAIL_IMAPSYNC_START_DATE} || true + sed 's_\(\"ImapSyncStartDate":\).*,_\1 "'${MAIL_IMAPSYNC_START_DATE}'",_' -i /etc/{{package_sysname}}/communityserver/mail.production.json + sed "/mail\.imap-sync-start-date/s/value=\"\S*\"/value=\"${MAIL_IMAPSYNC_START_DATE}\"/g" -i ${APP_ROOT_DIR}/web.appsettings.config + + sed "s!\"value\":.*!\"value\":\ \"${APP_DATA_DIR}\"!" -i /etc/{{package_sysname}}/communityserver/storage.production.json + sed "s!\"folder\":.*,!\"folder\":\ \"${APP_SERVICES_DIR}\",!" -i /etc/{{package_sysname}}/communityserver/appsettings.production.json + + if [ ! -f /etc/{{package_sysname}}/communityserver/openssl.cnf ] ; then + cp /etc/ssl/openssl.cnf \ + /etc/{{package_sysname}}/communityserver/openssl.cnf; + sed '/new_oids$/a openssl_conf = default_conf' -i /etc/{{package_sysname}}/communityserver/openssl.cnf + cat >> /etc/{{package_sysname}}/communityserver/openssl.cnf <>onlyoffice.list ;; - */WebStudio/[Ww]eb*.config | */ApiSystem/[Ww]eb*.config | */TeamLabSvc.exe.config | */ASC.Mail.StorageCleaner.exe.config | */ASC.Mail.Aggregator.CollectionService.exe.config | */ASC.Mail.Watchdog.Service.exe.config | */ASC.Mail.EmlDownloader.exe.config ) + */WebStudio/[Ww]eb*.config | */ApiSystem/[Ww]eb*.config | */TeamLabSvc.exe.config | */ASC.Mail.EmlDownloader.exe.config ) echo "%%attr(-, %{package_sysname}, %{package_sysname}) %%config \"$RELFILE\"" >>onlyoffice.list ;; @@ -108,6 +113,7 @@ rm -rf "$RPM_BUILD_ROOT" %attr(-, root, root) /etc/nginx/includes/%{package_sysname}-communityserver-* %attr(-, root, root) /etc/hyperfastcgi/* %attr(-, root, root) /etc/god/ +%attr(-, %{package_sysname}, %{package_sysname}) %config /etc/%{package_sysname}/communityserver/* %pre getent group %{package_sysname} >/dev/null || groupadd -r %{package_sysname} @@ -157,6 +163,8 @@ python3 -m pip install --upgrade $DIR/Tools/radicale/plugins/app_auth_plugin/. python3 -m pip install --upgrade $DIR/Tools/radicale/plugins/app_store_plugin/. python3 -m pip install --upgrade $DIR/Tools/radicale/plugins/app_rights_plugin/. +systemctl restart %{package_sysname}Radicale + %triggerin -- %{package_sysname}-xmppserver DIR=/var/www/%{package_sysname} @@ -243,6 +251,17 @@ APP_DATA_DIR="${DIR}/Data" mkdir -p "$APP_DATA_DIR" +#Mail configs changes +if [ -f $DIR/WebStudio/web.appsettings.config.rpmsave ]; then + MAIL_IMAPSYNC_START_DATE="$(sed -n '/"mail.imap-sync-start-date"/s_.*value\s*=\s*"\([^"]*\)".*_\1_p' ${DIR}/WebStudio/web.appsettings.config.rpmsave)"; +fi +MAIL_IMAPSYNC_START_DATE="${MAIL_IMAPSYNC_START_DATE:-$(date '+%Y-%m-%dT%H:%M:%''S')}"; +sed 's_\(\"ImapSyncStartDate":\).*,_\1 "'${MAIL_IMAPSYNC_START_DATE}'",_' -i /etc/%{package_sysname}/communityserver/mail.production.json +sed "/mail\.imap-sync-start-date/s/value=\"\S*\"/value=\"${MAIL_IMAPSYNC_START_DATE}\"/g" -i ${DIR}/WebStudio/web.appsettings.config + +sed "s!\"value\":.*!\"value\":\ \"${APP_DATA_DIR}\"!" -i /etc/%{package_sysname}/communityserver/storage.production.json +sed "s!\"folder\":.*,!\"folder\":\ \"${SERVICES_DIR}\",!" -i /etc/%{package_sysname}/communityserver/appsettings.production.json + if [ "$(ls -alhd ${APP_DATA_DIR} | awk '{ print $3 }')" != "%{package_sysname}" ]; then chown %{package_sysname}:%{package_sysname} ${APP_DATA_DIR} fi @@ -279,14 +298,16 @@ if [ $1 -ge 2 ]; then CORE_MACHINEKEY="$(sed -n '/"core.machinekey"/s!.*value\s*=\s*"\([^"]*\)".*!\1!p' ${DIR}/WebStudio/web.appsettings.config.rpmsave)"; fi - binDirs=("WebStudio" "ApiSystem" "Services/TeamLabSvc" "Services/MailAggregator" "Services/MailCleaner" "Services/MailWatchdog") + binDirs=("WebStudio" "ApiSystem" "Services/TeamLabSvc") for i in "${!binDirs[@]}"; do find "$DIR/${binDirs[$i]}" -type f -name "*.[cC]onfig" -exec sed -i "s/connectionString=.*/connectionString=\"$CONN_STR\" providerName=\"MySql.Data.MySqlClient\"\/>/" {} \; + sed -i "s/Server=.*/$CONN_STR\",/g" /etc/%{package_sysname}/communityserver/appsettings.production.json if [ ! -z "$CORE_MACHINEKEY" ]; then find "$DIR/${binDirs[$i]}" -type f -name "*.[cC]onfig" -exec sed -i "/core.machinekey/s!value=\".*\"!value=\"${CORE_MACHINEKEY}\"!g" {} \; + sed "s!\"machinekey\":.*!\"machinekey\":\"${CORE_MACHINEKEY}\",!" -i /etc/%{package_sysname}/communityserver/appsettings.production.json fi done @@ -399,7 +420,7 @@ fi if systemctl is-active elasticsearch | grep -q "active"; then #Checking that the elastic is not currently being updated - if [[ $(find /usr/share/elasticsearch/lib/ -name "elasticsearch-[0-99].[0-99].*" | wc -l) -eq 1 ]]; then + if [[ $(find /usr/share/elasticsearch/lib/ -name "elasticsearch-[0-9]*.jar" | wc -l) -eq 1 ]]; then systemctl restart elasticsearch.service fi fi diff --git a/build/install/rpm/onlyoffice-communityserver.spec b/build/install/rpm/onlyoffice-communityserver.spec index 5ef4addec..8e137ceb7 100644 --- a/build/install/rpm/onlyoffice-communityserver.spec +++ b/build/install/rpm/onlyoffice-communityserver.spec @@ -3,9 +3,9 @@ %global package_header_tag_url http://onlyoffice.com/ %global package_header_tag_vendor Ascensio System SIA %global package_header_tag_packager Ascensio System SIA -%global package_header_tag_requires mono-complete >= 6.8.0, nginx >= 1.9.5, mysql-server >= 5.7.0, wget, mono-webserver-hyperfastcgi, nodejs >= 12.0.0, redis >= 3.0.0, elasticsearch = 7.9.0-1, python3 >= 3.6, ffmpeg, jq +%global package_header_tag_requires mono-complete >= 6.8.0, nginx >= 1.9.5, mysql-server >= 5.7.0, wget, mono-webserver-hyperfastcgi, nodejs >= 12.0.0, redis >= 3.0.0, elasticsearch = 7.10.0-1, python3 >= 3.6, ffmpeg, jq %global package_section_description "Community Server is a free open-source collaborative system developed to manage documents, projects, customer relationship and emails, all in one place." -%global package_services god monoserve monoserveApiSystem onlyofficeSocketIO onlyofficeThumb onlyofficeTelegram onlyofficeBackup onlyofficeFeed onlyofficeIndex onlyofficeNotify onlyofficeMailAggregator onlyofficeMailWatchdog onlyofficeMailCleaner onlyofficeStorageMigrate onlyofficeStorageEncryption onlyofficeUrlShortener onlyofficeRadicale onlyofficeThumbnailBuilder onlyofficeSsoAuth +%global package_services god monoserve monoserveApiSystem onlyofficeSocketIO onlyofficeThumb onlyofficeTelegram onlyofficeBackup onlyofficeFeed onlyofficeIndex onlyofficeNotify onlyofficeMailAggregator onlyofficeMailWatchdog onlyofficeMailCleaner onlyofficeMailImap onlyofficeStorageMigrate onlyofficeStorageEncryption onlyofficeUrlShortener onlyofficeWebDav onlyofficeAutoCleanUp onlyofficeRadicale onlyofficeThumbnailBuilder onlyofficeSsoAuth %global package_sysname onlyoffice diff --git a/build/msbuild/ASC.Web.Core.BuildTask.dll b/build/msbuild/ASC.Web.Core.BuildTask.dll index 9a53b3584c8a42ed1aad1fbdf57a30ac5da989b7..9ffb8c66adadf6d7f65b88229223089b68e14166 100644 GIT binary patch literal 27648 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 NaA5nd*Z+4G_@CnkR&4+P literal 23552 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 CRestore;ReBuild 0 0 + true @@ -24,7 +25,6 @@ - @@ -34,6 +34,7 @@ + - - - - - - + + + + + + + + + + + + - Restore;Build + Build Debug ..\..\ $(RootDir)web\studio\ASC.Web.Studio\bin\ @@ -25,6 +25,8 @@ 0 0 1 + 1 + 1 $(RootDir).nuget\NuGet.exe mono $(RootDir).nuget\NuGet.exe @@ -35,69 +37,121 @@ module\ASC.Mail\Services\ASC.Mail.Aggregator.CollectionService\ASC.Mail.Aggregator.CollectionService.csproj + $(OutDir)Services\MailAggregator\ module\ASC.Mail\Services\ASC.Mail.EmlDownloader\ASC.Mail.EmlDownloader.csproj + $(OutDir)Services\MailAggregator\ module\ASC.Mail\Services\ASC.Mail.Watchdog.Service\ASC.Mail.Watchdog.Service.csproj + $(OutDir)Services\MailWatchdog\ module\ASC.Mail\Services\ASC.Mail.StorageCleaner\ASC.Mail.StorageCleaner.Service.csproj + $(OutDir)Services\MailCleaner\ ASC.Socket.IO.Svc.Launcher, ASC.Socket.IO.Svc module\ASC.Socket.IO.Svc\ASC.Socket.IO.Svc.csproj + $(OutDir)Services\SocketIO\ + $(OutDir)Services\TeamLabSvc\ + module\ASC.Socket.IO\ + $(OutDir)Services\ASC.Socket.IO ASC.TelegramService.Launcher, ASC.TelegramService module\ASC.TelegramService\ASC.TelegramService.csproj + $(OutDir)Services\Telegram\ + $(OutDir)Services\TeamLabSvc\ ASC.Thumbnails.Svc.Launcher, ASC.Thumbnails.Svc module\ASC.Thumbnails.Svc\ASC.Thumbnails.Svc.csproj + $(OutDir)Services\Thumbnails\ + $(OutDir)Services\TeamLabSvc\ + module\ASC.Thumbnails\ + $(OutDir)Services\ASC.Thumbnails ASC.SsoAuth.Svc.Launcher, ASC.SsoAuth.Svc module\ASC.SsoAuth.Svc\ASC.SsoAuth.Svc.csproj + $(OutDir)Services\SsoAuth\ + $(OutDir)Services\TeamLabSvc\ + module\ASC.SsoAuth\ + $(OutDir)Services\ASC.SsoAuth ASC.UrlShortener.Svc.Launcher, ASC.UrlShortener.Svc module\ASC.UrlShortener.Svc\ASC.UrlShortener.Svc.csproj + $(OutDir)Services\UrlShortener\ + $(OutDir)Services\TeamLabSvc\ + module\ASC.UrlShortener\ + $(OutDir)Services\ASC.UrlShortener + + + ASC.WebDav.Svc.Launcher, ASC.WebDav.Svc + module\ASC.WebDav.Svc\ASC.WebDav.Svc.csproj + $(OutDir)Services\WebDav\ + $(OutDir)Services\TeamLabSvc\ + module\ASC.WebDav\ + $(OutDir)Services\ASC.WebDav ASC.Radicale.Launcher, ASC.Radicale module\ASC.Radicale\ASC.Radicale.csproj + $(OutDir)Services\Radicale\ + $(OutDir)Services\TeamLabSvc\ ASC.Notify.NotifyServiceLauncher, ASC.Notify module\ASC.Notify\ASC.Notify\ASC.Notify.csproj + $(OutDir)Services\Notify\ + $(OutDir)Services\TeamLabSvc\ ASC.ElasticSearch.Launcher, ASC.ElasticSearch module\ASC.ElasticSearch\ASC.ElasticSearch.csproj + $(OutDir)Services\Index\ + $(OutDir)Services\TeamLabSvc\ ASC.Feed.Aggregator.FeedAggregatorLauncher, ASC.Feed.Aggregator module\ASC.Feed.Aggregator\ASC.Feed.Aggregator.csproj + $(OutDir)Services\Feed\ + $(OutDir)Services\TeamLabSvc\ ASC.Data.Backup.Service.BackupServiceLauncher, ASC.Data.Backup common\ASC.Data.Backup\ASC.Data.Backup.csproj + $(OutDir)Services\Backup\ + $(OutDir)Services\TeamLabSvc\ ASC.Data.Storage.Migration.Launcher, ASC.Data.Storage.Migration common\ASC.Data.Storage.Migration\ASC.Data.Storage.Migration.csproj + $(OutDir)Services\Migrate\ + $(OutDir)Services\TeamLabSvc\ ASC.Data.Storage.Encryption.Launcher, ASC.Data.Storage.Encryption common\ASC.Data.Storage.Encryption\ASC.Data.Storage.Encryption.csproj + $(OutDir)Services\Encryption\ + $(OutDir)Services\TeamLabSvc\ ASC.Files.ThumbnailBuilder.Launcher, ASC.Files.ThumbnailBuilder module\ASC.Files.ThumbnailBuilder\ASC.Files.ThumbnailBuilder.csproj + $(OutDir)Services\ThumbnailBuilder\ + $(OutDir)Services\TeamLabSvc\ + + + ASC.Files.AutoCleanUp.Launcher, ASC.Files.AutoCleanUp + module\ASC.Files.AutoCleanUp\ASC.Files.AutoCleanUp.csproj + $(OutDir)Services\AutoCleanUp\ + $(OutDir)Services\TeamLabSvc\ @@ -115,19 +169,29 @@ + + + TargetToInvoke=DeployService + + + TargetToInvoke=DeployMail + + + TargetToInvoke=DeployWeb + + + TargetToInvoke=DeployUpload + + + + + - - - - - - - - - + + @@ -148,46 +212,53 @@ + + + + + + + + + + + + + - - - $(OutDir)Services\$(ServiceName)\ - $(OutDir)Services\TeamLabSvc\ - - - + + + - + - - - - - - - + + + + + - + - + - + - - - - - - - + + + + + + + @@ -200,28 +271,24 @@ - - - - + + + + + + + + - - - $(OutDir)Services\$(ServiceName)\ - - - - - - + + + - - @@ -300,37 +367,37 @@ ..\config\private\Web.Storage.Config.Substitutions.xml - + ..\config\private\Mail.Agg.Substitutions.xml - + ..\config\private\Mail.Agg.Substitutions.xml - + ..\config\private\Mail.Agg.Log4net.Substitutions.xml - + ..\config\private\Mail.Agg.NLog.Substitutions.xml - + ..\config\private\Mail.Dog.Substitutions.xml - + ..\config\private\Mail.Dog.NLog.Substitutions.xml - + ..\config\private\Mail.Dog.Log4net.Substitutions.xml - + ..\config\private\Mail.Dog.NLog.Substitutions.xml - + ..\config\private\Mail.Cln.Substitutions.xml - + ..\config\private\Mail.Cln.Log4net.Substitutions.xml - + ..\config\private\Mail.Cln.NLog.Substitutions.xml diff --git a/build/sql/onlyoffice.data.sql b/build/sql/onlyoffice.data.sql index 690040c60..3018cc3a1 100644 --- a/build/sql/onlyoffice.data.sql +++ b/build/sql/onlyoffice.data.sql @@ -18,10 +18,14 @@ BEGIN commit; END IF; - replace into tenants_quota (tenant, name, max_file_size, max_total_size, active_users, features) values (-1, 'default', 102400, 10995116277760, 10000, 'domain,audit,controlpanel,healthcheck,ldap,sso,whitelabel,branding,ssbranding,update,support,portals:10000,discencryption,privacyroom,restore'); + replace into tenants_quota (tenant, name, max_file_size, max_total_size, active_users, features) values (-1, 'default', 102400, 10995116277760, 10000, 'update'); + + IF NOT EXISTS(SELECT * FROM core_settings WHERE tenant = -1 AND id = 'CustomMode') THEN replace into core_settings(tenant, id, value) values (-1, 'SmtpSettings', 0xF052E090A1A3750DADCD4E9961DA04AA51EF0197E2C0623CF12C5838BFA40A9B48BAEFCBE371587731D7E3DC9E7C6009742F9E415D56DB0F0AE08E32F8904B2C441CC657C64543EAEE262044A28B4335DCB0F0C4E9401D891FA06369F984CA2D475C86C237917961C5827769831585230A66AC7787E6FB56FD3E37389267A46A); + END IF; + insert ignore into core_settings(tenant, id, value) values (-1, 'FullTextSearchSettings', 0x0878CF0599B517CAA2D3DAED9D064C3EDCEEAF431F35A6F642DCADA04817E3513227BBB1DE6E2BABEB9E1077B2CF318C489814545E877501F633FBBE94022CFCDD025B5395973AF510943408BB56962EE35DA35F2F8374CF5FD12695359449D7CEFBC2C7BD112AE58752179AA2A59E5E17801E580CCC60FAEC8EBDD3D612C4886666D96D6CF060605E64C90A1FAA80C0); - insert ignore into core_settings(tenant, id, value) values (-1, 'CompanyWhiteLabelSettings', 0xF547048A4865171587D9CEBC8A496C601D96031F2C1C3E9160353942EE765DACD316F4B5F42892436FC4A21B9A6DF8FFD3BC4036B47E3A5A1B4C881B26609869FEBB6848BD88C02EEAC6A4CCB3E8F404290812F0E6E124A552BE81A58C64BB8BD3C9A8C0EDE1F9421281DE0C7AF82733A4BCE515E85694C4DDA78E22652BA2891FCE9578F97285A81E12FEDF5D6558611E3AA3E03EADDCAA98287C64A5510757A881B00C3345E6FC1E22B607CA2D753C63F1ED94C92366DBA0E4C2E6DB16F44A8AB091007AA7505D17E41530643C1FFAE822F8F99FD2E30C0DEF82DF65C43324507F3E5C68E4C5E22BE8A40C24423485); + insert ignore into core_settings(tenant, id, value) values (-1, 'CompanyWhiteLabelSettings', 0xF547048A4865171587D9CEBC8A496C601D96031F2C1C3E9160353942EE765DACD316F4B5F42892436FC4A21B9A6DF8FFB511FD7F2A41135A1ACECB919F9FF3691847BCA535553925BAFB396012A2DA500065FA6442611833F0D7D8A969640FFD1D53B6ECCB3544FB029695943A88542597525CE31E346F289A9B077E9564A9570A81E48AB1654D43B1D8BDC901D588D86BC6FDDD6AFFC611440E6E5AAEB644DEBC4D9D131A4456610F5118ABD672BFAF383830347D52FD714729C9050876A2BF63C430C6DF4FCCAE1F61EC14D5DA5522104AC4D1EB0E47D12083C3540B424A1373FF6345EBD2CC0F0D048F7F987DD45B); start transaction; insert ignore into core_subscription(source, action, recipient, object, tenant) values('asc.web.studio','send_whats_new','c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e','',-1); @@ -132,17 +136,18 @@ BEGIN insert ignore into core_acl (tenant, subject, action, object, acetype) values (-1, 'c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e', '77777777-32ae-425f-99b5-83176061d1ae', 'ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|1e04460243b54d7982f3fd6208a11960', 0); insert ignore into core_acl (tenant, subject, action, object, acetype) values (-1, 'c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e', '77777777-32ae-425f-99b5-83176061d1ae', 'ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|6743007c6f954d208c88a8601ce5e76d', 0); insert ignore into core_acl (tenant, subject, action, object, acetype) values (-1, 'c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e', '77777777-32ae-425f-99b5-83176061d1ae', 'ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|e67be73df9ae4ce18fec1880cb518cb4', 0); - insert ignore into core_acl (tenant, subject, action, object, acetype) values (-1, 'c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e', '77777777-32ae-425f-99b5-83176061d1ae', 'ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|ea942538e68e49079394035336ee0ba8', 0); + insert ignore into core_acl (tenant, subject, action, object, acetype) values (-1, 'c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e', '77777777-32ae-425f-99b5-83176061d1ae', 'ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|ea942538e68e49079394035336ee0ba8', 1); insert ignore into core_acl (tenant, subject, action, object, acetype) values (-1, 'c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e', '77777777-32ae-425f-99b5-83176061d1ae', 'ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|32d24cb57ece46069c9419216ba42086', 0); insert ignore into core_acl (tenant, subject, action, object, acetype) values (-1, 'c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e', '77777777-32ae-425f-99b5-83176061d1ae', 'ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|bf88953e3c434850a3fbb1e43ad53a3e', 0); insert ignore into core_acl (tenant, subject, action, object, acetype) values (-1, 'c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e', '77777777-32ae-425f-99b5-83176061d1ae', 'ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|2a9230378b2d487b9a225ac0918acf3f', 0); insert ignore into core_acl (tenant, subject, action, object, acetype) values (-1, 'c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e', '77777777-32ae-425f-99b5-83176061d1ae', 'ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|f4d98afdd336433287783c6945c81ea0', 0); - insert ignore into core_acl (tenant, subject, action, object, acetype) values (-1, 'c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e', '77777777-32ae-425f-99b5-83176061d1ae', 'ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|28b10049dd204f54b986873bc14ccfc7', 0); - insert ignore into core_acl (tenant, subject, action, object, acetype) values (-1, 'c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e', '77777777-32ae-425f-99b5-83176061d1ae', 'ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|3cfd481b46f24a4ab55cb8c0c9def02c', 0); - insert ignore into core_acl (tenant, subject, action, object, acetype) values (-1, 'c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e', '77777777-32ae-425f-99b5-83176061d1ae', 'ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|6a598c7491ae437da5f4ad339bd11bb2', 0); + insert ignore into core_acl (tenant, subject, action, object, acetype) values (-1, 'c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e', '77777777-32ae-425f-99b5-83176061d1ae', 'ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|28b10049dd204f54b986873bc14ccfc7', 1); + insert ignore into core_acl (tenant, subject, action, object, acetype) values (-1, 'c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e', '77777777-32ae-425f-99b5-83176061d1ae', 'ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|3cfd481b46f24a4ab55cb8c0c9def02c', 1); + insert ignore into core_acl (tenant, subject, action, object, acetype) values (-1, 'c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e', '77777777-32ae-425f-99b5-83176061d1ae', 'ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|6a598c7491ae437da5f4ad339bd11bb2', 1); insert ignore into core_acl (tenant, subject, action, object, acetype) values (-1, 'c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e', '77777777-32ae-425f-99b5-83176061d1ae', 'ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|742cf945cbbc4a5782d61600a12cf8ca', 1); insert ignore into core_acl (tenant, subject, action, object, acetype) values (-1, 'c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e', '77777777-32ae-425f-99b5-83176061d1ae', 'ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|853b6eb973ee438d9b098ffeedf36234', 1); - insert ignore into core_acl (tenant, subject, action, object, acetype) values (-1, "c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e", "77777777-32ae-425f-99b5-83176061d1ae", "ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|46cfa73af32046cf8d5bcd82e1d67f26", 0); + insert ignore into core_acl (tenant, subject, action, object, acetype) values (-1, "c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e", "77777777-32ae-425f-99b5-83176061d1ae", 'ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|46cfa73af32046cf8d5bcd82e1d67f26', 0); + insert ignore into core_acl (tenant, subject, action, object, acetype) values (-1, 'c5cc67d1-c3e8-43c0-a3ad-3928ae3e5b5e', '77777777-32ae-425f-99b5-83176061d1ae', 'ASC.Web.Core.WebItemSecurity+WebItemSecurityObject|37620ae5c40b45ce855a39dd7d76a1fa', 0); insert ignore into tenants_forbiden (address) values ('controlpanel'); insert ignore into tenants_forbiden (address) values ('localhost'); @@ -153,7 +158,7 @@ BEGIN insert ignore into crm_currency_info (resource_key, abbreviation, symbol, culture_name, is_convertable, is_basic) values ('Currency_ArgentinianPeso', 'ARS', '$a', 'AR', 1, 0); insert ignore into crm_currency_info (resource_key, abbreviation, symbol, culture_name, is_convertable, is_basic) values ('Currency_ArmenianDram', 'AMD', 'dram', 'AM', 0, 0); insert ignore into crm_currency_info (resource_key, abbreviation, symbol, culture_name, is_convertable, is_basic) values ('Currency_AustralianDollar', 'AUD', 'A$', 'AU', 1, 1); - insert ignore into crm_currency_info (resource_key, abbreviation, symbol, culture_name, is_convertable, is_basic) values ('Currency_AzerbaijaniManat', 'AZN', 'm', 'AZ', 0, 0); + insert ignore into crm_currency_info (resource_key, abbreviation, symbol, culture_name, is_convertable, is_basic) values ('Currency_AzerbaijaniManat', 'AZN', '₼', 'AZ', 0, 0); insert ignore into crm_currency_info (resource_key, abbreviation, symbol, culture_name, is_convertable, is_basic) values ('Currency_BangladeshiTaka', 'BDT', 'Tk', 'BD', 1, 0); insert ignore into crm_currency_info (resource_key, abbreviation, symbol, culture_name, is_convertable, is_basic) values ('Currency_BelarusianRuble', 'BYR', 'Br', 'BY', 0, 0); insert ignore into crm_currency_info (resource_key, abbreviation, symbol, culture_name, is_convertable, is_basic) values ('Currency_BrazilianReal', 'BRL', 'R$', 'BR', 1, 0); @@ -169,6 +174,7 @@ BEGIN insert ignore into crm_currency_info (resource_key, abbreviation, symbol, culture_name, is_convertable, is_basic) values ('Currency_CzechKoruna', 'CZK', 'Kc', 'CZ', 1, 0); insert ignore into crm_currency_info (resource_key, abbreviation, symbol, culture_name, is_convertable, is_basic) values ('Currency_DanishKrone', 'DKK', 'kr', 'DK', 1, 0); insert ignore into crm_currency_info (resource_key, abbreviation, symbol, culture_name, is_convertable, is_basic) values ('Currency_DominicanPeso', 'DOP', 'RD$', 'DO', 1, 0); + insert ignore into crm_currency_info (resource_key, abbreviation, symbol, culture_name, is_convertable, is_basic) values ('Currency_EthiopianBirr', 'ETB', 'Br', 'ET', 0, 0); insert ignore into crm_currency_info (resource_key, abbreviation, symbol, culture_name, is_convertable, is_basic) values ('Currency_Euro', 'EUR', '€', 'EU', 1, 1); insert ignore into crm_currency_info (resource_key, abbreviation, symbol, culture_name, is_convertable, is_basic) values ('Currency_GeorgianLari', 'GEL', 'ლარი', 'GE', 0, 0); insert ignore into crm_currency_info (resource_key, abbreviation, symbol, culture_name, is_convertable, is_basic) values ('Currency_HongKongDollar', 'HKD', 'HK$', 'HK', 1, 0); @@ -225,21 +231,45 @@ BEGIN truncate table files_converts; start transaction; insert into files_converts (input, output) values ('.csv', '.ods'); + insert into files_converts (input, output) values ('.csv', '.ots'); insert into files_converts (input, output) values ('.csv', '.pdf'); + insert into files_converts (input, output) values ('.csv', '.xlsm'); insert into files_converts (input, output) values ('.csv', '.xlsx'); + insert into files_converts (input, output) values ('.csv', '.xltm'); + insert into files_converts (input, output) values ('.csv', '.xltx'); + insert into files_converts (input, output) values ('.doc', '.docm'); insert into files_converts (input, output) values ('.doc', '.docx'); + insert into files_converts (input, output) values ('.doc', '.dotm'); + insert into files_converts (input, output) values ('.doc', '.dotx'); + insert into files_converts (input, output) values ('.doc', '.epub'); + insert into files_converts (input, output) values ('.doc', '.fb2'); + insert into files_converts (input, output) values ('.doc', '.html'); insert into files_converts (input, output) values ('.doc', '.odt'); + insert into files_converts (input, output) values ('.doc', '.ott'); insert into files_converts (input, output) values ('.doc', '.pdf'); insert into files_converts (input, output) values ('.doc', '.rtf'); insert into files_converts (input, output) values ('.doc', '.txt'); insert into files_converts (input, output) values ('.docm', '.docx'); + insert into files_converts (input, output) values ('.docm', '.dotm'); + insert into files_converts (input, output) values ('.docm', '.dotx'); + insert into files_converts (input, output) values ('.docm', '.epub'); + insert into files_converts (input, output) values ('.docm', '.fb2'); + insert into files_converts (input, output) values ('.docm', '.html'); insert into files_converts (input, output) values ('.docm', '.odt'); + insert into files_converts (input, output) values ('.docm', '.ott'); insert into files_converts (input, output) values ('.docm', '.pdf'); insert into files_converts (input, output) values ('.docm', '.rtf'); insert into files_converts (input, output) values ('.docm', '.txt'); insert into files_converts (input, output) values ('.doct', '.docx'); + insert into files_converts (input, output) values ('.docx', '.docm'); insert into files_converts (input, output) values ('.docx', '.docxf'); + insert into files_converts (input, output) values ('.docx', '.dotm'); + insert into files_converts (input, output) values ('.docx', '.dotx'); + insert into files_converts (input, output) values ('.docx', '.epub'); + insert into files_converts (input, output) values ('.docx', '.fb2'); + insert into files_converts (input, output) values ('.docx', '.html'); insert into files_converts (input, output) values ('.docx', '.odt'); + insert into files_converts (input, output) values ('.docx', '.ott'); insert into files_converts (input, output) values ('.docx', '.pdf'); insert into files_converts (input, output) values ('.docx', '.rtf'); insert into files_converts (input, output) values ('.docx', '.txt'); @@ -254,133 +284,299 @@ BEGIN insert into files_converts (input, output) values ('.docxf', '.pdf'); insert into files_converts (input, output) values ('.docxf', '.rtf'); insert into files_converts (input, output) values ('.docxf', '.txt'); + insert into files_converts (input, output) values ('.dot', '.docm'); insert into files_converts (input, output) values ('.dot', '.docx'); + insert into files_converts (input, output) values ('.dot', '.dotm'); + insert into files_converts (input, output) values ('.dot', '.dotx'); + insert into files_converts (input, output) values ('.dot', '.epub'); + insert into files_converts (input, output) values ('.dot', '.fb2'); + insert into files_converts (input, output) values ('.dot', '.html'); insert into files_converts (input, output) values ('.dot', '.odt'); + insert into files_converts (input, output) values ('.dot', '.ott'); insert into files_converts (input, output) values ('.dot', '.pdf'); insert into files_converts (input, output) values ('.dot', '.rtf'); insert into files_converts (input, output) values ('.dot', '.txt'); + insert into files_converts (input, output) values ('.dotm', '.docm'); insert into files_converts (input, output) values ('.dotm', '.docx'); + insert into files_converts (input, output) values ('.dotm', '.dotx'); + insert into files_converts (input, output) values ('.dotm', '.epub'); + insert into files_converts (input, output) values ('.dotm', '.fb2'); + insert into files_converts (input, output) values ('.dotm', '.html'); insert into files_converts (input, output) values ('.dotm', '.odt'); + insert into files_converts (input, output) values ('.dotm', '.ott'); insert into files_converts (input, output) values ('.dotm', '.pdf'); insert into files_converts (input, output) values ('.dotm', '.rtf'); insert into files_converts (input, output) values ('.dotm', '.txt'); + insert into files_converts (input, output) values ('.dotx', '.docm'); insert into files_converts (input, output) values ('.dotx', '.docx'); + insert into files_converts (input, output) values ('.dotx', '.dotm'); + insert into files_converts (input, output) values ('.dotx', '.epub'); + insert into files_converts (input, output) values ('.dotx', '.fb2'); + insert into files_converts (input, output) values ('.dotx', '.html'); insert into files_converts (input, output) values ('.dotx', '.odt'); + insert into files_converts (input, output) values ('.dotx', '.ott'); insert into files_converts (input, output) values ('.dotx', '.pdf'); insert into files_converts (input, output) values ('.dotx', '.rtf'); insert into files_converts (input, output) values ('.dotx', '.txt'); + insert into files_converts (input, output) values ('.epub', '.docm'); insert into files_converts (input, output) values ('.epub', '.docx'); + insert into files_converts (input, output) values ('.epub', '.dotm'); + insert into files_converts (input, output) values ('.epub', '.dotx'); + insert into files_converts (input, output) values ('.epub', '.fb2'); + insert into files_converts (input, output) values ('.epub', '.html'); insert into files_converts (input, output) values ('.epub', '.odt'); + insert into files_converts (input, output) values ('.epub', '.ott'); insert into files_converts (input, output) values ('.epub', '.pdf'); insert into files_converts (input, output) values ('.epub', '.rtf'); insert into files_converts (input, output) values ('.epub', '.txt'); + insert into files_converts (input, output) values ('.fb2', '.docm'); insert into files_converts (input, output) values ('.fb2', '.docx'); + insert into files_converts (input, output) values ('.fb2', '.dotm'); + insert into files_converts (input, output) values ('.fb2', '.dotx'); + insert into files_converts (input, output) values ('.fb2', '.epub'); + insert into files_converts (input, output) values ('.fb2', '.html'); insert into files_converts (input, output) values ('.fb2', '.odt'); + insert into files_converts (input, output) values ('.fb2', '.ott'); insert into files_converts (input, output) values ('.fb2', '.pdf'); insert into files_converts (input, output) values ('.fb2', '.rtf'); insert into files_converts (input, output) values ('.fb2', '.txt'); insert into files_converts (input, output) values ('.fodp', '.odp'); + insert into files_converts (input, output) values ('.fodp', '.otp'); insert into files_converts (input, output) values ('.fodp', '.pdf'); + insert into files_converts (input, output) values ('.fodp', '.potm'); + insert into files_converts (input, output) values ('.fodp', '.potx'); + insert into files_converts (input, output) values ('.fodp', '.pptm'); insert into files_converts (input, output) values ('.fodp', '.pptx'); insert into files_converts (input, output) values ('.fods', '.csv'); insert into files_converts (input, output) values ('.fods', '.ods'); + insert into files_converts (input, output) values ('.fods', '.ots'); insert into files_converts (input, output) values ('.fods', '.pdf'); + insert into files_converts (input, output) values ('.fods', '.xlsm'); insert into files_converts (input, output) values ('.fods', '.xlsx'); + insert into files_converts (input, output) values ('.fods', '.xltm'); + insert into files_converts (input, output) values ('.fods', '.xltx'); + insert into files_converts (input, output) values ('.fodt', '.docm'); insert into files_converts (input, output) values ('.fodt', '.docx'); + insert into files_converts (input, output) values ('.fodt', '.dotm'); + insert into files_converts (input, output) values ('.fodt', '.dotx'); + insert into files_converts (input, output) values ('.fodt', '.epub'); + insert into files_converts (input, output) values ('.fodt', '.fb2'); + insert into files_converts (input, output) values ('.fodt', '.html'); insert into files_converts (input, output) values ('.fodt', '.odt'); + insert into files_converts (input, output) values ('.fodt', '.ott'); insert into files_converts (input, output) values ('.fodt', '.pdf'); insert into files_converts (input, output) values ('.fodt', '.rtf'); insert into files_converts (input, output) values ('.fodt', '.txt'); + insert into files_converts (input, output) values ('.html', '.docm'); insert into files_converts (input, output) values ('.html', '.docx'); + insert into files_converts (input, output) values ('.html', '.dotm'); + insert into files_converts (input, output) values ('.html', '.dotx'); + insert into files_converts (input, output) values ('.html', '.epub'); + insert into files_converts (input, output) values ('.html', '.fb2'); insert into files_converts (input, output) values ('.html', '.odt'); + insert into files_converts (input, output) values ('.html', '.ott'); insert into files_converts (input, output) values ('.html', '.pdf'); insert into files_converts (input, output) values ('.html', '.rtf'); insert into files_converts (input, output) values ('.html', '.txt'); + insert into files_converts (input, output) values ('.mht', '.docm'); insert into files_converts (input, output) values ('.mht', '.docx'); + insert into files_converts (input, output) values ('.mht', '.dotm'); + insert into files_converts (input, output) values ('.mht', '.dotx'); + insert into files_converts (input, output) values ('.mht', '.epub'); + insert into files_converts (input, output) values ('.mht', '.fb2'); insert into files_converts (input, output) values ('.mht', '.odt'); + insert into files_converts (input, output) values ('.mht', '.ott'); insert into files_converts (input, output) values ('.mht', '.pdf'); insert into files_converts (input, output) values ('.mht', '.rtf'); insert into files_converts (input, output) values ('.mht', '.txt'); + insert into files_converts (input, output) values ('.odp', '.otp'); insert into files_converts (input, output) values ('.odp', '.pdf'); + insert into files_converts (input, output) values ('.odp', '.potm'); + insert into files_converts (input, output) values ('.odp', '.potx'); + insert into files_converts (input, output) values ('.odp', '.pptm'); insert into files_converts (input, output) values ('.odp', '.pptx'); insert into files_converts (input, output) values ('.otp', '.odp'); insert into files_converts (input, output) values ('.otp', '.pdf'); + insert into files_converts (input, output) values ('.otp', '.potm'); + insert into files_converts (input, output) values ('.otp', '.potx'); + insert into files_converts (input, output) values ('.otp', '.pptm'); insert into files_converts (input, output) values ('.otp', '.pptx'); insert into files_converts (input, output) values ('.ods', '.csv'); + insert into files_converts (input, output) values ('.ods', '.ots'); insert into files_converts (input, output) values ('.ods', '.pdf'); + insert into files_converts (input, output) values ('.ods', '.xlsm'); insert into files_converts (input, output) values ('.ods', '.xlsx'); + insert into files_converts (input, output) values ('.ods', '.xltm'); + insert into files_converts (input, output) values ('.ods', '.xltx'); insert into files_converts (input, output) values ('.ots', '.csv'); insert into files_converts (input, output) values ('.ots', '.ods'); insert into files_converts (input, output) values ('.ots', '.pdf'); + insert into files_converts (input, output) values ('.ots', '.xlsm'); insert into files_converts (input, output) values ('.ots', '.xlsx'); + insert into files_converts (input, output) values ('.ots', '.xltm'); + insert into files_converts (input, output) values ('.ots', '.xltx'); + insert into files_converts (input, output) values ('.odt', '.docm'); insert into files_converts (input, output) values ('.odt', '.docx'); + insert into files_converts (input, output) values ('.odt', '.dotm'); + insert into files_converts (input, output) values ('.odt', '.dotx'); + insert into files_converts (input, output) values ('.odt', '.epub'); + insert into files_converts (input, output) values ('.odt', '.fb2'); + insert into files_converts (input, output) values ('.odt', '.html'); + insert into files_converts (input, output) values ('.odt', '.ott'); insert into files_converts (input, output) values ('.odt', '.pdf'); insert into files_converts (input, output) values ('.odt', '.rtf'); insert into files_converts (input, output) values ('.odt', '.txt'); + insert into files_converts (input, output) values ('.ott', '.docm'); insert into files_converts (input, output) values ('.ott', '.docx'); + insert into files_converts (input, output) values ('.ott', '.dotm'); + insert into files_converts (input, output) values ('.ott', '.dotx'); + insert into files_converts (input, output) values ('.ott', '.epub'); + insert into files_converts (input, output) values ('.ott', '.fb2'); + insert into files_converts (input, output) values ('.ott', '.html'); insert into files_converts (input, output) values ('.ott', '.odt'); insert into files_converts (input, output) values ('.ott', '.pdf'); insert into files_converts (input, output) values ('.ott', '.rtf'); insert into files_converts (input, output) values ('.ott', '.txt'); + insert into files_converts (input, output) values ('.oxps', '.pdf'); insert into files_converts (input, output) values ('.pot', '.odp'); + insert into files_converts (input, output) values ('.pot', '.otp'); insert into files_converts (input, output) values ('.pot', '.pdf'); + insert into files_converts (input, output) values ('.pot', '.pptm'); insert into files_converts (input, output) values ('.pot', '.pptx'); + insert into files_converts (input, output) values ('.pot', '.potm'); + insert into files_converts (input, output) values ('.pot', '.potx'); insert into files_converts (input, output) values ('.potm', '.odp'); + insert into files_converts (input, output) values ('.potm', '.otp'); insert into files_converts (input, output) values ('.potm', '.pdf'); + insert into files_converts (input, output) values ('.potm', '.potx'); + insert into files_converts (input, output) values ('.potm', '.pptm'); insert into files_converts (input, output) values ('.potm', '.pptx'); insert into files_converts (input, output) values ('.potx', '.odp'); + insert into files_converts (input, output) values ('.potx', '.otp'); insert into files_converts (input, output) values ('.potx', '.pdf'); + insert into files_converts (input, output) values ('.potx', '.potm'); + insert into files_converts (input, output) values ('.potx', '.pptm'); insert into files_converts (input, output) values ('.potx', '.pptx'); insert into files_converts (input, output) values ('.pps', '.odp'); + insert into files_converts (input, output) values ('.pps', '.otp'); insert into files_converts (input, output) values ('.pps', '.pdf'); + insert into files_converts (input, output) values ('.pps', '.potm'); + insert into files_converts (input, output) values ('.pps', '.potx'); + insert into files_converts (input, output) values ('.pps', '.pptm'); insert into files_converts (input, output) values ('.pps', '.pptx'); insert into files_converts (input, output) values ('.ppsm', '.odp'); + insert into files_converts (input, output) values ('.ppsm', '.otp'); insert into files_converts (input, output) values ('.ppsm', '.pdf'); + insert into files_converts (input, output) values ('.ppsm', '.potm'); + insert into files_converts (input, output) values ('.ppsm', '.potx'); + insert into files_converts (input, output) values ('.ppsm', '.pptm'); insert into files_converts (input, output) values ('.ppsm', '.pptx'); insert into files_converts (input, output) values ('.ppsx', '.odp'); + insert into files_converts (input, output) values ('.ppsx', '.otp'); insert into files_converts (input, output) values ('.ppsx', '.pdf'); + insert into files_converts (input, output) values ('.ppsx', '.potm'); + insert into files_converts (input, output) values ('.ppsx', '.potx'); + insert into files_converts (input, output) values ('.ppsx', '.pptm'); insert into files_converts (input, output) values ('.ppsx', '.pptx'); insert into files_converts (input, output) values ('.ppt', '.odp'); + insert into files_converts (input, output) values ('.ppt', '.otp'); insert into files_converts (input, output) values ('.ppt', '.pdf'); + insert into files_converts (input, output) values ('.ppt', '.potm'); + insert into files_converts (input, output) values ('.ppt', '.potx'); + insert into files_converts (input, output) values ('.ppt', '.pptm'); insert into files_converts (input, output) values ('.ppt', '.pptx'); insert into files_converts (input, output) values ('.pptm', '.odp'); + insert into files_converts (input, output) values ('.pptm', '.otp'); insert into files_converts (input, output) values ('.pptm', '.pdf'); + insert into files_converts (input, output) values ('.pptm', '.potm'); + insert into files_converts (input, output) values ('.pptm', '.potx'); insert into files_converts (input, output) values ('.pptm', '.pptx'); insert into files_converts (input, output) values ('.pptt', '.pptx'); insert into files_converts (input, output) values ('.pptx', '.odp'); + insert into files_converts (input, output) values ('.pptx', '.otp'); insert into files_converts (input, output) values ('.pptx', '.pdf'); + insert into files_converts (input, output) values ('.pptx', '.potm'); + insert into files_converts (input, output) values ('.pptx', '.potx'); + insert into files_converts (input, output) values ('.pptx', '.pptm'); + insert into files_converts (input, output) values ('.rtf', '.docm'); insert into files_converts (input, output) values ('.rtf', '.docx'); + insert into files_converts (input, output) values ('.rtf', '.dotm'); + insert into files_converts (input, output) values ('.rtf', '.dotx'); + insert into files_converts (input, output) values ('.rtf', '.epub'); + insert into files_converts (input, output) values ('.rtf', '.fb2'); + insert into files_converts (input, output) values ('.rtf', '.html'); insert into files_converts (input, output) values ('.rtf', '.odt'); + insert into files_converts (input, output) values ('.rtf', '.ott'); insert into files_converts (input, output) values ('.rtf', '.pdf'); insert into files_converts (input, output) values ('.rtf', '.txt'); + insert into files_converts (input, output) values ('.txt', '.docm'); insert into files_converts (input, output) values ('.txt', '.docx'); + insert into files_converts (input, output) values ('.txt', '.dotm'); + insert into files_converts (input, output) values ('.txt', '.dotx'); + insert into files_converts (input, output) values ('.txt', '.epub'); + insert into files_converts (input, output) values ('.txt', '.fb2'); + insert into files_converts (input, output) values ('.txt', '.html'); insert into files_converts (input, output) values ('.txt', '.odt'); + insert into files_converts (input, output) values ('.txt', '.ott'); insert into files_converts (input, output) values ('.txt', '.pdf'); insert into files_converts (input, output) values ('.txt', '.rtf'); insert into files_converts (input, output) values ('.xls', '.csv'); insert into files_converts (input, output) values ('.xls', '.ods'); + insert into files_converts (input, output) values ('.xls', '.ots'); insert into files_converts (input, output) values ('.xls', '.pdf'); + insert into files_converts (input, output) values ('.xls', '.xlsm'); insert into files_converts (input, output) values ('.xls', '.xlsx'); + insert into files_converts (input, output) values ('.xls', '.xltm'); + insert into files_converts (input, output) values ('.xls', '.xltx'); insert into files_converts (input, output) values ('.xlsm', '.csv'); insert into files_converts (input, output) values ('.xlsm', '.ods'); + insert into files_converts (input, output) values ('.xlsm', '.ots'); insert into files_converts (input, output) values ('.xlsm', '.pdf'); insert into files_converts (input, output) values ('.xlsm', '.xlsx'); + insert into files_converts (input, output) values ('.xlsm', '.xltm'); + insert into files_converts (input, output) values ('.xlsm', '.xltx'); insert into files_converts (input, output) values ('.xlst', '.xlsx'); insert into files_converts (input, output) values ('.xlsx', '.csv'); insert into files_converts (input, output) values ('.xlsx', '.ods'); + insert into files_converts (input, output) values ('.xlsx', '.ots'); insert into files_converts (input, output) values ('.xlsx', '.pdf'); + insert into files_converts (input, output) values ('.xlsx', '.xlsm'); + insert into files_converts (input, output) values ('.xlsx', '.xltm'); + insert into files_converts (input, output) values ('.xlsx', '.xltx'); insert into files_converts (input, output) values ('.xlt', '.csv'); insert into files_converts (input, output) values ('.xlt', '.ods'); + insert into files_converts (input, output) values ('.xlt', '.ots'); insert into files_converts (input, output) values ('.xlt', '.pdf'); + insert into files_converts (input, output) values ('.xlt', '.xlsm'); insert into files_converts (input, output) values ('.xlt', '.xlsx'); + insert into files_converts (input, output) values ('.xlt', '.xltm'); + insert into files_converts (input, output) values ('.xlt', '.xltx'); insert into files_converts (input, output) values ('.xltm', '.csv'); insert into files_converts (input, output) values ('.xltm', '.ods'); + insert into files_converts (input, output) values ('.xltm', '.ots'); insert into files_converts (input, output) values ('.xltm', '.pdf'); + insert into files_converts (input, output) values ('.xltm', '.xlsm'); insert into files_converts (input, output) values ('.xltm', '.xlsx'); + insert into files_converts (input, output) values ('.xltm', '.xltx'); insert into files_converts (input, output) values ('.xltx', '.csv'); insert into files_converts (input, output) values ('.xltx', '.ods'); + insert into files_converts (input, output) values ('.xltx', '.ots'); insert into files_converts (input, output) values ('.xltx', '.pdf'); + insert into files_converts (input, output) values ('.xltx', '.xlsm'); insert into files_converts (input, output) values ('.xltx', '.xlsx'); + insert into files_converts (input, output) values ('.xltx', '.xltm'); + insert into files_converts (input, output) values ('.xml', '.docm'); + insert into files_converts (input, output) values ('.xml', '.docx'); + insert into files_converts (input, output) values ('.xml', '.dotm'); + insert into files_converts (input, output) values ('.xml', '.dotx'); + insert into files_converts (input, output) values ('.xml', '.epub'); + insert into files_converts (input, output) values ('.xml', '.fb2'); + insert into files_converts (input, output) values ('.xml', '.html'); + insert into files_converts (input, output) values ('.xml', '.odt'); + insert into files_converts (input, output) values ('.xml', '.ott'); + insert into files_converts (input, output) values ('.xml', '.pdf'); + insert into files_converts (input, output) values ('.xml', '.rtf'); + insert into files_converts (input, output) values ('.xml', '.txt'); insert into files_converts (input, output) values ('.xps', '.pdf'); commit; diff --git a/build/sql/onlyoffice.sql b/build/sql/onlyoffice.sql index 672ab9756..97983a4ce 100644 --- a/build/sql/onlyoffice.sql +++ b/build/sql/onlyoffice.sql @@ -227,6 +227,7 @@ CREATE TABLE IF NOT EXISTS `calendar_events` ( `uid` varchar(255) DEFAULT NULL, `status` smallint(6) NOT NULL DEFAULT '0', `time_zone` varchar(255) NULL DEFAULT NULL, + `has_attachments` tinyint(1) NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `calendar_id` (`tenant`,`calendar_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; @@ -367,6 +368,13 @@ CREATE TABLE IF NOT EXISTS `core_user` ( KEY `email` (`email`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE IF NOT EXISTS `core_userdav` ( + `tenant_id` int(11) NOT NULL, + `user_id` varchar(255) NOT NULL, + PRIMARY KEY (`user_id`), + KEY `tenant_id` (`tenant_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + CREATE TABLE IF NOT EXISTS `core_usergroup` ( `tenant` int(11) NOT NULL, `userid` varchar(38) NOT NULL, @@ -390,7 +398,6 @@ CREATE TABLE IF NOT EXISTS `core_usersecurity` ( `tenant` int(11) NOT NULL, `userid` varchar(38) NOT NULL, `pwdhash` varchar(512) DEFAULT NULL, - `pwdhashsha512` varchar(512) DEFAULT NULL, `LastModified` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`userid`), KEY `pwdhash` (`pwdhash`(255)), @@ -1016,6 +1023,13 @@ CREATE TABLE IF NOT EXISTS `files_link` ( KEY `linked_for` (`tenant_id`, `source_id`, `linked_id`, `linked_for`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE IF NOT EXISTS `files_properties` ( + `tenant_id` int(10) NOT NULL, + `entry_id` varchar(32) NOT NULL, + `data` MEDIUMTEXT NOT NULL, + PRIMARY KEY (`tenant_id`, `entry_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + CREATE TABLE IF NOT EXISTS `files_security` ( `tenant_id` int(10) NOT NULL, `entry_id` varchar(50) NOT NULL, @@ -1064,7 +1078,8 @@ CREATE TABLE IF NOT EXISTS `files_thirdparty_account` ( `create_on` datetime NOT NULL, `url` text, `tenant_id` int(11) NOT NULL, - PRIMARY KEY (`id`) + PRIMARY KEY (`id`), + INDEX `tenant_id` (`tenant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `files_thirdparty_app` ( @@ -1362,6 +1377,7 @@ CREATE TABLE IF NOT EXISTS `login_events` ( `page` varchar(300) DEFAULT NULL, `action` int(11) DEFAULT NULL, `description` varchar(500) DEFAULT NULL, + `active` int(10) NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `date` (`date`), KEY `tenant_id` (`tenant_id`,`user_id`) @@ -1547,6 +1563,7 @@ CREATE TABLE IF NOT EXISTS `mail_mail` ( `mime_in_reply_to` varchar(255) DEFAULT NULL, `chain_id` varchar(255) DEFAULT NULL, `chain_date` datetime NOT NULL DEFAULT '1975-01-01 00:00:00', + `read_request_status` int(10) NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `chain_index_folders` (`chain_id`,`id_mailbox`,`folder`), KEY `uidl` (`uidl`,`id_mailbox`), @@ -1555,7 +1572,7 @@ CREATE TABLE IF NOT EXISTS `mail_mail` ( KEY `list_conversations` (`tenant`, `id_user`, `folder`, `chain_date`), KEY `list_messages` (`tenant`, `id_user`, `folder`, `date_sent`), KEY `time_modified` (`time_modified`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC; CREATE TABLE IF NOT EXISTS `mail_mailbox` ( `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, @@ -2269,7 +2286,6 @@ CREATE TABLE IF NOT EXISTS `tenants_quota` ( `active_users` int(10) NOT NULL DEFAULT '0', `features` text, `price` decimal(10,2) NOT NULL DEFAULT '0.00', - `price2` decimal(10,2) NOT NULL DEFAULT '0.00', `avangate_id` varchar(128) DEFAULT NULL, `visible` int(10) NOT NULL DEFAULT '0', PRIMARY KEY (`tenant`) @@ -2323,7 +2339,8 @@ CREATE TABLE IF NOT EXISTS `tenants_tenants` ( UNIQUE KEY `alias` (`alias`), KEY `last_modified` (`last_modified`), KEY `mappeddomain` (`mappeddomain`), - KEY `version` (`version`) + KEY `version` (`version`), + KEY `status` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `tenants_version` ( diff --git a/build/sql/onlyoffice.upgradev110.sql b/build/sql/onlyoffice.upgradev110.sql index d6b610266..b981b3b86 100644 --- a/build/sql/onlyoffice.upgradev110.sql +++ b/build/sql/onlyoffice.upgradev110.sql @@ -34,7 +34,6 @@ BEGIN END IF; UPDATE `tenants_quota` SET `features` = 'domain,audit,controlpanel,healthcheck,ldap,sso,whitelabel,branding,ssbranding,update,support,portals:10000,discencryption,privacyroom' WHERE `tenant` = -1 and `name` NOT LIKE '%saas%'; - UPDATE `tenants_quota` SET `features` = 'docs,domain,audit,controlpanel,healthcheck,ldap,sso,whitelabel,branding,ssbranding,update,support,portals:10000,discencryption,privacyroom' WHERE tenant = -1000; CREATE TABLE IF NOT EXISTS `telegram_users` ( `portal_user_id` VARCHAR(38) NOT NULL, diff --git a/build/sql/onlyoffice.upgradev115.sql b/build/sql/onlyoffice.upgradev115.sql index 2b4a35e55..b1f28ef6d 100644 --- a/build/sql/onlyoffice.upgradev115.sql +++ b/build/sql/onlyoffice.upgradev115.sql @@ -29,7 +29,6 @@ BEGIN INSERT IGNORE INTO `crm_currency_info` (`resource_key`, `abbreviation`, `symbol`, `culture_name`, `is_convertable`, `is_basic`) values ('Currency_MongolianTugrik', 'MNT', '₮', 'MN', 0, 0); - UPDATE `tenants_quota` SET `features` = 'docs,domain,audit,controlpanel,healthcheck,ldap,sso,whitelabel,branding,ssbranding,update,support,portals:10000,discencryption,privacyroom,restore,contentsearch' WHERE tenant = -1000; UPDATE `tenants_quota` SET `features` = 'domain,audit,controlpanel,healthcheck,ldap,sso,whitelabel,branding,ssbranding,update,support,portals:10000,discencryption,privacyroom,restore,contentsearch' WHERE `tenant` = -1 and `name` NOT LIKE '%saas%'; END DLM00 diff --git a/build/sql/onlyoffice.upgradev120.sql b/build/sql/onlyoffice.upgradev120.sql new file mode 100644 index 000000000..0b8c3a959 --- /dev/null +++ b/build/sql/onlyoffice.upgradev120.sql @@ -0,0 +1,275 @@ +DELIMITER DLM00 + +DROP PROCEDURE IF EXISTS upgrade120 DLM00 + +CREATE PROCEDURE upgrade120() +BEGIN + + IF EXISTS(SELECT * FROM information_schema.`COLUMNS` WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'tenants_quota' AND COLUMN_NAME = 'price2') THEN + ALTER TABLE `tenants_quota` DROP COLUMN `price2`; + END IF; + + IF EXISTS(SELECT * FROM information_schema.`COLUMNS` WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'core_usersecurity' AND COLUMN_NAME = 'pwdhashsha512') THEN + ALTER TABLE `core_usersecurity` DROP COLUMN `pwdhashsha512`; + END IF; + + CREATE TABLE IF NOT EXISTS `core_userdav` ( + `tenant_id` int(11) NOT NULL, + `user_id` varchar(255) NOT NULL, + PRIMARY KEY (`user_id`), + KEY `tenant_id` (`tenant_id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + IF NOT EXISTS(SELECT * FROM information_schema.`STATISTICS` WHERE `TABLE_SCHEMA` = DATABASE() AND `TABLE_NAME` = 'tenants_tenants' AND `INDEX_NAME` = 'status') THEN + ALTER TABLE `tenants_tenants` ADD INDEX `status` (`status`); + END IF; + + IF NOT EXISTS(SELECT * FROM information_schema.`STATISTICS` WHERE `TABLE_SCHEMA` = DATABASE() AND `TABLE_NAME` = 'files_thirdparty_account' AND `INDEX_NAME` = 'tenant_id') THEN + ALTER TABLE `files_thirdparty_account` ADD INDEX `tenant_id` (`tenant_id`); + END IF; + + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xml', '.docx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xml', '.odt'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xml', '.pdf'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xml', '.rtf'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xml', '.txt'); + + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.doc', '.epub'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.docm', '.epub'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.docx', '.epub'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.docxf', '.epub'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.dot', '.epub'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.dotm', '.epub'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.dotx', '.epub'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.fb2', '.epub'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.fodt', '.epub'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.html', '.epub'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.mht', '.epub'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.odt', '.epub'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.ott', '.epub'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.rtf', '.epub'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.txt', '.epub'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xml', '.epub'); + + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.doc', '.fb2'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.docm', '.fb2'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.docx', '.fb2'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.docxf', '.fb2'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.dot', '.fb2'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.dotm', '.fb2'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.dotx', '.fb2'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.epub', '.fb2'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.fodt', '.fb2'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.html', '.fb2'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.mht', '.fb2'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.odt', '.fb2'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.ott', '.fb2'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.rtf', '.fb2'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.txt', '.fb2'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xml', '.fb2'); + + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.doc', '.html'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.docm', '.html'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.docx', '.html'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.docxf', '.html'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.dot', '.html'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.dotm', '.html'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.dotx', '.html'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.epub', '.html'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.fb2', '.html'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.fodt', '.html'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.odt', '.html'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.ott', '.html'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.rtf', '.html'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.txt', '.html'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xml', '.html'); + + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.doc', '.dotx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.docm', '.dotx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.docx', '.dotx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.docxf', '.dotx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.dot', '.dotx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.dotm', '.dotx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.epub', '.dotx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.fb2', '.dotx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.fodt', '.dotx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.html', '.dotx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.mht', '.dotx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.odt', '.dotx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.ott', '.dotx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.rtf', '.dotx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.txt', '.dotx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xml', '.dotx'); + + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.doc', '.ott'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.docm', '.ott'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.docx', '.ott'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.docxf', '.ott'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.dot', '.ott'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.dotm', '.ott'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.dotx', '.ott'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.epub', '.ott'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.fb2', '.ott'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.fodt', '.ott'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.html', '.ott'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.mht', '.ott'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.odt', '.ott'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.rtf', '.ott'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.txt', '.ott'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xml', '.ott'); + + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.fodp', '.potx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.odp', '.potx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.otp', '.potx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.pot', '.potx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.potm', '.potx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.pps', '.potx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.ppsm', '.potx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.ppsx', '.potx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.ppt', '.potx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.pptm', '.potx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.pptx', '.potx'); + + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.fodp', '.otp'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.odp', '.otp'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.pot', '.otp'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.potm', '.otp'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.potx', '.otp'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.pps', '.otp'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.ppsm', '.otp'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.ppsx', '.otp'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.ppt', '.otp'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.pptm', '.otp'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.pptx', '.otp'); + + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.csv', '.xltx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.fods', '.xltx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.ods', '.xltx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.ots', '.xltx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xls', '.xltx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xlsm', '.xltx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xlsx', '.xltx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xlt', '.xltx'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xltm', '.xltx'); + + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.csv', '.ots'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.fods', '.ots'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.ods', '.ots'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xls', '.ots'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xlsm', '.ots'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xlsx', '.ots'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xlt', '.ots'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xltm', '.ots'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xltx', '.ots'); + + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.oxps', '.pdf'); + + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.doc', '.docm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.docx', '.docm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.dot', '.docm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.dotm', '.docm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.dotx', '.docm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.epub', '.docm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.fb2', '.docm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.fodt', '.docm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.html', '.docm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.mht', '.docm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.odt', '.docm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.ott', '.docm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.rtf', '.docm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.txt', '.docm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xml', '.docm'); + + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.doc', '.dotm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.docm', '.dotm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.docx', '.dotm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.dot', '.dotm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.dotx', '.dotm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.epub', '.dotm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.fb2', '.dotm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.fodt', '.dotm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.html', '.dotm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.mht', '.dotm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.odt', '.dotm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.ott', '.dotm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.rtf', '.dotm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.txt', '.dotm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xml', '.dotm'); + + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.csv', '.xlsm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.fods', '.xlsm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.ods', '.xlsm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.ots', '.xlsm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xls', '.xlsm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xlsx', '.xlsm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xlt', '.xlsm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xltm', '.xlsm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xltx', '.xlsm'); + + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.csv', '.xltm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.fods', '.xltm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.ods', '.xltm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.ots', '.xltm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xls', '.xltm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xlsm', '.xltm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xlsx', '.xltm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xlt', '.xltm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.xltx', '.xltm'); + + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.fodp', '.pptm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.odp', '.pptm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.otp', '.pptm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.pot', '.pptm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.potm', '.pptm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.potx', '.pptm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.pps', '.pptm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.ppsm', '.pptm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.ppsx', '.pptm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.ppt', '.pptm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.pptx', '.pptm'); + + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.fodp', '.potm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.odp', '.potm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.otp', '.potm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.pot', '.potm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.potx', '.potm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.pps', '.potm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.ppsm', '.potm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.ppsx', '.potm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.ppt', '.potm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.pptm', '.potm'); + INSERT IGNORE INTO `files_converts` (`input`, `output`) VALUES ('.pptx', '.potm'); + + CREATE TABLE IF NOT EXISTS `files_properties` ( + `tenant_id` int(10) NOT NULL, + `entry_id` varchar(32) NOT NULL, + `data` MEDIUMTEXT NOT NULL, + PRIMARY KEY (`tenant_id`, `entry_id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + INSERT IGNORE INTO `crm_currency_info` (`resource_key`, `abbreviation`, `symbol`, `culture_name`, `is_convertable`, `is_basic`) values ('Currency_EthiopianBirr', 'ETB', 'Br', 'ET', 0, 0); + + UPDATE `crm_currency_info` SET `symbol`='₼' WHERE `abbreviation`='AZN'; + + IF NOT EXISTS(SELECT * FROM information_schema.`COLUMNS` WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'login_events' AND COLUMN_NAME = 'active') THEN + ALTER TABLE `login_events` ADD COLUMN `active` INT(10) NOT NULL DEFAULT '0' AFTER `description`; + END IF; + + IF NOT EXISTS(SELECT * FROM information_schema.`COLUMNS` WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'calendar_events' AND COLUMN_NAME = 'has_attachments') THEN + ALTER TABLE `calendar_events` ADD COLUMN `has_attachments` TINYINT(1) NOT NULL DEFAULT '0' AFTER `time_zone`; + END IF; + + ALTER TABLE `mail_mail` ROW_FORMAT=DYNAMIC; + IF NOT EXISTS(SELECT * FROM information_schema.`COLUMNS` WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'mail_mail' AND COLUMN_NAME = 'read_request_status') THEN + ALTER TABLE `mail_mail` ADD COLUMN `read_request_status` TINYINT(1) NOT NULL DEFAULT '0' AFTER `chain_date`; + END IF; + + UPDATE `core_settings` + SET `value`=0xF547048A4865171587D9CEBC8A496C601D96031F2C1C3E9160353942EE765DACD316F4B5F42892436FC4A21B9A6DF8FFB511FD7F2A41135A1ACECB919F9FF3691847BCA535553925BAFB396012A2DA500065FA6442611833F0D7D8A969640FFD1D53B6ECCB3544FB029695943A88542597525CE31E346F289A9B077E9564A9570A81E48AB1654D43B1D8BDC901D588D86BC6FDDD6AFFC611440E6E5AAEB644DEBC4D9D131A4456610F5118ABD672BFAF383830347D52FD714729C9050876A2BF63C430C6DF4FCCAE1F61EC14D5DA5522104AC4D1EB0E47D12083C3540B424A1373FF6345EBD2CC0F0D048F7F987DD45B + WHERE `tenant`=-1 AND `id`='CompanyWhiteLabelSettings'; + +END DLM00 + +CALL upgrade120() DLM00 + +DELIMITER ; diff --git a/common/ASC.ActiveDirectory/ASC.ActiveDirectory.csproj b/common/ASC.ActiveDirectory/ASC.ActiveDirectory.csproj index 2db3cc976..548e57aca 100644 --- a/common/ASC.ActiveDirectory/ASC.ActiveDirectory.csproj +++ b/common/ASC.ActiveDirectory/ASC.ActiveDirectory.csproj @@ -29,11 +29,6 @@ prompt 4 - - - ..\..\packages\Mono.Security.3.2.3.0\lib\net45\Mono.Security.dll - - @@ -91,13 +86,13 @@ - 5.1.2 + 6.2.0 3.2.3 - 12.0.3 + 13.0.1 2.3.8.1 diff --git a/common/ASC.ActiveDirectory/ComplexOperations/LdapOperation.cs b/common/ASC.ActiveDirectory/ComplexOperations/LdapOperation.cs index b192cd837..6554b8bf2 100644 --- a/common/ASC.ActiveDirectory/ComplexOperations/LdapOperation.cs +++ b/common/ASC.ActiveDirectory/ComplexOperations/LdapOperation.cs @@ -109,7 +109,7 @@ namespace ASC.ActiveDirectory.ComplexOperations CoreContext.TenantManager.SetCurrentTenant(CurrentTenant); - SecurityContext.AuthenticateMe(Core.Configuration.Constants.CoreSystem); + SecurityContext.CurrentAccount = Core.Configuration.Constants.CoreSystem; Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(_culture); Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(_culture); diff --git a/common/ASC.ActiveDirectory/ComplexOperations/LdapSaveSyncOperation.cs b/common/ASC.ActiveDirectory/ComplexOperations/LdapSaveSyncOperation.cs index e5e08539a..bbfda9b5c 100644 --- a/common/ASC.ActiveDirectory/ComplexOperations/LdapSaveSyncOperation.cs +++ b/common/ASC.ActiveDirectory/ComplexOperations/LdapSaveSyncOperation.cs @@ -178,7 +178,7 @@ namespace ASC.ActiveDirectory.ComplexOperations Logger.DebugFormat("CoreContext.UserManager.SaveUserInfo({0})", existingLDAPUser.GetUserInfoString()); - CoreContext.UserManager.SaveUserInfo(existingLDAPUser); + CoreContext.UserManager.SaveUserInfo(existingLDAPUser, syncCardDav: true); break; case LdapOperationType.SaveTest: case LdapOperationType.SyncTest: @@ -852,7 +852,7 @@ namespace ASC.ActiveDirectory.ComplexOperations Logger.DebugFormat("CoreContext.UserManager.SaveUserInfo({0})", removedUser.GetUserInfoString()); - CoreContext.UserManager.SaveUserInfo(removedUser); + CoreContext.UserManager.SaveUserInfo(removedUser, syncCardDav: true); break; case LdapOperationType.SaveTest: case LdapOperationType.SyncTest: diff --git a/common/ASC.ActiveDirectory/LdapUserManager.cs b/common/ASC.ActiveDirectory/LdapUserManager.cs index 932754dee..628a32f03 100644 --- a/common/ASC.ActiveDirectory/LdapUserManager.cs +++ b/common/ASC.ActiveDirectory/LdapUserManager.cs @@ -126,7 +126,7 @@ namespace ASC.ActiveDirectory _log.DebugFormat("CoreContext.UserManager.SaveUserInfo({0})", ldapUserInfo.GetUserInfoString()); - portalUserInfo = CoreContext.UserManager.SaveUserInfo(ldapUserInfo); + portalUserInfo = CoreContext.UserManager.SaveUserInfo(ldapUserInfo, syncCardDav: true); var passwordHash = LdapUtils.GeneratePassword(); @@ -175,7 +175,7 @@ namespace ASC.ActiveDirectory _log.DebugFormat("CoreContext.UserManager.SaveUserInfo({0})", otherUser.GetUserInfoString()); - CoreContext.UserManager.SaveUserInfo(otherUser); + CoreContext.UserManager.SaveUserInfo(otherUser, syncCardDav: true); return true; } @@ -525,7 +525,7 @@ namespace ASC.ActiveDirectory { _log.DebugFormat("CoreContext.UserManager.SaveUserInfo({0})", userToUpdate.GetUserInfoString()); - portlaUserInfo = CoreContext.UserManager.SaveUserInfo(userToUpdate); + portlaUserInfo = CoreContext.UserManager.SaveUserInfo(userToUpdate, syncCardDav: true); } return true; @@ -600,7 +600,7 @@ namespace ASC.ActiveDirectory try { CoreContext.TenantManager.SetCurrentTenant(tenant); - SecurityContext.AuthenticateMe(Core.Configuration.Constants.CoreSystem); + SecurityContext.CurrentAccount = Core.Configuration.Constants.CoreSystem; var uInfo = SyncLDAPUser(ldapUserInfo.Item1); @@ -613,7 +613,7 @@ namespace ASC.ActiveDirectory _log.DebugFormat("TryGetAndSyncLdapUserInfo(login: \"{0}\") disabling user {1} due to not being included in any ldap group", login, uInfo); uInfo.Status = EmployeeStatus.Terminated; uInfo.Sid = null; - CoreContext.UserManager.SaveUserInfo(uInfo); + CoreContext.UserManager.SaveUserInfo(uInfo, syncCardDav: true); CookiesManager.ResetUserCookie(uInfo.ID); } } @@ -655,7 +655,7 @@ namespace ASC.ActiveDirectory { try { - SecurityContext.AuthenticateMe(Core.Configuration.Constants.CoreSystem); + SecurityContext.CurrentAccount = Core.Configuration.Constants.CoreSystem; userInfo = SyncLDAPUser(ldapUserInfo.Item1); @@ -675,7 +675,7 @@ namespace ASC.ActiveDirectory { userInfo.Sid = null; userInfo.Status = EmployeeStatus.Terminated; - CoreContext.UserManager.SaveUserInfo(userInfo); + CoreContext.UserManager.SaveUserInfo(userInfo, syncCardDav: true); throw new Exception("The user did not pass the configuration check by ldap group settings"); } diff --git a/common/ASC.Common/ASC.Common.csproj b/common/ASC.Common/ASC.Common.csproj index 68318ad48..b983464a8 100644 --- a/common/ASC.Common/ASC.Common.csproj +++ b/common/ASC.Common/ASC.Common.csproj @@ -104,7 +104,6 @@ - @@ -166,6 +165,16 @@ + + + + + + + + + + @@ -213,6 +222,7 @@ + @@ -241,34 +251,40 @@ 2.2.9 - 5.1.2 + 6.2.0 1.6.0.1 - 2.0.8 + 2.0.9 3.1.4 - 8.0.25 + 8.0.29 - 12.0.3 + 13.0.1 - 4.7.0 + 4.7.11 1.2.0 - 2.0.519 + 2.2.88 + + + 7.2.1 - 4.0.5 + 5.4.0 + + + 7.2.1 4.5.1 @@ -276,15 +292,14 @@ 4.5.4 - - 2.4.1 - + + \ No newline at end of file diff --git a/common/ASC.Common/Caching/AscCache.cs b/common/ASC.Common/Caching/AscCache.cs index dd232ff8f..ed118030c 100644 --- a/common/ASC.Common/Caching/AscCache.cs +++ b/common/ASC.Common/Caching/AscCache.cs @@ -171,7 +171,7 @@ namespace ASC.Common.Caching if (onchange != null) { - onchange.ToArray().ForEach(r => r(obj, action)); + onchange.ToList().ForEach(r => r(obj, action)); } } diff --git a/common/ASC.Common/Caching/RedisCache.cs b/common/ASC.Common/Caching/RedisCache.cs index 8470b99e3..37ffa4e8c 100644 --- a/common/ASC.Common/Caching/RedisCache.cs +++ b/common/ASC.Common/Caching/RedisCache.cs @@ -16,86 +16,94 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Configuration; -using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; -using ASC.Common.Logging; - using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using StackExchange.Redis; using StackExchange.Redis.Extensions.Core; -using StackExchange.Redis.Extensions.Core.Extensions; +using StackExchange.Redis.Extensions.Core.Abstractions; +using StackExchange.Redis.Extensions.Core.Implementations; using StackExchange.Redis.Extensions.LegacyConfiguration; +using StackExchange.Redis.Extensions.Newtonsoft; + +using LogManager = ASC.Common.Logging.BaseLogManager; namespace ASC.Common.Caching { public class RedisCache : ICache, ICacheNotify { - private readonly string CacheId = Guid.NewGuid().ToString(); - private readonly StackExchangeRedisCacheClient redis; - private readonly ConcurrentDictionary>> actions = new ConcurrentDictionary>>(); - + private readonly IRedisDatabase _redis; public RedisCache() { var configuration = ConfigurationManagerExtension.GetSection("redisCacheClient") as RedisCachingSectionHandler; + if (configuration == null) throw new ConfigurationErrorsException("Unable to locate section into your configuration file. Take a look https://github.com/imperugo/StackExchange.Redis.Extensions"); - var stringBuilder = new StringBuilder(); - using (var stream = new StringWriter(stringBuilder)) - { - var opts = RedisCachingSectionHandler.GetConfig().ConfigurationOptions; - opts.SyncTimeout = 60000; - var connectionMultiplexer = (IConnectionMultiplexer)ConnectionMultiplexer.Connect(opts, stream); - redis = new StackExchangeRedisCacheClient(connectionMultiplexer, new Serializer()); - LogManager.GetLogger("ASC").Debug(stringBuilder.ToString()); - } - } + var redisConfiguration = RedisCachingSectionHandler.GetConfig(); + var connectionPoolManager = new RedisCacheConnectionPoolManager(redisConfiguration); + + _redis = new RedisCacheClient(connectionPoolManager, new NewtonsoftSerializer(), redisConfiguration).GetDbFromConfiguration(); + } public T Get(string key) where T : class { - return redis.Get(key); + return Task.Run(() => _redis.GetAsync(key)) + .GetAwaiter() + .GetResult(); } public void Insert(string key, object value, TimeSpan sligingExpiration) { - redis.Replace(key, value, sligingExpiration); + Task.Run(() => _redis.ReplaceAsync(key, value, sligingExpiration)) + .GetAwaiter() + .GetResult(); } public void Insert(string key, object value, DateTime absolutExpiration) { - redis.Replace(key, value, absolutExpiration == DateTime.MaxValue ? DateTimeOffset.MaxValue : new DateTimeOffset(absolutExpiration)); + Task.Run(() => _redis.ReplaceAsync(key, value, absolutExpiration == DateTime.MaxValue ? DateTimeOffset.MaxValue : new DateTimeOffset(absolutExpiration))) + .GetAwaiter() + .GetResult(); } public void Remove(string key) { - redis.Remove(key); + Task.Run(() => _redis.RemoveAsync(key)) + .GetAwaiter() + .GetResult(); } public void Remove(Regex pattern) { var glob = pattern.ToString().Replace(".*", "*").Replace(".", "?"); - var keys = redis.SearchKeys(glob); + var keys = Task.Run(() => _redis.SearchKeysAsync(glob)) + .GetAwaiter() + .GetResult(); + if (keys.Any()) { - redis.RemoveAll(keys); + Task.Run(() => _redis.RemoveAllAsync(keys)) + .GetAwaiter() + .GetResult(); } + } public IDictionary HashGetAll(string key) { - var dic = redis.Database.HashGetAll(key); + var dic = _redis.Database.HashGetAll(key); + return dic .Select(e => { @@ -122,7 +130,8 @@ namespace ASC.Common.Caching public T HashGet(string key, string field) { - var value = (string)redis.Database.HashGet(key, field); + var value = (string)(_redis.Database.HashGet(key, field)); + try { return value != null ? JsonConvert.DeserializeObject(value) : default(T); @@ -130,6 +139,7 @@ namespace ASC.Common.Caching catch (Exception ex) { LogManager.GetLogger("ASC").Error(string.Format("RedisCache HashGet key: {0}, field: {1}", key, field), ex); + return default(T); } } @@ -138,149 +148,54 @@ namespace ASC.Common.Caching { if (value != null) { - redis.Database.HashSet(key, field, JsonConvert.SerializeObject(value)); + _redis.Database.HashSet(key, field, JsonConvert.SerializeObject(value)); } else { - redis.Database.HashDelete(key, field); + _redis.Database.HashDelete(key, field); } } - public void Publish(T obj, CacheNotifyAction action) + public void Publish(T obj, CacheNotifyAction cacheNotifyAction) { - redis.Publish("asc:channel:" + typeof(T).FullName, new RedisCachePubSubItem() { CacheId = CacheId, Object = obj, Action = action }); + var channelName = $"asc:channel:{cacheNotifyAction}:{typeof(T).FullName}".ToLowerInvariant(); - ConcurrentBag> onchange; - actions.TryGetValue(typeof(T), out onchange); - if (onchange != null) - { - onchange.ToArray().ForEach(r => r(obj, action)); - } + Task.Run(() => _redis.PublishAsync(channelName, new RedisCachePubSubItem() { Object = obj, Action = cacheNotifyAction })) + .GetAwaiter() + .GetResult(); } public void Subscribe(Action onchange) { - redis.Subscribe>("asc:channel:" + typeof(T).FullName, (i) => + foreach (var cacheNotifyAction in Enum.GetNames(typeof(CacheNotifyAction))) { - if (i.CacheId != CacheId) + var channelName = $"asc:channel:{cacheNotifyAction}:{typeof(T).FullName}".ToLowerInvariant(); + + Task.Run(() => _redis.SubscribeAsync>(channelName, (i) => { onchange(i.Object, i.Action); - } - }); - if (onchange != null) - { - Action action = (o, a) => onchange((T)o, a); - actions.AddOrUpdate(typeof(T), - new ConcurrentBag> { action }, - (type, bag) => - { - bag.Add(action); - return bag; - }); - } - else - { - ConcurrentBag> removed; - actions.TryRemove(typeof(T), out removed); + return Task.FromResult(true); + })).GetAwaiter() + .GetResult(); } } + public void PushMailAction(string QueueName, T value) where T : class + { + if (value != null) + { + Task.Run(() => _redis.ListAddToLeftAsync(QueueName, value)) + .GetAwaiter() + .GetResult(); + } + } - [Serializable] class RedisCachePubSubItem { - public string CacheId { get; set; } - public T Object { get; set; } public CacheNotifyAction Action { get; set; } } - - class Serializer : ISerializer - { - private readonly Encoding enc = Encoding.UTF8; - - - public byte[] Serialize(object item) - { - try - { - var s = JsonConvert.SerializeObject(item); - return enc.GetBytes(s); - } - catch (Exception e) - { - LogManager.GetLogger("ASC").Error("Redis Serialize", e); - throw; - } - } - - public object Deserialize(byte[] obj) - { - try - { - var resolver = new ContractResolver(); - var settings = new JsonSerializerSettings { ContractResolver = resolver }; - var s = enc.GetString(obj); - return JsonConvert.DeserializeObject(s, typeof(object), settings); - } - catch (Exception e) - { - LogManager.GetLogger("ASC").Error("Redis Deserialize", e); - throw; - } - } - - public T Deserialize(byte[] obj) - { - try - { - var resolver = new ContractResolver(); - var settings = new JsonSerializerSettings { ContractResolver = resolver }; - var s = enc.GetString(obj); - return JsonConvert.DeserializeObject(s, settings); - } - catch (Exception e) - { - LogManager.GetLogger("ASC").Error("Redis Deserialize", e); - throw; - } - } - - public async Task SerializeAsync(object item) - { - return await Task.Factory.StartNew(() => Serialize(item)); - } - - public Task DeserializeAsync(byte[] obj) - { - return Task.Factory.StartNew(() => Deserialize(obj)); - } - - public Task DeserializeAsync(byte[] obj) - { - return Task.Factory.StartNew(() => Deserialize(obj)); - } - - - class ContractResolver : DefaultContractResolver - { - protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) - { - var prop = base.CreateProperty(member, memberSerialization); - if (!prop.Writable) - { - var property = member as PropertyInfo; - if (property != null) - { - var hasPrivateSetter = property.GetSetMethod(true) != null; - prop.Writable = hasPrivateSetter; - } - } - return prop; - } - } - } } } diff --git a/common/ASC.Common/Data/DbManager.cs b/common/ASC.Common/Data/DbManager.cs index e164ffe72..459b42d43 100644 --- a/common/ASC.Common/Data/DbManager.cs +++ b/common/ASC.Common/Data/DbManager.cs @@ -27,6 +27,7 @@ using ASC.Common.Data.Sql; using ASC.Common.Logging; using ASC.Common.Web; +using LogManager = ASC.Common.Logging.BaseLogManager; namespace ASC.Common.Data { diff --git a/common/ASC.Common/Geolocation/GeolocationHelper.cs b/common/ASC.Common/Geolocation/GeolocationHelper.cs index 5901ccfa2..f9fce4b62 100644 --- a/common/ASC.Common/Geolocation/GeolocationHelper.cs +++ b/common/ASC.Common/Geolocation/GeolocationHelper.cs @@ -24,6 +24,8 @@ using ASC.Common.Data.Sql; using ASC.Common.Data.Sql.Expressions; using ASC.Common.Logging; +using LogManager = ASC.Common.Logging.BaseLogManager; + namespace ASC.Geolocation { public class GeolocationHelper diff --git a/common/ASC.Common/Logging/Log.cs b/common/ASC.Common/Logging/Log.cs index 7fc6d9862..429821dd6 100644 --- a/common/ASC.Common/Logging/Log.cs +++ b/common/ASC.Common/Logging/Log.cs @@ -46,42 +46,27 @@ namespace ASC.Common.Logging void Debug(object message); void Debug(object message, Exception exception); void DebugFormat(string format, params object[] args); - void DebugFormat(string format, object arg0); - void DebugFormat(string format, object arg0, object arg1); - void DebugFormat(string format, object arg0, object arg1, object arg2); void DebugFormat(IFormatProvider provider, string format, params object[] args); void Info(object message); void Info(string message, Exception exception); void InfoFormat(string format, params object[] args); - void InfoFormat(string format, object arg0); - void InfoFormat(string format, object arg0, object arg1); - void InfoFormat(string format, object arg0, object arg1, object arg2); void InfoFormat(IFormatProvider provider, string format, params object[] args); void Warn(object message); void Warn(object message, Exception exception); void WarnFormat(string format, params object[] args); - void WarnFormat(string format, object arg0); - void WarnFormat(string format, object arg0, object arg1); - void WarnFormat(string format, object arg0, object arg1, object arg2); void WarnFormat(IFormatProvider provider, string format, params object[] args); void Error(object message); void Error(object message, Exception exception); void ErrorFormat(string format, params object[] args); - void ErrorFormat(string format, object arg0); - void ErrorFormat(string format, object arg0, object arg1); - void ErrorFormat(string format, object arg0, object arg1, object arg2); void ErrorFormat(IFormatProvider provider, string format, params object[] args); void Fatal(object message); void Fatal(string message, Exception exception); void FatalFormat(string format, params object[] args); - void FatalFormat(string format, object arg0); - void FatalFormat(string format, object arg0, object arg1); - void FatalFormat(string format, object arg0, object arg1, object arg2); void FatalFormat(IFormatProvider provider, string format, params object[] args); string LogDirectory { get; } @@ -108,7 +93,7 @@ namespace ASC.Common.Logging public bool IsTraceEnabled { get; private set; } - public Log(string name) + public Log(string name, Func getAlias) { loger = log4net.LogManager.GetLogger(name); @@ -145,21 +130,6 @@ namespace ASC.Common.Logging if (IsDebugEnabled) loger.DebugFormat(format, args); } - public void DebugFormat(string format, object arg0) - { - if (IsDebugEnabled) loger.DebugFormat(format, arg0); - } - - public void DebugFormat(string format, object arg0, object arg1) - { - if (IsDebugEnabled) loger.DebugFormat(format, arg0, arg1); - } - - public void DebugFormat(string format, object arg0, object arg1, object arg2) - { - if (IsDebugEnabled) loger.DebugFormat(format, arg0, arg1, arg2); - } - public void DebugFormat(IFormatProvider provider, string format, params object[] args) { if (IsDebugEnabled) loger.DebugFormat(provider, format, args); @@ -193,21 +163,6 @@ namespace ASC.Common.Logging if (IsInfoEnabled) loger.InfoFormat(format, args); } - public void InfoFormat(string format, object arg0) - { - if (IsInfoEnabled) loger.InfoFormat(format, arg0); - } - - public void InfoFormat(string format, object arg0, object arg1) - { - if (IsInfoEnabled) loger.InfoFormat(format, arg0, arg1); - } - - public void InfoFormat(string format, object arg0, object arg1, object arg2) - { - if (IsInfoEnabled) loger.InfoFormat(format, arg0, arg1, arg2); - } - public void InfoFormat(IFormatProvider provider, string format, params object[] args) { if (IsInfoEnabled) loger.InfoFormat(provider, format, args); @@ -229,21 +184,6 @@ namespace ASC.Common.Logging if (IsWarnEnabled) loger.WarnFormat(format, args); } - public void WarnFormat(string format, object arg0) - { - if (IsWarnEnabled) loger.WarnFormat(format, arg0); - } - - public void WarnFormat(string format, object arg0, object arg1) - { - if (IsWarnEnabled) loger.WarnFormat(format, arg0, arg1); - } - - public void WarnFormat(string format, object arg0, object arg1, object arg2) - { - if (IsWarnEnabled) loger.WarnFormat(format, arg0, arg1, arg2); - } - public void WarnFormat(IFormatProvider provider, string format, params object[] args) { if (IsWarnEnabled) loger.WarnFormat(provider, format, args); @@ -265,21 +205,6 @@ namespace ASC.Common.Logging if (IsErrorEnabled) loger.ErrorFormat(format, args); } - public void ErrorFormat(string format, object arg0) - { - if (IsErrorEnabled) loger.ErrorFormat(format, arg0); - } - - public void ErrorFormat(string format, object arg0, object arg1) - { - if (IsErrorEnabled) loger.ErrorFormat(format, arg0, arg1); - } - - public void ErrorFormat(string format, object arg0, object arg1, object arg2) - { - if (IsErrorEnabled) loger.ErrorFormat(format, arg0, arg1, arg2); - } - public void ErrorFormat(IFormatProvider provider, string format, params object[] args) { if (IsErrorEnabled) loger.ErrorFormat(provider, format, args); @@ -301,21 +226,6 @@ namespace ASC.Common.Logging if (IsFatalEnabled) loger.FatalFormat(format, args); } - public void FatalFormat(string format, object arg0) - { - if (IsFatalEnabled) loger.FatalFormat(format, arg0); - } - - public void FatalFormat(string format, object arg0, object arg1) - { - if (IsFatalEnabled) loger.FatalFormat(format, arg0, arg1); - } - - public void FatalFormat(string format, object arg0, object arg1, object arg2) - { - if (IsFatalEnabled) loger.FatalFormat(format, arg0, arg1, arg2); - } - public void FatalFormat(IFormatProvider provider, string format, params object[] args) { if (IsFatalEnabled) loger.FatalFormat(provider, format, args); @@ -328,23 +238,20 @@ namespace ASC.Common.Logging return log4net.GlobalContext.Properties["LogDirectory"].ToString(); } } + } public class LogNLog : ILog { private readonly NLog.ILogger loger; private readonly string name; + private Func getAlias; public bool IsDebugEnabled { get; private set; } - public bool IsInfoEnabled { get; private set; } - public bool IsWarnEnabled { get; private set; } - public bool IsErrorEnabled { get; private set; } - public bool IsFatalEnabled { get; private set; } - public bool IsTraceEnabled { get; private set; } static LogNLog() @@ -355,18 +262,16 @@ namespace ASC.Common.Logging { if (args[i] == "--log" && !string.IsNullOrEmpty(args[i + 1])) { - NLog.LogManager.Configuration.Variables["svcName"] = args[i + 1].Trim().Trim('"'); + LogManager.Configuration.Variables["svcName"] = args[i + 1].Trim().Trim('"'); } } - - NLog.Targets.Target.Register("SelfCleaning"); } - public LogNLog(string name) + public LogNLog(string name, Func getAlias) { this.name = name; - - loger = NLog.LogManager.GetLogger(name); + loger = LogManager.GetLogger(name); + this.getAlias = getAlias; IsDebugEnabled = loger.IsDebugEnabled; IsInfoEnabled = loger.IsInfoEnabled; @@ -378,207 +283,161 @@ namespace ASC.Common.Logging public void Trace(object message) { - if (IsTraceEnabled) loger.Log(LogLevel.Trace, message); + Log(LogLevel.Trace, message); } public void TraceFormat(string message, object arg0) { - if (IsTraceEnabled) loger.Log(LogLevel.Trace, string.Format(message, arg0)); + Log(LogLevel.Trace, string.Format(message, arg0)); } public void Debug(object message) { - if (IsDebugEnabled) loger.Debug(message); + Log(LogLevel.Debug, message); } public void Debug(object message, Exception exception) { - if (IsDebugEnabled) loger.Debug(exception, "{0}", message); + Log(LogLevel.Debug, message, exception); } public void DebugFormat(string format, params object[] args) { - if (IsDebugEnabled) loger.Debug(format, args); - } - - public void DebugFormat(string format, object arg0) - { - if (IsDebugEnabled) loger.Debug(format, arg0); - } - - public void DebugFormat(string format, object arg0, object arg1) - { - if (IsDebugEnabled) loger.Debug(format, arg0, arg1); - } - - public void DebugFormat(string format, object arg0, object arg1, object arg2) - { - if (IsDebugEnabled) loger.Debug(format, arg0, arg1, arg2); + Log(LogLevel.Debug, string.Format(format, args)); } public void DebugFormat(IFormatProvider provider, string format, params object[] args) { - if (IsDebugEnabled) loger.Debug(provider, format, args); + Log(LogLevel.Debug, string.Format(format, args), provider); } public void DebugWithProps(string message, params KeyValuePair[] props) { - if (!IsDebugEnabled) return; - - var theEvent = new LogEventInfo { Message = message, LoggerName = name, Level = LogLevel.Debug }; - - foreach (var p in props) - { - theEvent.Properties[p.Key] = p.Value; - } - - loger.Log(theEvent); + Log(LogLevel.Debug, message, props); } public void Info(object message) { - if (IsInfoEnabled) loger.Info(message); + Log(LogLevel.Info, message); } public void Info(string message, Exception exception) { - if (IsInfoEnabled) loger.Info(exception, message); + Log(LogLevel.Info, message,exception); } public void InfoFormat(string format, params object[] args) { - if (IsInfoEnabled) loger.Info(format, args); - } - - public void InfoFormat(string format, object arg0) - { - if (IsInfoEnabled) loger.Info(format, arg0); - } - - public void InfoFormat(string format, object arg0, object arg1) - { - if (IsInfoEnabled) loger.Info(format, arg0, arg1); - } - - public void InfoFormat(string format, object arg0, object arg1, object arg2) - { - if (IsInfoEnabled) loger.Info(format, arg0, arg1, arg2); + Log(LogLevel.Info, string.Format(format, args)); } public void InfoFormat(IFormatProvider provider, string format, params object[] args) { - if (IsInfoEnabled) loger.Info(provider, format, args); + Log(LogLevel.Info, string.Format(format, args), provider); } public void Warn(object message) { - if (IsWarnEnabled) loger.Warn(message); + Log(LogLevel.Warn, message); } public void Warn(object message, Exception exception) { - if (IsWarnEnabled) loger.Warn(exception, "{0}", message); + Log(LogLevel.Warn, message, exception); } public void WarnFormat(string format, params object[] args) { - if (IsWarnEnabled) loger.Warn(format, args); - } - - public void WarnFormat(string format, object arg0) - { - if (IsWarnEnabled) loger.Warn(format, arg0); - } - - public void WarnFormat(string format, object arg0, object arg1) - { - if (IsWarnEnabled) loger.Warn(format, arg0, arg1); - } - - public void WarnFormat(string format, object arg0, object arg1, object arg2) - { - if (IsWarnEnabled) loger.Warn(format, arg0, arg1, arg2); + Log(LogLevel.Warn, string.Format(format, args)); } public void WarnFormat(IFormatProvider provider, string format, params object[] args) { - if (IsWarnEnabled) loger.Warn(provider, format, args); + Log(LogLevel.Warn, string.Format(format, args), provider); } - public void Error(object message) { - if (IsErrorEnabled) loger.Error(message); + Log(LogLevel.Error, message); } public void Error(object message, Exception exception) { - if (IsErrorEnabled) loger.Error(exception, "{0}", message); + Log(LogLevel.Error, message, exception); } public void ErrorFormat(string format, params object[] args) { - if (IsErrorEnabled) loger.Error(format, args); - } - - public void ErrorFormat(string format, object arg0) - { - if (IsErrorEnabled) loger.Error(format, arg0); - } - - public void ErrorFormat(string format, object arg0, object arg1) - { - if (IsErrorEnabled) loger.Error(format, arg0, arg1); - } - - public void ErrorFormat(string format, object arg0, object arg1, object arg2) - { - if (IsErrorEnabled) loger.Error(format, arg0, arg1, arg2); + Log(LogLevel.Error, string.Format(format,args)); } public void ErrorFormat(IFormatProvider provider, string format, params object[] args) { - if (IsErrorEnabled) loger.Error(provider, format, args); + Log(LogLevel.Error, string.Format(format, args), provider); } public void Fatal(object message) { - if (IsFatalEnabled) loger.Fatal(message); + Log(LogLevel.Fatal, message); } public void Fatal(string message, Exception exception) { - if (IsFatalEnabled) loger.Fatal(exception, message); + Log(LogLevel.Fatal, message,exception); } public void FatalFormat(string format, params object[] args) { - if (IsFatalEnabled) loger.Fatal(format, args); - } - - public void FatalFormat(string format, object arg0) - { - if (IsFatalEnabled) loger.Fatal(format, arg0); - } - - public void FatalFormat(string format, object arg0, object arg1) - { - if (IsFatalEnabled) loger.Fatal(format, arg0, arg1); - } - - public void FatalFormat(string format, object arg0, object arg1, object arg2) - { - if (IsFatalEnabled) loger.Fatal(format, arg0, arg1, arg2); + Log(LogLevel.Fatal, string.Format(format,args)); } public void FatalFormat(IFormatProvider provider, string format, params object[] args) { - if (IsFatalEnabled) loger.Fatal(provider, format, args); + Log(LogLevel.Fatal, string.Format(format, args), provider); + } + + private void Log(LogLevel level, object message) + { + var theEvent = new LogEventInfo { Message = message.ToString(), LoggerName = name, Level = level }; + Log(theEvent); + } + + private void Log(LogLevel level, object message, params KeyValuePair[] props) + { + var theEvent = new LogEventInfo { Message = message.ToString(), LoggerName = name, Level = level }; + foreach (var p in props) + { + theEvent.Properties[p.Key] = p.Value; + } + Log(theEvent); + } + + private void Log(LogLevel level, object message, Exception exception) + { + var theEvent = new LogEventInfo { Message = message.ToString(), LoggerName = name, Level = level, Exception = exception }; + Log(theEvent); + } + + private void Log(LogLevel level, object message, IFormatProvider provider) + { + var theEvent = new LogEventInfo { Message = message.ToString(), LoggerName = name, Level = level, FormatProvider = provider }; + Log(theEvent); + } + + private void Log(LogEventInfo theEvent) + { + var alias = getAlias == null ? null : getAlias(); + if (alias != null) + { + theEvent.Properties["alias"] = alias + " - "; + } + loger.Log(theEvent); } public string LogDirectory { get { return NLog.LogManager.Configuration.Variables["logDirectory"].Text; } } + } public class NullLog : ILog @@ -642,18 +501,6 @@ namespace ASC.Common.Logging { } - public void InfoFormat(string format, object arg0) - { - } - - public void InfoFormat(string format, object arg0, object arg1) - { - } - - public void InfoFormat(string format, object arg0, object arg1, object arg2) - { - } - public void InfoFormat(IFormatProvider provider, string format, params object[] args) { } @@ -670,18 +517,6 @@ namespace ASC.Common.Logging { } - public void WarnFormat(string format, object arg0) - { - } - - public void WarnFormat(string format, object arg0, object arg1) - { - } - - public void WarnFormat(string format, object arg0, object arg1, object arg2) - { - } - public void WarnFormat(IFormatProvider provider, string format, params object[] args) { } @@ -698,18 +533,6 @@ namespace ASC.Common.Logging { } - public void ErrorFormat(string format, object arg0) - { - } - - public void ErrorFormat(string format, object arg0, object arg1) - { - } - - public void ErrorFormat(string format, object arg0, object arg1, object arg2) - { - } - public void ErrorFormat(IFormatProvider provider, string format, params object[] args) { } @@ -726,32 +549,21 @@ namespace ASC.Common.Logging { } - public void FatalFormat(string format, object arg0) - { - } - - public void FatalFormat(string format, object arg0, object arg1) - { - } - - public void FatalFormat(string format, object arg0, object arg1, object arg2) - { - } - public void FatalFormat(IFormatProvider provider, string format, params object[] args) { } public string LogDirectory { get { return ""; } } + } - public class LogManager + public class BaseLogManager { internal static IContainer Builder { get; set; } internal static ConcurrentDictionary Logs; - static LogManager() + static BaseLogManager() { var container = AutofacConfigLoader.Load("core"); if (container != null) @@ -762,14 +574,13 @@ namespace ASC.Common.Logging Logs = new ConcurrentDictionary(); } - public static ILog GetLogger(string name) + public static ILog GetLogger(string name, Func getAlias = null) { ILog result; if (!Logs.TryGetValue(name, out result)) { - result = Logs.AddOrUpdate(name, Builder != null ? Builder.Resolve(new TypedParameter(typeof(string), name)) : new NullLog(), (k, v) => v); + result = Logs.AddOrUpdate(name, Builder != null ? Builder.Resolve(new TypedParameter(typeof(string), name), new TypedParameter(typeof(Func), getAlias)) : new NullLog(), (k, v) => v); } - return result; } } diff --git a/common/ASC.Common/Logging/SelfCleaningTarget.cs b/common/ASC.Common/Logging/SelfCleaningTarget.cs deleted file mode 100644 index f76c7fae2..000000000 --- a/common/ASC.Common/Logging/SelfCleaningTarget.cs +++ /dev/null @@ -1,141 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -using NLog; -using NLog.Common; -using NLog.Targets; - -namespace ASC.Common.Logging -{ - [Target("SelfCleaning")] - public class SelfCleaningTarget : FileTarget - { - private static DateTime _lastCleanDate; - - private static int? _cleanPeriod; - - private static int GetCleanPeriod() - { - if (_cleanPeriod != null) - return _cleanPeriod.Value; - - var value = 30; - - const string key = "cleanPeriod"; - - if (NLog.LogManager.Configuration.Variables.Keys.Contains(key)) - { - var variable = NLog.LogManager.Configuration.Variables[key]; - - if (variable != null && !string.IsNullOrEmpty(variable.Text)) - { - int.TryParse(variable.Text, out value); - } - } - - _cleanPeriod = value; - - return value; - } - - private void Clean() - { - var filePath = string.Empty; - var dirPath = string.Empty; - - try - { - if (FileName == null) - return; - - filePath = ((NLog.Layouts.SimpleLayout)FileName).Text; - - if (string.IsNullOrEmpty(filePath)) - return; - - dirPath = Path.GetDirectoryName(filePath); - - if (string.IsNullOrEmpty(dirPath)) - return; - - if (!Path.IsPathRooted(dirPath)) - dirPath = Path.Combine(Environment.CurrentDirectory, dirPath); - - var directory = new DirectoryInfo(dirPath); - - if (!directory.Exists) - return; - - var files = directory.GetFiles(); - - var cleanPeriod = GetCleanPeriod(); - - foreach (var file in files.Where(file => (DateTime.UtcNow.Date - file.CreationTimeUtc.Date).Days > cleanPeriod)) - { - file.Delete(); - } - } - catch (Exception err) - { - base.Write(new LogEventInfo - { - Exception = err, - Level = LogLevel.Error, - Message = String.Format("file: {0}, dir: {1}, mess: {2}", filePath, dirPath, err.Message), - LoggerName = "SelfCleaningTarget" - }); - } - } - - protected override void Write(IList logEvents) - { - if (DateTime.UtcNow.Date > _lastCleanDate.Date) - { - _lastCleanDate = DateTime.UtcNow.Date; - Clean(); - } - - var buffer = new List(); - - foreach (var logEvent in logEvents) - { - buffer.Add(logEvent); - if (buffer.Count < 10) continue; - base.Write(buffer); - buffer.Clear(); - } - - base.Write(buffer); - } - - protected override void Write(LogEventInfo logEvent) - { - if (DateTime.UtcNow.Date > _lastCleanDate.Date) - { - _lastCleanDate = DateTime.UtcNow.Date; - Clean(); - } - - base.Write(logEvent); - } - } -} diff --git a/common/ASC.Common/Notify/Context.cs b/common/ASC.Common/Notify/Context.cs index c0ba18ffe..c655b6139 100644 --- a/common/ASC.Common/Notify/Context.cs +++ b/common/ASC.Common/Notify/Context.cs @@ -19,12 +19,13 @@ using System; using System.Collections.Generic; using System.Linq; -using ASC.Common.Logging; using ASC.Notify.Channels; using ASC.Notify.Engine; using ASC.Notify.Model; using ASC.Notify.Sinks; +using LogManager = ASC.Common.Logging.BaseLogManager; + namespace ASC.Notify { public sealed class Context : INotifyRegistry diff --git a/common/ASC.Common/Notify/Engine/DispatchEngine.cs b/common/ASC.Common/Notify/Engine/DispatchEngine.cs index 35309ba54..331916653 100644 --- a/common/ASC.Common/Notify/Engine/DispatchEngine.cs +++ b/common/ASC.Common/Notify/Engine/DispatchEngine.cs @@ -21,6 +21,8 @@ using System.Configuration; using ASC.Common.Logging; using ASC.Notify.Messages; +using LogManager = ASC.Common.Logging.BaseLogManager; + namespace ASC.Notify.Engine { public class DispatchEngine diff --git a/common/ASC.Common/Notify/Engine/NotifyEngine.cs b/common/ASC.Common/Notify/Engine/NotifyEngine.cs index 4109f065d..b7a40a2b1 100644 --- a/common/ASC.Common/Notify/Engine/NotifyEngine.cs +++ b/common/ASC.Common/Notify/Engine/NotifyEngine.cs @@ -28,6 +28,8 @@ using ASC.Notify.Messages; using ASC.Notify.Patterns; using ASC.Notify.Recipients; +using LogManager = ASC.Common.Logging.BaseLogManager; + namespace ASC.Notify.Engine { public class NotifyEngine : INotifyEngine diff --git a/common/ASC.Common/Notify/Engine/NotifyRequest.cs b/common/ASC.Common/Notify/Engine/NotifyRequest.cs index a7021d6b7..26510269b 100644 --- a/common/ASC.Common/Notify/Engine/NotifyRequest.cs +++ b/common/ASC.Common/Notify/Engine/NotifyRequest.cs @@ -19,12 +19,13 @@ using System; using System.Collections; using System.Collections.Generic; -using ASC.Common.Logging; using ASC.Notify.Messages; using ASC.Notify.Model; using ASC.Notify.Patterns; using ASC.Notify.Recipients; +using LogManager = ASC.Common.Logging.BaseLogManager; + namespace ASC.Notify.Engine { public class NotifyRequest diff --git a/common/ASC.Common/Notify/Model/ISubscriptionProvider.cs b/common/ASC.Common/Notify/Model/ISubscriptionProvider.cs index f9d3a1c90..666dcc1af 100644 --- a/common/ASC.Common/Notify/Model/ISubscriptionProvider.cs +++ b/common/ASC.Common/Notify/Model/ISubscriptionProvider.cs @@ -15,15 +15,12 @@ */ -#region usings - using System; using System.Linq; -using ASC.Common.Logging; using ASC.Notify.Recipients; -#endregion +using LogManager = ASC.Common.Logging.BaseLogManager; namespace ASC.Notify.Model { diff --git a/common/ASC.Common/Radicale/CalDavCalendar.cs b/common/ASC.Common/Radicale/CalDavCalendar.cs new file mode 100644 index 000000000..f07acd1b8 --- /dev/null +++ b/common/ASC.Common/Radicale/CalDavCalendar.cs @@ -0,0 +1,96 @@ +using System; +using System.Threading.Tasks; +using System.Web; + +namespace ASC.Common.Radicale +{ + public class CalDavCalendar : RadicaleEntity + { + public bool IsShared { set; get; } = false; + + public readonly string strUpdateTemplate = "" + + "" + + "" + + "" + + "{0}{1}" + + "{2}" + + ""; + + public readonly string strCreateTemplate = "" + + "" + + "" + + "" + + "" + + "{0}" + + "{1}" + + "{2}"; + + public CalDavCalendar(string uid, bool isShared) + { + Uid = uid; + IsShared = isShared; + } + + public async Task CreateAsync(string name, string description, string backgroundColor, Uri uri, string userName, string authorization) + { + var requestUrl = uri.Scheme + "://" + uri.Host + "/caldav/" + HttpUtility.UrlEncode(userName) + "/" + Uid + (IsShared ? "-readonly" : ""); + + var davRequest = new DavRequest() + { + Url = requestUrl, + Authorization = authorization, + Data = GetData(strCreateTemplate, name, description, backgroundColor) + }; + + try + { + return await RadicaleClient.CreateAsync(davRequest).ConfigureAwait(false); + } + catch (Exception ex) + { + throw new RadicaleException(ex.Message); + } + + } + + public async Task Update(string name, string description, string backgroundColor, Uri uri, string userName, string authorization) + { + var requestUrl = GetRadicaleUrl(uri.ToString(), userName, IsShared, isRedirectUrl: true, entityId: Uid); + + var davRequest = new DavRequest() + { + Url = requestUrl, + Authorization = authorization, + Data = GetData(strUpdateTemplate, name, description, backgroundColor) + }; + + return await RadicaleClient.UpdateAsync(davRequest).ConfigureAwait(false); + } + + public async Task GetCollection(string url, string authorization) + { + var davRequest = new DavRequest() + { + Url = url, + Authorization = authorization + }; + + return await RadicaleClient.GetAsync(davRequest).ConfigureAwait(false); + } + + public async Task UpdateItem(string url, string authorization, string data, string headerUrl = "") + { + var davRequest = new DavRequest() + { + Url = url, + Authorization = authorization, + Header = headerUrl, + Data = data + }; + + return await RadicaleClient.UpdateItemAsync(davRequest).ConfigureAwait(false); + } + + + } +} diff --git a/common/ASC.Common/Radicale/CardDavAddressbook.cs b/common/ASC.Common/Radicale/CardDavAddressbook.cs new file mode 100644 index 000000000..413afff5b --- /dev/null +++ b/common/ASC.Common/Radicale/CardDavAddressbook.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using System.Web; + +using ASC.Common.Logging; +using ASC.Common.Radicale.Core; + +namespace ASC.Common.Radicale +{ + public class CardDavAddressbook : RadicaleEntity + { + private static readonly ILog Logger = BaseLogManager.GetLogger("ASC.Radicale"); + + public readonly string strTemplate = "" + + "" + "" + + "" + + "" + + "{0}" + + "{1}" + + "{2}" + + ""; + + public async Task Create(string name, string description, string backgroundColor, string uri, string authorization, bool isReadonly = true) + { + var rewriterUri = uri.StartsWith("http") ? uri : ""; + + var davRequest = new DavRequest() + { + Url = uri, + Authorization = authorization, + Header = rewriterUri, + Data = GetData(strTemplate, name, description, backgroundColor) + }; + + return await RadicaleClient.CreateAsync(davRequest).ConfigureAwait(false); + } + + public async Task Update(string name, string description, string backgroundColor, string uri, string userName, string authorization, bool isReadonly = true) + { + var addbookId = isReadonly ? readonlyAddBookName : defaultAddBookName; + + var header = uri.StartsWith("http") ? uri : ""; + + var requestUrl = defaultRadicaleUrl + "/" + HttpUtility.UrlEncode(userName) + "/" + addbookId; + + var davRequest = new DavRequest() + { + Url = requestUrl, + Authorization = authorization, + Data = GetData(strTemplate, name, description, backgroundColor), + Header = header + }; + + return await RadicaleClient.UpdateAsync(davRequest).ConfigureAwait(false); + } + + + public async Task GetCollection(string url, string authorization, string myUri) + { + var path = (new Uri(url).AbsolutePath.StartsWith("/carddav")) ? (new Uri(url).AbsolutePath.Remove(0, 8)) : new Uri(url).AbsolutePath; + var defaultUrlconn = defaultRadicaleUrl + path; + var davRequest = new DavRequest() + { + Url = defaultUrlconn, + Authorization = authorization, + Header = myUri + }; + + return await RadicaleClient.GetAsync(davRequest).ConfigureAwait(false); + } + + public async Task UpdateItem(string url, string authorization, string data, string headerUrl = "") + { + var path = (new Uri(url).AbsolutePath.StartsWith("/carddav")) ? (new Uri(url).AbsolutePath.Remove(0, 8)) : new Uri(url).AbsolutePath; + var requrl = defaultRadicaleUrl + path; + var davRequest = new DavRequest() + { + Url = requrl, + Authorization = authorization, + Header = headerUrl, + Data = data + }; + + return await RadicaleClient.UpdateItemAsync(davRequest).ConfigureAwait(false); + } + + public string GetUserSerialization(CardDavItem user) + { + var sex = (user.Sex.HasValue) ? user.Sex.Value ? "M" : "W" : string.Empty; + + var builder = new StringBuilder(); + + builder.AppendLine("BEGIN:VCARD"); + builder.AppendLine("UID:" + user.ID.ToString()); + builder.AppendLine("N:" + user.LastName + ";" + user.FirstName); + builder.AppendLine("FN:" + user.FirstName + " " + user.LastName); + builder.AppendLine("EMAIL:" + user.Email); + builder.AppendLine("TEL:" + user.MobilePhone); + builder.AppendLine($"BDAY:{user.BirthDate:s}"); + builder.AppendLine("TITLE:" + user.Title); + builder.AppendLine("URL:" + ""); + builder.AppendLine("GENDER:" + sex); + builder.AppendLine($"REV:{DateTime.Now:s}"); + builder.AppendLine("TZ:" + DateTimeOffset.Now.Offset); + builder.AppendLine("ORG:"); + builder.AppendLine("END:VCARD"); + + return builder.ToString(); + } + + public void Delete(string uri, Guid userID, string email, int tenantId = 0) + { + var authorization = GetSystemAuthorization(); + var deleteUrlBook = GetRadicaleUrl(uri, email.ToLower(), true, true); + var davRequest = new DavRequest() + { + Url = deleteUrlBook, + Authorization = authorization + }; + try + { + RadicaleClient.RemoveAsync(davRequest).ConfigureAwait(false); + var dbConn = new DbRadicale(); + dbConn.RemoveCardDavUser(tenantId, userID.ToString()); + } + catch (Exception ex) + { + Logger.Error("ERROR: " + ex.Message); + } + } + + public void UpdateItemForAllAddBooks(List emailList, string uri, CardDavItem user, int tenantId = 0, string changedEmail = null) + { + + var authorization = GetSystemAuthorization(); + if (changedEmail != null) + { + var deleteUrlBook = GetRadicaleUrl(uri, changedEmail.ToLower(), true, true); + var davRequest = new DavRequest() + { + Url = deleteUrlBook, + Authorization = authorization + }; + RadicaleClient.RemoveAsync(davRequest).ConfigureAwait(false); + + try + { + var dbConn = new DbRadicale(); + dbConn.RemoveCardDavUser(tenantId, user.ID.ToString()); + } + catch (Exception ex) + { + Logger.Error("ERROR: " + ex.Message); + } + } + + foreach (string email in emailList) + { + try + { + var currentEmail = email.ToLower(); + var userData = GetUserSerialization(user); + var requestUrl = GetRadicaleUrl(uri, currentEmail, true, true, itemID: user.ID.ToString()); + UpdateItem(requestUrl, authorization, userData, uri).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.Error("ERROR: " + ex.Message); + } + } + } + } +} diff --git a/common/ASC.Common/Radicale/CardDavItem.cs b/common/ASC.Common/Radicale/CardDavItem.cs new file mode 100644 index 000000000..3e0f8339d --- /dev/null +++ b/common/ASC.Common/Radicale/CardDavItem.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ASC.Common.Radicale +{ + public class CardDavItem + { + public Guid ID { get; set; } + + public string FirstName { get; set; } + + public string LastName { get; set; } + + public string UserName { get; set; } + + public DateTime? BirthDate { get; set; } + + public bool? Sex { get; set; } + + public string Title { get; set; } + + public string Email { get; set; } + + public List Contacts { get; set; } + + public string MobilePhone { get; set; } + + public override string ToString() + { + return String.Format("{0} {1}", FirstName, LastName).Trim(); + } + + public override int GetHashCode() + { + return ID.GetHashCode(); + } + + public CardDavItem(Guid iD, string firstName, string lastName, string userName, DateTime? birthDate, bool? sex, string title, string email, List contacts, string mobilePhone) + { + ID = iD; + FirstName = firstName; + LastName = lastName; + UserName = userName; + BirthDate = birthDate; + Sex = sex; + Title = title; + Email = email; + Contacts = contacts; + MobilePhone = mobilePhone; + } + } +} diff --git a/common/ASC.Common/Radicale/Core/DbRadicale.cs b/common/ASC.Common/Radicale/Core/DbRadicale.cs new file mode 100644 index 000000000..7ce700407 --- /dev/null +++ b/common/ASC.Common/Radicale/Core/DbRadicale.cs @@ -0,0 +1,39 @@ + +using System; + +using ASC.Common.Data; +using ASC.Common.Data.Sql; + +namespace ASC.Common.Radicale.Core +{ + public class DbRadicale + { + private IDbManager db = DbManager.FromHttpContext("default"); + public void SaveCardDavUser(int tenant, string id) + { + var i = new SqlInsert("core_userdav").ReplaceExists(true) + .InColumnValue("tenant_id", tenant) + .InColumnValue("user_id", id); + + db.ExecuteNonQuery(i); + } + + public void RemoveCardDavUser(int tenant, string id) + { + var i = new SqlDelete("core_userdav").Where("user_id", id).Where("tenant_id", tenant); + db.ExecuteNonQuery(i); + } + + public Boolean IsExistCardDavUser(int tenant, string id) + { + var q = new SqlQuery("core_userdav") + .Select("1") + .Where("user_id", id) + .Where("tenant_id", tenant) + .SetMaxResults(1); + + return db.ExecuteScalar(q); + } + + } +} diff --git a/common/ASC.Common/Radicale/Core/RadicaleClient.cs b/common/ASC.Common/Radicale/Core/RadicaleClient.cs new file mode 100644 index 000000000..61217807b --- /dev/null +++ b/common/ASC.Common/Radicale/Core/RadicaleClient.cs @@ -0,0 +1,120 @@ +using System; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using System.Threading.Tasks; + +using ASC.Common.Logging; + +namespace ASC.Common.Radicale +{ + public static class RadicaleClient + { + private static readonly ILog Logger = BaseLogManager.GetLogger("ASC.Radicale"); + + public async static Task CreateAsync(DavRequest davRequest) + { + davRequest.Method = "MKCOL"; + var response = await RequestAsync(davRequest).ConfigureAwait(false); + return GetDavResponse(response); + } + + public async static Task GetAsync(DavRequest davRequest) + { + davRequest.Method = "GET"; + var response = await RequestAsync(davRequest).ConfigureAwait(false); + var davResponse = new DavResponse() + { + StatusCode = (int)response.StatusCode + }; + + if (response.StatusCode == System.Net.HttpStatusCode.OK) + { + davResponse.Completed = true; + davResponse.Data = await response.Content.ReadAsStringAsync(); + } + else + { + davResponse.Completed = false; + davResponse.Error = response.ReasonPhrase; + } + + return davResponse; + } + + + public async static Task UpdateItemAsync(DavRequest davRequest) + { + davRequest.Method = "PUT"; + var response = await RequestAsync(davRequest).ConfigureAwait(false); + return GetDavResponse(response); + } + + public async static Task UpdateAsync(DavRequest davRequest) + { + davRequest.Method = "PROPPATCH"; + var response = await RequestAsync(davRequest).ConfigureAwait(false); + return GetDavResponse(response); + } + + public async static Task RemoveAsync(DavRequest davRequest) + { + davRequest.Method = "DELETE"; + await RequestAsync(davRequest).ConfigureAwait(false); + } + + private async static Task RequestAsync(DavRequest davRequest) + { + try + { + using (var hc = new HttpClient()) + { + hc.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes(davRequest.Authorization))); + if (!String.IsNullOrEmpty(davRequest.Header)) hc.DefaultRequestHeaders.Add("X_REWRITER_URL", davRequest.Header); + var method = new HttpMethod(davRequest.Method); + var request = new HttpRequestMessage(method, davRequest.Url); + + if (davRequest.Data != null) + { + request.Content = new StringContent(davRequest.Data); + } + return await hc.SendAsync(request).ConfigureAwait(false); + + } + } + catch (AggregateException ex) + { + throw new RadicaleException(ex.Message); + } + catch (Exception ex) + { + Logger.Error(ex.Message); + throw new RadicaleException(ex.Message); + } + } + + + private static DavResponse GetDavResponse(HttpResponseMessage response) + { + if (response.IsSuccessStatusCode) + { + return new DavResponse() + { + Completed = true, + Data = response.IsSuccessStatusCode ? response.RequestMessage.RequestUri.ToString() : response.ReasonPhrase, + }; + + } + else + { + return new DavResponse() + { + Completed = false, + StatusCode = (int)response.StatusCode, + Error = response.ReasonPhrase + }; + } + + } + } +} diff --git a/common/ASC.Common/Radicale/Core/RadicaleException.cs b/common/ASC.Common/Radicale/Core/RadicaleException.cs new file mode 100644 index 000000000..7feac0cd5 --- /dev/null +++ b/common/ASC.Common/Radicale/Core/RadicaleException.cs @@ -0,0 +1,15 @@ +using System; + +namespace ASC.Common.Radicale +{ + [Serializable] + public class RadicaleException : Exception + { + public RadicaleException(string message) + : base(message) + { + } + + + } +} diff --git a/common/ASC.Common/Radicale/DavRequest.cs b/common/ASC.Common/Radicale/DavRequest.cs new file mode 100644 index 000000000..4d4f56991 --- /dev/null +++ b/common/ASC.Common/Radicale/DavRequest.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; + +namespace ASC.Common.Radicale +{ + public class DavRequest + { + public string Url { get; set; } + public string Authorization { get; set; } + public string Method { get; set; } + public string Data { get; set; } + public string Header { get; set; } + } +} diff --git a/common/ASC.Common/Radicale/DavResponse.cs b/common/ASC.Common/Radicale/DavResponse.cs new file mode 100644 index 000000000..f73389790 --- /dev/null +++ b/common/ASC.Common/Radicale/DavResponse.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; + +namespace ASC.Common.Radicale +{ + [DataContract] + public class DavResponse + { + [DataMember] + public bool Completed { get; set; } + [DataMember] + public int StatusCode { get; set; } + [DataMember] + public string Data { get; set; } + [DataMember] + public string Error { get; set; } + + public override string ToString() + { + return StatusCode.ToString() + " " + Error; + } + + } +} diff --git a/common/ASC.Common/Radicale/IRadicaleEntity.cs b/common/ASC.Common/Radicale/IRadicaleEntity.cs new file mode 100644 index 000000000..346639713 --- /dev/null +++ b/common/ASC.Common/Radicale/IRadicaleEntity.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ASC.Common.Radicale +{ + interface IRadicaleEntity + { + string Create(DavRequest davrequest); + void Update(DavRequest davRequest); + void Remove(DavRequest davRequest); + void UpdateItem(DavRequest davRequest); + DavResponse GetCollection(DavRequest davRequest); + + } +} diff --git a/common/ASC.Common/Radicale/RadicaleEntity.cs b/common/ASC.Common/Radicale/RadicaleEntity.cs new file mode 100644 index 000000000..4dc75887b --- /dev/null +++ b/common/ASC.Common/Radicale/RadicaleEntity.cs @@ -0,0 +1,60 @@ +using System; +using System.Configuration; +using System.Text.RegularExpressions; +using System.Web; + +using ASC.Security.Cryptography; + +namespace ASC.Common.Radicale +{ + public abstract class RadicaleEntity + { + public string Uid { get; set; } + + public static readonly string defaultRadicaleUrl = (ConfigurationManagerExtension.AppSettings["radicale.path"] != null) ? ConfigurationManagerExtension.AppSettings["radicale.path"] : "http://localhost:5232"; + + public readonly string defaultAddBookName = "11111111-1111-1111-1111-111111111111"; + + public readonly string readonlyAddBookName = "11111111-1111-1111-1111-111111111111-readonly"; + + public string GetRadicaleUrl(string url, string email, bool isReadonly = false, bool isCardDav = false, bool isRedirectUrl = false, string entityId = "", string itemID = "") + { + string requestUrl; + var currentUserName = url.StartsWith("http") ? email.ToLower() + "@" + new Uri(url).Host : email.ToLower() + "@" + url; + var protocolType = (!isCardDav) ? "/caldav/" : "/carddav/"; + var serverUrl = isRedirectUrl ? new Uri(url).Scheme + "://" + new Uri(url).Host + protocolType : + defaultRadicaleUrl; + if (isCardDav) + { + var addbookId = isReadonly ? readonlyAddBookName : defaultAddBookName; + requestUrl = (itemID != "") ? defaultRadicaleUrl + "/" + HttpUtility.UrlEncode(currentUserName) + "/" + addbookId + "/" + itemID + ".vcf" : + (isRedirectUrl) ? serverUrl + HttpUtility.UrlEncode(currentUserName) + "/" + addbookId : + defaultRadicaleUrl + "/" + HttpUtility.UrlEncode(currentUserName) + "/" + addbookId; + } + else + { + requestUrl = (itemID != "") ? serverUrl + HttpUtility.UrlEncode(currentUserName) + "/" + entityId + (isReadonly ? "-readonly" : "") + + "/" + HttpUtility.UrlEncode(itemID) + ".ics" : + serverUrl + HttpUtility.UrlEncode(currentUserName) + "/" + entityId + (isReadonly ? "-readonly" : ""); + } + return requestUrl; + } + + public string GetSystemAuthorization() + { + return ConfigurationManagerExtension.AppSettings["radicale.admin.data"] + ":" + InstanceCrypto.Encrypt(ConfigurationManagerExtension.AppSettings["radicale.admin.data"]); + } + + protected string GetData(string sample, string name, string description, string backgroundColor) + { + string[] numbers = Regex.Split(backgroundColor, @"\D+"); + var color = numbers.Length > 4 ? HexFromRGB(int.Parse(numbers[1]), int.Parse(numbers[2]), int.Parse(numbers[3])) : "#000000"; + return string.Format(sample, name, color, description); + } + + private string HexFromRGB(int r, int g, int b) + { + return string.Format("#{0:X2}{1:X2}{2:X2}", r, g, b); + } + } +} diff --git a/common/ASC.Common/Threading/Workers/WorkerQueue.cs b/common/ASC.Common/Threading/Workers/WorkerQueue.cs index dcedcf2f2..81899b098 100644 --- a/common/ASC.Common/Threading/Workers/WorkerQueue.cs +++ b/common/ASC.Common/Threading/Workers/WorkerQueue.cs @@ -22,6 +22,7 @@ using System.Threading; using ASC.Common.Logging; +using LogManager = ASC.Common.Logging.BaseLogManager; namespace ASC.Common.Threading.Workers { diff --git a/common/ASC.Common/Utils/HttpRequestExtensions.cs b/common/ASC.Common/Utils/HttpRequestExtensions.cs index 2152f3447..6e2d17c7d 100644 --- a/common/ASC.Common/Utils/HttpRequestExtensions.cs +++ b/common/ASC.Common/Utils/HttpRequestExtensions.cs @@ -155,6 +155,11 @@ namespace System.Web || !string.IsNullOrEmpty(request.UserAgent) && request.UserAgent.Contains("SailfishOS")); } + public static bool MobileApp(this HttpRequest request) + { + return !string.IsNullOrEmpty(request.UserAgent) && (request.UserAgent.Contains("iOS") || request.UserAgent.Contains("Android")); + } + private static Uri ParseRewriterUrl(string s) { diff --git a/common/ASC.Common/Utils/StringUtils.cs b/common/ASC.Common/Utils/StringUtils.cs new file mode 100644 index 000000000..4ec87e2da --- /dev/null +++ b/common/ASC.Common/Utils/StringUtils.cs @@ -0,0 +1,44 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System.Linq; +using System.Text; +using System.Xml; + +namespace ASC.Common.Utils +{ + public static class StringUtils + { + /// + /// Removes control characters and other non-UTF-8 characters + /// + /// The string to process + /// A string with no control characters or entities above 0x00FD + public static string NormalizeStringForMySql(string inString) + { + if (string.IsNullOrEmpty(inString)) + return inString; + + var newString = new StringBuilder(inString.Length); + + foreach (var ch in inString.Where(XmlConvert.IsXmlChar)) + newString.Append(ch); + + return newString.ToString(); + } + } +} diff --git a/common/ASC.Common/Utils/TimeZoneConverter/TimeZoneConverter.cs b/common/ASC.Common/Utils/TimeZoneConverter/TimeZoneConverter.cs index 7e3f9231c..f2ed77ac9 100644 --- a/common/ASC.Common/Utils/TimeZoneConverter/TimeZoneConverter.cs +++ b/common/ASC.Common/Utils/TimeZoneConverter/TimeZoneConverter.cs @@ -26,6 +26,8 @@ using System.Xml.XPath; using ASC.Common.Logging; +using LogManager = ASC.Common.Logging.BaseLogManager; + namespace ASC.Common.Utils { public class TimeZoneConverter diff --git a/common/ASC.Common/Web/AbstractHttpAsyncHandler.cs b/common/ASC.Common/Web/AbstractHttpAsyncHandler.cs index d9e752a86..c190db5e5 100644 --- a/common/ASC.Common/Web/AbstractHttpAsyncHandler.cs +++ b/common/ASC.Common/Web/AbstractHttpAsyncHandler.cs @@ -20,11 +20,11 @@ using System.Globalization; using System.Security.Principal; using System.Threading; using System.Web; -using System.Web.SessionState; + namespace ASC.Common.Web { - public abstract class AbstractHttpAsyncHandler : IHttpAsyncHandler, IReadOnlySessionState + public abstract class AbstractHttpAsyncHandler : IHttpAsyncHandler { private Action processRequest; private IPrincipal principal; diff --git a/common/ASC.Common/Web/MimeMapping.cs b/common/ASC.Common/Web/MimeMapping.cs index 473ed9713..64550950c 100644 --- a/common/ASC.Common/Web/MimeMapping.cs +++ b/common/ASC.Common/Web/MimeMapping.cs @@ -435,6 +435,7 @@ namespace ASC.Common.Web AddMimeMapping(".otp", "application/vnd.oasis.opendocument.presentation-template"); AddMimeMapping(".ots", "application/vnd.oasis.opendocument.spreadsheet-template"); AddMimeMapping(".ott", "application/vnd.oasis.opendocument.text-template"); + AddMimeMapping(".oxps", "application/oxps"); AddMimeMapping(".p", "text/x-pascal"); AddMimeMapping(".p10", "application/pkcs10"); AddMimeMapping(".p10", "application/x-pkcs10"); diff --git a/common/ASC.Core.Common/ASC.Core.Common.csproj b/common/ASC.Core.Common/ASC.Core.Common.csproj index 88aaceaeb..a3e51dbc7 100644 --- a/common/ASC.Core.Common/ASC.Core.Common.csproj +++ b/common/ASC.Core.Common/ASC.Core.Common.csproj @@ -38,7 +38,6 @@ - @@ -69,6 +68,7 @@ + @@ -81,10 +81,14 @@ + + + + @@ -114,6 +118,8 @@ + + @@ -135,7 +141,6 @@ - @@ -198,22 +203,22 @@ - 5.1.2 + 6.2.0 - 3.5.6.3 + 3.7.3.20 - 3.5.7.6 + 3.7.1.29 - 3.5.0.59 + 3.7.0.57 - 2.11.1 + 2.15.0 - 12.0.3 + 13.0.1 4.3.2 @@ -226,6 +231,5 @@ - \ No newline at end of file diff --git a/common/ASC.Core.Common/Audit/BaseEvent.cs b/common/ASC.Core.Common/Audit/BaseEvent.cs new file mode 100644 index 000000000..35f7d0953 --- /dev/null +++ b/common/ASC.Core.Common/Audit/BaseEvent.cs @@ -0,0 +1,70 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System; +using System.Collections.Generic; + +namespace ASC.AuditTrail +{ + public class BaseEvent + { + public int Id { get; set; } + + public int TenantId { get; set; } + + public Guid UserId { get; set; } + + public bool Mobile { get; set; } + + public IList Description { get; set; } + + [Event("IpCol")] + public string IP { get; set; } + + [Event("BrowserCol")] + public string Browser { get; set; } + + [Event("PlatformCol")] + public string Platform { get; set; } + + [Event("DateCol")] + public DateTime Date { get; set; } + + [Event("UserCol")] + public string UserName { get; set; } + + [Event("PageCol")] + public string Page { get; set; } + + [Event("ActionCol")] + public string ActionText { get; set; } + } + + [AttributeUsage(AttributeTargets.Property)] + public class EventAttribute : Attribute + { + public string Resource { get; private set; } + + public int Order { get; private set; } + + public EventAttribute(string resource, int order = 0) + { + Resource = resource; + Order = order; + } + } +} diff --git a/common/ASC.Core.Common/BaseCommonLinkUtility.cs b/common/ASC.Core.Common/BaseCommonLinkUtility.cs index d881f4c84..c0e197fb1 100644 --- a/common/ASC.Core.Common/BaseCommonLinkUtility.cs +++ b/common/ASC.Core.Common/BaseCommonLinkUtility.cs @@ -100,10 +100,12 @@ namespace ASC.Core.Common if (!string.IsNullOrEmpty(tenant.MappedDomain)) { var mapped = tenant.MappedDomain.ToLowerInvariant(); + if (!mapped.Contains(Uri.SchemeDelimiter)) { - mapped = Uri.UriSchemeHttp + Uri.SchemeDelimiter + mapped; + mapped = result.Scheme + Uri.SchemeDelimiter + mapped; } + result = new UriBuilder(mapped); } } diff --git a/common/ASC.Core.Common/Billing/BillingClient.cs b/common/ASC.Core.Common/Billing/BillingClient.cs index 53b60b79c..4a72c427f 100644 --- a/common/ASC.Core.Common/Billing/BillingClient.cs +++ b/common/ASC.Core.Common/Billing/BillingClient.cs @@ -83,9 +83,9 @@ namespace ASC.Core.Billing return payments; } - public IDictionary> GetPaymentUrls(string portalId, string[] products, string affiliateId = null, string campaign = null, string currency = null, string language = null, string customerId = null, string quantity = null) + public IDictionary GetPaymentUrls(string portalId, string[] products, string affiliateId = null, string campaign = null, string currency = null, string language = null, string customerId = null, string quantity = null) { - var urls = new Dictionary>(); + var urls = new Dictionary(); var additionalParameters = new List>() { Tuple.Create("PaymentSystemId", AvangatePaymentSystemId.ToString()) }; if (!string.IsNullOrEmpty(affiliateId)) @@ -123,40 +123,59 @@ namespace ASC.Core.Billing var result = Request("GetPaymentUrl", portalId, parameters); var paymentUrls = JsonConvert.DeserializeObject>(result); - var upgradeUrls = new Dictionary(); - if (!string.IsNullOrEmpty(portalId) - //TODO: remove - && false) - { - try - { - //max 100 products - result = Request("GetPaymentUpgradeUrl", portalId, parameters); - upgradeUrls = JsonConvert.DeserializeObject>(result); - } - catch (BillingNotFoundException) - { - } - } - foreach (var p in products) { string url; var paymentUrl = (Uri)null; - var upgradeUrl = (Uri)null; if (paymentUrls.TryGetValue(p, out url) && !string.IsNullOrEmpty(url = ToUrl(url))) { paymentUrl = new Uri(url); } - if (upgradeUrls.TryGetValue(p, out url) && !string.IsNullOrEmpty(url = ToUrl(url))) - { - upgradeUrl = new Uri(url); - } - urls[p] = Tuple.Create(paymentUrl, upgradeUrl); + urls[p] = paymentUrl; } return urls; } + public string GetPaymentUrl(string portalId, string[] products, string affiliateId = null, string campaign = null, string currency = null, string language = null, string customerId = null, string quantity = null) + { + var additionalParameters = new List>() { Tuple.Create("PaymentSystemId", AvangatePaymentSystemId.ToString()) }; + if (!string.IsNullOrEmpty(affiliateId)) + { + additionalParameters.Add(Tuple.Create("AffiliateId", affiliateId)); + } + if (!string.IsNullOrEmpty(campaign)) + { + additionalParameters.Add(Tuple.Create("campaign", campaign)); + } + if (!string.IsNullOrEmpty(currency)) + { + additionalParameters.Add(Tuple.Create("Currency", currency)); + } + if (!string.IsNullOrEmpty(language)) + { + additionalParameters.Add(Tuple.Create("Language", language)); + } + if (!string.IsNullOrEmpty(customerId)) + { + additionalParameters.Add(Tuple.Create("CustomerID", customerId)); + } + if (!string.IsNullOrEmpty(quantity)) + { + additionalParameters.Add(Tuple.Create("Quantity", quantity)); + } + + var parameters = products + .Distinct() + .Select(p => Tuple.Create("ProductId", p)) + .Concat(additionalParameters) + .ToArray(); + + var result = Request("GetSinglePaymentUrl", portalId, parameters); + var paymentUrl = JsonConvert.DeserializeObject(result); + + return paymentUrl; + } + public IDictionary> GetProductPriceInfo(params string[] productIds) { if (productIds == null) diff --git a/common/ASC.Core.Common/Billing/CouponManager.cs b/common/ASC.Core.Common/Billing/CouponManager.cs deleted file mode 100644 index 53590f3a0..000000000 --- a/common/ASC.Core.Common/Billing/CouponManager.cs +++ /dev/null @@ -1,321 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.Configuration; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Security.Cryptography; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Web; - -using ASC.Common.Logging; - -using Newtonsoft.Json; - -namespace ASC.Core.Common.Billing -{ - public class CouponManager - { - private static IEnumerable Products { get; set; } - private static IEnumerable Groups { get; set; } - private static readonly int Percent; - private static readonly int Schedule; - private static readonly string VendorCode; - private static readonly byte[] Secret; - private static readonly Uri BaseAddress; - private static readonly string ApiVersion; - private static readonly SemaphoreSlim SemaphoreSlim = new SemaphoreSlim(1, 1); - private static readonly ILog Log; - - static CouponManager() - { - SemaphoreSlim = new SemaphoreSlim(1, 1); - Log = LogManager.GetLogger("ASC"); - - try - { - var cfg = (AvangateCfgSectionHandler)ConfigurationManagerExtension.GetSection("avangate"); - Secret = Encoding.UTF8.GetBytes(cfg.Secret); - VendorCode = cfg.Vendor; - Percent = cfg.Percent; - Schedule = cfg.Schedule; - BaseAddress = new Uri(cfg.BaseAddress); - ApiVersion = "/rest/" + cfg.ApiVersion.TrimStart('/'); - Groups = (cfg.Groups ?? "").Split(',', '|', ' '); - } - catch (Exception e) - { - Secret = Encoding.UTF8.GetBytes(""); - VendorCode = ""; - Percent = AvangateCfgSectionHandler.DefaultPercent; - Schedule = AvangateCfgSectionHandler.DefaultShedule; - BaseAddress = new Uri(AvangateCfgSectionHandler.DefaultAdress); - ApiVersion = AvangateCfgSectionHandler.DefaultApiVersion; - Groups = new List(); - Log.Fatal(e); - } - } - - public static string CreateCoupon() - { - return CreatePromotionAsync().Result; - } - - private static async Task CreatePromotionAsync() - { - try - { - using (var httpClient = PrepaireClient()) - using (var content = new StringContent(await Promotion.GeneratePromotion(Percent, Schedule), Encoding.Default, "application/json")) - using (var response = await httpClient.PostAsync(string.Format("{0}/promotions/", ApiVersion), content)) - { - if (!response.IsSuccessStatusCode) - throw new HttpException((int)response.StatusCode, response.ReasonPhrase); - - var result = await response.Content.ReadAsStringAsync(); - await Task.Delay(1000 - DateTime.UtcNow.Millisecond); // otherwise authorize exception - var createdPromotion = JsonConvert.DeserializeObject(result); - return createdPromotion.Coupon.Code; - } - } - catch (Exception ex) - { - Log.Error(ex.Message, ex); - throw; - } - } - - internal static async Task> GetProducts() - { - if (Products != null) return Products; - - await SemaphoreSlim.WaitAsync(); - - if (Products != null) - { - SemaphoreSlim.Release(); - return Products; - } - - try - { - using (var httpClient = PrepaireClient()) - using (var response = await httpClient.GetAsync(string.Format("{0}/products/?Limit=1000&Enabled=true", ApiVersion))) - { - if (!response.IsSuccessStatusCode) - throw new HttpException((int)response.StatusCode, response.ReasonPhrase); - - var result = await response.Content.ReadAsStringAsync(); - Log.Debug(result); - - var products = JsonConvert.DeserializeObject>(result); - products = products.Where(r => r.ProductGroup != null && Groups.Contains(r.ProductGroup.Code)).ToList(); - return Products = products; - } - } - catch (Exception ex) - { - Log.Error(ex.Message, ex); - throw; - } - finally - { - SemaphoreSlim.Release(); - } - } - - private static HttpClient PrepaireClient() - { - ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; - const string applicationJson = "application/json"; - var httpClient = new HttpClient { BaseAddress = BaseAddress, Timeout = TimeSpan.FromMinutes(3) }; - httpClient.DefaultRequestHeaders.TryAddWithoutValidation("accept", applicationJson); - httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", applicationJson); - httpClient.DefaultRequestHeaders.TryAddWithoutValidation("X-Avangate-Authentication", CreateAuthHeader()); - return httpClient; - } - - private static string CreateAuthHeader() - { - using (var hmac = new HMACMD5(Secret)) - { - var date = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss"); - var hash = VendorCode.Length + VendorCode + date.Length + date; - var data = hmac.ComputeHash(Encoding.UTF8.GetBytes(hash)); - - var sBuilder = new StringBuilder(); - foreach (var t in data) - { - sBuilder.Append(t.ToString("x2")); - } - - var stringBuilder = new StringBuilder(); - stringBuilder.AppendFormat("code='{0}' ", VendorCode); - stringBuilder.AppendFormat("date='{0}' ", date); - stringBuilder.AppendFormat("hash='{0}'", sBuilder); - return stringBuilder.ToString(); - } - } - } - - class Promotion - { - public string Code { get; set; } - public string Name { get; set; } - public string Type { get; set; } - public string StartDate { get; set; } - public string EndDate { get; set; } - public bool Enabled { get; set; } - public int MaximumOrdersNumber { get; set; } - public bool InstantDiscount { get; set; } - public string ChannelType { get; set; } - public string ApplyRecurring { get; set; } - public Coupon Coupon { get; set; } - public Discount Discount { get; set; } - public IEnumerable Products { get; set; } - public int PublishToAffiliatesNetwork { get; set; } - public int AutoApply { get; set; } - - public static async Task GeneratePromotion(int percent, int schedule) - { - try - { - var tenant = CoreContext.TenantManager.GetCurrentTenant(); - var startDate = DateTime.UtcNow.Date; - var endDate = startDate.AddDays(schedule); - var code = tenant.TenantAlias; - - var promotion = new Promotion - { - Type = "REGULAR", - Enabled = true, - MaximumOrdersNumber = 1, - InstantDiscount = false, - ChannelType = "ECOMMERCE", - ApplyRecurring = "NONE", - PublishToAffiliatesNetwork = 0, - AutoApply = 0, - - StartDate = startDate.ToString("yyyy-MM-dd"), - EndDate = endDate.ToString("yyyy-MM-dd"), - Name = string.Format("{0} {1}% off", code, percent), - Coupon = new Coupon { Type = "SINGLE", Code = code }, - Discount = new Discount { Type = "PERCENT", Value = percent }, - Products = (await CouponManager.GetProducts()).Select(r => new CouponProduct { Code = r.ProductCode }) - - }; - - return JsonConvert.SerializeObject(promotion); - } - catch (Exception ex) - { - LogManager.GetLogger("ASC").Error(ex.Message, ex); - throw; - } - } - } - - class Coupon - { - public string Type { get; set; } - public string Code { get; set; } - } - - class Discount - { - public string Type { get; set; } - public int Value { get; set; } - } - - class AvangateProduct - { - public string ProductCode { get; set; } - public string ProductName { get; set; } - public AvangateProductGroup ProductGroup { get; set; } - } - - class AvangateProductGroup - { - public string Name { get; set; } - public string Code { get; set; } - } - - class CouponProduct - { - public string Code { get; set; } - } - - class AvangateCfgSectionHandler : ConfigurationSection - { - public const string DefaultAdress = "https://api.avangate.com/"; - public const string DefaultApiVersion = "4.0"; - public const int DefaultPercent = 5; - public const int DefaultShedule = 10; - - [ConfigurationProperty("secret")] - public string Secret - { - get { return (string)this["secret"]; } - } - - [ConfigurationProperty("vendor")] - public string Vendor - { - get { return (string)this["vendor"]; } - set { this["vendor"] = value; } - } - - [ConfigurationProperty("percent", DefaultValue = DefaultPercent)] - public int Percent - { - get { return Convert.ToInt32(this["percent"]); } - set { this["percent"] = value; } - } - - [ConfigurationProperty("schedule", DefaultValue = DefaultShedule)] - public int Schedule - { - get { return Convert.ToInt32(this["schedule"]); } - set { this["schedule"] = value; } - } - - [ConfigurationProperty("groups")] - public string Groups - { - get { return (string)this["groups"]; } - } - - [ConfigurationProperty("address", DefaultValue = DefaultAdress)] - public string BaseAddress - { - get { return (string)this["address"]; } - set { this["address"] = value; } - } - - [ConfigurationProperty("apiVersion", DefaultValue = DefaultApiVersion)] - public string ApiVersion - { - get { return (string)this["apiVersion"]; } - set { this["apiVersion"] = value; } - } - } -} \ No newline at end of file diff --git a/common/ASC.Core.Common/Billing/ITariffService.cs b/common/ASC.Core.Common/Billing/ITariffService.cs index cbcfbc90a..28f1482cf 100644 --- a/common/ASC.Core.Common/Billing/ITariffService.cs +++ b/common/ASC.Core.Common/Billing/ITariffService.cs @@ -34,6 +34,8 @@ namespace ASC.Core.Billing Uri GetShoppingUri(int? tenant, int quotaId, string affiliateId, string currency = null, string language = null, string customerId = null, string quantity = null); + Uri GetShoppingUri(string[] productIds, string affiliateId = null, string currency = null, string language = null, string customerId = null, string quantity = null); + IDictionary> GetProductPriceInfo(params string[] productIds); string GetButton(int tariffId, string partnerId); diff --git a/common/ASC.Core.Common/Billing/License/License.cs b/common/ASC.Core.Common/Billing/License/License.cs index 7bb317fe2..ec9d5234b 100644 --- a/common/ASC.Core.Common/Billing/License/License.cs +++ b/common/ASC.Core.Common/Billing/License/License.cs @@ -31,42 +31,18 @@ namespace ASC.Core.Billing public string OriginalLicense { get; set; } - [DataMember(Name = "affiliate_id")] - public string AffiliateId { get; set; } - - [DataMember(Name = "whitelabel")] - public bool WhiteLabel { get; set; } - [DataMember(Name = "customization")] public bool Customization { get; set; } - [DataMember(Name = "branding")] - public bool Branding { get; set; } - - [DataMember(Name = "ssbranding")] - public bool SSBranding { get; set; } - [DataMember(Name = "end_date")] public DateTime DueDate { get; set; } - [DataMember(Name = "portal_count")] - public int PortalCount { get; set; } - [DataMember(Name = "trial")] public bool Trial { get; set; } - [DataMember(Name = "user_quota")] - public int ActiveUsers { get; set; } - [DataMember(Name = "customer_id")] public string CustomerId { get; set; } - [DataMember(Name = "signature")] - public string Signature { get; set; } - - [DataMember(Name = "discencryption")] - public bool? DiscEncryption { get; set; } - [DataMember(Name = "users_count")] public int DSUsersCount { get; set; } @@ -76,6 +52,9 @@ namespace ASC.Core.Billing [DataMember(Name = "connections")] public int DSConnections { get; set; } + [DataMember(Name = "signature")] + public string Signature { get; set; } + public static License Parse(string licenseString) { diff --git a/common/ASC.Core.Common/Billing/License/LicenseReader.cs b/common/ASC.Core.Common/Billing/License/LicenseReader.cs index 96a929f94..ec89d2fd1 100644 --- a/common/ASC.Core.Common/Billing/License/LicenseReader.cs +++ b/common/ASC.Core.Common/Billing/License/LicenseReader.cs @@ -18,7 +18,6 @@ using System; using System.Configuration; using System.IO; -using System.Linq; using ASC.Common.Logging; using ASC.Core.Tenants; @@ -33,7 +32,6 @@ namespace ASC.Core.Billing private static readonly string LicensePathTemp; public const string CustomerIdKey = "CustomerId"; - public const int MaxUserCount = 10000; static LicenseReader() @@ -159,29 +157,6 @@ namespace ASC.Core.Billing throw new BillingNotConfiguredException("License not correct", license.OriginalLicense); } - if (license.DueDate.Date < VersionReleaseDate) - { - throw new LicenseExpiredException("License expired", license.OriginalLicense); - } - - if (license.ActiveUsers.Equals(default(int)) || license.ActiveUsers < 1) - license.ActiveUsers = MaxUserCount; - - if (license.ActiveUsers < CoreContext.UserManager.GetUsers(EmployeeStatus.Default, EmployeeType.User).Length) - { - throw new LicenseQuotaException("License quota", license.OriginalLicense); - } - - if (license.PortalCount <= 0) - { - license.PortalCount = CoreContext.TenantManager.GetTenantQuota(Tenant.DEFAULT_TENANT).CountPortals; - } - var activePortals = CoreContext.TenantManager.GetTenants().Count(); - if (activePortals > 1 && license.PortalCount < activePortals) - { - throw new LicensePortalException("License portal count", license.OriginalLicense); - } - return license.DueDate.Date; } @@ -195,40 +170,16 @@ namespace ASC.Core.Billing var quota = new TenantQuota(-1000) { - ActiveUsers = license.ActiveUsers, + ActiveUsers = Constants.MaxEveryoneCount, MaxFileSize = defaultQuota.MaxFileSize, MaxTotalSize = defaultQuota.MaxTotalSize, Name = "license", DocsEdition = true, - HasDomain = true, - Audit = true, - ControlPanel = true, - HealthCheck = true, - Ldap = true, - Sso = true, Customization = license.Customization, - WhiteLabel = license.WhiteLabel || license.Customization, - Branding = license.Branding, - SSBranding = license.SSBranding, Update = true, - Support = true, Trial = license.Trial, - CountPortals = license.PortalCount, - DiscEncryption = true, - PrivacyRoom = true, - Restore = true, - ContentSearch = true }; - if (defaultQuota.Name != "overdue" && !defaultQuota.Trial) - { - quota.WhiteLabel |= defaultQuota.WhiteLabel; - quota.Branding |= defaultQuota.Branding; - quota.SSBranding |= defaultQuota.SSBranding; - - quota.CountPortals = Math.Max(defaultQuota.CountPortals, quota.CountPortals); - } - CoreContext.TenantManager.SaveTenantQuota(quota); var tariff = new Tariff @@ -238,13 +189,6 @@ namespace ASC.Core.Billing }; CoreContext.PaymentManager.SetTariff(-1, tariff); - - if (!string.IsNullOrEmpty(license.AffiliateId)) - { - var tenant = CoreContext.TenantManager.GetCurrentTenant(); - tenant.AffiliateId = license.AffiliateId; - CoreContext.TenantManager.SaveTenant(tenant); - } } private static void LogError(Exception error) @@ -265,63 +209,5 @@ namespace ASC.Core.Billing } } } - - private static readonly DateTime _date = DateTime.MinValue; - - public static DateTime VersionReleaseDate - { - get - { - // release sign is not longer requered - return _date; - - /* - if (_date != DateTime.MinValue) return _date; - - _date = DateTime.MaxValue; - try - { - var versionDate = ConfigurationManagerExtension.AppSettings["version.release-date"]; - var sign = ConfigurationManagerExtension.AppSettings["version.release-date.sign"]; - - if (!sign.StartsWith("ASC ")) - { - throw new Exception("sign without ASC"); - } - - var splitted = sign.Substring(4).Split(':'); - var pkey = splitted[0]; - if (pkey != versionDate) - { - throw new Exception("sign with different date"); - } - - var date = splitted[1]; - var orighash = splitted[2]; - - var skey = MachinePseudoKeys.GetMachineConstant(); - - using (var hasher = new HMACSHA1(skey)) - { - var data = string.Join("\n", date, pkey); - var hash = hasher.ComputeHash(Encoding.UTF8.GetBytes(data)); - if (HttpServerUtility.UrlTokenEncode(hash) != orighash && Convert.ToBase64String(hash) != orighash) - { - throw new Exception("incorrect hash"); - } - } - - var year = Int32.Parse(versionDate.Substring(0, 4)); - var month = Int32.Parse(versionDate.Substring(4, 2)); - var day = Int32.Parse(versionDate.Substring(6, 2)); - _date = new DateTime(year, month, day); - } - catch (Exception ex) - { - LogManager.GetLogger("ASC").Error("VersionReleaseDate", ex); - } - return _date;*/ - } - } } } \ No newline at end of file diff --git a/common/ASC.Core.Common/Billing/PaymentInfo.cs b/common/ASC.Core.Common/Billing/PaymentInfo.cs index 0eb02494d..32cea7c0a 100644 --- a/common/ASC.Core.Common/Billing/PaymentInfo.cs +++ b/common/ASC.Core.Common/Billing/PaymentInfo.cs @@ -40,6 +40,8 @@ namespace ASC.Core.Billing public Decimal Price { get; set; } + public int Qty { get; set; } + public string PaymentCurrency { get; set; } public string PaymentMethod { get; set; } diff --git a/common/ASC.Core.Common/Billing/TariffService.cs b/common/ASC.Core.Common/Billing/TariffService.cs index 4bf80ee1b..be4236c81 100644 --- a/common/ASC.Core.Common/Billing/TariffService.cs +++ b/common/ASC.Core.Common/Billing/TariffService.cs @@ -143,6 +143,28 @@ namespace ASC.Core.Billing } catch (BillingNotFoundException) { + var q = quotaService.GetTenantQuota(tariff.QuotaId); + + if (q != null + && !q.Trial + && !q.Free + && !q.NonProfit + && !q.Open + && !q.Custom) + { + var asynctariff = Tariff.CreateDefault(); + asynctariff.DueDate = DateTime.Today.AddDays(-1); + asynctariff.Prolongable = false; + asynctariff.Autorenewal = false; + asynctariff.State = TariffState.NotPaid; + + if (SaveBillingInfo(tenantId, asynctariff)) + { + asynctariff = CalculateTariff(tenantId, asynctariff); + ClearCache(tenantId); + cache.Insert(key, asynctariff, DateTime.UtcNow.Add(GetCacheExpiration())); + } + } } catch (Exception error) { @@ -246,10 +268,10 @@ namespace ASC.Core.Billing ? GetBillingUrlCacheKey(tenant.Value) : String.Format("notenant{0}", !string.IsNullOrEmpty(affiliateId) ? "_" + affiliateId : ""); key += quota.Visible ? "" : "0"; - var urls = cache.Get>>(key) as IDictionary>; + var urls = cache.Get>(key) as IDictionary; if (urls == null) { - urls = new Dictionary>(); + urls = new Dictionary(); if (BillingClient.Configured) { try @@ -282,36 +304,66 @@ namespace ASC.Core.Billing ResetCacheExpiration(); - Tuple tuple; - if (!string.IsNullOrEmpty(quota.AvangateId) && urls.TryGetValue(quota.AvangateId, out tuple)) + Uri url; + if (!string.IsNullOrEmpty(quota.AvangateId) && urls.TryGetValue(quota.AvangateId, out url)) { - var result = tuple.Item2; + if (url == null) return null; - if (result == null) - { - result = tuple.Item1; - } - else - { - var tariff = tenant.HasValue ? GetTariff(tenant.Value) : null; - if (tariff == null || tariff.QuotaId == quotaId || tariff.State >= TariffState.Delay) - { - result = tuple.Item1; - } - } - - if (result == null) return null; - - result = new Uri(result.ToString() + url = new Uri(url.ToString() .Replace("__Currency__", HttpUtility.UrlEncode(currency ?? "")) .Replace("__Language__", HttpUtility.UrlEncode((language ?? "").ToLower())) .Replace("__CustomerID__", HttpUtility.UrlEncode(customerId ?? "")) .Replace("__Quantity__", HttpUtility.UrlEncode(quantity ?? ""))); - return result; + return url; } return null; } + public Uri GetShoppingUri(string[] productIds, string affiliateId = null, string currency = null, string language = null, string customerId = null, string quantity = null) + { + var key = "shopingurl" + string.Join("_", productIds) + (!string.IsNullOrEmpty(affiliateId) ? "_" + affiliateId : ""); + var url = cache.Get(key); + if (url == null) + { + url = string.Empty; + if (BillingClient.Configured) + { + try + { + var client = GetBillingClient(); + url = + client.GetPaymentUrl( + null, + productIds, + affiliateId, + null, + !string.IsNullOrEmpty(currency) ? "__Currency__" : null, + !string.IsNullOrEmpty(language) ? "__Language__" : null, + !string.IsNullOrEmpty(customerId) ? "__CustomerID__" : null, + !string.IsNullOrEmpty(quantity) ? "__Quantity__" : null + ); + } + catch (Exception error) + { + log.Error(error); + } + } + cache.Insert(key, url, DateTime.UtcNow.Add(TimeSpan.FromMinutes(10))); + } + + ResetCacheExpiration(); + + + if (string.IsNullOrEmpty(url)) return null; + + var result = new Uri(url.ToString() + .Replace("__Currency__", HttpUtility.UrlEncode(currency ?? "")) + .Replace("__Language__", HttpUtility.UrlEncode((language ?? "").ToLower())) + .Replace("__CustomerID__", HttpUtility.UrlEncode(customerId ?? "")) + .Replace("__Quantity__", HttpUtility.UrlEncode(quantity ?? ""))); + return result; + } + public IDictionary> GetProductPriceInfo(params string[] productIds) { if (productIds == null) @@ -496,7 +548,6 @@ namespace ASC.Core.Billing defaultQuota.Name = "overdue"; defaultQuota.Features = q.Features; - defaultQuota.Support = false; quotaService.SaveTenantQuota(defaultQuota); } diff --git a/common/ASC.Core.Common/Caching/CachedAzService.cs b/common/ASC.Core.Common/Caching/CachedAzService.cs index b839f389d..caa306735 100644 --- a/common/ASC.Core.Common/Caching/CachedAzService.cs +++ b/common/ASC.Core.Common/Caching/CachedAzService.cs @@ -28,10 +28,8 @@ namespace ASC.Core.Caching private readonly ICache cache; private readonly ICacheNotify cacheNotify; - public TimeSpan CacheExpiration { get; set; } - public CachedAzService(IAzService service) { if (service == null) throw new ArgumentNullException("service"); @@ -41,7 +39,7 @@ namespace ASC.Core.Caching CacheExpiration = TimeSpan.FromMinutes(10); cacheNotify = AscCache.Notify; - cacheNotify.Subscribe((r, a) => UpdateCache(r.Tenant, r, a == CacheNotifyAction.Remove)); + cacheNotify.Subscribe((r, a) => UpdateCache(r.Tenant, r, a == CacheNotifyAction.Remove)); } @@ -60,14 +58,14 @@ namespace ASC.Core.Caching public AzRecord SaveAce(int tenant, AzRecord r) { r = service.SaveAce(tenant, r); - cacheNotify.Publish(r, CacheNotifyAction.InsertOrUpdate); + cacheNotify.Publish((AzRecordCache)r, CacheNotifyAction.InsertOrUpdate); return r; } public void RemoveAce(int tenant, AzRecord r) { service.RemoveAce(tenant, r); - cacheNotify.Publish(r, CacheNotifyAction.Remove); + cacheNotify.Publish((AzRecordCache)r, CacheNotifyAction.Remove); } diff --git a/common/ASC.Core.Common/Caching/CachedSubscriptionService.cs b/common/ASC.Core.Common/Caching/CachedSubscriptionService.cs index 91227c08b..1c985245f 100644 --- a/common/ASC.Core.Common/Caching/CachedSubscriptionService.cs +++ b/common/ASC.Core.Common/Caching/CachedSubscriptionService.cs @@ -68,7 +68,7 @@ namespace ASC.Core.Caching } } }); - notify.Subscribe((m, a) => + notify.Subscribe((m, a) => { var store = GetSubsciptionsStore(m.Tenant, m.SourceId, m.ActionId); lock (store) @@ -146,7 +146,7 @@ namespace ASC.Core.Caching public void SetSubscriptionMethod(SubscriptionMethod m) { service.SetSubscriptionMethod(m); - notify.Publish(m, CacheNotifyAction.Any); + notify.Publish((SubscriptionMethodCache)m, CacheNotifyAction.Any); } diff --git a/common/ASC.Core.Common/Caching/CachedTenantService.cs b/common/ASC.Core.Common/Caching/CachedTenantService.cs index 174d69d98..0c2471503 100644 --- a/common/ASC.Core.Common/Caching/CachedTenantService.cs +++ b/common/ASC.Core.Common/Caching/CachedTenantService.cs @@ -53,7 +53,7 @@ namespace ASC.Core.Caching CacheExpiration = TimeSpan.FromMinutes(2); SettingsExpiration = TimeSpan.FromMinutes(2); cacheNotify = AscCache.Notify; - cacheNotify.Subscribe((t, a) => + cacheNotify.Subscribe((t, a) => { var tenants = GetTenantStore(); tenants.Remove(t.TenantId); @@ -134,14 +134,14 @@ namespace ASC.Core.Caching public Tenant SaveTenant(Tenant tenant) { tenant = service.SaveTenant(tenant); - cacheNotify.Publish(new Tenant() { TenantId = tenant.TenantId }, CacheNotifyAction.InsertOrUpdate); + cacheNotify.Publish(new TenantCacheItem() { TenantId = tenant.TenantId }, CacheNotifyAction.InsertOrUpdate); return tenant; } public void RemoveTenant(int id, bool auto = false) { service.RemoveTenant(id, auto); - cacheNotify.Publish(new Tenant() { TenantId = id }, CacheNotifyAction.InsertOrUpdate); + cacheNotify.Publish(new TenantCacheItem() { TenantId = id }, CacheNotifyAction.InsertOrUpdate); } public IEnumerable GetTenantVersions() @@ -260,4 +260,9 @@ namespace ASC.Core.Caching } } } + + public class TenantCacheItem + { + public int TenantId { get; set; } + } } diff --git a/common/ASC.Core.Common/Caching/CachedUserService.cs b/common/ASC.Core.Common/Caching/CachedUserService.cs index 21acb5027..5bf202ff0 100644 --- a/common/ASC.Core.Common/Caching/CachedUserService.cs +++ b/common/ASC.Core.Common/Caching/CachedUserService.cs @@ -38,14 +38,10 @@ namespace ASC.Core.Caching private readonly TrustInterval trustInterval; private int getchanges; - public TimeSpan CacheExpiration { get; set; } - public TimeSpan DbExpiration { get; set; } - public TimeSpan PhotoExpiration { get; set; } - public CachedUserService(IUserService service) { if (service == null) throw new ArgumentNullException("service"); @@ -59,10 +55,10 @@ namespace ASC.Core.Caching PhotoExpiration = TimeSpan.FromMinutes(10); cacheNotify = AscCache.Notify; - cacheNotify.Subscribe((u, a) => InvalidateCache(u)); - cacheNotify.Subscribe((p, a) => cache.Remove(p.Key)); - cacheNotify.Subscribe((g, a) => InvalidateCache()); - cacheNotify.Subscribe((r, a) => UpdateUserGroupRefCache(r, a == CacheNotifyAction.Remove)); + cacheNotify.Subscribe((u, a) => InvalidateCache(u)); + cacheNotify.Subscribe((p, a) => cache.Remove(p.Key)); + cacheNotify.Subscribe((g, a) => InvalidateCache()); + cacheNotify.Subscribe((r, a) => UpdateUserGroupRefCache(r, a == CacheNotifyAction.Remove)); } @@ -140,14 +136,14 @@ namespace ASC.Core.Caching public UserInfo SaveUser(int tenant, UserInfo user) { user = service.SaveUser(tenant, user); - cacheNotify.Publish(user, CacheNotifyAction.InsertOrUpdate); + cacheNotify.Publish(new UserInfoCacheItem { ID = user.ID, Tenant = user.Tenant }, CacheNotifyAction.Any); return user; } public void RemoveUser(int tenant, Guid id) { service.RemoveUser(tenant, id); - cacheNotify.Publish(new UserInfo { Tenant = tenant, ID = id }, CacheNotifyAction.Remove); + cacheNotify.Publish(new UserInfoCacheItem { Tenant = tenant, ID = id }, CacheNotifyAction.Any); } public byte[] GetUserPhoto(int tenant, Guid id) @@ -164,7 +160,7 @@ namespace ASC.Core.Caching public void SetUserPhoto(int tenant, Guid id, byte[] photo) { service.SetUserPhoto(tenant, id, photo); - cacheNotify.Publish(new UserPhoto { Key = GetUserPhotoCacheKey(tenant, id) }, CacheNotifyAction.Remove); + cacheNotify.Publish(new UserPhotoCacheItem { Key = GetUserPhotoCacheKey(tenant, id) }, CacheNotifyAction.Remove); } public DateTime GetUserPasswordStamp(int tenant, Guid id) @@ -201,14 +197,14 @@ namespace ASC.Core.Caching public Group SaveGroup(int tenant, Group group) { group = service.SaveGroup(tenant, group); - cacheNotify.Publish(group, CacheNotifyAction.InsertOrUpdate); + cacheNotify.Publish(new GroupCacheItem { ID = group.Id }, CacheNotifyAction.Any); return group; } public void RemoveGroup(int tenant, Guid id) { service.RemoveGroup(tenant, id); - cacheNotify.Publish(new Group { Id = id }, CacheNotifyAction.Remove); + cacheNotify.Publish(new GroupCacheItem { ID = id }, CacheNotifyAction.Any); } @@ -237,7 +233,7 @@ namespace ASC.Core.Caching public UserGroupRef SaveUserGroupRef(int tenant, UserGroupRef r) { r = service.SaveUserGroupRef(tenant, r); - cacheNotify.Publish(r, CacheNotifyAction.InsertOrUpdate); + cacheNotify.Publish((UserGroupRefCacheItem)r, CacheNotifyAction.InsertOrUpdate); return r; } @@ -246,7 +242,7 @@ namespace ASC.Core.Caching service.RemoveUserGroupRef(tenant, userId, groupId, refType); var r = new UserGroupRef(userId, groupId, refType) { Tenant = tenant }; - cacheNotify.Publish(r, CacheNotifyAction.Remove); + cacheNotify.Publish((UserGroupRefCacheItem)r, CacheNotifyAction.Remove); } public void InvalidateCache() @@ -254,7 +250,7 @@ namespace ASC.Core.Caching InvalidateCache(null); } - private void InvalidateCache(UserInfo userInfo) + private void InvalidateCache(UserInfoCacheItem userInfo) { if (CoreContext.Configuration.Personal && userInfo != null) { @@ -390,7 +386,6 @@ namespace ASC.Core.Caching } } - private static string GetUserPhotoCacheKey(int tenant, Guid userId) { return tenant.ToString() + "userphoto" + userId.ToString(); @@ -415,11 +410,44 @@ namespace ASC.Core.Caching return tenant.ToString() + USERS + userId; } + public IEnumerable GetDavUserEmails(int tenant) + { + return service.GetDavUserEmails(tenant); + } [Serializable] class UserPhoto { public string Key { get; set; } } + + } + + public class UserPhotoCacheItem + { + public string Key { get; set; } + } + + public class UserInfoCacheItem + { + public Guid ID { get; set; } + public int Tenant { get; set; } + } + + public class UserGroupRefCacheItem + { + public string UserId { get; set; } + public string GroupId { get; set; } + public bool Removed { get; set; } + public string RefType { get; set; } + public Int64 LastModified { get; set; } + public Int32 Tenant { get; set; } + } + + class GroupCacheItem + { + public Guid ID { get; set; } + } + } diff --git a/common/ASC.Core.Common/Configuration/Consumer.cs b/common/ASC.Core.Common/Configuration/Consumer.cs index 8f480eafd..7c7c9a0c5 100644 --- a/common/ASC.Core.Common/Configuration/Consumer.cs +++ b/common/ASC.Core.Common/Configuration/Consumer.cs @@ -135,7 +135,7 @@ namespace ASC.Core.Common.Configuration this[providerProp.Key] = null; } - Cache.Publish(this, CacheNotifyAction.Remove); + Cache.Publish(new ConsumerCacheItem { Name = this.Name, TenantId = CoreContext.TenantManager.GetCurrentTenant().TenantId }, CacheNotifyAction.Remove); } public bool Contains(KeyValuePair item) @@ -353,4 +353,11 @@ namespace ASC.Core.Common.Configuration return Builder.Resolve>(); } } + + public class ConsumerCacheItem + { + public string Name { get; set; } + public int TenantId { get; set; } + + } } diff --git a/common/ASC.Core.Common/Context/Impl/PaymentManager.cs b/common/ASC.Core.Common/Context/Impl/PaymentManager.cs index 8c47775ea..c7a5b52e4 100644 --- a/common/ASC.Core.Common/Context/Impl/PaymentManager.cs +++ b/common/ASC.Core.Common/Context/Impl/PaymentManager.cs @@ -86,6 +86,18 @@ namespace ASC.Core return tariffService.GetShoppingUri(null, quotaId, affiliateId, currency, language, customerId, quantity); } + // used in www + public Uri GetShoppingUri(string productId, string currency = null, string language = null, string customerId = null, string quantity = null, string affiliateId = null) + { + return tariffService.GetShoppingUri(new[] { productId }, affiliateId, currency, language, customerId, quantity); + } + + // used in www + public Uri GetShoppingUri(string[] productIds, string currency = null, string language = null, string customerId = null, string quantity = null, string affiliateId = null) + { + return tariffService.GetShoppingUri(productIds, affiliateId, currency, language, customerId, quantity); + } + public void ActivateKey(string key) diff --git a/common/ASC.Core.Common/Context/Impl/TenantManager.cs b/common/ASC.Core.Common/Context/Impl/TenantManager.cs index 2aff86f83..1d9f187bb 100644 --- a/common/ASC.Core.Common/Context/Impl/TenantManager.cs +++ b/common/ASC.Core.Common/Context/Impl/TenantManager.cs @@ -221,6 +221,7 @@ namespace ASC.Core { currentQuota.ActiveUsers = tariff.Quantity; currentQuota.MaxTotalSize *= currentQuota.ActiveUsers; + currentQuota.Price *= currentQuota.ActiveUsers; } return currentQuota; diff --git a/common/ASC.Core.Common/Context/Impl/UserManager.cs b/common/ASC.Core.Common/Context/Impl/UserManager.cs index 6c4525855..c5c6eb0cb 100644 --- a/common/ASC.Core.Common/Context/Impl/UserManager.cs +++ b/common/ASC.Core.Common/Context/Impl/UserManager.cs @@ -18,10 +18,15 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Web; +using ASC.Common.Caching; +using ASC.Common.Logging; +using ASC.Common.Radicale; using ASC.Core.Caching; using ASC.Core.Tenants; using ASC.Core.Users; +using ASC.Security.Cryptography; namespace ASC.Core { @@ -31,6 +36,7 @@ namespace ASC.Core private readonly IDictionary systemUsers; + public static readonly ICache Cache = AscCache.Default; public UserManager(IUserService service) { @@ -197,7 +203,7 @@ namespace ASC.Core return findUsers.ToArray(); } - public UserInfo SaveUserInfo(UserInfo u, bool isVisitor = false) + public UserInfo SaveUserInfo(UserInfo u, bool isVisitor = false, bool syncCardDav = false) { if (IsSystemUser(u.ID)) return systemUsers[u.ID]; if (u.ID == Guid.Empty) SecurityContext.DemandPermissions(Constants.Action_AddRemoveUser); @@ -237,10 +243,85 @@ namespace ASC.Core throw new InvalidOperationException("Can not disable tenant owner."); } + var oldUserData = userService.GetUserByUserName(CoreContext.TenantManager.GetCurrentTenant().TenantId, u.UserName); var newUser = userService.SaveUser(CoreContext.TenantManager.GetCurrentTenant().TenantId, u); + if (syncCardDav) + { + var tenant = CoreContext.TenantManager.GetCurrentTenant(); + var cardDavAB = new CardDavAddressbook(); + var myUri = (HttpContext.Current != null) ? HttpContext.Current.Request.GetUrlRewriter().ToString() : + (Cache.Get("REWRITE_URL" + tenant.TenantId) != null) ? + new Uri(Cache.Get("REWRITE_URL" + tenant.TenantId)).ToString() : tenant.GetTenantDomain(); + + var rootAuthorization = cardDavAB.GetSystemAuthorization(); + var allUserEmails = CoreContext.UserManager.GetDavUserEmails().ToList(); + var cardDavAddBook = new CardDavAddressbook(); + + if (oldUserData != null && oldUserData.Status != newUser.Status && newUser.Status == EmployeeStatus.Terminated) + { + var userAuthorization = oldUserData.Email.ToLower() + ":" + InstanceCrypto.Encrypt(oldUserData.Email); + var requestUrlBook = cardDavAB.GetRadicaleUrl(myUri, newUser.Email.ToLower(), true, true); + var collection = cardDavAB.GetCollection(requestUrlBook, userAuthorization, myUri.ToString()).Result; + if (collection.Completed && collection.StatusCode != 404) + { + cardDavAB.Delete(myUri, newUser.ID, newUser.Email, tenant.TenantId); + } + foreach (string email in allUserEmails) + { + var requestUrlItem = cardDavAddBook.GetRadicaleUrl(myUri.ToString(), email.ToLower(), true, true, itemID: newUser.ID.ToString()); + try + { + var davItemRequest = new DavRequest() + { + Url = requestUrlItem, + Authorization = rootAuthorization, + Header = myUri + }; + RadicaleClient.RemoveAsync(davItemRequest).ConfigureAwait(false); + } + catch (Exception ex) + { + LogManager.GetLogger("ASC").Error("ERROR: " + ex.Message); + } + } + } + else + { + + try + { + var cardDavUser = new CardDavItem(u.ID, u.FirstName, u.LastName, u.UserName, u.BirthDate, u.Sex, u.Title, u.Email, u.Contacts, u.MobilePhone); + + try + { + cardDavAB.UpdateItemForAllAddBooks(allUserEmails, myUri, cardDavUser, CoreContext.TenantManager.GetCurrentTenant().TenantId, oldUserData != null && oldUserData.Email != newUser.Email ? oldUserData.Email : null); + } + catch (Exception ex) + { + LogManager.GetLogger("ASC").Error("ERROR: " + ex.Message); + } + + } + catch (Exception ex) + { + LogManager.GetLogger("ASC").Error("ERROR: " + ex.Message); + } + } + + } + + return newUser; } + #region CardDAV/methods + + public IEnumerable GetDavUserEmails() + { + return userService.GetDavUserEmails(CoreContext.TenantManager.GetCurrentTenant().TenantId); + } + + #endregion public void DeleteUser(Guid id) { @@ -251,7 +332,61 @@ namespace ASC.Core throw new InvalidOperationException("Can not remove tenant owner."); } + var delUser = CoreContext.UserManager.GetUsers(id); userService.RemoveUser(CoreContext.TenantManager.GetCurrentTenant().TenantId, id); + var tenant = CoreContext.TenantManager.GetCurrentTenant(); + try + { + + var curreMail = delUser.Email.ToLower(); + var currentAccountPaswd = InstanceCrypto.Encrypt(curreMail); + var userAuthorization = curreMail + ":" + currentAccountPaswd; + var cardDavAddBook = new CardDavAddressbook(); + var rootAuthorization = cardDavAddBook.GetSystemAuthorization(); + var myUri = (HttpContext.Current != null) ? HttpContext.Current.Request.GetUrlRewriter().ToString() : + (Cache.Get("REWRITE_URL" + tenant.TenantId) != null) ? + new Uri(Cache.Get("REWRITE_URL" + tenant.TenantId)).ToString() : tenant.GetTenantDomain(); + var davUsersEmails = CoreContext.UserManager.GetDavUserEmails(); + var requestUrlBook = cardDavAddBook.GetRadicaleUrl(myUri, delUser.Email.ToLower(), true, true); + var addBookCollection = cardDavAddBook.GetCollection(requestUrlBook, userAuthorization, myUri.ToString()).Result; + + + if (addBookCollection.Completed && addBookCollection.StatusCode != 404) + { + var davbookRequest = new DavRequest() + { + Url = requestUrlBook, + Authorization = rootAuthorization, + Header = myUri + }; + RadicaleClient.RemoveAsync(davbookRequest).ConfigureAwait(false); + } + + foreach (string email in davUsersEmails) + { + var requestUrlItem = cardDavAddBook.GetRadicaleUrl(myUri.ToString(), email.ToLower(), true, true, itemID: delUser.ID.ToString()); + try + { + var davItemRequest = new DavRequest() + { + Url = requestUrlItem, + Authorization = rootAuthorization, + Header = myUri + }; + RadicaleClient.RemoveAsync(davItemRequest).ConfigureAwait(false); + } + catch (Exception ex) + { + LogManager.GetLogger("ASC").Error("ERROR: " + ex.Message); + } + } + + } + catch (Exception ex) + { + LogManager.GetLogger("ASC").Error("ERROR: " + ex.Message); + } + } public void SaveUserPhoto(Guid id, byte[] photo) @@ -370,6 +505,16 @@ namespace ASC.Core new UserGroupRef(userId, groupId, UserGroupRefType.Contains)); GetUsers(userId).ResetGroupCache(); + var user = CoreContext.UserManager.GetUsers(userId); + if (groupId == Constants.GroupVisitor.ID) + { + var tenant = CoreContext.TenantManager.GetCurrentTenant(); + var myUri = (HttpContext.Current != null) ? HttpContext.Current.Request.GetUrlRewriter().ToString() : + (Cache.Get("REWRITE_URL" + tenant.TenantId) != null) ? + new Uri(Cache.Get("REWRITE_URL" + tenant.TenantId)).ToString() : tenant.GetTenantDomain(); + var cardDavAB = new CardDavAddressbook(); + cardDavAB.Delete(myUri, user.ID, user.Email, tenant.TenantId); + } } public void RemoveUserFromGroup(Guid userId, Guid groupId) diff --git a/common/ASC.Core.Common/Context/SecurityContext.cs b/common/ASC.Core.Common/Context/SecurityContext.cs index 87aaf1b45..705670632 100644 --- a/common/ASC.Core.Common/Context/SecurityContext.cs +++ b/common/ASC.Core.Common/Context/SecurityContext.cs @@ -28,6 +28,7 @@ using ASC.Common.Security; using ASC.Common.Security.Authentication; using ASC.Common.Security.Authorizing; using ASC.Core.Billing; +using ASC.Core.Data; using ASC.Core.Security.Authentication; using ASC.Core.Security.Authorizing; using ASC.Core.Tenants; @@ -40,11 +41,6 @@ namespace ASC.Core private static readonly ILog log = LogManager.GetLogger("ASC.Core"); - public static IAccount CurrentAccount - { - get { return Principal.Identity is IAccount ? (IAccount)Principal.Identity : Configuration.Constants.Guest; } - } - public static bool IsAuthenticated { get { return CurrentAccount.IsAuthenticated; } @@ -59,8 +55,7 @@ namespace ASC.Core PermissionResolver = new PermissionResolver(azManager); } - - public static string AuthenticateMe(string login, string passwordHash) + public static string AuthenticateMe(string login, string passwordHash, Func funcLoginEvent = null) { if (login == null) throw new ArgumentNullException("login"); if (passwordHash == null) throw new ArgumentNullException("passwordHash"); @@ -68,7 +63,7 @@ namespace ASC.Core var tenantid = CoreContext.TenantManager.GetCurrentTenant().TenantId; var u = CoreContext.UserManager.GetUsersByPasswordHash(tenantid, login, passwordHash); - return AuthenticateMe(new UserAccount(u, tenantid)); + return AuthenticateMe(new UserAccount(u, tenantid), funcLoginEvent); } public static bool AuthenticateMe(string cookie) @@ -80,6 +75,7 @@ namespace ASC.Core int indexTenant; DateTime expire; int indexUser; + int loginEventId; if (cookie.Equals("Bearer", StringComparison.InvariantCulture)) { @@ -93,7 +89,7 @@ namespace ASC.Core } log.InfoFormat("Empty Bearer cookie: {0} {1}", ipFrom, address); } - else if (CookieStorage.DecryptCookie(cookie, out tenant, out userid, out indexTenant, out expire, out indexUser)) + else if (CookieStorage.DecryptCookie(cookie, out tenant, out userid, out indexTenant, out expire, out indexUser, out loginEventId)) { if (tenant != CoreContext.TenantManager.GetCurrentTenant().TenantId) { @@ -119,7 +115,13 @@ namespace ASC.Core return false; } - AuthenticateMe(new UserAccount(new UserInfo { ID = userid }, tenant)); + var settingLoginEvents = DbLoginEventsManager.GetLoginEventIds(tenant, userid); + if (loginEventId != 0 && !settingLoginEvents.Contains(loginEventId)) + { + return false; + } + + CurrentAccount = new UserAccount(new UserInfo { ID = userid }, tenant); return true; } catch (InvalidCredentialException ice) @@ -134,7 +136,7 @@ namespace ASC.Core } catch (Exception err) { - log.ErrorFormat("Authenticate error: cookie {0}, tenant {1}, userid {2}: {5}", + log.ErrorFormat("Authenticate error: cookie {0}, tenant {1}, userid {2}: {3}", cookie, tenant, userid, err); } } @@ -154,57 +156,86 @@ namespace ASC.Core return false; } - public static string AuthenticateMe(IAccount account) + public static string AuthenticateMe(Guid userId, Func funcLoginEvent = null) { - if (account == null || account.Equals(Configuration.Constants.Guest)) throw new InvalidCredentialException("account"); + var account = CoreContext.Authentication.GetAccountByID(userId); + return AuthenticateMe(account, funcLoginEvent); + } + + private static string AuthenticateMe(IAccount account, Func funcLoginEvent) + { + CurrentAccount = account; - var roles = new List { Role.Everyone }; string cookie = null; - - if (account is ISystemAccount && account.ID == Configuration.Constants.CoreSystem.ID) - { - roles.Add(Role.System); - } - if (account is IUserAccount) { - var u = CoreContext.UserManager.GetUsers(account.ID); + int loginEventId = 0; + if (funcLoginEvent != null) + { + loginEventId = funcLoginEvent(); + } - if (u.ID == Users.Constants.LostUser.ID) - { - throw new InvalidCredentialException("Invalid username or password."); - } - if (u.Status != EmployeeStatus.Active) - { - throw new SecurityException("Account disabled."); - } - // for LDAP users only - if (u.Sid != null) - { - if (!CoreContext.TenantManager.GetTenantQuota(CoreContext.TenantManager.GetCurrentTenant().TenantId).Ldap) - { - throw new BillingException("Your tariff plan does not support this option.", "Ldap"); - } - } - if (CoreContext.UserManager.IsUserInGroup(u.ID, Users.Constants.GroupAdmin.ID)) - { - roles.Add(Role.Administrators); - } - roles.Add(Role.Users); - - account = new UserAccount(u, CoreContext.TenantManager.GetCurrentTenant().TenantId); - cookie = CookieStorage.EncryptCookie(CoreContext.TenantManager.GetCurrentTenant().TenantId, account.ID); + cookie = CookieStorage.EncryptCookie(CoreContext.TenantManager.GetCurrentTenant().TenantId, account.ID, loginEventId); } - Principal = new GenericPrincipal(account, roles.ToArray()); - return cookie; } - public static string AuthenticateMe(Guid userId) + public static IAccount CurrentAccount { - return AuthenticateMe(CoreContext.Authentication.GetAccountByID(userId)); + get { return Principal.Identity is IAccount ? (IAccount)Principal.Identity : Configuration.Constants.Guest; } + set + { + var account = value; + if (account == null || account.Equals(Configuration.Constants.Guest)) throw new InvalidCredentialException("account"); + + var roles = new List { Role.Everyone }; + + if (account is ISystemAccount && account.ID == Configuration.Constants.CoreSystem.ID) + { + roles.Add(Role.System); + } + + if (account is IUserAccount) + { + var u = CoreContext.UserManager.GetUsers(account.ID); + + if (u.ID == Users.Constants.LostUser.ID) + { + throw new InvalidCredentialException("Invalid username or password."); + } + if (u.Status != EmployeeStatus.Active) + { + throw new SecurityException("Account disabled."); + } + // for LDAP users only + if (u.Sid != null) + { + if (!(CoreContext.Configuration.Standalone || CoreContext.TenantManager.GetTenantQuota(CoreContext.TenantManager.GetCurrentTenant().TenantId).Ldap)) + { + throw new BillingException("Your tariff plan does not support this option.", "Ldap"); + } + } + if (CoreContext.UserManager.IsUserInGroup(u.ID, Users.Constants.GroupAdmin.ID)) + { + roles.Add(Role.Administrators); + } + roles.Add(Role.Users); + + account = new UserAccount(u, CoreContext.TenantManager.GetCurrentTenant().TenantId); + } + + Principal = new GenericPrincipal(account, roles.ToArray()); + } + } + + public static Guid CurrentUser + { + set + { + CurrentAccount = CoreContext.Authentication.GetAccountByID(value); + } } public static void Logout() diff --git a/common/ASC.Core.Common/Contracts/HealthCheck/HealthCheckSvc.cs b/common/ASC.Core.Common/Contracts/HealthCheck/HealthCheckSvc.cs new file mode 100644 index 000000000..8cc5bf0f7 --- /dev/null +++ b/common/ASC.Core.Common/Contracts/HealthCheck/HealthCheckSvc.cs @@ -0,0 +1,95 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +using System; +using System.Configuration; +using System.Diagnostics; +using System.Net; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +using ASC.Common.Logging; + +namespace ASC.Core.Common.Contracts +{ + public class HealthCheckSvc + { + private readonly string port; + private readonly string needResult; + private readonly string path; + private readonly int pingInterval; + private readonly string url; + private CancellationTokenSource PingCancellationTokenSource = new CancellationTokenSource(); + private ILog Log; + + public HealthCheckSvc(string port, string needResult, ILog log, string path = "") + { + this.port = port; + this.needResult = needResult; + this.path = path; + Log = log; + + var appSettings = ConfigurationManagerExtension.AppSettings; + pingInterval = int.Parse(appSettings["ping.interval"]); + url = appSettings["ping.url"]; + } + + private bool Ping() + { + using (var client = new WebClient { Encoding = Encoding.UTF8 }) + { + try + { + var result = client.DownloadString(new Uri(url + ":" + port + path)); + return result == needResult; + } + catch + { + return false; + } + } + } + + public void StartPing() + { + var task = new Task(() => + { + while (true) + { + Thread.Sleep(pingInterval); + Log.Debug("Ping"); + if (!Ping()) + { + Log.Error("Error pong"); + Process.GetCurrentProcess().Kill(); + } + else + { + Log.Debug("Pong"); + } + } + }, PingCancellationTokenSource.Token, TaskCreationOptions.LongRunning); + task.Start(TaskScheduler.Default); + } + + public void StopPing() + { + PingCancellationTokenSource.Cancel(); + PingCancellationTokenSource.Dispose(); + } + } +} diff --git a/common/ASC.Core.Common/Core/AzRecord.cs b/common/ASC.Core.Common/Core/AzRecord.cs index b699524bc..471f96dc7 100644 --- a/common/ASC.Core.Common/Core/AzRecord.cs +++ b/common/ASC.Core.Common/Core/AzRecord.cs @@ -59,6 +59,45 @@ namespace ASC.Core ObjectId = objectId; } + public static implicit operator AzRecord(AzRecordCache cache) + { + var result = new AzRecord() + { + Tenant = cache.Tenant + }; + + + if (Guid.TryParse(cache.SubjectId, out var subjectId)) + { + result.SubjectId = subjectId; + } + + if (Guid.TryParse(cache.ActionId, out var actionId)) + { + result.ActionId = actionId; + } + + result.ObjectId = cache.ObjectId; + + if (Enum.TryParse(cache.Reaction, out var reaction)) + { + result.Reaction = reaction; + } + + return result; + } + + public static implicit operator AzRecordCache(AzRecord cache) + { + return new AzRecordCache + { + SubjectId = cache.SubjectId.ToString(), + ActionId = cache.ActionId.ToString(), + ObjectId = cache.ObjectId, + Reaction = cache.Reaction.ToString(), + Tenant = cache.Tenant + }; + } public override bool Equals(object obj) { @@ -80,4 +119,13 @@ namespace ASC.Core Reaction.GetHashCode(); } } + + public class AzRecordCache + { + public String SubjectId { get; set; } + public String ActionId { get; set; } + public String ObjectId { get; set; } + public String Reaction { get; set; } + public int Tenant { get; set; } + } } diff --git a/common/ASC.Core.Common/Core/IUserService.cs b/common/ASC.Core.Common/Core/IUserService.cs index f4025dc73..39f823700 100644 --- a/common/ASC.Core.Common/Core/IUserService.cs +++ b/common/ASC.Core.Common/Core/IUserService.cs @@ -38,6 +38,8 @@ namespace ASC.Core void RemoveUser(int tenant, Guid id); + IEnumerable GetDavUserEmails(int tenant); + byte[] GetUserPhoto(int tenant, Guid id); void SetUserPhoto(int tenant, Guid id, byte[] photo); diff --git a/common/ASC.Core.Common/Core/SubscriptionMethod.cs b/common/ASC.Core.Common/Core/SubscriptionMethod.cs index 04093d6f6..0898fb44f 100644 --- a/common/ASC.Core.Common/Core/SubscriptionMethod.cs +++ b/common/ASC.Core.Common/Core/SubscriptionMethod.cs @@ -51,5 +51,39 @@ namespace ASC.Core get; set; } + + public static implicit operator SubscriptionMethod(SubscriptionMethodCache cache) + { + return new SubscriptionMethod() + { + Tenant = cache.Tenant, + SourceId = cache.SourceId, + ActionId = cache.ActionId, + RecipientId = cache.RecipientId + }; + } + + public static implicit operator SubscriptionMethodCache(SubscriptionMethod cache) + { + return new SubscriptionMethodCache + { + Tenant = cache.Tenant, + SourceId = cache.SourceId, + ActionId = cache.ActionId, + RecipientId = cache.RecipientId + }; + } + + } + + + + public class SubscriptionMethodCache + { + public string RecipientId; + public string ActionId; + public string SourceId; + public string[] Methods; + public int Tenant; } } diff --git a/common/ASC.Core.Common/Core/UserGroupRef.cs b/common/ASC.Core.Common/Core/UserGroupRef.cs index a2fd244f1..d65e730bb 100644 --- a/common/ASC.Core.Common/Core/UserGroupRef.cs +++ b/common/ASC.Core.Common/Core/UserGroupRef.cs @@ -18,6 +18,8 @@ using System; using System.Diagnostics; +using ASC.Core.Caching; + namespace ASC.Core { [DebuggerDisplay("{UserId} - {GroupId}")] @@ -91,5 +93,39 @@ namespace ASC.Core var r = obj as UserGroupRef; return r != null && r.Tenant == Tenant && r.UserId == UserId && r.GroupId == GroupId && r.RefType == RefType; } + + public static implicit operator UserGroupRef(UserGroupRefCacheItem cache) + { + var result = new UserGroupRef + { + UserId = new Guid(cache.UserId), + GroupId = new Guid(cache.GroupId) + }; + + if (Enum.TryParse(cache.RefType, out var refType)) + { + result.RefType = refType; + } + + result.Tenant = cache.Tenant; + result.LastModified = new DateTime(cache.LastModified); + result.Removed = cache.Removed; + + return result; + } + + public static implicit operator UserGroupRefCacheItem(UserGroupRef cache) + { + return new UserGroupRefCacheItem + { + GroupId = cache.GroupId.ToString(), + UserId = cache.UserId.ToString(), + RefType = cache.RefType.ToString(), + LastModified = cache.LastModified.Ticks, + Removed = cache.Removed, + Tenant = cache.Tenant + }; + } + } } diff --git a/common/ASC.Core.Common/Data/DbLoginEventsManager.cs b/common/ASC.Core.Common/Data/DbLoginEventsManager.cs new file mode 100644 index 000000000..8a70961a6 --- /dev/null +++ b/common/ASC.Core.Common/Data/DbLoginEventsManager.cs @@ -0,0 +1,188 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Common.Caching; +using ASC.Common.Data; +using ASC.Common.Data.Sql; +using ASC.Common.Data.Sql.Expressions; +using ASC.AuditTrail; +using ASC.Core.Tenants; +using ASC.MessagingSystem; + +namespace ASC.Core.Data +{ + public class DbLoginEventsManager + { + private const string guidLoginEvent = "F4D8BBF6-EB63-4781-B55E-5885EAB3D759"; + + private static readonly ICache cache = AscCache.Memory; + private static readonly TimeSpan expirationTimeout = TimeSpan.FromMinutes(5); + private static string dbId = "default"; + + private static readonly List loginActions = new List + { + MessageAction.LoginSuccess, + MessageAction.LoginSuccessViaSocialAccount, + MessageAction.LoginSuccessViaSms, + MessageAction.LoginSuccessViaApi, + MessageAction.LoginSuccessViaSocialApp, + MessageAction.LoginSuccessViaApiSms, + MessageAction.LoginSuccessViaSSO, + MessageAction.LoginSuccessViaApiSocialAccount, + MessageAction.LoginSuccesViaTfaApp, + MessageAction.LoginSuccessViaApiTfa + }; + + public static List GetLoginEventIds(int tenantId, Guid userId) + { + var commonKey = GetCacheKey(tenantId, userId); + var cacheKeys = cache.Get>(commonKey); + if (cacheKeys != null) + { + return cacheKeys; + } + + using (var db = GetDbManager()) + { + var where = GetActiveConnectionsWhere(tenantId, userId); + var query = new SqlQuery("login_events") + .Select("id") + .Where(where); + var resultList = db.ExecuteList(query).Select(row => (int)row[0]).ToList(); + + if (resultList != null) + { + cache.Insert(commonKey, resultList, expirationTimeout); + } + + return resultList; + } + } + + public static List GetLoginEvents(int tenantId, Guid userId) + { + using (var db = GetDbManager()) + { + var where = GetActiveConnectionsWhere(tenantId, userId); + var query = new SqlQuery("login_events") + .Select("id", "substring_index(ip, ':', 1) ip", "platform", "browser", "date") + .Where(where) + .OrderBy("id", false); + var loginInfo = db.ExecuteList(query).Select(row => new BaseEvent + { + Id = (int)row[0], + IP = (string)row[1], + Platform = (string)row[2], + Browser = (string)row[3], + Date = TenantUtil.DateTimeFromUtc((DateTime)row[4]) + }).ToList(); + return loginInfo; + } + } + + private static Exp GetActiveConnectionsWhere(int tenantId, Guid userId) + { + var exp = Exp.Empty; + exp &= Exp.Eq("tenant_id", tenantId); + exp &= Exp.Eq("user_id", userId); + exp &= Exp.In("action", loginActions); + exp &= Exp.Gt("date", DateTime.UtcNow.AddYears(-1)); + exp &= Exp.Eq("active", true); + return exp; + } + + public static void LogOutEvent(int loginEventId) + { + using (var db = GetDbManager()) + { + var query = new SqlUpdate("login_events") + .Set("active", false) + .Where("id", loginEventId); + db.ExecuteNonQuery(query); + } + ResetCache(); + } + + public static void LogOutAllActiveConnections(int tenantId, Guid userId) + { + using (var db = GetDbManager()) + { + var query = new SqlUpdate("login_events") + .Set("active", false) + .Where("tenant_id", tenantId) + .Where("user_id", userId) + .Where("active", true); + db.ExecuteNonQuery(query); + } + ResetCache(tenantId, userId); + } + + public static void LogOutAllActiveConnectionsForTenant(int tenantId) + { + using (var db = GetDbManager()) + { + var query = new SqlUpdate("login_events") + .Set("active", false) + .Where("tenant_id", tenantId) + .Where("active", true); + db.ExecuteNonQuery(query); + } + } + + public static void LogOutAllActiveConnectionsExceptThis(int loginEventId, int tenantId, Guid userId) + { + using (var db = GetDbManager()) + { + var query = new SqlUpdate("login_events") + .Set("active", false) + .Where("tenant_id", tenantId) + .Where("user_id", userId) + .Where(!Exp.Eq("id", loginEventId)) + .Where("active", true); + db.ExecuteNonQuery(query); + } + ResetCache(tenantId, userId); + } + + public static void ResetCache(int tenantId, Guid userId) + { + var key = GetCacheKey(tenantId, userId); + cache.Remove(key); + } + + public static void ResetCache() + { + var tenantId = CoreContext.TenantManager.GetCurrentTenant().TenantId; + var userId = SecurityContext.CurrentAccount.ID; + ResetCache(tenantId, userId); + } + + private static string GetCacheKey(int tenantId, Guid userId) + { + return string.Join("", guidLoginEvent, tenantId, userId); + } + + private static IDbManager GetDbManager() + { + return DbManager.FromHttpContext(dbId); + } + } +} diff --git a/common/ASC.Core.Common/Data/DbQuotaService.cs b/common/ASC.Core.Common/Data/DbQuotaService.cs index 9b07749ac..2518f682a 100644 --- a/common/ASC.Core.Common/Data/DbQuotaService.cs +++ b/common/ASC.Core.Common/Data/DbQuotaService.cs @@ -52,7 +52,7 @@ namespace ASC.Core.Data private IEnumerable GetTenantQuotas(Exp where) { var q = new SqlQuery(tenants_quota) - .Select("tenant", "name", "max_file_size", "max_total_size", "active_users", "features", "price", "price2", "avangate_id", "visible") + .Select("tenant", "name", "max_file_size", "max_total_size", "active_users", "features", "price", "avangate_id", "visible") .Where(where); return ExecList(q) @@ -64,9 +64,8 @@ namespace ASC.Core.Data ActiveUsers = Convert.ToInt32(r[4]) != 0 ? Convert.ToInt32(r[4]) : int.MaxValue, Features = (string)r[5], Price = Convert.ToDecimal(r[6]), - Price2 = Convert.ToDecimal(r[7]), - AvangateId = (string)r[8], - Visible = Convert.ToBoolean(r[9]), + AvangateId = (string)r[7], + Visible = Convert.ToBoolean(r[8]), }); } @@ -82,7 +81,6 @@ namespace ASC.Core.Data .InColumnValue("active_users", quota.ActiveUsers) .InColumnValue("features", quota.Features) .InColumnValue("price", quota.Price) - .InColumnValue("price2", quota.Price2) .InColumnValue("avangate_id", quota.AvangateId) .InColumnValue("visible", quota.Visible); diff --git a/common/ASC.Core.Common/Data/DbSettingsManager.cs b/common/ASC.Core.Common/Data/DbSettingsManager.cs index c431fa810..fe54dc497 100644 --- a/common/ASC.Core.Common/Data/DbSettingsManager.cs +++ b/common/ASC.Core.Common/Data/DbSettingsManager.cs @@ -145,10 +145,10 @@ namespace ASC.Core.Data .Where("tenantid", tenantId) .Where("userid", userId.ToString()); - var result = db.ExecuteScalar(q); - if (result != null) + var result = db.ExecuteScalar(q); + if (!string.IsNullOrEmpty(result)) { - var data = result is string ? Encoding.UTF8.GetBytes((string)result) : (byte[])result; + var data = Encoding.UTF8.GetBytes(result); settings = Deserialize(data); } else @@ -171,9 +171,7 @@ namespace ASC.Core.Data { using (var stream = new MemoryStream(data)) { - var settings = data[0] == 0 - ? new BinaryFormatter().Deserialize(stream) - : GetJsonSerializer(typeof(T)).ReadObject(stream); + var settings = GetJsonSerializer(typeof(T)).ReadObject(stream); return (T)settings; } } @@ -203,12 +201,12 @@ namespace ASC.Core.Data return jsonSerializers[type]; } } - - - [Serializable] - class SettingsCacheItem - { - public string Key { get; set; } - } } + + [Serializable] + class SettingsCacheItem + { + public string Key { get; set; } + } + } \ No newline at end of file diff --git a/common/ASC.Core.Common/Data/DbTenantService.cs b/common/ASC.Core.Common/Data/DbTenantService.cs index f4d09b6f7..252fef23f 100644 --- a/common/ASC.Core.Common/Data/DbTenantService.cs +++ b/common/ASC.Core.Common/Data/DbTenantService.cs @@ -97,27 +97,12 @@ namespace ASC.Core.Data .Where("u.id", userId) .Where("u.status", EmployeeStatus.Active) .Where("u.removed", false) - .Where(Exp.Or( - Exp.Eq("s.pwdhash", GetPasswordHash(userId, passwordHash)), - Exp.Eq("s.pwdhash", Hasher.Base64Hash(passwordHash, HashAlg.SHA256)) //todo: remove old scheme - )); + .Where(Exp.Eq("s.pwdhash", GetPasswordHash(userId, passwordHash))); return ExecList(q).ConvertAll(ToTenant); } else { - var q = TenantsQuery(Exp.Empty) - .InnerJoin("core_user u", Exp.EqColumns("t.id", "u.tenant")) - .InnerJoin("core_usersecurity s", Exp.EqColumns("u.id", "s.userid")) - .Where("t.status", (int)TenantStatus.Active) - .Where(login.Contains('@') ? "u.email" : "u.id", login) - .Where("u.status", EmployeeStatus.Active) - .Where("u.removed", false) - .Where("s.pwdhash", Hasher.Base64Hash(passwordHash, HashAlg.SHA256)); - - //old password - var result = ExecList(q).ConvertAll(ToTenant); - var usersQuery = new SqlQuery("core_user u") .Select("u.id") .Where("u.email", login) @@ -128,16 +113,12 @@ namespace ASC.Core.Data GetPasswordHash(new Guid((string)r[0]), passwordHash) ); - q = TenantsQuery(Exp.Empty) + var q = TenantsQuery(Exp.Empty) .InnerJoin("core_usersecurity s", Exp.EqColumns("t.id", "s.tenant")) .Where("t.status", (int)TenantStatus.Active) .Where(Exp.In("s.pwdhash", passwordHashs)); - //new password - result = result.Concat(ExecList(q).ConvertAll(ToTenant)).ToList(); - result.Distinct(); - - return result; + return ExecList(q).ConvertAll(ToTenant); } } @@ -450,7 +431,7 @@ namespace ASC.Core.Data // cut number suffix while (true) { - if (6 < domain.Length && char.IsNumber(domain, domain.Length - 1)) + if (TenantDomainValidator.MinLength < domain.Length && char.IsNumber(domain, domain.Length - 1)) { domain = domain.Substring(0, domain.Length - 1); } diff --git a/common/ASC.Core.Common/Data/DbUserService.cs b/common/ASC.Core.Common/Data/DbUserService.cs index 0c3846725..9a4c0428b 100644 --- a/common/ASC.Core.Common/Data/DbUserService.cs +++ b/common/ASC.Core.Common/Data/DbUserService.cs @@ -24,7 +24,6 @@ using ASC.Common.Data.Sql; using ASC.Common.Data.Sql.Expressions; using ASC.Core.Tenants; using ASC.Core.Users; -using ASC.Security.Cryptography; namespace ASC.Core.Data { @@ -47,6 +46,7 @@ namespace ASC.Core.Data return ExecList(q).ConvertAll(ToUser).SingleOrDefault(); } + public UserInfo GetUser(int tenant, string email) { var q = GetUserQuery(tenant, default(DateTime)) @@ -55,14 +55,6 @@ namespace ASC.Core.Data return ExecList(q).ConvertAll(ToUser).SingleOrDefault(); } - public UserInfo GetUserByUserName(int tenant, string userName) - { - var q = GetUserQuery(tenant, default(DateTime)) - .Where("userName", userName) - .Where("removed", false); - return ExecList(q).ConvertAll(ToUser).SingleOrDefault(); - } - public UserInfo GetUserByPasswordHash(int tenant, string login, string passwordHash) { if (string.IsNullOrEmpty(login)) throw new ArgumentNullException("login"); @@ -70,15 +62,10 @@ namespace ASC.Core.Data Guid userId; if (Guid.TryParse(login, out userId)) { - RegeneratePassword(tenant, userId); - var q = GetUserQuery() .InnerJoin("core_usersecurity s", Exp.EqColumns("u.id", "s.userid")) .Where("u.id", userId) - .Where(Exp.Or( - Exp.Eq("s.pwdhash", GetPasswordHash(userId, passwordHash)), - Exp.Eq("s.pwdhash", Hasher.Base64Hash(passwordHash, HashAlg.SHA256)) //todo: remove old scheme - )) + .Where(Exp.Eq("s.pwdhash", GetPasswordHash(userId, passwordHash))) .Where("u.removed", false); if (tenant != Tenant.DEFAULT_TENANT) { @@ -97,30 +84,17 @@ namespace ASC.Core.Data } var users = ExecList(q).ConvertAll(ToUser); - UserInfo result = null; foreach (var user in users) { - RegeneratePassword(tenant, user.ID); - q = new SqlQuery("core_usersecurity s") .SelectCount() .Where("s.userid", user.ID) - .Where(Exp.Or( - Exp.Eq("s.pwdhash", GetPasswordHash(user.ID, passwordHash)), - Exp.Eq("s.pwdhash", Hasher.Base64Hash(passwordHash, HashAlg.SHA256)) //todo: remove old scheme - )); + .Where(Exp.Eq("s.pwdhash", GetPasswordHash(user.ID, passwordHash))); var count = ExecScalar(q); - if (count > 0) - { - if (tenant != Tenant.DEFAULT_TENANT) return user; - - //need for regenerate all passwords only - //todo: remove with old scheme - result = user; - } + if (count > 0) return user; } - return result; + return null; } } @@ -132,26 +106,6 @@ namespace ASC.Core.Data return ExecList(q).ConvertAll(ToUser); } - //todo: remove - private void RegeneratePassword(int tenant, Guid userId) - { - var q = new SqlQuery("core_usersecurity") - .Select("tenant", "pwdhashsha512") - .Where("userid", userId.ToString()); - if (tenant != Tenant.DEFAULT_TENANT) - { - q.Where("tenant", tenant); - } - var result = ExecList(q) - .ConvertAll(r => new Tuple(Convert.ToInt32(r[0]), (string)r[1])) - .FirstOrDefault(); - if (result == null || string.IsNullOrEmpty(result.Item2)) return; - - var password = Crypto.GetV(result.Item2, 1, false); - var passwordHash = PasswordHasher.GetClientPassword(password); - SetUserPasswordHash(result.Item1, userId, passwordHash); - } - public UserInfo SaveUser(int tenant, UserInfo user) { if (user == null) throw new ArgumentNullException("user"); @@ -286,8 +240,8 @@ namespace ASC.Core.Data var i = Insert("core_usersecurity", tenant) .InColumnValue("userid", id.ToString()) .InColumnValue("pwdhash", h1) - .InColumnValue("pwdhashsha512", null) //todo: remove - ; + .InColumnValue("LastModified", DateTime.UtcNow); + ExecNonQuery(i); } @@ -541,5 +495,28 @@ namespace ASC.Core.Data Tenant = Convert.ToInt32(r[5]) }; } + + public UserInfo GetUserByUserName(int tenant, string userName) + { + var q = GetUserQuery(tenant, default(DateTime)) + .Where("userName", userName) + .Where("removed", false); + return ExecList(q).ConvertAll(ToUser).SingleOrDefault(); + } + + #region Dav/methods + + public IEnumerable GetDavUserEmails(int tenant) + { + var query = new SqlQuery("core_userdav dav") + .Select("u.email") + .InnerJoin("core_user u", Exp.EqColumns("u.tenant", "dav.tenant_id") & Exp.EqColumns("u.id", "dav.user_id")) + .Where("dav.tenant_id", tenant) + .Distinct(); + + return ExecList(query).ConvertAll(r => (string)r[0]); + } + + #endregion } } \ No newline at end of file diff --git a/common/ASC.Core.Common/HostedSolution.cs b/common/ASC.Core.Common/HostedSolution.cs index 4c904255b..47c42ede1 100644 --- a/common/ASC.Core.Common/HostedSolution.cs +++ b/common/ASC.Core.Common/HostedSolution.cs @@ -178,7 +178,7 @@ namespace ASC.Core var tenantSettings = settingsManager.LoadSettingsFor(tenantId, Guid.Empty); var expires = tenantSettings.IsDefault() ? DateTime.UtcNow.AddYears(1) : DateTime.UtcNow.AddMinutes(tenantSettings.LifeTime); var userSettings = settingsManager.LoadSettingsFor(tenantId, user.ID); - return CookieStorage.EncryptCookie(tenantId, user.ID, tenantSettings.Index, expires, userSettings.Index); + return CookieStorage.EncryptCookie(tenantId, user.ID, tenantSettings.Index, expires, userSettings.Index, 0); } public Tariff GetTariff(int tenant, bool withRequestToPaymentSystem = true) diff --git a/common/ASC.Core.Common/Logging/LogManager.cs b/common/ASC.Core.Common/Logging/LogManager.cs new file mode 100644 index 000000000..e876f4654 --- /dev/null +++ b/common/ASC.Core.Common/Logging/LogManager.cs @@ -0,0 +1,33 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using ASC.Core; + +namespace ASC.Common.Logging +{ + public class LogManager + { + public static ILog GetLogger(string name) + { + return BaseLogManager.GetLogger(name, () => + { + var tenant = CoreContext.TenantManager.GetCurrentTenant(false); + return tenant == null ? null : tenant.TenantAlias; + }); + } + } +} diff --git a/module/ASC.MessagingSystem/MessageAction.cs b/common/ASC.Core.Common/Messaging/MessageAction.cs similarity index 91% rename from module/ASC.MessagingSystem/MessageAction.cs rename to common/ASC.Core.Common/Messaging/MessageAction.cs index 08e3fadf3..f46d4089e 100644 --- a/module/ASC.MessagingSystem/MessageAction.cs +++ b/common/ASC.Core.Common/Messaging/MessageAction.cs @@ -1,4 +1,4 @@ -/* +/* * * (c) Copyright Ascensio System Limited 2010-2021 * @@ -100,6 +100,7 @@ namespace ASC.MessagingSystem SubtaskUpdated = 2035, SubtaskUpdatedStatus = 2036, SubtaskDeleted = 2037, + SubtaskMoved = 2058, DiscussionCreated = 2038, DiscussionUpdated = 2039, @@ -370,6 +371,10 @@ namespace ASC.MessagingSystem UserDataReassigns = 4030, UserDataRemoving = 4031, + UserLogoutActiveConnections = 4034, + UserLogoutActiveConnection = 4035, + UserLogoutActiveConnectionsForUser = 4036, + #endregion #region Documents @@ -385,10 +390,15 @@ namespace ASC.MessagingSystem FileLocked = 5006, FileUnlocked = 5007, FileUpdatedAccess = 5008, + FileUpdatedAccessFor = 5068, FileSendAccessLink = 5036, // not used + FileOpenedForChange = 5054, + FileRemovedFromList = 5058, + FileExternalLinkAccessUpdated = 5060, FileDownloaded = 5009, FileDownloadedAs = 5010, + FileRevisionDownloaded = 5062, FileUploaded = 5011, FileImported = 5012, @@ -398,18 +408,23 @@ namespace ASC.MessagingSystem FileMoved = 5015, FileMovedWithOverwriting = 5016, FileMovedToTrash = 5017, - FileDeleted = 5018, // not used + FileDeleted = 5018, FolderCreated = 5019, FolderRenamed = 5020, FolderUpdatedAccess = 5021, + FolderUpdatedAccessFor = 5066, FolderCopied = 5022, FolderCopiedWithOverwriting = 5023, FolderMoved = 5024, + FolderMovedFrom = 5067, FolderMovedWithOverwriting = 5025, FolderMovedToTrash = 5026, - FolderDeleted = 5027, // not used + FolderDeleted = 5027, + FolderRemovedFromList = 5059, + + FolderDownloaded = 5057, ThirdPartyCreated = 5028, ThirdPartyUpdated = 5029, @@ -420,6 +435,7 @@ namespace ASC.MessagingSystem DocumentsForcesave = 5049, DocumentsStoreForcesave = 5048, DocumentsUploadingFormatsSettingsUpdated = 5033, + DocumentsExternalShareSettingsUpdated = 5069, // last FileConverted = 5035, @@ -428,6 +444,15 @@ namespace ASC.MessagingSystem DocumentSignComplete = 5046, DocumentSendToSign = 5045, + FileMarkedAsFavorite = 5055, + FileRemovedFromFavorite = 5056, + FileMarkedAsRead = 5063, + FileReaded = 5064, + + TrashEmptied = 5061, + + FolderMarkedAsRead = 5065, + #endregion #region Settings @@ -502,7 +527,7 @@ namespace ASC.MessagingSystem PrivacyRoomEnable = 5051, PrivacyRoomDisable = 5052, - StartStorageDecryption = 5053, // last + StartStorageDecryption = 5053, #endregion @@ -535,4 +560,4 @@ namespace ASC.MessagingSystem #endregion } -} \ No newline at end of file +} diff --git a/common/ASC.Core.Common/Notify/EmailSenderSink.cs b/common/ASC.Core.Common/Notify/EmailSenderSink.cs index 8657222b8..dd5221da9 100644 --- a/common/ASC.Core.Common/Notify/EmailSenderSink.cs +++ b/common/ASC.Core.Common/Notify/EmailSenderSink.cs @@ -32,6 +32,7 @@ namespace ASC.Core.Notify { private static readonly string senderName = ASC.Core.Configuration.Constants.NotifyEMailSenderSysName; private readonly INotifySender sender; + private ILog Log = LogManager.GetLogger("ASC.Notify"); public EmailSenderSink(INotifySender sender) @@ -122,7 +123,7 @@ namespace ASC.Core.Notify } catch (Exception e) { - LogManager.GetLogger("ASC.Notify").Error("Error creating reply to tag for: " + replyTag.Value, e); + Log.Error("Error creating reply to tag for: " + replyTag.Value, e); } } @@ -147,7 +148,7 @@ namespace ASC.Core.Notify } catch (Exception e) { - LogManager.GetLogger("ASC.Notify").Error("Error creating AutoSubmitted tag for: " + autoSubmittedTag.Value, e); + Log.Error("Error creating AutoSubmitted tag for: " + autoSubmittedTag.Value, e); } } diff --git a/common/ASC.Core.Common/Notify/MentionProvider.cs b/common/ASC.Core.Common/Notify/MentionProvider.cs new file mode 100644 index 000000000..4200257a2 --- /dev/null +++ b/common/ASC.Core.Common/Notify/MentionProvider.cs @@ -0,0 +1,33 @@ +using System.Linq; +using System.Text.RegularExpressions; + +using ASC.Core.Users; + +namespace ASC.Core.Common.Notify +{ + public static class MentionProvider + { + /// + /// Returns array of users that were mentioned in comments + /// + /// Comment content + /// Array of users + public static UserInfo[] GetMentionedUsers(string content) + { + var mentionRegex = new Regex("@(.*?)"); + + var matchCollection = mentionRegex.Matches(content); + + if (matchCollection.Count > 0) + { + return matchCollection.Cast() + .Select(match => CoreContext.UserManager.GetUserByEmail(match.Groups[1].Value)) + .Where(user => user.ID != Constants.LostUser.ID) + .ToArray(); + } + + return new UserInfo[] { }; + } + + } +} diff --git a/common/ASC.Core.Common/Notify/ReplyToTagProvider.cs b/common/ASC.Core.Common/Notify/ReplyToTagProvider.cs deleted file mode 100644 index 4d1bf996b..000000000 --- a/common/ASC.Core.Common/Notify/ReplyToTagProvider.cs +++ /dev/null @@ -1,82 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Text.RegularExpressions; - -using ASC.Notify.Patterns; - -namespace ASC.Core.Common.Notify -{ - /// - /// Class that generates 'mail to' addresses to create new TeamLab entities from post client - /// - public static class ReplyToTagProvider - { - private static readonly Regex EntityType = new Regex(@"blog|forum.topic|event|photo|file|wiki|bookmark|project\.milestone|project\.task|project\.message"); - - private const string TagName = "replyto"; - - /// - /// Creates 'replyto' tag that can be used to comment some TeamLab entity - /// - /// Name of entity e.g. 'blog', 'project.task', etc. - /// Uniq id of the entity - /// New TeamLab tag - public static TagValue Comment(string entity, string entityId) - { - return Comment(entity, entityId, null); - } - - /// - /// Creates 'replyto' tag that can be used to comment some TeamLab entity - /// - /// Name of entity e.g. 'blog', 'project.task', etc. - /// Uniq id of the entity - /// Comment's parent comment id - /// New TeamLab tag - public static TagValue Comment(string entity, string entityId, string parentId) - { - if (String.IsNullOrEmpty(entity) || !EntityType.Match(entity).Success) throw new ArgumentException(@"Not supported entity type", entity); - if (String.IsNullOrEmpty(entityId)) throw new ArgumentException(@"Entity Id is null or empty", entityId); - - string pId = parentId != Guid.Empty.ToString() && parentId != null ? parentId : String.Empty; - return new TagValue(TagName, String.Format("reply_{0}_{1}_{2}@{3}", entity, entityId, pId, AutoreplyDomain)); - } - - /// - /// Creates 'replyto' tag that can be used to create TeamLab project message - /// - /// Id of the project to create message - /// New TeamLab tag - public static TagValue Message(int projectId) - { - return new TagValue(TagName, String.Format("message_{0}@{1}", projectId, AutoreplyDomain)); - } - - private static string AutoreplyDomain - { - get - { - // we use mapped domains for standalone portals because it is the only way to reach autoreply service - // mapped domains are no allowed for SAAS because of http(s) problem - var tenant = CoreContext.TenantManager.GetCurrentTenant(); - return tenant.GetTenantDomain(CoreContext.Configuration.Standalone); - } - } - } -} diff --git a/common/ASC.Core.Common/Notify/Senders/AWSSender.cs b/common/ASC.Core.Common/Notify/Senders/AWSSender.cs index baabc1fac..5be3313da 100644 --- a/common/ASC.Core.Common/Notify/Senders/AWSSender.cs +++ b/common/ASC.Core.Common/Notify/Senders/AWSSender.cs @@ -66,9 +66,7 @@ namespace ASC.Core.Notify.Senders CoreContext.TenantManager.SetCurrentTenant(m.Tenant); if (!CoreContext.Configuration.SmtpSettings.IsDefaultSettings) { - _useCoreSettings = true; result = base.Send(m); - _useCoreSettings = false; } else { diff --git a/common/ASC.Core.Common/Notify/Senders/SmtpSender.cs b/common/ASC.Core.Common/Notify/Senders/SmtpSender.cs index 791855c42..a8a47378a 100644 --- a/common/ASC.Core.Common/Notify/Senders/SmtpSender.cs +++ b/common/ASC.Core.Common/Notify/Senders/SmtpSender.cs @@ -44,49 +44,70 @@ namespace ASC.Core.Notify.Senders "; protected ILog Log { get; private set; } + private IDictionary _initProperties; private string _host; private int _port; private bool _ssl; private ICredentials _credentials; - protected bool _useCoreSettings; const int NETWORK_TIMEOUT = 30000; public SmtpSender() { + _initProperties = new Dictionary(); + Log = LogManager.GetLogger("ASC.Notify"); } public virtual void Init(IDictionary properties) { - if (properties.ContainsKey("useCoreSettings") && bool.Parse(properties["useCoreSettings"])) + _initProperties = properties; + } + + private void BuildSmtpSettings() + { + if (CoreContext.Configuration.SmtpSettings.IsDefaultSettings && _initProperties.ContainsKey("host") && !String.IsNullOrEmpty(_initProperties["host"])) { - _useCoreSettings = true; + _host = _initProperties["host"]; + + if (_initProperties.ContainsKey("port") && !string.IsNullOrEmpty(_initProperties["port"])) + { + _port = int.Parse(_initProperties["port"]); + } + else + { + _port = 25; + } + + if (_initProperties.ContainsKey("enableSsl") && !string.IsNullOrEmpty(_initProperties["enableSsl"])) + { + _ssl = bool.Parse(_initProperties["enableSsl"]); + } + else + { + _ssl = false; + } + + if (_initProperties.ContainsKey("userName")) + { + _credentials = new NetworkCredential( + _initProperties["userName"], + _initProperties["password"]); + } } else { - _host = properties["host"]; - _port = properties.ContainsKey("port") ? int.Parse(properties["port"]) : 25; - _ssl = properties.ContainsKey("enableSsl") && bool.Parse(properties["enableSsl"]); - if (properties.ContainsKey("userName")) - { - _credentials = new NetworkCredential( - properties["userName"], - properties["password"]); - } + var s = CoreContext.Configuration.SmtpSettings; + + _host = s.Host; + _port = s.Port; + _ssl = s.EnableSSL; + _credentials = !string.IsNullOrEmpty(s.CredentialsUserName) + ? new NetworkCredential(s.CredentialsUserName, s.CredentialsUserPassword) + : null; } } - private void InitUseCoreSettings() - { - var s = CoreContext.Configuration.SmtpSettings; - _host = s.Host; - _port = s.Port; - _ssl = s.EnableSSL; - _credentials = !string.IsNullOrEmpty(s.CredentialsUserName) - ? new NetworkCredential(s.CredentialsUserName, s.CredentialsUserPassword) - : null; - } public virtual NoticeSendResult Send(NotifyMessage m) { @@ -97,8 +118,7 @@ namespace ASC.Core.Notify.Senders { try { - if (_useCoreSettings) - InitUseCoreSettings(); + BuildSmtpSettings(); var mail = BuildMailMessage(m); diff --git a/common/ASC.Core.Common/Notify/Signalr/MailNotificationState.cs b/common/ASC.Core.Common/Notify/Signalr/MailNotificationState.cs new file mode 100644 index 000000000..c90041054 --- /dev/null +++ b/common/ASC.Core.Common/Notify/Signalr/MailNotificationState.cs @@ -0,0 +1,29 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +namespace ASC.Core.Common.Notify.Signalr +{ + public enum MailNotificationState + { + SendReceiptError = -2, + SendMessageError = -1, + SentMessageSuccess = 0, + SentIcalRequest = 1, + SentIcalResponse = 2, + SentIcalCancel = 3, + ReadingConfirmed = 4 + } +} diff --git a/common/ASC.Core.Common/Notify/Signalr/SignalrServiceClient.cs b/common/ASC.Core.Common/Notify/Signalr/SignalrServiceClient.cs index c8c7d3418..c2d84c947 100644 --- a/common/ASC.Core.Common/Notify/Signalr/SignalrServiceClient.cs +++ b/common/ASC.Core.Common/Notify/Signalr/SignalrServiceClient.cs @@ -26,6 +26,7 @@ using System.Text; using ASC.Common.Logging; using ASC.Core.Common.Notify.Jabber; +using ASC.Core.Common.Notify.Signalr; using ASC.Security.Cryptography; using Newtonsoft.Json; @@ -195,7 +196,7 @@ namespace ASC.Core.Notify.Signalr } } - public void SendMailNotification(int tenant, string userId, int state) + public void SendMailNotification(int tenant, string userId, MailNotificationState state) { try { diff --git a/common/ASC.Core.Common/Notify/Telegram/Dao/CachedTelegramDao.cs b/common/ASC.Core.Common/Notify/Telegram/Dao/CachedTelegramDao.cs index 455da0cdf..50dbc9a7f 100644 --- a/common/ASC.Core.Common/Notify/Telegram/Dao/CachedTelegramDao.cs +++ b/common/ASC.Core.Common/Notify/Telegram/Dao/CachedTelegramDao.cs @@ -90,7 +90,7 @@ namespace ASC.Core.Common.Notify.Telegram return users; } - public void RegisterUser(Guid userId, int tenantId, int telegramId) + public void RegisterUser(Guid userId, int tenantId, long telegramId) { tgDao.RegisterUser(userId, tenantId, telegramId); diff --git a/common/ASC.Core.Common/Notify/Telegram/Dao/TelegramDao.cs b/common/ASC.Core.Common/Notify/Telegram/Dao/TelegramDao.cs index 057fdb9c1..b9fa0eb14 100644 --- a/common/ASC.Core.Common/Notify/Telegram/Dao/TelegramDao.cs +++ b/common/ASC.Core.Common/Notify/Telegram/Dao/TelegramDao.cs @@ -36,7 +36,7 @@ namespace ASC.Core.Common.Notify.Telegram _databaseID = dbid; } - public void RegisterUser(Guid userId, int tenantId, int telegramId) + public void RegisterUser(Guid userId, int tenantId, long telegramId) { using (var db = GetDbManager()) { diff --git a/common/ASC.Core.Common/Notify/Telegram/Dao/TelegramUser.cs b/common/ASC.Core.Common/Notify/Telegram/Dao/TelegramUser.cs index 8d057a1d6..af0c1fecf 100644 --- a/common/ASC.Core.Common/Notify/Telegram/Dao/TelegramUser.cs +++ b/common/ASC.Core.Common/Notify/Telegram/Dao/TelegramUser.cs @@ -23,6 +23,6 @@ namespace ASC.Core.Common.Notify.Telegram { public Guid PortalUserId { get; set; } public int TenantId { get; set; } - public int TelegramId { get; set; } + public long TelegramId { get; set; } } } diff --git a/common/ASC.Core.Common/Security/Authentication/CookieStorage.cs b/common/ASC.Core.Common/Security/Authentication/CookieStorage.cs index 092769b92..8b805bdfe 100644 --- a/common/ASC.Core.Common/Security/Authentication/CookieStorage.cs +++ b/common/ASC.Core.Common/Security/Authentication/CookieStorage.cs @@ -21,6 +21,7 @@ using System.Web; using ASC.Common.Logging; using ASC.Core.Tenants; +using ASC.MessagingSystem; using ASC.Security.Cryptography; namespace ASC.Core.Security.Authentication @@ -29,13 +30,14 @@ namespace ASC.Core.Security.Authentication { private const string DateTimeFormat = "yyyy-MM-dd HH:mm:ss,fff"; - public static bool DecryptCookie(string cookie, out int tenant, out Guid userid, out int indexTenant, out DateTime expire, out int indexUser) + public static bool DecryptCookie(string cookie, out int tenant, out Guid userid, out int indexTenant, out DateTime expire, out int indexUser, out int loginEventId) { tenant = Tenant.DEFAULT_TENANT; userid = Guid.Empty; indexTenant = 0; expire = DateTime.MaxValue; indexUser = 0; + loginEventId = 0; if (string.IsNullOrEmpty(cookie)) { @@ -52,29 +54,57 @@ namespace ASC.Core.Security.Authentication if (5 < s.Length) indexTenant = int.Parse(s[5]); if (6 < s.Length) expire = DateTime.ParseExact(s[6], DateTimeFormat, CultureInfo.InvariantCulture); if (7 < s.Length) indexUser = int.Parse(s[7]); + if (8 < s.Length) + { + loginEventId = !string.IsNullOrEmpty(s[8]) ? int.Parse(s[8]) : 0; + } return true; } catch (Exception err) { - LogManager.GetLogger("ASC.Core").ErrorFormat("Authenticate error: cookie {0}, tenant {1}, userid {2}, indexTenant {3}, expire {4}: {5}", - cookie, tenant, userid, indexTenant, expire.ToString(DateTimeFormat), err); + LogManager.GetLogger("ASC.Core").ErrorFormat("Authenticate error: cookie {0}, tenant {1}, userid {2}, indexTenant {3}, expire {4}, loginEvent {5}: {6}", + cookie, tenant, userid, indexTenant, expire.ToString(DateTimeFormat), loginEventId, err); } return false; } + public static int GetLoginEventIdFromCookie(string cookie) + { + int loginEventId = 0; + if (string.IsNullOrEmpty(cookie)) + { + return loginEventId; + } - public static string EncryptCookie(int tenant, Guid userid) + try + { + cookie = (HttpUtility.UrlDecode(cookie) ?? "").Replace(' ', '+'); + var s = InstanceCrypto.Decrypt(cookie).Split('$'); + if (8 < s.Length) + { + loginEventId = !string.IsNullOrEmpty(s[8]) ? int.Parse(s[8]) : 0; + } + } + catch (Exception err) + { + LogManager.GetLogger("ASC.Core").ErrorFormat("Failed to get login event ID from cookie: cookie {0}, loginEvent {1}: {2}", + cookie, loginEventId, err); + } + return loginEventId; + } + + public static string EncryptCookie(int tenant, Guid userid, int loginEventId) { var settingsTenant = TenantCookieSettings.GetForTenant(tenant); var expires = TenantCookieSettings.GetExpiresTime(tenant); var settingsUser = TenantCookieSettings.GetForUser(tenant, userid); - return EncryptCookie(tenant, userid, settingsTenant.Index, expires, settingsUser.Index); + return EncryptCookie(tenant, userid, settingsTenant.Index, expires, settingsUser.Index, loginEventId); } - public static string EncryptCookie(int tenant, Guid userid, int indexTenant, DateTime expires, int indexUser) + public static string EncryptCookie(int tenant, Guid userid, int indexTenant, DateTime expires, int indexUser, int loginEventId) { - var s = string.Format("{0}${1}${2}${3}${4}${5}${6}${7}", + var s = string.Format("{0}${1}${2}${3}${4}${5}${6}${7}${8}", string.Empty, //login tenant, string.Empty, // password @@ -82,7 +112,8 @@ namespace ASC.Core.Security.Authentication userid.ToString("N"), indexTenant, expires.ToString(DateTimeFormat, CultureInfo.InvariantCulture), - indexUser); + indexUser, + loginEventId != 0 ? loginEventId.ToString() : null); return InstanceCrypto.Encrypt(s); } diff --git a/common/ASC.Core.Common/Tenants/TenantDomainValidator.cs b/common/ASC.Core.Common/Tenants/TenantDomainValidator.cs index 3cb90b582..74b22c933 100644 --- a/common/ASC.Core.Common/Tenants/TenantDomainValidator.cs +++ b/common/ASC.Core.Common/Tenants/TenantDomainValidator.cs @@ -26,7 +26,7 @@ namespace ASC.Core.Tenants private static readonly Regex ValidDomain = new Regex("^[a-z0-9]([a-z0-9-]){1,98}[a-z0-9]$", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); - private static readonly int MinLength; + public static readonly int MinLength; private const int MaxLength = 100; static TenantDomainValidator() diff --git a/common/ASC.Core.Common/Tenants/TenantQuota.cs b/common/ASC.Core.Common/Tenants/TenantQuota.cs index 4b5303b92..16b23d493 100644 --- a/common/ASC.Core.Common/Tenants/TenantQuota.cs +++ b/common/ASC.Core.Common/Tenants/TenantQuota.cs @@ -55,9 +55,6 @@ namespace ASC.Core.Tenants [DataMember(Name = "Price", Order = 70)] public decimal Price { get; set; } - [DataMember(Name = "Price2", Order = 80)] - public decimal Price2 { get; set; } - [DataMember(Name = "AvangateId", Order = 90)] public string AvangateId { get; set; } @@ -102,12 +99,6 @@ namespace ASC.Core.Tenants set { SetFeature("open", value); } } - public bool ControlPanel - { - get { return GetFeature("controlpanel"); } - set { SetFeature("controlpanel", value); } - } - public bool Update { get { return GetFeature("update"); } @@ -138,12 +129,6 @@ namespace ASC.Core.Tenants set { SetFeature("domain", value); } } - public bool HealthCheck - { - get { return GetFeature("healthcheck"); } - set { SetFeature("healthcheck", value); } - } - public bool HasMigration { get { return GetFeature("migration"); } @@ -162,18 +147,6 @@ namespace ASC.Core.Tenants set { SetFeature("sso", value); } } - public bool Branding - { - get { return GetFeature("branding"); } - set { SetFeature("branding", value); } - } - - public bool SSBranding - { - get { return GetFeature("ssbranding"); } - set { SetFeature("ssbranding", value); } - } - public bool WhiteLabel { get { return GetFeature("whitelabel"); } @@ -186,24 +159,18 @@ namespace ASC.Core.Tenants set { SetFeature("customization", value); } } - public bool DiscEncryption - { - get { return GetFeature("discencryption"); } - set { SetFeature("discencryption", value); } - } - - public bool PrivacyRoom - { - get { return GetFeature("privacyroom"); } - set { SetFeature("privacyroom", value); } - } - public bool EnableMailServer { get { return GetFeature("mailserver"); } set { SetFeature("mailserver", value); } } + public bool Custom + { + get { return GetFeature("custom"); } + set { SetFeature("custom", value); } + } + public int CountAdmin { get @@ -254,32 +221,6 @@ namespace ASC.Core.Tenants set { SetFeature("contentsearch", value); } } - public int CountPortals - { - get - { - var features = (Features ?? string.Empty).Split(' ', ',', ';').ToList(); - var portals = features.FirstOrDefault(f => f.StartsWith("portals:")); - int countPortals; - if (portals == null || !Int32.TryParse(portals.Replace("portals:", ""), out countPortals) || countPortals <= 0) - { - countPortals = 0; - } - return countPortals; - } - set - { - var features = (Features ?? string.Empty).Split(' ', ',', ';').ToList(); - var portals = features.FirstOrDefault(f => f.StartsWith("portals:")); - features.Remove(portals); - if (value > 0) - { - features.Add("portals:" + value); - } - Features = string.Join(",", features.ToArray()); - } - } - public bool ThirdParty { get { return GetFeature("thirdparty"); } diff --git a/common/ASC.Data.Backup/ASC.Data.Backup.csproj b/common/ASC.Data.Backup/ASC.Data.Backup.csproj index a8b36a1dd..c3cf7517e 100644 --- a/common/ASC.Data.Backup/ASC.Data.Backup.csproj +++ b/common/ASC.Data.Backup/ASC.Data.Backup.csproj @@ -72,6 +72,7 @@ + @@ -189,19 +190,16 @@ - 3.5.6.3 + 3.7.3.20 - 3.5.7.6 + 3.7.1.29 - 8.0.25 + 8.0.29 - 1.3.1 - - - 2.4.1 + 1.3.2 diff --git a/common/ASC.Data.Backup/IDataOperator.cs b/common/ASC.Data.Backup/IDataOperator.cs index 4f4a78b58..15060610a 100644 --- a/common/ASC.Data.Backup/IDataOperator.cs +++ b/common/ASC.Data.Backup/IDataOperator.cs @@ -30,6 +30,7 @@ namespace ASC.Data.Backup { Stream GetEntry(string key); IEnumerable GetEntries(string key); + IEnumerable GetDirectories(string key); } } \ No newline at end of file diff --git a/common/ASC.Data.Backup/Service/BackupCleanerTempFileService.cs b/common/ASC.Data.Backup/Service/BackupCleanerTempFileService.cs new file mode 100644 index 000000000..65178672b --- /dev/null +++ b/common/ASC.Data.Backup/Service/BackupCleanerTempFileService.cs @@ -0,0 +1,73 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading; + +using ASC.Common.Logging; + +namespace ASC.Data.Backup.Service +{ + public class BackupCleanerTempFileService + { + private bool isStarted; + private readonly ILog log = LogManager.GetLogger("ASC.Backup.CleanerTempfile"); + private Timer timer; + private string tempFolder = BackupWorker.TempFolder; + public void Start() + { + if (!isStarted) + { + log.Info("staring backup cleaner temp file service..."); + timer = new Timer(_ => CleanerTempFileTask(), null, TimeSpan.Zero, TimeSpan.FromDays(1)); + log.Info("backup cleaner temp file service service started"); + isStarted = true; + } + } + + private void CleanerTempFileTask() + { + var date = DateTime.UtcNow.AddDays(-7); + Regex regex = new Regex(@"^\w*_\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}.tar.gz$"); + var files = Directory.EnumerateFiles(tempFolder).Where(f => regex.IsMatch(Path.GetFileName(f)) && new FileInfo(f).LastWriteTimeUtc < date); + foreach (var file in files) + { + File.Delete(file); + log.Info(string.Format("delete file {0}", Path.GetFileName(file))); + } + } + + public void Stop() + { + if (isStarted) + { + log.Info("stoping backup cleaner temp file service..."); + if (timer != null) + { + timer.Change(Timeout.Infinite, Timeout.Infinite); + timer.Dispose(); + timer = null; + } + log.Info("backup cleaner temp file service stoped"); + isStarted = false; + } + } + } +} diff --git a/common/ASC.Data.Backup/Service/BackupServiceLauncher.cs b/common/ASC.Data.Backup/Service/BackupServiceLauncher.cs index 070c21de2..4e7f23db3 100644 --- a/common/ASC.Data.Backup/Service/BackupServiceLauncher.cs +++ b/common/ASC.Data.Backup/Service/BackupServiceLauncher.cs @@ -26,6 +26,7 @@ namespace ASC.Data.Backup.Service private ServiceHost host; private BackupCleanerService cleanerService; private BackupSchedulerService schedulerService; + private BackupCleanerTempFileService deleterTempService; public void Start() { @@ -45,6 +46,8 @@ namespace ASC.Data.Backup.Service schedulerService = new BackupSchedulerService { Period = config.Scheduler.Period }; schedulerService.Start(); } + deleterTempService = new BackupCleanerTempFileService(); + deleterTempService.Start(); } public void Stop() @@ -65,6 +68,11 @@ namespace ASC.Data.Backup.Service schedulerService.Stop(); schedulerService = null; } + if (deleterTempService != null) + { + deleterTempService.Stop(); + deleterTempService = null; + } } } } diff --git a/common/ASC.Data.Backup/Service/BackupWorker.cs b/common/ASC.Data.Backup/Service/BackupWorker.cs index 265f5d013..2240f68a6 100644 --- a/common/ASC.Data.Backup/Service/BackupWorker.cs +++ b/common/ASC.Data.Backup/Service/BackupWorker.cs @@ -17,6 +17,7 @@ using System; using System.Collections.Generic; +using System.Configuration; using System.IO; using System.Linq; using System.Security.Cryptography; @@ -46,10 +47,17 @@ namespace ASC.Data.Backup.Service private static int limit; private static string upgradesPath; + static BackupWorker() + { + TempFolder = Path.Combine(TempPath.GetTempPath(), "backup"); + if (!Directory.Exists(TempFolder)) + { + Directory.CreateDirectory(TempFolder); + } + } + public static void Start(BackupConfigurationSection config) { - TempFolder = TempPath.GetTempPath(); - limit = config.Limit; upgradesPath = config.UpgradesPath; currentRegion = config.WebConfigs.CurrentRegion; @@ -438,7 +446,7 @@ namespace ASC.Data.Backup.Service restoredTenant = CoreContext.TenantManager.GetTenant(columnMapper.GetTenantMapping()); restoredTenant.SetStatus(TenantStatus.Active); restoredTenant.TenantAlias = tenant.TenantAlias; - restoredTenant.PaymentId = string.Empty; + restoredTenant.PaymentId = string.IsNullOrEmpty(restoredTenant.PaymentId) ? ConfigurationManagerExtension.AppSettings["core.payment-region"] + TenantId : restoredTenant.PaymentId; if (string.IsNullOrEmpty(restoredTenant.MappedDomain) && !string.IsNullOrEmpty(tenant.MappedDomain)) { restoredTenant.MappedDomain = tenant.MappedDomain; diff --git a/common/ASC.Data.Backup/Storage/DocumentsBackupStorage.cs b/common/ASC.Data.Backup/Storage/DocumentsBackupStorage.cs index 108fbc459..0852f7804 100644 --- a/common/ASC.Data.Backup/Storage/DocumentsBackupStorage.cs +++ b/common/ASC.Data.Backup/Storage/DocumentsBackupStorage.cs @@ -45,12 +45,12 @@ namespace ASC.Data.Backup.Storage CoreContext.TenantManager.SetCurrentTenant(tenantId); if (!userId.Equals(Guid.Empty)) { - SecurityContext.AuthenticateMe(userId); + SecurityContext.CurrentUser = userId; } else { var tenant = CoreContext.TenantManager.GetTenant(tenantId); - SecurityContext.AuthenticateMe(tenant.OwnerId); + SecurityContext.CurrentUser = tenant.OwnerId; } using (var folderDao = GetFolderDao()) diff --git a/common/ASC.Data.Backup/Tasks/BackupPortalTask.cs b/common/ASC.Data.Backup/Tasks/BackupPortalTask.cs index 9e5f7b9ab..03ec50cfc 100644 --- a/common/ASC.Data.Backup/Tasks/BackupPortalTask.cs +++ b/common/ASC.Data.Backup/Tasks/BackupPortalTask.cs @@ -17,6 +17,7 @@ using System; using System.Collections.Generic; +using System.Configuration; using System.Data; using System.Data.Common; using System.IO; @@ -96,20 +97,38 @@ namespace ASC.Data.Backup.Tasks private void DoDump(IDataWriteOperator writer) { + Dictionary> databases = new Dictionary>(); + using (var dbManager = DbManager.FromHttpContext("default", 100000)) + { + dbManager.ExecuteList("select id, connection_string from mail_server_server").ForEach((r => + { + var dbName = GetDbName((int)r[0], JsonConvert.DeserializeObject>(Convert.ToString(r[1]))["DbConnection"].ToString()); + using (var dbManager1 = DbManager.FromHttpContext(dbName, 100000)) + { + var tables = dbManager1.ExecuteList("show tables;").Select(res => Convert.ToString(res[0])).ToList(); + databases.Add(dbName, tables); + } + })); + } + + using (var dbManager = DbManager.FromHttpContext("default", 100000)) + { + var tables = dbManager.ExecuteList("show tables;").Select(res => Convert.ToString(res[0])).ToList(); + databases.Add("default", tables); + } + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(true.ToString()))) { writer.WriteEntry(KeyHelper.GetDumpKey(), stream); } - List tables; var files = new List(); - using (var dbManager = DbManager.FromHttpContext("default", 100000)) + var stepscount = 0; + foreach(var db in databases) { - tables = dbManager.ExecuteList("show tables;").Select(r => Convert.ToString(r[0])).ToList(); + stepscount += db.Value.Count * 4;// (schema + data) * (dump + zip) } - - var stepscount = tables.Count * 4; // (schema + data) * (dump + zip) if (ProcessStorage) { var tenants = CoreContext.TenantManager.GetTenants(false).Select(r => r.TenantId); @@ -123,14 +142,42 @@ namespace ASC.Data.Backup.Tasks SetStepsCount(stepscount); + foreach(var db in databases) + { + DoDump(writer, db.Key, db.Value); + } + var dir = Path.GetDirectoryName(BackupFilePath); + var subDir = Path.Combine(dir, Path.GetFileNameWithoutExtension(BackupFilePath)); + Logger.DebugFormat("dir remove start {0}", subDir); + Directory.Delete(subDir, true); + Logger.DebugFormat("dir remove end {0}", subDir); + + if (ProcessStorage) + { + DoDumpStorage(writer, files); + } + } + + private void DoDump(IDataWriteOperator writer, string dbName, List tables) + { var excluded = ModuleProvider.AllModules.Where(r => IgnoredModules.Contains(r.ModuleName)).SelectMany(r => r.Tables).Select(r => r.Name).ToList(); excluded.AddRange(IgnoredTables); excluded.Add("res_"); var dir = Path.GetDirectoryName(BackupFilePath); var subDir = Path.Combine(dir, Path.GetFileNameWithoutExtension(BackupFilePath)); - var schemeDir = Path.Combine(subDir, KeyHelper.GetDatabaseSchema()); - var dataDir = Path.Combine(subDir, KeyHelper.GetDatabaseData()); + var schemeDir = ""; + var dataDir = ""; + if (dbName == "default") + { + schemeDir = Path.Combine(subDir, KeyHelper.GetDatabaseSchema()); + dataDir = Path.Combine(subDir, KeyHelper.GetDatabaseData()); + } + else + { + schemeDir = Path.Combine(subDir, dbName, KeyHelper.GetDatabaseSchema()); + dataDir = Path.Combine(subDir, dbName, KeyHelper.GetDatabaseData()); + } if (!Directory.Exists(schemeDir)) { @@ -141,7 +188,11 @@ namespace ASC.Data.Backup.Tasks Directory.CreateDirectory(dataDir); } - var dict = tables.ToDictionary(t => t, SelectCount); + var dict = new Dictionary(); + foreach (var table in tables) + { + dict.Add(table, SelectCount(table, dbName)); + } tables.Sort((pair1, pair2) => dict[pair1].CompareTo(dict[pair2])); for (var i = 0; i < tables.Count; i += TasksLimit) @@ -150,10 +201,10 @@ namespace ASC.Data.Backup.Tasks for (var j = 0; j < TasksLimit && i + j < tables.Count; j++) { var t = tables[i + j]; - tasks.Add(Task.Run(() => DumpTableScheme(t, schemeDir))); + tasks.Add(Task.Run(() => DumpTableScheme(t, schemeDir, dbName))); if (!excluded.Any(t.StartsWith)) { - tasks.Add(Task.Run(() => DumpTableData(t, dataDir, dict[t]))); + tasks.Add(Task.Run(() => DumpTableData(t, dataDir, dict[t], dbName, writer))); } else { @@ -165,15 +216,6 @@ namespace ASC.Data.Backup.Tasks ArchiveDir(writer, subDir); } - - Logger.DebugFormat("dir remove start {0}", subDir); - Directory.Delete(subDir, true); - Logger.DebugFormat("dir remove end {0}", subDir); - - if (ProcessStorage) - { - DoDumpStorage(writer, files); - } } private IEnumerable GetFiles(int tenantId) @@ -196,12 +238,12 @@ namespace ASC.Data.Backup.Tasks return files; } - private void DumpTableScheme(string t, string dir) + private void DumpTableScheme(string t, string dir, string dbName) { try { Logger.DebugFormat("dump table scheme start {0}", t); - using (var dbManager = DbManager.FromHttpContext("default", 100000)) + using (var dbManager = DbManager.FromHttpContext(dbName, 100000)) { var createScheme = dbManager.ExecuteList(string.Format("SHOW CREATE TABLE `{0}`", t)); var creates = new StringBuilder(); @@ -224,19 +266,18 @@ namespace ASC.Data.Backup.Tasks Logger.DebugFormat("dump table scheme stop {0}", t); } - catch (Exception e) + catch { - Logger.Error(e); - throw; + } } - private int SelectCount(string t) + private int SelectCount(string t, string dbName) { try { - using (var dbManager = DbManager.FromHttpContext("default", 100000)) + using (var dbManager = DbManager.FromHttpContext(dbName, 100000)) { dbManager.ExecuteNonQuery("analyze table " + t); return dbManager.ExecuteScalar(new SqlQuery("information_schema.`TABLES`").Select("table_rows").Where("TABLE_NAME", t).Where("TABLE_SCHEMA", dbManager.Connection.Database)); @@ -250,7 +291,7 @@ namespace ASC.Data.Backup.Tasks } - private void DumpTableData(string t, string dir, int count) + private void DumpTableData(string t, string dir, int count, string dbName, IDataWriteOperator writer) { try { @@ -268,7 +309,7 @@ namespace ASC.Data.Backup.Tasks int primaryIndexStart = 0; List columns; - using (var dbManager = DbManager.FromHttpContext("default", 100000)) + using (var dbManager = DbManager.FromHttpContext(dbName, 100000)) { var columnsData = dbManager.ExecuteList(string.Format("SHOW COLUMNS FROM `{0}`;", t)); columns = columnsData @@ -307,9 +348,7 @@ namespace ASC.Data.Backup.Tasks } } - var path = Path.Combine(dir, t); - var offset = 0; do @@ -318,12 +357,12 @@ namespace ASC.Data.Backup.Tasks if (searchWithPrimary) { - result = GetDataWithPrimary(t, columns, primaryIndex, primaryIndexStart, primaryIndexStep); + result = GetDataWithPrimary(t, columns, primaryIndex, primaryIndexStart, primaryIndexStep, dbName); primaryIndexStart += primaryIndexStep; } else { - result = GetData(t, columns, offset); + result = GetData(t, columns, offset, dbName); } offset += Limit; @@ -334,11 +373,8 @@ namespace ASC.Data.Backup.Tasks SaveToFile(path, t, columns, result); - if (resultCount < Limit) break; - } while (true); - SetStepCompleted(); Logger.DebugFormat("dump table data stop {0}", t); } @@ -349,9 +385,9 @@ namespace ASC.Data.Backup.Tasks } } - private List GetData(string t, List columns, int offset) + private List GetData(string t, List columns, int offset, string dbName) { - using (var dbManager = DbManager.FromHttpContext("default", 100000)) + using (var dbManager = DbManager.FromHttpContext(dbName, 100000)) { var query = new SqlQuery(t) .Select(columns.ToArray()) @@ -360,9 +396,9 @@ namespace ASC.Data.Backup.Tasks return dbManager.ExecuteList(query); } } - private List GetDataWithPrimary(string t, List columns, string primary, int start, int step) + private List GetDataWithPrimary(string t, List columns, string primary, int start, int step, string dbName) { - using (var dbManager = DbManager.FromHttpContext("default", 100000)) + using (var dbManager = DbManager.FromHttpContext(dbName, 100000)) { var query = new SqlQuery(t) .Select(columns.ToArray()) @@ -372,6 +408,20 @@ namespace ASC.Data.Backup.Tasks } } + private string GetDbName(int id, string connectionString) + { + connectionString = connectionString + ";convert zero datetime=True"; + var connectionSettings = new ConnectionStringSettings("mailservice-" + id, connectionString, "MySql.Data.MySqlClient"); + + if (DbRegistry.IsDatabaseRegistered(connectionSettings.Name)) + { + DbRegistry.UnRegisterDatabase(connectionSettings.Name); + } + + DbRegistry.RegisterDatabase(connectionSettings.Name, connectionSettings); + return connectionSettings.Name; + } + private void SaveToFile(string path, string t, IReadOnlyCollection columns, List data) { Logger.DebugFormat("save to file {0}", t); @@ -394,7 +444,7 @@ namespace ASC.Data.Backup.Tasks for (var i = 0; i < obj.Length; i++) { var byteArray = obj[i] as byte[]; - if (byteArray != null) + if (byteArray != null && byteArray.Length != 0) { sw.Write("0x"); foreach (var b in byteArray) @@ -402,8 +452,16 @@ namespace ASC.Data.Backup.Tasks } else { - var ser = new JsonSerializer(); - ser.Serialize(writer, obj[i]); + var s = obj[i] as string; + if (s != null) + { + sw.Write("'" + s.Replace("\r", "\\r").Replace("\n", "\\n") + "'"); + } + else + { + var ser = new JsonSerializer(); + ser.Serialize(writer, obj[i]); + } } if (i != obj.Length - 1) { diff --git a/common/ASC.Data.Backup/Tasks/Modules/FilesModuleSpecifics.cs b/common/ASC.Data.Backup/Tasks/Modules/FilesModuleSpecifics.cs index 1c4a97e21..91da4bea4 100644 --- a/common/ASC.Data.Backup/Tasks/Modules/FilesModuleSpecifics.cs +++ b/common/ASC.Data.Backup/Tasks/Modules/FilesModuleSpecifics.cs @@ -36,6 +36,7 @@ namespace ASC.Data.Backup.Tasks.Modules private const string BunchRightNodeStartCrmOpportunity = "crm/opportunity/"; private const string BunchRightNodeStartMy = "files/my/"; private const string BunchRightNodeStartTrash = "files/trash/"; + private ILog Log = LogManager.GetLogger("ASC"); private readonly TableInfo[] _tables = new[] { @@ -244,7 +245,7 @@ namespace ASC.Data.Backup.Tasks.Modules } catch (Exception err) { - LogManager.GetLogger("ASC").ErrorFormat("Can not prepare value {0}: {1}", value, err); + Log.ErrorFormat("Can not prepare value {0}: {1}", value, err); value = null; } return true; @@ -275,7 +276,7 @@ namespace ASC.Data.Backup.Tasks.Modules } catch (Exception ex) { - LogManager.GetLogger("ASC").ErrorFormat("Can not prepare data {0}: {1}", row[providerColumn] as string, ex); + Log.ErrorFormat("Can not prepare data {0}: {1}", row[providerColumn] as string, ex); data.Rows.Remove(row); i--; } diff --git a/common/ASC.Data.Backup/Tasks/Modules/MailModuleSpecifics.cs b/common/ASC.Data.Backup/Tasks/Modules/MailModuleSpecifics.cs index 52e1f5a7f..fb528e0da 100644 --- a/common/ASC.Data.Backup/Tasks/Modules/MailModuleSpecifics.cs +++ b/common/ASC.Data.Backup/Tasks/Modules/MailModuleSpecifics.cs @@ -32,6 +32,8 @@ namespace ASC.Data.Backup.Tasks.Modules { internal class MailModuleSpecifics : ModuleSpecificsBase { + private ILog Log = LogManager.GetLogger("ASC"); + private readonly TableInfo[] _tables = new[] { new TableInfo("mail_attachment", "tenant", "id"), @@ -243,7 +245,7 @@ namespace ASC.Data.Backup.Tasks.Modules } catch (Exception err) { - LogManager.GetLogger("ASC").ErrorFormat("Can not prepare value {0}: {1}", value, err); + Log.ErrorFormat("Can not prepare value {0}: {1}", value, err); value = null; } return true; @@ -270,7 +272,7 @@ namespace ASC.Data.Backup.Tasks.Modules } catch (Exception ex) { - LogManager.GetLogger("ASC").ErrorFormat("Can not prepare data {0}: {1}", row[address] as string, ex); + Log.ErrorFormat("Can not prepare data {0}: {1}", row[address] as string, ex); data.Rows.Remove(row); i--; } diff --git a/common/ASC.Data.Backup/Tasks/PortalTaskBase.cs b/common/ASC.Data.Backup/Tasks/PortalTaskBase.cs index 68c33e21c..33bae525e 100644 --- a/common/ASC.Data.Backup/Tasks/PortalTaskBase.cs +++ b/common/ASC.Data.Backup/Tasks/PortalTaskBase.cs @@ -17,10 +17,12 @@ using System; using System.Collections.Generic; +using System.Configuration; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; +using System.Threading; using System.Threading.Tasks; using ASC.Common.Data; @@ -63,6 +65,20 @@ namespace ASC.Data.Backup.Tasks ProcessStorage = true; } + protected string RegisterDatabase(int id, string connectionString) + { + connectionString = connectionString + ";convert zero datetime=True"; + var connectionSettings = new ConnectionStringSettings("mailservice-" + id, connectionString, "MySql.Data.MySqlClient"); + + if (DbRegistry.IsDatabaseRegistered(connectionSettings.Name)) + { + DbRegistry.UnRegisterDatabase(connectionSettings.Name); + } + + DbRegistry.RegisterDatabase(connectionSettings.Name, connectionSettings); + return connectionSettings.Name; + } + public void IgnoreModule(ModuleName moduleName) { if (!IgnoredModules.Contains(moduleName)) @@ -220,14 +236,15 @@ namespace ASC.Data.Backup.Tasks return result; } - protected async Task RunMysqlFile(Stream stream, string delimiter = ";") + protected async Task RunMysqlFile(Stream stream, string db, string delimiter = ";") { - using (var dbManager = DbManager.FromHttpContext("default", 100000)) + using (var dbManager = DbManager.FromHttpContext(db, 100000)) using (var tr = dbManager.BeginTransaction()) { + await dbManager.ExecuteNonQueryAsync("SET FOREIGN_KEY_CHECKS=0;"); if (stream == null) return; - using (var reader = new StreamReader(stream, Encoding.UTF8)) + using (var reader = new StreamReader(stream)) { string commandText; @@ -235,6 +252,7 @@ namespace ASC.Data.Backup.Tasks { while ((commandText = await reader.ReadLineAsync()) != null) { + var firstText = commandText; while (!commandText.EndsWith(delimiter)) { var newline = await reader.ReadLineAsync(); @@ -247,11 +265,78 @@ namespace ASC.Data.Backup.Tasks try { + commandText = commandText.Replace("\\r", "\r").Replace("\\n", "\n"); await dbManager.ExecuteNonQueryAsync(commandText, null); } - catch (Exception e) + catch (Exception) { - Logger.Error("Restore", e); + try + { + if (commandText.StartsWith("REPLACE INTO")) + { + var innerValues = commandText.Split(',').ToList(); + for (int i = 0; i < innerValues.Count(); i++) + { + var flag1 = false; + var flag2 = false; + if (innerValues[i].StartsWith("(")) + { + flag1 = true; + innerValues[i] = innerValues[i].TrimStart('('); + } + else if (innerValues[i].EndsWith(")") && !innerValues[i].StartsWith("'") + || innerValues[i].EndsWith("')") && innerValues[i] != "')") + { + flag2 = true; + innerValues[i] = innerValues[i].TrimEnd(')'); + } + if (i == innerValues.Count() - 1) + { + innerValues[i] = innerValues[i].Remove(innerValues[i].Length - 2, 2); + } + if (innerValues[i].StartsWith("\'") && ((!innerValues[i].EndsWith("\'") || innerValues[i] == "'") + || i != innerValues.Count() - 1 && (!innerValues[i + 1].StartsWith("\'") && innerValues[i + 1].EndsWith("\'") && !innerValues[i + 1].StartsWith("(\'") || innerValues[i + 1] == "'"))) + { + innerValues[i] += "," + innerValues[i + 1]; + innerValues.RemoveAt(i + 1); + } + if (innerValues[i].StartsWith("\'") && innerValues[i].EndsWith("\'")) + { + if (innerValues[i] != "''") + { + var sw = new StringWriter(); + sw.Write("0x"); + foreach (var b in Encoding.UTF8.GetBytes(innerValues[i].Trim('\''))) + sw.Write("{0:x2}", b); + innerValues[i] = string.Format("CONVERT({0} USING utf8)", sw.ToString()); + } + } + if (flag1) + { + innerValues[i] = "(" + innerValues[i]; + } + else if (flag2) + { + innerValues[i] = innerValues[i] + ")"; + } + if (i == innerValues.Count() - 1) + { + innerValues[i] = innerValues[i] + ");"; + } + } + commandText = string.Join(",", innerValues).ToString(); + await dbManager.ExecuteNonQueryAsync(commandText, null); + } + else + { + Thread.Sleep(1000);//avoiding deadlock + await dbManager.ExecuteNonQueryAsync(commandText, null); + } + } + catch (Exception ex) + { + Logger.Error("Restore", ex); + } } } } diff --git a/common/ASC.Data.Backup/Tasks/RestorePortalTask.cs b/common/ASC.Data.Backup/Tasks/RestorePortalTask.cs index cbe91b910..e70616c6f 100644 --- a/common/ASC.Data.Backup/Tasks/RestorePortalTask.cs +++ b/common/ASC.Data.Backup/Tasks/RestorePortalTask.cs @@ -33,6 +33,8 @@ using ASC.Data.Backup.Extensions; using ASC.Data.Backup.Tasks.Modules; using ASC.Data.Storage; +using Newtonsoft.Json; + namespace ASC.Data.Backup.Tasks { public class RestorePortalTask : PortalTaskBase @@ -136,10 +138,11 @@ namespace ASC.Data.Backup.Tasks Logger.Debug("end restore portal"); } - private void RestoreFromDump(IDataReadOperator dataReader) + private void RestoreFromDump(IDataReadOperator dataReader) { var keyBase = KeyHelper.GetDatabaseSchema(); - var keys = dataReader.GetEntries(keyBase).Select(r=> Path.GetFileName(r)).ToList(); + var keys = dataReader.GetEntries(keyBase).Select(r => Path.GetFileName(r)).ToList(); + var dbs = dataReader.GetDirectories("").Where(r => Path.GetFileName(r).StartsWith("mailservice")).Select(r => Path.GetFileName(r)).ToList(); var upgrades = new List(); var upgradesPath = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), UpgradesPath)); @@ -150,6 +153,14 @@ namespace ASC.Data.Backup.Tasks var stepscount = keys.Count * 2 + upgrades.Count; + Dictionary> databases = new Dictionary>(); + foreach (var db in dbs) + { + var keys1 = dataReader.GetEntries(db + "/" + keyBase).Select(k => Path.GetFileName(k)).ToList(); + stepscount += keys1.Count() * 2; + databases.Add(db, keys1); + } + if (ProcessStorage) { var storageModules = StorageFactory.GetModuleList(ConfigPath).Where(IsStorageModuleAllowed); @@ -166,7 +177,6 @@ namespace ASC.Data.Backup.Tasks SetStepsCount(stepscount); } - for (var i = 0; i < keys.Count; i += TasksLimit) { var tasks = new List(TasksLimit * 2); @@ -174,12 +184,39 @@ namespace ASC.Data.Backup.Tasks for (var j = 0; j < TasksLimit && i + j < keys.Count; j++) { var key1 = Path.Combine(KeyHelper.GetDatabaseSchema(), keys[i + j]); - tasks.Add(RestoreFromDumpFile(dataReader, key1).ContinueWith(r => RestoreFromDumpFile(dataReader, KeyHelper.GetDatabaseData(key1.Substring(keyBase.Length + 1))))); + var key2 = Path.Combine(KeyHelper.GetDatabaseData(), keys[i + j]); + tasks.Add(RestoreFromDumpFile(dataReader, key1, key2)); } Task.WaitAll(tasks.ToArray()); } + using (var dbManager = DbManager.FromHttpContext("default", 100000)) + { + dbManager.ExecuteList("select id, connection_string from mail_server_server").ForEach(r => + { + RegisterDatabase((int)r[0], JsonConvert.DeserializeObject>(Convert.ToString(r[1]))["DbConnection"].ToString()); + }); + } + + foreach (var database in databases) + { + + for (var i = 0; i < database.Value.Count; i += TasksLimit) + { + var tasks = new List(TasksLimit * 2); + + for (var j = 0; j < TasksLimit && i + j < database.Value.Count; j++) + { + var key1 = Path.Combine(database.Key, KeyHelper.GetDatabaseSchema(), database.Value[i + j]); + var key2 = Path.Combine(database.Key, KeyHelper.GetDatabaseData(), database.Value[i + j]); + tasks.Add(RestoreFromDumpFile(dataReader, key1, key2, database.Key)); + } + + Task.WaitAll(tasks.ToArray()); + } + } + var comparer = new SqlComparer(); foreach (var u in upgrades.OrderBy(Path.GetFileName, comparer)) { @@ -195,7 +232,7 @@ namespace ASC.Data.Backup.Tasks } else { - RunMysqlFile(s).Wait(); + RunMysqlFile(s, "default").Wait(); } } @@ -203,14 +240,25 @@ namespace ASC.Data.Backup.Tasks } } - private async Task RestoreFromDumpFile(IDataReadOperator dataReader, string fileName) + private async Task RestoreFromDumpFile(IDataReadOperator dataReader, string fileName1, string fileName2 = null, string db = "default") { - Logger.DebugFormat("Restore from {0}", fileName); - using (var stream = dataReader.GetEntry(fileName)) + Logger.DebugFormat("Restore from {0}", fileName1); + using (var stream = dataReader.GetEntry(fileName1)) { - await RunMysqlFile(stream); + await RunMysqlFile(stream, db); } SetStepCompleted(); + + Logger.DebugFormat("Restore from {0}", fileName2); + if (fileName2 != null) + { + using (var stream = dataReader.GetEntry(fileName2)) + { + await RunMysqlFile(stream, db); + } + + SetStepCompleted(); + } } private class SqlComparer : IComparer diff --git a/common/ASC.Data.Backup/ZipOperator.cs b/common/ASC.Data.Backup/ZipOperator.cs index d57d629cf..405dd959e 100644 --- a/common/ASC.Data.Backup/ZipOperator.cs +++ b/common/ASC.Data.Backup/ZipOperator.cs @@ -91,6 +91,13 @@ namespace ASC.Data.Backup return files; } + public IEnumerable GetDirectories(string key) + { + var path = Path.Combine(tmpdir, key); + var files = Directory.EnumerateDirectories(path); + return files; + } + public void Dispose() { if (Directory.Exists(tmpdir)) diff --git a/common/ASC.Data.Encryption/ASC.Data.Encryption.csproj b/common/ASC.Data.Encryption/ASC.Data.Encryption.csproj index 5f532d30e..0b7f05019 100644 --- a/common/ASC.Data.Encryption/ASC.Data.Encryption.csproj +++ b/common/ASC.Data.Encryption/ASC.Data.Encryption.csproj @@ -12,6 +12,7 @@ v4.8 512 true + ..\..\web\studio\ASC.Web.Studio\bin\ true diff --git a/common/ASC.Data.Storage.Encryption/ASC.Data.Storage.Encryption.csproj b/common/ASC.Data.Storage.Encryption/ASC.Data.Storage.Encryption.csproj index 3ad97bf8b..f4fc2128a 100644 --- a/common/ASC.Data.Storage.Encryption/ASC.Data.Storage.Encryption.csproj +++ b/common/ASC.Data.Storage.Encryption/ASC.Data.Storage.Encryption.csproj @@ -65,7 +65,7 @@ - 4.7.0 + 4.7.11 diff --git a/common/ASC.Data.Storage/ASC.Data.Storage.csproj b/common/ASC.Data.Storage/ASC.Data.Storage.csproj index 310ed0a9f..bf654026e 100644 --- a/common/ASC.Data.Storage/ASC.Data.Storage.csproj +++ b/common/ASC.Data.Storage/ASC.Data.Storage.csproj @@ -140,19 +140,19 @@ - 3.0.0 + 3.5.0 - 1.45.0.1911 + 1.54.0.2234 - 2.5.0 + 3.5.0 1.1.3 - 0.2.0 + 1.0.0 0.8.5 diff --git a/common/ASC.Data.Storage/BaseStorage.cs b/common/ASC.Data.Storage/BaseStorage.cs index 2e665423c..817ec5fa0 100644 --- a/common/ASC.Data.Storage/BaseStorage.cs +++ b/common/ASC.Data.Storage/BaseStorage.cs @@ -318,7 +318,6 @@ namespace ASC.Data.Storage public abstract string GetUploadForm(string domain, string directoryPath, string redirectTo, long maxUploadSize, string contentType, string contentDisposition, string submitLabel); - public abstract string GetUploadedUrl(string domain, string directoryPath); public abstract string GetUploadUrl(); public abstract string GetPostParams(string domain, string directoryPath, long maxUploadSize, string contentType, diff --git a/common/ASC.Data.Storage/ChunkedUploader/CommonChunkedUploadSession.cs b/common/ASC.Data.Storage/ChunkedUploader/CommonChunkedUploadSession.cs index c145e145e..d032a121a 100644 --- a/common/ASC.Data.Storage/ChunkedUploader/CommonChunkedUploadSession.cs +++ b/common/ASC.Data.Storage/ChunkedUploader/CommonChunkedUploadSession.cs @@ -18,7 +18,8 @@ using System; using System.Collections.Generic; using System.IO; -using System.Runtime.Serialization.Formatters.Binary; + +using Newtonsoft.Json; namespace ASC.Core.ChunkedUploader { @@ -79,19 +80,39 @@ namespace ASC.Core.ChunkedUploader public T GetItemOrDefault(string key) { - return Items.ContainsKey(key) && Items[key] is T ? (T)Items[key] : default(T); + if (Items.ContainsKey(key) && Items[key] != null) + { + if (Items[key] is T) + { + return (T)Items[key]; + } + + var jToken = Items[key] as Newtonsoft.Json.Linq.JToken; + if (jToken != null) + { + var item = jToken.ToObject(); + Items[key] = item; + return item; + } + } + return default(T); } public Stream Serialize() { - var stream = new MemoryStream(); - new BinaryFormatter().Serialize(stream, this); - return stream; + var str = JsonConvert.SerializeObject(this); + var res = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(str)); + return res; } - public static CommonChunkedUploadSession Deserialize(Stream stream) + public static T Deserialize(Stream stream) { - return (CommonChunkedUploadSession)new BinaryFormatter().Deserialize(stream); + using (var reader = new StreamReader(stream, System.Text.Encoding.UTF8)) + { + var str = reader.ReadToEnd(); + var res = JsonConvert.DeserializeObject(str); + return res; + } } public virtual object Clone() diff --git a/common/ASC.Data.Storage/ChunkedUploader/CommonChunkedUploadSessionHolder.cs b/common/ASC.Data.Storage/ChunkedUploader/CommonChunkedUploadSessionHolder.cs index 6fd1733de..246edd929 100644 --- a/common/ASC.Data.Storage/ChunkedUploader/CommonChunkedUploadSessionHolder.cs +++ b/common/ASC.Data.Storage/ChunkedUploader/CommonChunkedUploadSessionHolder.cs @@ -68,11 +68,11 @@ namespace ASC.Core.ChunkedUploader DataStore.Delete(Domain, GetPathWithId(s.Id)); } - public CommonChunkedUploadSession Get(string sessionId) - { + public T Get(string sessionId) + { using (var stream = DataStore.GetReadStream(Domain, GetPathWithId(sessionId))) { - return CommonChunkedUploadSession.Deserialize(stream); + return CommonChunkedUploadSession.Deserialize(stream); } } @@ -126,11 +126,14 @@ namespace ASC.Core.ChunkedUploader { var tempPath = uploadSession.TempPath; var uploadId = uploadSession.UploadId; - var chunkNumber = uploadSession.GetItemOrDefault("ChunksUploaded") + 1; + + int chunkNumber; + int.TryParse(uploadSession.GetItemOrDefault("ChunksUploaded"), out chunkNumber); + chunkNumber++; var eTag = DataStore.UploadChunk(Domain, tempPath, uploadId, stream, MaxChunkUploadSize, chunkNumber, length); - uploadSession.Items["ChunksUploaded"] = chunkNumber; + uploadSession.Items["ChunksUploaded"] = chunkNumber.ToString(); uploadSession.BytesUploaded += length; var eTags = uploadSession.GetItemOrDefault>("ETag") ?? new List(); diff --git a/common/ASC.Data.Storage/Configuration/StorageSettings.cs b/common/ASC.Data.Storage/Configuration/StorageSettings.cs index 2bd0254fe..820acd626 100644 --- a/common/ASC.Data.Storage/Configuration/StorageSettings.cs +++ b/common/ASC.Data.Storage/Configuration/StorageSettings.cs @@ -44,10 +44,12 @@ namespace ASC.Data.Storage.Configuration static BaseStorageSettings() { - AscCache.Notify.Subscribe((i, a) => + AscCache.Notify.Subscribe((i, a) => { if (a == CacheNotifyAction.Remove) { + CoreContext.TenantManager.SetCurrentTenant(i.TenantId); + var settings = StorageSettings.Load(); if (i.Name == settings.Module) { diff --git a/common/ASC.Data.Storage/DiscStorage/DiscDataStore.cs b/common/ASC.Data.Storage/DiscStorage/DiscDataStore.cs index 79d398c07..9ff4a694f 100644 --- a/common/ASC.Data.Storage/DiscStorage/DiscDataStore.cs +++ b/common/ASC.Data.Storage/DiscStorage/DiscDataStore.cs @@ -501,11 +501,6 @@ namespace ASC.Data.Storage.DiscStorage throw new NotSupportedException("This operation supported only on s3 storage"); } - public override string GetUploadedUrl(string domain, string directoryPath) - { - throw new NotSupportedException("This operation supported only on s3 storage"); - } - public override string GetUploadUrl() { throw new NotSupportedException("This operation supported only on s3 storage"); diff --git a/common/ASC.Data.Storage/Encryption/EncryptionFactory.cs b/common/ASC.Data.Storage/Encryption/EncryptionFactory.cs index 0f3f46426..993e51ded 100644 --- a/common/ASC.Data.Storage/Encryption/EncryptionFactory.cs +++ b/common/ASC.Data.Storage/Encryption/EncryptionFactory.cs @@ -42,7 +42,7 @@ namespace ASC.Data.Storage.Encryption if (Builder != null) { - result = Builder.Resolve(new TypedParameter(typeof(string), storageName), new TypedParameter(typeof(EncryptionSettings), encryptionSettings)); + result = ResolutionExtensions.ResolveOptional(Builder, new TypedParameter(typeof(string), storageName), new TypedParameter(typeof(EncryptionSettings), encryptionSettings)); } return result ?? new FakeCrypt(storageName, encryptionSettings); diff --git a/common/ASC.Data.Storage/GoogleCloud/GoogleCloudStorage.cs b/common/ASC.Data.Storage/GoogleCloud/GoogleCloudStorage.cs index 12dc197e6..a0201d25e 100644 --- a/common/ASC.Data.Storage/GoogleCloud/GoogleCloudStorage.cs +++ b/common/ASC.Data.Storage/GoogleCloud/GoogleCloudStorage.cs @@ -705,8 +705,15 @@ namespace ASC.Data.Storage.GoogleCloud using (var mStream = new MemoryStream(Encoding.UTF8.GetBytes(_json ?? ""))) { + TimeSpan signDuration; + + if (expires.Date == DateTime.MinValue) + signDuration = expires.TimeOfDay; + else + signDuration = expires.Subtract(DateTime.UtcNow); + var preSignedURL = UrlSigner.FromServiceAccountData(mStream) - .Sign(_bucket, MakePath(domain, path), expires, null); + .Sign(_bucket, MakePath(domain, path), signDuration, null); //TODO: CNAME! return preSignedURL; @@ -859,11 +866,6 @@ namespace ASC.Data.Storage.GoogleCloud throw new NotImplementedException(); } - public override string GetUploadedUrl(string domain, string directoryPath) - { - throw new NotImplementedException(); - } - public override string GetUploadUrl() { throw new NotImplementedException(); diff --git a/common/ASC.Data.Storage/IDataStore.cs b/common/ASC.Data.Storage/IDataStore.cs index f94a991e5..99c1c43e8 100644 --- a/common/ASC.Data.Storage/IDataStore.cs +++ b/common/ASC.Data.Storage/IDataStore.cs @@ -309,7 +309,6 @@ namespace ASC.Data.Storage string GetUploadForm(string domain, string directoryPath, string redirectTo, long maxUploadSize, string contentType, string contentDisposition, string submitLabel); - string GetUploadedUrl(string domain, string directoryPath); string GetUploadUrl(); string GetPostParams(string domain, string directoryPath, long maxUploadSize, string contentType, diff --git a/common/ASC.Data.Storage/RackspaceCloud/RackspaceCloudStorage.cs b/common/ASC.Data.Storage/RackspaceCloud/RackspaceCloudStorage.cs index 589305a81..2b060d268 100644 --- a/common/ASC.Data.Storage/RackspaceCloud/RackspaceCloudStorage.cs +++ b/common/ASC.Data.Storage/RackspaceCloud/RackspaceCloudStorage.cs @@ -676,11 +676,6 @@ namespace ASC.Data.Storage.RackspaceCloud throw new NotImplementedException(); } - public override string GetUploadedUrl(string domain, string directoryPath) - { - throw new NotImplementedException(); - } - public override string GetUploadUrl() { throw new NotImplementedException(); diff --git a/common/ASC.Data.Storage/S3/S3Storage.cs b/common/ASC.Data.Storage/S3/S3Storage.cs index a1fd752dd..a54bd1084 100644 --- a/common/ASC.Data.Storage/S3/S3Storage.cs +++ b/common/ASC.Data.Storage/S3/S3Storage.cs @@ -618,15 +618,7 @@ namespace ASC.Data.Storage.S3 var response = client.ListObjects(request); foreach (var s3Object in response.S3Objects) { - client.CopyObject(new CopyObjectRequest - { - SourceBucket = _bucket, - SourceKey = s3Object.Key, - DestinationBucket = _bucket, - DestinationKey = s3Object.Key.Replace(srckey, dstkey), - CannedACL = GetDomainACL(newdomain), - ServerSideEncryptionMethod = _sse - }); + CopyFile(s3Object.Key, s3Object.Key.Replace(srckey, dstkey), newdomain); client.DeleteObject(new DeleteObjectRequest { @@ -639,31 +631,17 @@ namespace ASC.Data.Storage.S3 public override Uri Move(string srcdomain, string srcpath, string newdomain, string newpath, bool quotaCheckFileSize = true) { - using (var client = GetClient()) - { - var srcKey = MakePath(srcdomain, srcpath); - var dstKey = MakePath(newdomain, newpath); - var size = GetFileSize(srcdomain, srcpath); + var srcKey = MakePath(srcdomain, srcpath); + var dstKey = MakePath(newdomain, newpath); + var size = GetFileSize(srcdomain, srcpath); - var request = new CopyObjectRequest - { - SourceBucket = _bucket, - SourceKey = srcKey, - DestinationBucket = _bucket, - DestinationKey = dstKey, - CannedACL = GetDomainACL(newdomain), - MetadataDirective = S3MetadataDirective.REPLACE, - ServerSideEncryptionMethod = _sse - }; + CopyFile(srcKey, dstKey, newdomain, S3MetadataDirective.REPLACE); + Delete(srcdomain, srcpath); - client.CopyObject(request); - Delete(srcdomain, srcpath); + QuotaUsedDelete(srcdomain, size); + QuotaUsedAdd(newdomain, size, quotaCheckFileSize); - QuotaUsedDelete(srcdomain, size); - QuotaUsedAdd(newdomain, size, quotaCheckFileSize); - - return GetUri(newdomain, newpath); - } + return GetUri(newdomain, newpath); } public override Uri SaveTemp(string domain, out string assignedPath, Stream stream) @@ -866,50 +844,6 @@ namespace ASC.Data.Storage.S3 return policyBase64; } - public override string GetUploadedUrl(string domain, string directoryPath) - { - if (HttpContext.Current != null) - { - var buket = HttpContext.Current.Request.QueryString["bucket"]; - var key = HttpContext.Current.Request.QueryString["key"]; - var etag = HttpContext.Current.Request.QueryString["etag"]; - var destkey = MakePath(domain, directoryPath) + "/"; - - if (!string.IsNullOrEmpty(buket) && !string.IsNullOrEmpty(key) && string.Equals(buket, _bucket) && - key.StartsWith(destkey)) - { - var domainpath = key.Substring(MakePath(domain, string.Empty).Length); - var skipQuota = false; - if (HttpContext.Current.Session != null) - { - var isCounted = HttpContext.Current.Session[etag]; - skipQuota = isCounted != null; - } - //Add to quota controller - if (QuotaController != null && !skipQuota) - { - try - { - var size = GetFileSize(domain, domainpath); - QuotaUsedAdd(domain, size); - - if (HttpContext.Current.Session != null) - { - HttpContext.Current.Session.Add(etag, size); - } - } - catch (Exception) - { - - } - } - return GetUriInternal(key).ToString(); - } - } - return string.Empty; - } - - public override string[] ListFilesRelative(string domain, string path, string pattern, bool recursive) { return GetS3Objects(domain, path) @@ -1044,18 +978,7 @@ namespace ASC.Data.Storage.S3 var dstKey = MakePath(newdomain, newpath); var size = GetFileSize(srcdomain, srcpath); - var request = new CopyObjectRequest - { - SourceBucket = _bucket, - SourceKey = srcKey, - DestinationBucket = _bucket, - DestinationKey = dstKey, - CannedACL = GetDomainACL(newdomain), - MetadataDirective = S3MetadataDirective.REPLACE, - ServerSideEncryptionMethod = _sse - }; - - client.CopyObject(request); + CopyFile(srcKey, dstKey, newdomain, S3MetadataDirective.REPLACE); QuotaUsedAdd(newdomain, size); @@ -1075,15 +998,7 @@ namespace ASC.Data.Storage.S3 var response = client.ListObjects(request); foreach (var s3Object in response.S3Objects) { - client.CopyObject(new CopyObjectRequest - { - SourceBucket = _bucket, - SourceKey = s3Object.Key, - DestinationBucket = _bucket, - DestinationKey = s3Object.Key.Replace(srckey, dstkey), - CannedACL = GetDomainACL(newdomain), - ServerSideEncryptionMethod = _sse - }); + CopyFile(s3Object.Key, s3Object.Key.Replace(srckey, dstkey), newdomain); QuotaUsedAdd(newdomain, s3Object.Size); } @@ -1248,20 +1163,98 @@ namespace ASC.Data.Storage.S3 { if (string.IsNullOrEmpty(_recycleDir)) return; - var copyObjectRequest = new CopyObjectRequest + CopyFile(key, GetRecyclePath(key), domain, S3MetadataDirective.REPLACE, S3StorageClass.Glacier); + } + + private void CopyFile(string sourceKey, string destinationKey, string newdomain, S3MetadataDirective metadataDirective = S3MetadataDirective.COPY, S3StorageClass storageClass = null) + { + using (var client = GetClient()) { - SourceBucket = _bucket, - SourceKey = key, - DestinationBucket = _bucket, - DestinationKey = GetRecyclePath(key), - CannedACL = GetDomainACL(domain), - MetadataDirective = S3MetadataDirective.REPLACE, - ServerSideEncryptionMethod = _sse, - StorageClass = S3StorageClass.Glacier, + var metadataRequest = new GetObjectMetadataRequest + { + BucketName = _bucket, + Key = sourceKey + }; - }; + var metadataResponse = client.GetObjectMetadata(metadataRequest); + var objectSize = metadataResponse.ContentLength; - client.CopyObject(copyObjectRequest); + if (objectSize >= 100 * 1024 * 1024L) //100 megabytes + { + var copyResponses = new List(); + + var initiateRequest = + new InitiateMultipartUploadRequest + { + BucketName = _bucket, + Key = destinationKey, + CannedACL = GetDomainACL(newdomain), + ServerSideEncryptionMethod = _sse + }; + + if (storageClass != null) + { + initiateRequest.StorageClass = storageClass; + } + + var initResponse = client.InitiateMultipartUpload(initiateRequest); + + var uploadId = initResponse.UploadId; + + var partSize = 5 * (long)Math.Pow(2, 20); // Part size is 5 MB. + + long bytePosition = 0; + for (int i = 1; bytePosition < objectSize; i++) + { + var copyRequest = new CopyPartRequest + { + DestinationBucket = _bucket, + DestinationKey = destinationKey, + SourceBucket = _bucket, + SourceKey = sourceKey, + UploadId = uploadId, + FirstByte = bytePosition, + LastByte = bytePosition + partSize - 1 >= objectSize ? objectSize - 1 : bytePosition + partSize - 1, + PartNumber = i + }; + + copyResponses.Add(client.CopyPart(copyRequest)); + + bytePosition += partSize; + } + + var completeRequest = + new CompleteMultipartUploadRequest + { + BucketName = _bucket, + Key = destinationKey, + UploadId = initResponse.UploadId + }; + completeRequest.AddPartETags(copyResponses); + + var completeUploadResponse = client.CompleteMultipartUpload(completeRequest); + } + else + { + var request = new CopyObjectRequest + { + SourceBucket = _bucket, + SourceKey = sourceKey, + DestinationBucket = _bucket, + DestinationKey = destinationKey, + CannedACL = GetDomainACL(newdomain), + ServerSideEncryptionMethod = _sse, + MetadataDirective = metadataDirective, + }; + + if (storageClass != null) + { + request.StorageClass = storageClass; + } + + client.CopyObject(request); + } + } } private IAmazonCloudFront GetCloudFrontClient() diff --git a/common/ASC.Data.Storage/Selectel/SelectelStorage.cs b/common/ASC.Data.Storage/Selectel/SelectelStorage.cs index 6f2bfd576..02f2578af 100644 --- a/common/ASC.Data.Storage/Selectel/SelectelStorage.cs +++ b/common/ASC.Data.Storage/Selectel/SelectelStorage.cs @@ -664,11 +664,6 @@ namespace ASC.Data.Storage.Selectel throw new NotImplementedException(); } - public override string GetUploadedUrl(string domain, string directoryPath) - { - throw new NotImplementedException(); - } - public override string GetUploadUrl() { throw new NotImplementedException(); diff --git a/common/ASC.Data.Storage/StaticUploader.cs b/common/ASC.Data.Storage/StaticUploader.cs index d3d5db50e..0b0041bcf 100644 --- a/common/ASC.Data.Storage/StaticUploader.cs +++ b/common/ASC.Data.Storage/StaticUploader.cs @@ -177,7 +177,7 @@ namespace ASC.Data.Storage { var tenant = CoreContext.TenantManager.GetTenant(tenantId); CoreContext.TenantManager.SetCurrentTenant(tenant); - SecurityContext.AuthenticateMe(tenant.OwnerId); + SecurityContext.CurrentUser = tenant.OwnerId; var dataStore = CdnStorageSettings.Load().DataStore; @@ -192,7 +192,7 @@ namespace ASC.Data.Storage } Result = dataStore.GetInternalUri("", path, TimeSpan.Zero, null).AbsoluteUri.ToLower(); - LogManager.GetLogger("ASC").DebugFormat("UploadFile {0}", Result); + Log.DebugFormat("UploadFile {0}", Result); return Result; } } diff --git a/common/ASC.Data.Storage/StorageUploader.cs b/common/ASC.Data.Storage/StorageUploader.cs index 52f2afb62..8acf4cb4f 100644 --- a/common/ASC.Data.Storage/StorageUploader.cs +++ b/common/ASC.Data.Storage/StorageUploader.cs @@ -127,7 +127,7 @@ namespace ASC.Data.Storage Log.DebugFormat("Tenant: {0}", tenantId); var tenant = CoreContext.TenantManager.GetTenant(tenantId); CoreContext.TenantManager.SetCurrentTenant(tenant); - SecurityContext.AuthenticateMe(tenant.OwnerId); + SecurityContext.CurrentUser = tenant.OwnerId; foreach (var module in Modules) { diff --git a/common/ASC.IPSecurity/IPSecurity.cs b/common/ASC.IPSecurity/IPSecurity.cs index b18e4fcc3..cac82b0a1 100644 --- a/common/ASC.IPSecurity/IPSecurity.cs +++ b/common/ASC.IPSecurity/IPSecurity.cs @@ -16,9 +16,11 @@ using System; +using System.Collections.Generic; using System.Configuration; using System.Linq; using System.Net; +using System.Net.Sockets; using System.Web; using ASC.Common.Logging; @@ -45,6 +47,8 @@ namespace ASC.IPSecurity private static readonly string CurrentIpForTest = ConfigurationManagerExtension.AppSettings["ipsecurity.test"]; + private static readonly string MyNetworks = ConfigurationManagerExtension.AppSettings["ipsecurity.mynetworks"]; + public static bool Verify(Tenant tenant) { if (!IpSecurityEnabled) return true; @@ -75,6 +79,11 @@ namespace ASC.IPSecurity { return true; } + + if (IsMyNetwork(ips)) + { + return true; + } } catch (Exception ex) { @@ -106,5 +115,42 @@ namespace ASC.IPSecurity var portIdx = ip.IndexOf(':'); return portIdx > 0 ? ip.Substring(0, portIdx) : ip; } + + private static bool IsMyNetwork(string[] ips) + { + try + { + if (!string.IsNullOrEmpty(MyNetworks)) + { + var myNetworkIps = MyNetworks.Split(new[] { ",", " " }, StringSplitOptions.RemoveEmptyEntries); + + if (ips.Any(requestIp => myNetworkIps.Any(ipAddress => MatchIPs(GetIpWithoutPort(requestIp), ipAddress)))) + { + return true; + } + } + + var hostName = Dns.GetHostName(); + var hostAddresses = Dns.GetHostAddresses(Dns.GetHostName()); + + var localIPs = new List { IPAddress.IPv6Loopback, IPAddress.Loopback }; + + localIPs.AddRange(hostAddresses.Where(ip => ip.AddressFamily == AddressFamily.InterNetwork || ip.AddressFamily == AddressFamily.InterNetworkV6)); + + foreach (var ipAddress in localIPs) + { + if (ips.Contains(ipAddress.ToString())) + { + return true; + } + } + } + catch (Exception ex) + { + Log.ErrorFormat("Can't verify local network from request with IP-address: {0}", string.Join(",", ips), ex); + } + + return false; + } } } \ No newline at end of file diff --git a/common/ASC.Notify.Textile/ASC.Notify.Textile.csproj b/common/ASC.Notify.Textile/ASC.Notify.Textile.csproj index 7380ad234..dba61d2f5 100644 --- a/common/ASC.Notify.Textile/ASC.Notify.Textile.csproj +++ b/common/ASC.Notify.Textile/ASC.Notify.Textile.csproj @@ -54,12 +54,16 @@ + + + NotifyTemplateResource.resx + NotifyTemplateResource.resx diff --git a/common/ASC.Notify.Textile/MarkDownStyler.cs b/common/ASC.Notify.Textile/MarkDownStyler.cs new file mode 100644 index 000000000..963e1168e --- /dev/null +++ b/common/ASC.Notify.Textile/MarkDownStyler.cs @@ -0,0 +1,95 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System; +using System.Text.RegularExpressions; +using System.Web; + +using ASC.Common.Notify.Patterns; +using ASC.Notify.Messages; +using ASC.Notify.Patterns; + +namespace ASC.Notify.Textile +{ + public class MarkDownStyler : IPatternStyler + { + static readonly Regex VelocityArguments = new Regex(NVelocityPatternFormatter.NoStylePreffix + "(?.*?)" + NVelocityPatternFormatter.NoStyleSuffix, RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Compiled); + static readonly Regex LinkReplacer = new Regex(@"""(?[\w\W]+?)"":""(?[^""]+)""", RegexOptions.Singleline | RegexOptions.Compiled); + static readonly Regex DivPTagReplacer = new Regex(@"(<(p|div).*?>)|(<\/(p|div)>)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled | RegexOptions.Singleline); + static readonly Regex TagReplacer = new Regex(@"<(.|\n)*?>", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled | RegexOptions.Singleline); + static readonly Regex MultiLineBreaksReplacer = new Regex(@"(?:\r\n|\r(?!\n)|(?!<\r)\n){3,}", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + static readonly Regex SymbolReplacer = new Regex(@"\[(.*?)]\(([^()]*)\)|[]\\[(){}*_|#+=.!~>`-]", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + static readonly Regex LinkSymbolReplacer = new Regex(@"[]\\[(){}*_|#+=.!~>`-]", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + static readonly Regex BoldReplacer = new Regex(@"<(strong|\/strong)\\>", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + static readonly Regex StrikeThroughReplacer = new Regex(@"<(s|\/s)\\>", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + static readonly Regex UnderLineReplacer = new Regex(@"<(u|\/u)\\>", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + static readonly Regex ItalicReplacer = new Regex(@"<(em|\/em)\\>", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + static readonly Regex HTMLLinkReplacer = new Regex(@"(.*?)<\/a>", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + + public void ApplyFormating(NoticeMessage message) + { + var body = string.Empty; + if (!string.IsNullOrEmpty(message.Subject)) + { + body += VelocityArguments.Replace(message.Subject, ArgMatchReplace) + Environment.NewLine; + message.Subject = string.Empty; + } + if (string.IsNullOrEmpty(message.Body)) return; + var lines = message.Body.Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.None); + for (var i = 0; i < lines.Length - 1; i++) + { + if (string.IsNullOrEmpty(lines[i])) { body += Environment.NewLine; continue; } + lines[i] = VelocityArguments.Replace(lines[i], ArgMatchReplace); + body += LinkReplacer.Replace(lines[i], EvalLink) + Environment.NewLine; + } + lines[lines.Length - 1] = VelocityArguments.Replace(lines[lines.Length - 1], ArgMatchReplace); + body += LinkReplacer.Replace(lines[lines.Length - 1], EvalLink); + body = DivPTagReplacer.Replace(body, ""); + body = HTMLLinkReplacer.Replace(body, @"[$2]($1)"); + body = HttpUtility.HtmlDecode(body); + body = SymbolReplacer.Replace(body, m => m.Groups[1].Success ? $@"[{LinkSymbolReplacer.Replace(m.Groups[1].Value, @"\$&")}]({m.Groups[2].Value})" : $@"\{m.Value}"); + body = BoldReplacer.Replace(body, "*"); + body = StrikeThroughReplacer.Replace(body, "~"); + body = UnderLineReplacer.Replace(body, "__"); + body = ItalicReplacer.Replace(body, "_"); + body = TagReplacer.Replace(body, ""); + body = MultiLineBreaksReplacer.Replace(body, Environment.NewLine); + message.Body = body; + } + + private static string EvalLink(Match match) + { + if (match.Success) + { + if (match.Groups["text"].Success && match.Groups["link"].Success) + { + if (match.Groups["text"].Value.Equals(match.Groups["link"].Value, StringComparison.OrdinalIgnoreCase)) + { + return " " + match.Groups["text"].Value + " "; + } + return match.Groups["text"].Value + string.Format(" ( {0} )", match.Groups["link"].Value); + } + } + return match.Value; + } + + private static string ArgMatchReplace(Match match) + { + return match.Result("${arg}"); + } + } +} diff --git a/common/ASC.Notify.Textile/Resources/NotifyTemplateResource.az-Latn-AZ.resx b/common/ASC.Notify.Textile/Resources/NotifyTemplateResource.az-Latn-AZ.resx new file mode 100644 index 000000000..405cc7713 --- /dev/null +++ b/common/ASC.Notify.Textile/Resources/NotifyTemplateResource.az-Latn-AZ.resx @@ -0,0 +1,242 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + <tr border="0" cellspacing="0" cellpadding="0"><td colspan="3" style="height: 10px; line-height: 10px; background: #fff; padding: 0; margin: 0;">&nbsp;</td></tr> + <tr border="0" cellspacing="0" cellpadding="0"> + <td style="vertical-align: top; margin: 0; padding: 34px 0 0 0; width: 210px; height: 108px; background: #f6f6f6; -moz-border-radius-topleft: 6px; -webkit-border-top-left-radius: 6px; border-top-left-radius: 6px;"> + <img src="%IMAGEPATH%/tech-100.png" style="width: 100px; height: 100px;margin: 0 auto;" /> + </td> + <td style="vertical-align: top; margin: 0; padding: 34px 0 0 0; width: 180px; height: 108px; background: #f6f6f6;"> + <img src="%IMAGEPATH%/mailus-100.png" style="width: 100px; height: 100px;margin: 0 auto;" /> + </td> + <td style="vertical-align: top; margin: 0; padding: 34px 0 0 0; width: 210px; height: 108px; background: #f6f6f6; -moz-border-radius-topright: 6px; -webkit-border-top-right-radius: 6px; border-top-right-radius: 6px;"> + <img src="%IMAGEPATH%/demo-100.png" style="width: 100px; height: 100px;margin: 0 auto;" /> + </td> + </tr> + <tr border="0" cellspacing="0" cellpadding="0"> + <td style="vertical-align: top; margin: 0; padding: 0; width: 210px; height: 16px; background: #f6f6f6;">Texniki dəstək lazımdır?</td> + <td style="vertical-align: top; margin: 0; padding: 0; width: 180px; height: 16px; background: #f6f6f6;">Satışla bağlı Suallar</td> + <td style="vertical-align: top; margin: 0; padding: 0; width: 210px; height: 16px; background: #f6f6f6;">Demo Sifariş Et</td> + </tr> + <tr border="0" cellspacing="0" cellpadding="0"> + <td style="vertical-align: top; margin: 0; padding: 0 0 30px; width: 210px; background: #f6f6f6;-moz-border-radius-bottomleft: 6px; -webkit-border-bottom-left-radius: 6px; border-bottom-left-radius: 6px;"> + <a href="%SUPPORTURL%" target="_blank" style="color: #333;">Sualınızı göndərin</a> + </td> + <td style="vertical-align: top; margin: 0; padding: 0 0 30px; width: 180px; background: #f6f6f6;"> + <a href="mailto:%SALESEMAIL%" style="color: #333;">Bizə e-məktub yazın</a> + </td> + <td style="vertical-align: top; margin: 0; padding: 0 0 30px; width: 210px; background: #f6f6f6;-moz-border-radius-bottomright: 6px; -webkit-border-bottom-right-radius: 6px; border-bottom-right-radius: 6px;"> + <a href="%DEMOURL%" target="_blank" style="color: #333;">Sorğu göndərin</a> + </td> + </tr> + + + <tr border="0" cellspacing="0" cellpadding="0"><td colspan="3" style="height: 10px; line-height: 10px; background: #fff; padding: 0; margin: 0;">&nbsp;</td></tr> + <tr border="0" cellspacing="0" cellpadding="0"> + <td style="vertical-align: top; margin: 0; padding: 34px 0 0 0; width: 210px; height: 108px; background: #f6f6f6; -moz-border-radius-topleft: 6px; -webkit-border-top-left-radius: 6px; border-top-left-radius: 6px;"> + <img src="%IMAGEPATH%/tech-100.png" style="width: 100px; height: 100px;margin: 0 auto;" /> + </td> + <td style="vertical-align: top; margin: 0; padding: 34px 0 0 0; width: 180px; height: 108px; background: #f6f6f6;"> + <img src="%IMAGEPATH%/mailus-100.png" style="width: 100px; height: 100px;margin: 0 auto;" /> + </td> + <td style="vertical-align: top; margin: 0; padding: 34px 0 0 0; width: 210px; height: 108px; background: #f6f6f6; -moz-border-radius-topright: 6px; -webkit-border-top-right-radius: 6px; border-top-right-radius: 6px;"> + <img src="%IMAGEPATH%/demo-100.png" style="width: 100px; height: 100px;margin: 0 auto;" /> + </td> + </tr> + <tr border="0" cellspacing="0" cellpadding="0"> + <td style="vertical-align: top; margin: 0; padding: 0; width: 210px; height: 16px; background: #f6f6f6;">Texniki dəstək lazımdır?</td> + <td style="vertical-align: top; margin: 0; padding: 0; width: 180px; height: 16px; background: #f6f6f6;">Satışla bağlı Suallar</td> + <td style="vertical-align: top; margin: 0; padding: 0; width: 210px; height: 16px; background: #f6f6f6;">Demo Sifariçedin</td> + </tr> + <tr border="0" cellspacing="0" cellpadding="0"> + <td style="vertical-align: top; margin: 0; padding: 0 0 30px; width: 210px; background: #f6f6f6;-moz-border-radius-bottomleft: 6px; -webkit-border-bottom-left-radius: 6px; border-bottom-left-radius: 6px;"> + <a href="http://dev.onlyoffice.org/" target="_blank" style="color: #333;">İcmadan soruşun</a> + </td> + <td style="vertical-align: top; margin: 0; padding: 0 0 30px; width: 180px; background: #f6f6f6;"> + <a href="mailto:sales@onlyoffice.com" style="color: #333;">Bizə e-məktub yazın</a> + </td> + <td style="vertical-align: top; margin: 0; padding: 0 0 30px; width: 210px; background: #f6f6f6;-moz-border-radius-bottomright: 6px; -webkit-border-bottom-right-radius: 6px; border-bottom-right-radius: 6px;"> + <a href="http://www.onlyoffice.com/demo-order.aspx" target="_blank" style="color: #333;">Bizə sorğu göndərin</a> + </td> + </tr> + + + <body style="margin: 0; padding: 0; text-align: center; width: 100%; font-family: Arial, sans-serif; font-size: 14px; color: #333;"> +<div style="background-color: #fff; width: 600px; margin: 0 auto; text-align: left;"> + <table cellspacing="0" cellpadding="0" style="font-family: Arial; font-size: 14px; color: #333; background: #fff; text-align: center; width: 600px; margin: 0; padding: 0; border: 0 none; border-collapse: collapse; empty-cells: show; border-spacing: 0;"> + <tbody> + <tr border="0" cellspacing="0" cellpadding="0"> + <td colspan="3" style="margin: 0; padding: 0; background-color: #fff; height: 65px; padding: 20px 0 0 30px;"> + <div style="text-align: left; height: 65px; width: 570px; margin: 0; padding: 0;"> + <a href="%SITEURL%" style="text-decoration: none; display: inline-block; width: 216px; height: 35px; margin: 0; padding: 0;" target="_blank"> + <img src="%LOGO%" style="border: 0px none; width: 216px; height: 35px; margin: 0; padding: 0;" alt="%LOGOTEXT%" /> + </a> + </div> + </td> + </tr> + <tr border="0" cellspacing="0" cellpadding="0"> + <td colspan="3" style="margin: 0; padding: 15px 30px 15px; background-color: #f6f6f6; -moz-border-radius: 6px; -webkit-border-radius: 6px; border-radius: 6px;"> + <div style="font-family: Arial; font-size: 14px; color: #333; margin: 0; padding: 0; width: 540px; height: auto; overflow: hidden; word-wrap: break-word; vertical-align: top; text-align: left; border: 0 none;"> + %CONTENT% + </div> + </td> + </tr> + %FOOTER% + </tbody> + </table> + + <table cellspacing="0" cellpadding="0" style="font-family: Arial; font-size: 14px; color: #333; text-align: center; vertical-align: top; width: 600px; margin: 0; padding: 0; border-collapse: collapse; border: 0; border-spacing: 0; "> + <tbody> + %FOOTERSOCIAL% + <tr border="0" cellspacing="0" cellpadding="0"> + <td colspan="6" style="width: 600px; vertical-align: top; margin: 0; padding: 20px 30px 15px;"> + <p style="color: #7b7b7b; font-family: Arial, Tahoma; font-size: 12px; margin: 0; padding: 0; text-align: center; width: 540px;"> + %TEXTFOOTER% + </p> + </td> + </tr> + </tbody> + </table> +</div> +</body> + + + <body style="margin: 0; padding: 0; text-align: center; width: 100%; font-family: Arial, sans-serif; font-size: 14px; color: #333;"> +<div style="background-color: #fff; width: 600px; margin: 0 auto; text-align: left;"> + <table cellspacing="0" cellpadding="0" style="font-family: Arial; font-size: 14px; color: #333; background: #fff; text-align: center; width: 600px; margin: 0; padding: 0; border: 0 none; border-collapse: collapse; empty-cells: show; border-spacing: 0;"> + <tbody> + <tr border="0" cellspacing="0" cellpadding="0"> + <td colspan="3" style="margin: 0; padding: 0; background-color: #fff; height: 65px; padding: 20px 0 0 30px;"> + <div style="text-align: left; height: 65px; width: 570px; margin: 0; padding: 0;"> + <a href="%SITEURL%" style="text-decoration: none; display: inline-block; width: 216px; height: 35px; margin: 0; padding: 0;" target="_blank"> + <img src="%LOGO%" style="border: 0px none; width: 216px; height: 35px; margin: 0; padding: 0;" alt="%LOGOTEXT%" /> + </a> + </div> + </td> + </tr> + <tr border="0" cellspacing="0" cellpadding="0"> + <td colspan="3" style="margin: 0; padding: 0; background-color: #f6f6f6; -moz-border-radius: 6px; -webkit-border-radius: 6px; border-radius: 6px;"> + <div style="width: 600px; height: 340px; background: url('https://d2nlctn12v279m.cloudfront.net/media/newsletters/images/personal-wellcome-bg.png') 0 0 no-repeat; margin: 0; padding: 0; border: 0 none;"></div> +<div style="font-family: Arial; font-size: 14px; color: #333; margin: 0; padding: 15px 30px 15px; width: 540px; height: auto; overflow: hidden; word-wrap: break-word; vertical-align: top; text-align: left; border: 0 none;"> + %CONTENT% + </div> + </td> + </tr> + %FOOTER% + </tbody> + </table> + + <table cellspacing="0" cellpadding="0" style="font-family: Arial; font-size: 14px; color: #333; text-align: center; vertical-align: top; width: 600px; margin: 0; padding: 0; border-collapse: collapse; border: 0; border-spacing: 0; "> + <tbody> + %FOOTERSOCIAL% + <tr border="0" cellspacing="0" cellpadding="0"> + <td colspan="6" style="width: 600px; vertical-align: top; margin: 0; padding: 20px 30px 15px;"> + <p style="color: #7b7b7b; font-family: Arial, Tahoma; font-size: 12px; margin: 0; padding: 0; text-align: center; width: 540px;"> + %TEXTFOOTER% + </p> + </td> + </tr> + </tbody> + </table> +</div> +</body> + + + <tr border="0" cellspacing="0" cellpadding="0"> + <td style="width: 40px; vertical-align: top; margin: 0; padding: 22px 5px 0 155px;"> + <a href="https://www.facebook.com/pages/OnlyOffice/833032526736775" style="width: 40px; height: 40px; display: block; margin: 0; padding: 0;" target="_blank"> + <img src="%IMAGEPATH%/social-fb-40.png" alt="Facebook" style="width: 40px; height: 40px;" /> + </a> + </td> + <td style="width: 40px; vertical-align: top; margin: 0; padding: 22px 5px 0 5px;"> + <a href="https://twitter.com/ONLY_OFFICE" style="width: 40px; height: 40px; display: block; margin: 0; padding: 0;" target="_blank"> + <img src="%IMAGEPATH%/social-tw-40.png" alt="Twitter" style="width: 40px; height: 40px;" /> + </a> + </td> + <td style="width: 40px; vertical-align: top; margin: 0; padding: 22px 5px 0 5px;"> + <a href="https://www.onlyoffice.com/blog" style="width: 40px; height: 40px; display: block; margin: 0; padding: 0;" target="_blank"> + <img src="%IMAGEPATH%/social-blog-40.png" alt="ONLYOFFICE" style="width: 40px; height: 40px;" /> + </a> + </td> + <td style="width: 40px; vertical-align: top; margin: 0; padding: 22px 5px 0 5px;"> + <a href="https://www.youtube.com/user/onlyofficeTV" style="width: 40px; height: 40px; display: block; margin: 0; padding: 0;" target="_blank"> + <img src="%IMAGEPATH%/social-youtube-40.png" alt="YouTube" style="width: 40px; height: 40x;" /> + </a> + </td> + <td style="width: 40px; vertical-align: top; margin: 0; padding: 22px 5px 0 5px;"> + <a href="https://vk.com/onlyoffice" style="width: 40px; height: 40px; display: block; margin: 0; padding: 0;" target="_blank"> + <img src="%IMAGEPATH%/social-vk-40.png" alt="VK" style="width: 40px; height: 40px;" /> + </a> + </td> + <td style="width: 40px; vertical-align: top; margin: 0; padding: 22px 155px 0 5px;"> + <a href="https://www.instagram.com/the_onlyoffice" style="width: 40px; height: 40px; display: block; margin: 0; padding: 0;" target="_blank"> + <img src="%IMAGEPATH%/social-inst-40.png" alt="Instagram" style="width: 40px; height: 40px;" /> + </a> + </td> + </tr> + + + Bu e-poçt avtomatik olaraq yaradılır və ona cavab vermək lazım deyil. +<br />Siz <a href="{0}" style="color: #7b7b7b;" target="_blank">{0}</a>-nin qeydiyyatdan keçmiş istifadəçisi olduğunuz üçün bu e-poçtu alırsınız. +<br />Bu e-poçtların artıq sizə göndərilməsini istəmirsinizsə, aşağıdakı linkə klikləyin: <a href="{1}" style="color: #7b7b7b;" target="_blank">Abunəlikdən çıxın</a> +<br /> + + diff --git a/common/TMResourceManager/ASC.Resource.Data/ASC.Resource.Data.csproj b/common/TMResourceManager/ASC.Resource.Data/ASC.Resource.Data.csproj index e709b0810..8e060cb10 100644 --- a/common/TMResourceManager/ASC.Resource.Data/ASC.Resource.Data.csproj +++ b/common/TMResourceManager/ASC.Resource.Data/ASC.Resource.Data.csproj @@ -57,17 +57,26 @@ {76de7717-3d4b-4a5b-b740-15b8913df0cb} ASC.Common + + {a51d0454-4afa-46de-89d4-b03d37e1816c} + ASC.Core.Common + - - 1.13.7 - - 12.0.3 + 13.0.1 + + 1.3.2 + + + + + + \ No newline at end of file diff --git a/common/TMResourceManager/ASC.Resource.Data/JsonManager.cs b/common/TMResourceManager/ASC.Resource.Data/JsonManager.cs index 39c498a0f..a2086cf47 100644 --- a/common/TMResourceManager/ASC.Resource.Data/JsonManager.cs +++ b/common/TMResourceManager/ASC.Resource.Data/JsonManager.cs @@ -19,13 +19,15 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text.RegularExpressions; using System.Xml; using ASC.Common.Logging; -using Ionic.Zip; +using ICSharpCode.SharpZipLib.Zip; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using TMResourceData.Model; @@ -67,7 +69,21 @@ namespace TMResourceData } else { - jsonObj = JsonConvert.DeserializeObject>(jsonString); + var reader = JObject.Parse(jsonString).CreateReader(); + while (reader.Read()) + { + if (reader.TokenType == JsonToken.String) + { + var key = reader.Path; + key = Regex.Replace(Regex.Replace(key, @"\[\'(\s)*", ".$1"), @"(\s)*\'\]", "$1").TrimStart('.').TrimEnd('.'); + + if (reader.Value != null) + { + var value = reader.Value.ToString(); + jsonObj.Add(key, value); + } + } + } } var fileID = ResourceData.AddFile(fileName, projectName, moduleName); @@ -98,106 +114,151 @@ namespace TMResourceData } public static string ExportJson(string project, string module, List languages, string exportPath, - bool withDefaultValue = true) + bool withDefaultValue = true, bool withStructurJson = true) { - using (var fastZip = new ZipFile()) + var filter = new ResCurrent { - var filter = new ResCurrent - { - Project = new ResProject { Name = project }, - Module = new ResModule { Name = module } - }; + Project = new ResProject { Name = project }, + Module = new ResModule { Name = module } + }; - var zipDirectory = Directory.CreateDirectory(exportPath + module); - foreach (var language in languages) - { - filter.Language = new ResCulture { Title = language }; + var zipDirectory = Directory.CreateDirectory(exportPath + module); - var words = - ResourceData.GetListResWords(filter, string.Empty).GroupBy(x => x.ResFile.FileID).ToList(); - if (!words.Any()) + foreach (var language in languages) + { + filter.Language = new ResCulture { Title = language }; + + var words = + ResourceData.GetListResWords(filter, string.Empty).GroupBy(x => x.ResFile.FileID).ToList(); + if (!words.Any()) + { + Console.WriteLine("Error!!! Can't find appropriate project and module. Possibly wrong names!"); + return null; + } + + foreach (var fileWords in words) + { + var wordsDictionary = new Dictionary(); + foreach ( + var word in + fileWords.OrderBy(x => x.Title).Where(word => !wordsDictionary.ContainsKey(word.Title))) { - Console.WriteLine("Error!!! Can't find appropriate project and module. Possibly wrong names!"); - return null; + if (string.IsNullOrEmpty(word.ValueTo) && !withDefaultValue) continue; + + wordsDictionary[word.Title] = word.ValueTo ?? word.ValueFrom; + if (!string.IsNullOrEmpty(wordsDictionary[word.Title])) + { + wordsDictionary[word.Title] = wordsDictionary[word.Title].TrimEnd('\n').TrimEnd('\r'); + } } - foreach (var fileWords in words) + if (!wordsDictionary.Any()) continue; + + var firstWord = fileWords.FirstOrDefault(); + var fileName = firstWord == null + ? module + : Path.GetFileNameWithoutExtension(firstWord.ResFile.FileName); + var ext = Path.GetExtension(firstWord.ResFile.FileName); + + var zipFileName = zipDirectory.FullName + "\\" + fileName + + (language == "Neutral" ? string.Empty : "." + language) + ext; + using (TextWriter writer = new StreamWriter(zipFileName)) { - var wordsDictionary = new Dictionary(); - foreach ( - var word in - fileWords.OrderBy(x => x.Title).Where(word => !wordsDictionary.ContainsKey(word.Title))) + if (ext == ".json") { - if (string.IsNullOrEmpty(word.ValueTo) && !withDefaultValue) continue; - - wordsDictionary[word.Title] = word.ValueTo ?? word.ValueFrom; - if (!string.IsNullOrEmpty(wordsDictionary[word.Title])) + if (withStructurJson) { - wordsDictionary[word.Title] = wordsDictionary[word.Title].TrimEnd('\n').TrimEnd('\r'); + var collectionNames = new List(); + var wrOrder = 0; + JObject jObject = null; + var writeJtoken = new JTokenWriter(); + + writeJtoken.WriteStartObject(); + foreach (var vordsKV in wordsDictionary) + { + var strNameSplit = vordsKV.Key.Split('.'); + + for (var a = 0; a < strNameSplit.Length; a++) + { + while (collectionNames.Count < a + 1) collectionNames.Add(""); + + if (collectionNames[a] != null && collectionNames[a] == strNameSplit[a]) + continue; + if (wrOrder > a) + { + for (var b = a; b < collectionNames.Count; b++) collectionNames[b] = null; + while (wrOrder > a) + { + writeJtoken.WriteEndObject(); + wrOrder--; + } + } + writeJtoken.WritePropertyName(strNameSplit[a]); + if (a < strNameSplit.Length - 1) + { + writeJtoken.WriteStartObject(); + wrOrder++; + collectionNames[a] = strNameSplit[a]; + } + else + writeJtoken.WriteValue(vordsKV.Value); + } + } + jObject = (JObject)writeJtoken.Token; + writer.Write(jObject); } - } - - var firstWord = fileWords.FirstOrDefault(); - var fileName = firstWord == null - ? module - : Path.GetFileNameWithoutExtension(firstWord.ResFile.FileName); - var ext = Path.GetExtension(firstWord.ResFile.FileName); - - var zipFileName = zipDirectory.FullName + "\\" + fileName + - (language == "Neutral" ? string.Empty : "." + language) + ext; - using (TextWriter writer = new StreamWriter(zipFileName)) - { - if (ext == ".json") + else { var obj = JsonConvert.SerializeObject(wordsDictionary, Formatting.Indented); writer.Write(obj); } - else + } + else + { + var data = new XmlDocument(); + var resources = data.CreateElement("resources"); + + foreach (var ind in wordsDictionary) { - var data = new XmlDocument(); - var resources = data.CreateElement("resources"); + var stringAttr = data.CreateAttribute("name"); + stringAttr.Value = ind.Key; - foreach (var ind in wordsDictionary) - { - var stringAttr = data.CreateAttribute("name"); - stringAttr.Value = ind.Key; + var child = data.CreateElement("string"); + child.Attributes.Append(stringAttr); + child.InnerText = ind.Value; - var child = data.CreateElement("string"); - child.Attributes.Append(stringAttr); - child.InnerText = ind.Value; + resources.AppendChild(child); + } - resources.AppendChild(child); - } + data.AppendChild(resources); - data.AppendChild(resources); + var settings = new XmlWriterSettings + { + Indent = true, + IndentChars = " ", + NewLineChars = Environment.NewLine, + NewLineHandling = NewLineHandling.Replace, + OmitXmlDeclaration = false, + ConformanceLevel = ConformanceLevel.Fragment + }; - var settings = new XmlWriterSettings - { - Indent = true, - IndentChars = " ", - NewLineChars = Environment.NewLine, - NewLineHandling = NewLineHandling.Replace, - OmitXmlDeclaration = false, - ConformanceLevel = ConformanceLevel.Fragment - }; - - using (var xmlTextWriter = XmlWriter.Create(writer, settings)) - { - data.WriteTo(xmlTextWriter); - xmlTextWriter.Flush(); - } + using (var xmlTextWriter = XmlWriter.Create(writer, settings)) + { + data.WriteTo(xmlTextWriter); + xmlTextWriter.Flush(); } } } } - - var zipPath = exportPath + "\\" + module + ".zip"; - fastZip.AddDirectory(zipDirectory.FullName); - fastZip.Save(zipPath); - - zipDirectory.Delete(true); - return zipPath; } + + var zipPath = zipDirectory.FullName + ".zip"; + var fastZip = new FastZip(); + fastZip.CreateEmptyDirectories = true; + fastZip.CreateZip(zipPath, zipDirectory.FullName, true, null); + zipDirectory.Delete(true); + + return zipPath; } private static string GetCultureFromFileName(string fileName) diff --git a/common/TMResourceManager/ASC.Resource.Data/ResourceData.cs b/common/TMResourceManager/ASC.Resource.Data/ResourceData.cs index 30dfef98a..a6cb8b3d7 100644 --- a/common/TMResourceManager/ASC.Resource.Data/ResourceData.cs +++ b/common/TMResourceManager/ASC.Resource.Data/ResourceData.cs @@ -295,7 +295,7 @@ namespace TMResourceData var exist = new SqlQuery(ResDataTable + " rd3") .Select("rd3.title") .Where("rd3.fileid = rd1.fileid") - .Where("rd3.title = concat('del_', rd1.title)") + .Where("rd1.title = REPLACE(rd3.title, 'del_', '') and LOCATE('del_', rd3.title)") .Where("rd3.cultureTitle = rd1.cultureTitle"); var sql = new SqlQuery(ResDataTable + " rd1") @@ -307,7 +307,7 @@ namespace TMResourceData .Where("rd1.cultureTitle", "Neutral") .Where("rd1.flag != 4") .Where("rd1.resourceType", "text") - .Where(!Exp.Like("rd1.title", @"del\_", SqlLike.StartWith) & !Exp.Exists(exist)) + .Where(!Exp.Like("rd1.title", @"del\_", SqlLike.StartWith) & !Exp.Like("rd1.title", @".del\_", SqlLike.AnyWhere) & !Exp.Exists(exist)) .OrderBy("rd1.id", true); if (!String.IsNullOrEmpty(search)) diff --git a/common/Tests/ASC.BenchmarkTest/App.config b/common/Tests/ASC.BenchmarkTest/App.config index 35c34b6a5..91da55d06 100644 --- a/common/Tests/ASC.BenchmarkTest/App.config +++ b/common/Tests/ASC.BenchmarkTest/App.config @@ -161,18 +161,15 @@ - - - - - - - - - + + + + + + diff --git a/common/Tests/ASC.BenchmarkTest/DbManagerTest.cs b/common/Tests/ASC.BenchmarkTest/DbManagerTest.cs index c05653687..d0ddc44df 100644 --- a/common/Tests/ASC.BenchmarkTest/DbManagerTest.cs +++ b/common/Tests/ASC.BenchmarkTest/DbManagerTest.cs @@ -1,4 +1,21 @@ -using System; +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System; using System.IO; using System.Linq; using System.Threading.Tasks; diff --git a/common/Tests/ASC.BenchmarkTest/Program.cs b/common/Tests/ASC.BenchmarkTest/Program.cs index d8538a1fc..0c3f71f3d 100644 --- a/common/Tests/ASC.BenchmarkTest/Program.cs +++ b/common/Tests/ASC.BenchmarkTest/Program.cs @@ -1,4 +1,21 @@ -using BenchmarkDotNet.Running; +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using BenchmarkDotNet.Running; namespace ASC.BenchmarkTest { diff --git a/licenses/3rd-Party.license b/licenses/3rd-Party.license index 19fc904d4..a599e205f 100644 --- a/licenses/3rd-Party.license +++ b/licenses/3rd-Party.license @@ -4,11 +4,7 @@ Community Server uses code from the following 3rd party projects: nuget packages ==================================================================================================== -AjaxMin - JavaScript and CSS minification Library for use in .NET applications that want to provide minification or parsing functionality. (http://www.apache.org/licenses/LICENSE-2.0) -License: Apache License 2.0 -License File: AjaxMin.license - -AjaxPro.2 - .NET Library that provides AJAX related methods to simplify the communication between server and client. (https://github.com/michaelschwarz/Ajax.NET-Professional/blob/master/LICENS) +AjaxPro.2 - .NET Library that provides AJAX related methods to simplify the communication between server and client. (https://github.com/michaelschwarz/Ajax.NET-Professional/blob/master/LICENSE) License: MIT License License File: AjaxPro.2.license @@ -108,10 +104,6 @@ DotNetOpenAuth.Ultimate - A single assembly that adds OpenID 1.1/2.0, OAuth 1.0( License: MS-PL License File: DotNetOpenAuth.Ultimate.license -DotNetZip - A fork of the DotNetZip project without signing with a solution that compiles cleanly. This project aims to follow semver to avoid versioning conflicts. DotNetZip is a FAST, FREE class library and toolset for manipulating zip files. Use VB, C# or any .NET language to easily create, extract, or update zip files. (https://raw.githubusercontent.com/haf/DotNetZip.Semverd/master/LICENSE) -License: Ms-PL , BSD-style (3 clause), Apache 2 license ??? -License File: DotNetZip.license - Dropbox.Api - Portable class library for accessing the Dropbox v2 API (https://github.com/dropbox/dropbox-sdk-dotnet/blob/master/LICENSE) License: MIT License License File: Dropbox.Api.license @@ -132,6 +124,10 @@ Flurl.Signed - A fluent, portable URL builder. To make HTTP calls off the fluent License: MIT License License File: Flurl.Signed.license +FolkerKinzel.VCards -.NET library to read, write and convert VCF files that match the vCard standards 2.1, 3.0 and 4.0. (https://github.com/FolkerKinzel/VCards/blob/master/LICENSE) +License: MIT License +License File: FolkerKinzel.VCards.license + FredCK.FCKeditorV2 - FCKeditor is a text editor to be used inside web pages. In 2009 it was renamed CKEditor. (http://ckeditor.com/what-is-ckeditor) (https://download.cksource.com/FCKeditor/FCKeditor/2.6.3/) License: GPL 2 (or later) or LGPL 2.1 (or later) or MPL 1.1 (or later) License File: FredCK.FCKeditorV2.license @@ -340,6 +336,10 @@ Microsoft.IdentityModel.Tokens - Includes types that provide support for Securit License: MIT License License File: Microsoft.IdentityModel.Tokens.license +Microsoft.IdentityModel.Tokens.Jwt - Includes types that provide support for creating, serializing and validating JSON Web Tokens. (https://licenses.nuget.org/MIT) +License: MIT License +License File: Microsoft.IdentityModel.Tokens.Jwt.license + Microsoft.Net.Http - This package includes HttpClient for sending requests over HTTP, as well as HttpRequestMessage and HttpResponseMessage for processing HTTP messages. (https://dotnet.microsoft.com/en/dotnet_library_license.htm) License: MICROSOFT .NET LIBRARY LICENSE License File: Microsoft.Net.Http.license @@ -424,6 +424,10 @@ Novell.Directory.LDAP - LDAP client library (https://github.com/dsbenghe/Novell. License: MIT License License File: Novell.Directory.LDAP.license +NUglify - NUglify provides minify and compression methods for CSS, JavaScript and HTML files. (https://github.com/trullock/NUglify/blob/master/license.txt) +License: BSD-2-Clause License +License File: NUglify.license + NUnit - NUnit features a fluent assert syntax, parameterized, generic and theory tests and is user-extensible. (https://github.com/nunit/nunit/blob/master/LICENSE.txt) License: MIT License License File: NUnit.license @@ -776,6 +780,10 @@ npm packages License: MIT License License File: @authenio-samlify-node-xmllint.license +axios - Promise based HTTP client for the browser and node.js (https://github.com/axios/axios/blob/master/LICENSE) +License: MIT License +License File: axios.license + body-parser - Node.js body parsing middleware. (https://github.com/expressjs/body-parser/blob/master/LICENSE) License: MIT License License File: body-parser.license @@ -828,10 +836,18 @@ filenamify-url - Convert a URL to a valid filename (https://github.com/sindresor License: MIT License License File: filenamify-url.license +form-data - A library to create readable "multipart/form-data" streams. Can be used to submit forms and file uploads to other web applications.The API of this library is inspired by the XMLHttpRequest-2 FormData Interface. (https://github.com/form-data/form-data/blob/master/License) +License: MIT License +License File: form-data.license + formidable - A Node.js module for parsing form data, especially file uploads. (https://github.com/node-formidable/formidable/blob/master/LICENSE) License: MIT License License File: formidable.license +get-byte - A Universal way of getting a Byte at a Given Index from a variety of in-memory data formats, including Uint8Array, Buffer, DataView, and Array Buffer (https://github.com/DanielJDufour/get-byte/blob/main/LICENSE) +License: CC0-1.0 License +License File: get-byte.license + graceful-fs - graceful-fs functions as a drop-in replacement for the fs module, making various improvements. (https://github.com/isaacs/node-graceful-fs) License: ISC License License File: graceful-fs.license @@ -848,6 +864,14 @@ gulp-just-replace - The gulp-replace is fine. But it's painful to install it o License: MIT License License File: gulp-just-replace.license +help - substack way of --help. v3 of help requires at least Node.js v4. To use help with an older version, please use help@2.x. (https://github.com/evanlucas/help/blob/master/LICENSE) +License: MIT License +License File: help.license + +ipaddr - an IPv6 and IPv4 address manipulation library. (https://github.com/whitequark/ipaddr.js/blob/master/LICENSE) +License: MIT License +License File: ipaddr.license + lodash - The modern build of lodash exported as Node.js/io.js modules. (https://github.com/lodash/lodash/blob/master/LICENSE) License: MIT License License File: lodash.license @@ -928,6 +952,10 @@ tmp - A simple temporary file and directory creator for node.js. (https://githu License: MIT License License File: tmp.license +to-byte-array - Convert a string or buffer into a plain array of bytes. (https://github.com/finwo/to-byte-array/blob/master/LICENSE.md) +License: MIT License +License File: to-byte-array.license + ua-parser-js - A JavaScript-based User-Agent string parser. Can be used either in browser (client-side) or in node.js (server-side) environment. Also available as jQuery/Zepto plugin, Bower/Meteor package, & RequireJS/AMD module.  (https://github.com/faisalman/ua-parser-js/blob/master/license.md) License: MIT License License File: ua-parser-js.license @@ -936,6 +964,10 @@ url - This module has utilities for URL resolution and parsing meant to have fea License: MIT License License File: url.license +webdav-server - WebDav server. (https://github.com/OpenMarshal/npm-WebDAV-Server/blob/master/LICENSE) +License: Unlicense License +License File: webdav-server.license + winston - A logger for just about everything. (https://github.com/winstonjs/winston/blob/master/LICENSE) License: MIT License License File: winston.license @@ -1132,11 +1164,15 @@ jstree.min.js - jsTree is jquery plugin, that provides interactive trees (https: License: MIT License License File: jstree.license -linkify.min.js - Linkify is a zero-dependency JavaScript plugin for finding links in plain-text and converting them to HTML tags (https://github.com/Soapbox/linkifyjs/blob/master/LICENSE) +linkify.min.js - Linkify is a JavaScript plugin for finding links in plain-text and converting them to HTML tags. (https://github.com/Soapbox/linkifyjs/blob/master/LICENSE) License: MIT License License File: linkify.license -linkify-string.min.js - Linkify is a zero-dependency JavaScript plugin for finding links in plain-text and converting them to HTML tags (https://github.com/Soapbox/linkifyjs/blob/master/LICENSE) +linkify-html.min.js - Linkify is a JavaScript plugin for finding links in plain-text and converting them to HTML tags. (https://github.com/Soapbox/linkifyjs/blob/master/LICENSE) +License: MIT License +License File: linkify-html.license + +linkify-string.min.js - Linkify is a JavaScript plugin for finding links in plain-text and converting them to HTML tags. (https://github.com/Soapbox/linkifyjs/blob/master/LICENSE) License: MIT License License File: linkify-string.license diff --git a/licenses/javascript plugins/linkify-html.license b/licenses/javascript plugins/linkify-html.license new file mode 100644 index 000000000..93d193376 --- /dev/null +++ b/licenses/javascript plugins/linkify-html.license @@ -0,0 +1,19 @@ +Copyright (c) 2018 SoapBox Innovations Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/licenses/npm packages/axios.license b/licenses/npm packages/axios.license new file mode 100644 index 000000000..2d8d66aa4 --- /dev/null +++ b/licenses/npm packages/axios.license @@ -0,0 +1,19 @@ +Copyright (c) 2014-present Matt Zabriskie + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/licenses/npm packages/form-data.license b/licenses/npm packages/form-data.license new file mode 100644 index 000000000..143bad2ee --- /dev/null +++ b/licenses/npm packages/form-data.license @@ -0,0 +1,19 @@ +Copyright (c) 2012 Felix Geisendörfer (felix@debuggable.com) and contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. \ No newline at end of file diff --git a/licenses/npm packages/get-byte.license b/licenses/npm packages/get-byte.license new file mode 100644 index 000000000..1625c1793 --- /dev/null +++ b/licenses/npm packages/get-byte.license @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. \ No newline at end of file diff --git a/licenses/npm packages/help.license b/licenses/npm packages/help.license new file mode 100644 index 000000000..843a01e14 --- /dev/null +++ b/licenses/npm packages/help.license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Evan Lucas + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/licenses/npm packages/ipaddr.license b/licenses/npm packages/ipaddr.license new file mode 100644 index 000000000..957be9280 --- /dev/null +++ b/licenses/npm packages/ipaddr.license @@ -0,0 +1,19 @@ +Copyright (C) 2011-2017 whitequark + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/licenses/npm packages/to-byte-array.license b/licenses/npm packages/to-byte-array.license new file mode 100644 index 000000000..ea55c9468 --- /dev/null +++ b/licenses/npm packages/to-byte-array.license @@ -0,0 +1,8 @@ +The MIT License (MIT) +Copyright © 2019 Robin Bron + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/licenses/npm packages/webdav-server.license b/licenses/npm packages/webdav-server.license new file mode 100644 index 000000000..6bb8a2915 --- /dev/null +++ b/licenses/npm packages/webdav-server.license @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to \ No newline at end of file diff --git a/licenses/nuget packages/AjaxMin.license b/licenses/nuget packages/AjaxMin.license deleted file mode 100644 index 490b5c70e..000000000 --- a/licenses/nuget packages/AjaxMin.license +++ /dev/null @@ -1,53 +0,0 @@ -Apache License - -Version 2.0, January 2004 - -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. - -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of this License; and -You must cause any modified files to carry prominent notices stating that You changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. - -You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. -5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/licenses/nuget packages/DotNetZip.license b/licenses/nuget packages/DotNetZip.license deleted file mode 100644 index 0984ceab5..000000000 --- a/licenses/nuget packages/DotNetZip.license +++ /dev/null @@ -1,88 +0,0 @@ -Software Licenses that apply to the DotNetZip library and tools - -As DotNetZip includes work derived from other projects, you are required to comply with the terms and conditions for each of them. These licenses include BSD, Apache, and zlib. - -To use the software, you must accept the licenses. If you do not accept the licenses, do not use the software. - -Original intellectual property in DotNetZip is provided under the Ms-PL: - - Copyright (c) 2006 - 2011 Dino Chiesa - Copyright (c) 2006, 2007, 2008, 2009 Dino Chiesa and Microsoft Corporation. - - Microsoft Public License (Ms-PL) - - This license governs use of the accompanying software, the DotNetZip library ("the software"). If you use the software, you accept this license. If you do not accept the license, do not use the software. - - 1. Definitions - - The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. - - A "contribution" is the original software, or any additions or changes to the software. - - A "contributor" is any person that distributes its contribution under this license. - - "Licensed patents" are a contributor's patent claims that read directly on its contribution. - - 2. Grant of Rights - - (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. - - (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. - - 3. Conditions and Limitations - - (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. - - (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. - - (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. - - (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. - - (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. - --------------------------------------------------------------- - -The managed ZLIB code included in Ionic.Zlib.dll and Ionic.Zip.dll is derived from jzlib. - -jzlib ( https://github.com/ymnk/jzlib ) is provided under a BSD-style (3 clause) - - Copyright (c) 2000,2001,2002,2003 ymnk, JCraft, Inc. - - Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - 3. The names of the authors may not be used to endorse or promote products derived from this software without specific prior written permission. - THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - --------------------------------------------------------------- - -The jzlib library, itself, is a re-implementation of ZLIB v1.1.3 in pure Java. - -zlib is provided under the zlib license: - - Copyright (C) 1995-2004 Jean-loup Gailly and Mark Adler - The ZLIB software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. - Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: - 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - Jean-loup Gailly jloup@gzip.org? Mark Adler madler@alumni.caltech.edu - --------------------------------------------------------------- - -The managed BZIP2 code included in Ionic.BZip2.dll and Ionic.Zip.dll is modified code, based on Java code in the Apache commons compress library. - -Apache Commons Compress ( http://commons.apache.org/proper/commons-compress/ ) is provided under the Apache 2 license: - Apache Commons Compress - Copyright 2002-2014 The Apache Software Foundation - - Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - -Many thanks to Julian Seward for the original C implementation of BZip2 ( http://www.bzip.org/ ). - - - - diff --git a/licenses/nuget packages/FolkerKinzel.VCards.license b/licenses/nuget packages/FolkerKinzel.VCards.license new file mode 100644 index 000000000..cfa02f6d9 --- /dev/null +++ b/licenses/nuget packages/FolkerKinzel.VCards.license @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Folker Kinzel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/licenses/nuget packages/Microsoft.IdentityModel.Tokens.Jwt.license b/licenses/nuget packages/Microsoft.IdentityModel.Tokens.Jwt.license new file mode 100644 index 000000000..7bb386e6f --- /dev/null +++ b/licenses/nuget packages/Microsoft.IdentityModel.Tokens.Jwt.license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/licenses/nuget packages/NUglify.license b/licenses/nuget packages/NUglify.license new file mode 100644 index 000000000..ea9b0c1b6 --- /dev/null +++ b/licenses/nuget packages/NUglify.license @@ -0,0 +1,41 @@ +Copyright (c) 2016, Alexandre Mutel +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification +, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------- +The Microsoft Ajax Minifier was originally released under the following license: +------------------------------------------------------------------------------- + +Copyright 2010-2015 Microsoft Corporation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/module/ASC.Api/ASC.Api.CRM/ASC.Api.CRM.csproj b/module/ASC.Api/ASC.Api.CRM/ASC.Api.CRM.csproj index 06c0424cf..48adebcf1 100644 --- a/module/ASC.Api/ASC.Api.CRM/ASC.Api.CRM.csproj +++ b/module/ASC.Api/ASC.Api.CRM/ASC.Api.CRM.csproj @@ -180,10 +180,10 @@ - 5.1.2 + 6.2.0 - 12.0.3 + 13.0.1 diff --git a/module/ASC.Api/ASC.Api.CRM/CRMApi.Cases.cs b/module/ASC.Api/ASC.Api.CRM/CRMApi.Cases.cs index 3e0ae13c7..35c3160d4 100644 --- a/module/ASC.Api/ASC.Api.CRM/CRMApi.Cases.cs +++ b/module/ASC.Api/ASC.Api.CRM/CRMApi.Cases.cs @@ -37,15 +37,15 @@ namespace ASC.Api.CRM public partial class CRMApi { /// - /// Close the case with the ID specified in the request + /// Closes a case with the ID specified in the request. /// - /// Close case + /// Close a case /// Cases /// Case ID /// /// /// - /// Case + /// Case /// [Update(@"case/{caseid:[0-9]+}/close")] public CasesWrapper CloseCases(int caseid) @@ -61,15 +61,15 @@ namespace ASC.Api.CRM } /// - /// Resume the case with the ID specified in the request + /// Resumes a case with the ID specified in the request. /// - /// Resume case + /// Resume a case /// Cases /// Case ID /// /// /// - /// Case + /// Case /// [Update(@"case/{caseid:[0-9]+}/reopen")] public CasesWrapper ReOpenCases(int caseid) @@ -85,15 +85,15 @@ namespace ASC.Api.CRM } /// - /// Creates the case with the parameters specified in the request + /// Creates a case with the parameters specified in the request. /// - /// Create case + /// Create a case /// Case title - /// Participants - /// User field list + /// Case participants + /// List of case custom fields /// Case privacy: private or not /// List of users with access to the case - /// Notify users in accessList about the case + /// Notifies users from the access list about the case /// Case /// Cases /// @@ -155,16 +155,16 @@ namespace ASC.Api.CRM } /// - /// Updates the selected case with the parameters specified in the request + /// Updates the selected case with the parameters specified in the request. /// - /// Update case + /// Update a case /// Case ID - /// Case title - /// Participants - /// User field list + /// New case title + /// New case participants + /// New list of case custom fields /// Case privacy: private or not - /// List of users with access to the case - /// Notify users in accessList about the case + /// New list of users with access to the case + /// Notifies users from the access list about the case /// Cases /// Case /// @@ -229,17 +229,17 @@ namespace ASC.Api.CRM } /// - /// Sets access rights for the selected case with the parameters specified in the request + /// Sets access rights to the selected case with the parameters specified in the request. /// /// Case ID /// Case privacy: private or not /// List of users with access to the case - /// Set rights to case + /// Set access rights to the case /// Cases /// /// /// - /// Case + /// Case /// [Update(@"case/{caseid:[0-9]+}/access")] public CasesWrapper SetAccessToCases(int caseid, bool isPrivate, IEnumerable accessList) @@ -290,17 +290,17 @@ namespace ASC.Api.CRM } /// - /// Sets access rights for other users to the list of cases with the IDs specified in the request + /// Sets access rights to the list of cases with the IDs specified in the request. /// - /// Case ID list + /// List of case IDs /// Case privacy: private or not /// List of users with access - /// Set case access rights + /// Set access rights to the cases by IDs /// Cases /// /// /// - /// Case list + /// List of cases /// [Update(@"case/access")] public IEnumerable SetAccessToBatchCases(IEnumerable casesid, bool isPrivate, IEnumerable accessList) @@ -325,19 +325,19 @@ namespace ASC.Api.CRM } /// - /// Sets access rights for other users to the list of all cases matching the parameters specified in the request + /// Sets access rights to the list of all the cases matching the parameters specified in the request. /// /// Contact ID /// Case status - /// Tags + /// Case tags /// Case privacy: private or not /// List of users with access - /// Set case access rights + /// Set access rights to the cases by parameters /// Cases /// /// /// - /// Case list + /// List of cases /// [Update(@"case/filter/access")] public IEnumerable SetAccessToBatchCases( @@ -368,11 +368,12 @@ namespace ASC.Api.CRM } /// - /// Returns the detailed information about the case with the ID specified in the request + /// Returns the detailed information about a case with the ID specified in the request. /// - /// Get case by ID + /// Get a case by ID /// Cases /// Case ID + /// Case /// /// [Read(@"case/{caseid:[0-9]+}")] @@ -387,15 +388,15 @@ namespace ASC.Api.CRM } /// - /// Returns the list of all cases matching the parameters specified in the request + /// Returns a list of all the cases matching the parameters specified in the request. /// - /// Get case list + /// Get cases /// Contact ID /// Case status - /// Tags + /// Case tags /// Cases /// - /// Case list + /// List of cases /// [Read(@"case/filter")] public IEnumerable GetCases(int contactid, bool? isClosed, IEnumerable tags) @@ -469,15 +470,15 @@ namespace ASC.Api.CRM } /// - /// Deletes the case with the ID specified in the request + /// Deletes a case with the ID specified in the request. /// - /// Delete case + /// Delete a case /// Case ID /// Cases /// /// /// - /// Case + /// Case /// [Delete(@"case/{caseid:[0-9]+}")] public CasesWrapper DeleteCase(int caseid) @@ -495,15 +496,15 @@ namespace ASC.Api.CRM } /// - /// Deletes the group of cases with the IDs specified in the request + /// Deletes a group of cases with the IDs specified in the request. /// - /// Case ID list + /// List of case IDs /// /// - /// Delete case group + /// Delete cases by IDs /// Cases /// - /// Case list + /// List of cases /// [Update(@"case")] public IEnumerable DeleteBatchCases(IEnumerable casesids) @@ -521,17 +522,17 @@ namespace ASC.Api.CRM } /// - /// Deletes the list of all cases matching the parameters specified in the request + /// Deletes a list of all the cases matching the parameters specified in the request. /// /// Contact ID /// Case status - /// Tags + /// Case tags /// /// - /// Delete case group + /// Delete cases by parameters /// Cases /// - /// Case list + /// List of cases /// [Delete(@"case/filter")] public IEnumerable DeleteBatchCases(int contactid, bool? isClosed, IEnumerable tags) @@ -547,12 +548,12 @@ namespace ASC.Api.CRM } /// - /// Returns the list of all contacts associated with the case with the ID specified in the request + /// Returns a list of all the contacts related to the case with the ID specified in the request. /// - /// Get all case contacts + /// Get case contacts /// Case ID /// Cases - /// Contact list + /// List of contacts /// [Read(@"case/{caseid:[0-9]+}/contact")] public IEnumerable GetCasesMembers(int caseid) @@ -564,16 +565,16 @@ namespace ASC.Api.CRM } /// - /// Adds the selected contact to the case with the ID specified in the request + /// Adds the selected contact to the case with the ID specified in the request. /// - /// Add case contact + /// Add a case contact /// Cases /// Case ID /// Contact ID /// /// /// - /// Participant + /// Contact /// [Create(@"case/{caseid:[0-9]+}/contact")] public ContactWrapper AddMemberToCases(int caseid, int contactid) @@ -595,16 +596,16 @@ namespace ASC.Api.CRM } /// - /// Delete the selected contact from the case with the ID specified in the request + /// Deletes the selected contact from the case with the ID specified in the request. /// - /// Delete case contact + /// Delete a case contact /// Cases /// Case ID /// Contact ID /// /// /// - /// Participant + /// Contact /// [Delete(@"case/{caseid:[0-9]+}/contact/{contactid:[0-9]+}")] public ContactWrapper DeleteMemberFromCases(int caseid, int contactid) @@ -628,13 +629,13 @@ namespace ASC.Api.CRM } /// - /// Returns the list of 30 cases in the CRM module with prefix + /// Returns a list of 30 cases from the CRM module with a prefix specified in the request. /// - /// - /// + /// Case prefix + /// Contact ID /// Cases /// - /// Cases list + /// List of cases /// /// false [Read(@"case/byprefix")] diff --git a/module/ASC.Api/ASC.Api.CRM/CRMApi.ContactInfo.cs b/module/ASC.Api/ASC.Api.CRM/CRMApi.ContactInfo.cs index ab9dc5e0b..1a9efcefd 100644 --- a/module/ASC.Api/ASC.Api.CRM/CRMApi.ContactInfo.cs +++ b/module/ASC.Api/ASC.Api.CRM/CRMApi.ContactInfo.cs @@ -38,15 +38,13 @@ namespace ASC.Api.CRM public partial class CRMApi { /// - /// Returns the list of all available contact categories + /// Returns a list of all the available contact categories of the specified information type. /// - /// - /// Contact information type - /// - /// Get all categories + /// Contact information type + /// Get contact categories by information type /// Contacts /// - /// List of all available contact categories + /// List of contact categories /// [Read(@"contact/data/{infoType}/category")] public IEnumerable GetContactInfoCategory(ContactInfoType infoType) @@ -55,11 +53,11 @@ namespace ASC.Api.CRM } /// - /// Returns the list of all available contact information types + /// Returns a list of all the available contact information types. /// - /// Get all contact info types + /// Get contact information types /// Contacts - /// + /// List of all the contact information types [Read(@"contact/data/infoType")] public IEnumerable GetContactInfoType() { @@ -67,13 +65,13 @@ namespace ASC.Api.CRM } /// - /// Returns the detailed information for the contact + /// Returns the detailed information on the contact with the ID specified in the request. /// /// Contact ID /// Get contact information /// Contacts /// - /// Contact information + /// Contact information /// [Read(@"contact/{contactid:[0-9]+}/data")] public IEnumerable GetContactInfo(int contactid) @@ -90,11 +88,11 @@ namespace ASC.Api.CRM } /// - /// Returns the detailed list of all information available for the contact with the ID specified in the request + /// Returns the detailed contact information with the ID specified in the request. /// /// Contact ID /// Contact information ID - /// Get contact info + /// Get contact information by ID /// Contacts /// Contact information /// @@ -114,19 +112,19 @@ namespace ASC.Api.CRM } /// - /// Adds the information with the parameters specified in the request to the contact with the selected ID + /// Adds the information with the parameters specified in the request to the contact with the selected ID. /// ///Contact ID ///Contact information type - ///Data - ///Contact importance: primary or not - ///Category - /// Add contact info + ///New data + ///Contact information importance: primary or not + ///Contact information category + ///Add contact information ///Contacts /// /// /// - /// Contact information + /// Contact information /// /// [Create(@"contact/{contactid:[0-9]+}/data")] @@ -176,16 +174,16 @@ namespace ASC.Api.CRM } /// - /// Adds the address information to the contact with the selected ID + /// Adds the address information to the contact with the ID specified in the request. /// /// Contact ID /// Address data - /// Add address info + /// Add contact address information /// Contacts /// /// /// - /// Contact information + /// Contact information /// /// /// @@ -199,6 +197,11 @@ namespace ASC.Api.CRM if (contact == null || !CRMSecurity.CanEdit(contact)) throw new ItemNotFoundException(); if (address == null) throw new ArgumentException("Value cannot be null", "address"); + if (address.City == null) address.City = ""; + if (address.Country == null) address.Country = ""; + if (address.State == null) address.State = ""; + if (address.Street == null) address.Street = ""; + if (address.Zip == null) address.Zip = ""; if (!Enum.IsDefined(typeof(AddressCategory), address.Category)) throw new ArgumentException("Value does not fall within the expected range.", "address.Category"); @@ -231,21 +234,21 @@ namespace ASC.Api.CRM } /// - /// Creates contact information (add new information to the old list) with the parameters specified in the request for the contact with the selected ID + /// Creates contact information (add new information to the existing list) with the parameters specified in the request for the contact with the selected ID. /// - ///Group contact info + ///Add new contact information /// Contact ID /// Contact information /// /// /// /// Contacts /// /// - /// Contact information + /// Contact information /// /// false [Create(@"contact/{contactid:[0-9]+}/batch")] @@ -281,19 +284,19 @@ namespace ASC.Api.CRM } /// - /// Updates the information with the parameters specified in the request for the contact with the selected ID + /// Updates the contact information with the parameters specified in the request. /// ///Contact information record ID ///Contact ID - ///Contact information type - ///Data - ///Contact importance: primary or not - ///Contact information category - ///Update contact info + ///New contact information type + ///New data + ///New contact information importance: primary or not + ///New contact information category + ///Update contact information ///Contacts /// /// - /// Contact information + /// Updated contact information /// [Update(@"contact/{contactid:[0-9]+}/data/{id:[0-9]+}")] public ContactInfoWrapper UpdateContactInfo(int id, int contactid, ContactInfoType? infoType, string data, bool? isPrimary, string category) @@ -343,17 +346,17 @@ namespace ASC.Api.CRM } /// - /// Updates the address information with the parameters specified in the request for the contact with the selected ID + /// Updates the contact address information with the parameter specified in the request. /// /// Contact information record ID /// Contact ID - /// Address data - /// Update address info + /// New address data + /// Update contact address information /// Contacts /// /// /// - /// Contact information + /// Contact information with the updated address /// [Update(@"contact/{contactid:[0-9]+}/addressdata/{id:[0-9]+}")] public ContactInfoWrapper UpdateContactInfoAddress(int id, int contactid, Address address) @@ -398,19 +401,19 @@ namespace ASC.Api.CRM } /// - /// Updates contact information (delete old information and add new list) with the parameters specified in the request for the contact with the selected ID + /// Updates contact information (delete the existing information and add a new list) with the parameters specified in the request for the contact with the selected ID. /// - ///Group contact info update + ///Update contact information ///Contact ID - ///Contact information + ///New contact information /// ///Contacts /// /// - /// Contact information + /// Updated contact information /// /// false [Update(@"contact/{contactid:[0-9]+}/batch")] @@ -447,14 +450,14 @@ namespace ASC.Api.CRM } /// - /// Returns the detailed information for the contact with the selected ID by the information type specified in the request + /// Returns the detailed contact information by the information type specified in the request. /// /// Contact ID /// Contact information type /// Get contact information by type /// Contacts /// - /// Contact information + /// Contact information /// [Read(@"contact/{contactid:[0-9]+}/data/{infoType}")] public IEnumerable GetContactInfo(int contactid, ContactInfoType infoType) @@ -469,16 +472,16 @@ namespace ASC.Api.CRM /// - /// Deletes the contact information for the contact with the ID specified in the request + /// Deletes the selected information for the contact with the ID specified in the request. /// /// Contact ID /// Contact information record ID - /// Delete contact info + /// Delete contact information /// Contacts /// /// /// - /// Contact information + /// Contact information /// [Delete(@"contact/{contactid:[0-9]+}/data/{id:[0-9]+}")] public ContactInfoWrapper DeleteContactInfo(int contactid, int id) diff --git a/module/ASC.Api/ASC.Api.CRM/CRMApi.Contacts.cs b/module/ASC.Api/ASC.Api.CRM/CRMApi.Contacts.cs index fc436a7ce..02e91193e 100644 --- a/module/ASC.Api/ASC.Api.CRM/CRMApi.Contacts.cs +++ b/module/ASC.Api/ASC.Api.CRM/CRMApi.Contacts.cs @@ -51,11 +51,11 @@ namespace ASC.Api.CRM public partial class CRMApi { /// - /// Returns the detailed information about the contact with the ID specified in the request + /// Returns the detailed information about a contact with the ID specified in the request. /// /// Contact ID /// Contact - /// Get contact by ID + /// Get a contact by ID /// Contacts /// /// @@ -77,15 +77,15 @@ namespace ASC.Api.CRM } /// - /// Returns the contact list for the project with the ID specified in the request + /// Returns contacts for the project with the ID specified in the request. /// /// - /// Get contacts by project ID + /// Get contacts by project ID /// /// Project ID /// Contacts /// - /// Contact list + /// List of contacts /// /// [Read(@"contact/project/{projectid:[0-9]+}")] @@ -98,15 +98,15 @@ namespace ASC.Api.CRM } /// - /// Links the selected contact to the project with the ID specified in the request + /// Links the selected contact to the project with the ID specified in the request. /// /// Contact ID /// Project ID /// Contacts - /// Link contact with project + /// Link a contact to the project /// /// - /// Contact Info + /// Contact information [Create(@"contact/{contactid:[0-9]+}/project/{projectid:[0-9]+}")] public ContactWrapper SetRelativeContactToProject(int contactid, int projectid) { @@ -132,16 +132,16 @@ namespace ASC.Api.CRM } /// - /// Links the selected contacts to the project with the ID specified in the request + /// Links the selected contacts to the project with the ID specified in the request. /// - /// Contact IDs array + /// Array of contact IDs /// Project ID /// Contacts - /// Link contact list with project + /// Link contacts to the project /// /// /// - /// Contact list + /// List of contacts /// [Create(@"contact/project/{projectid:[0-9]+}")] public IEnumerable SetRelativeContactListToProject(IEnumerable contactid, int projectid) @@ -173,14 +173,14 @@ namespace ASC.Api.CRM } /// - /// Removes the link with the selected project from the contact with the ID specified in the request + /// Removes a link to the selected project from the contact with the ID specified in the request. /// /// Contact ID /// Project ID /// Contacts - /// Remove contact from project + /// Remove a contact from the project /// - /// Contact info + /// Contact information /// [Delete(@"contact/{contactid:[0-9]+}/project/{projectid:[0-9]+}")] public ContactBaseWrapper RemoveRelativeContactToProject(int contactid, int projectid) @@ -206,15 +206,15 @@ namespace ASC.Api.CRM } /// - /// Adds the selected opportunity to the contact with the ID specified in the request. The same as AddMemberToDeal + /// Adds the selected opportunity to the contact with the ID specified in the request. /// /// Opportunity ID /// Contact ID - /// Add contact opportunity + /// Add a contact opportunity /// Contacts /// /// - /// Opportunity + /// Opportunity /// [Create(@"contact/{contactid:[0-9]+}/opportunity/{opportunityid:[0-9]+}")] public OpportunityWrapper AddDealToContact(int contactid, int opportunityid) @@ -236,15 +236,15 @@ namespace ASC.Api.CRM } /// - /// Deletes the selected opportunity from the contact with the ID specified in the request + /// Deletes the selected opportunity from the contact with the ID specified in the request. /// /// Opportunity ID /// Contact ID - /// Delete contact opportunity + /// Delete a contact opportunity /// Contacts /// /// - /// Opportunity + /// Opportunity /// [Delete(@"contact/{contactid:[0-9]+}/opportunity/{opportunityid:[0-9]+}")] public OpportunityWrapper DeleteDealFromContact(int contactid, int opportunityid) @@ -263,20 +263,20 @@ namespace ASC.Api.CRM } /// - /// Returns the list of all contacts in the CRM module matching the parameters specified in the request + /// Returns a list of all the contacts in the CRM module matching the parameters specified in the request. /// - /// Tag + /// Contact tag /// Contact stage ID (warmth) /// Contact type ID - /// + /// Contact list view /// Start date /// End date /// Responsible ID - /// Responsible ID - /// Get contact list + /// Contact privacy: private or not + /// Get filtered contacts /// Contacts /// - /// Contact list + /// List of contacts /// [Read(@"contact/filter")] public IEnumerable GetContacts( @@ -378,14 +378,14 @@ namespace ASC.Api.CRM } /// - /// Returns the list of the contacts for auto complete feature. + /// Searches for contacts by their emails. /// - /// String part of contact name, lastname or email. - /// Max result count - /// Search contact list + /// String part of contact name, lastname or email + /// Maximum result count + /// Search contacts by email /// Contacts /// - /// Contact list + /// List of contacts /// /// false [Read(@"contact/simple/byEmail")] @@ -399,20 +399,20 @@ namespace ASC.Api.CRM } /// - /// Returns the list of all contacts in the CRM module matching the parameters specified in the request + /// Returns a list of all the contacts with their tasks in the CRM module matching the parameters specified in the request. /// - /// Tag + /// Contact tag /// Contact stage ID (warmth) /// Contact type ID - /// + /// Contact list view /// Responsible ID - /// Responsible ID + /// Contact privacy: private or not /// Start date /// End date - /// Get contact list + /// Get filtered contacts with tasks /// Contacts /// - /// Contact list + /// List of contacts /// /// false [Read(@"contact/simple/filter")] @@ -513,15 +513,15 @@ namespace ASC.Api.CRM } /// - /// Get the group of contacts with the IDs specified in the request + /// Returns a group of contacts with the IDs specified in the request and their emails. /// - /// Contact ID list + /// List of contact IDs /// /// - /// Get contact group + /// Get contacts with emails /// Contacts /// - /// Contact list + /// List of contacts /// /// false [Read(@"contact/mail")] @@ -536,20 +536,20 @@ namespace ASC.Api.CRM } /// - /// Deletes the list of all contacts in the CRM module matching the parameters specified in the request + /// Deletes a list of all the contacts in the CRM module matching the parameters specified in the request. /// - /// Tag + /// Contact tags /// Contact stage ID (warmth) /// Contact type ID - /// + /// Contact list view /// Start date /// End date /// /// - /// Delete the list of all contacts + /// Delete contacts by parameters /// Contacts /// - /// Contact list + /// List of contacts /// [Delete(@"contact/filter")] public IEnumerable DeleteBatchContacts( @@ -585,14 +585,14 @@ namespace ASC.Api.CRM /// - /// Returns the list of all the persons linked to the company with the ID specified in the request + /// Returns a list of all the persons linked to the company with the ID specified in the request. /// /// Company ID /// - /// Get company linked persons list + /// Get persons linked to the company /// Contacts /// - /// Linked persons + /// Company linked persons /// [Read(@"contact/company/{companyid:[0-9]+}/person")] public IEnumerable GetPeopleFromCompany(int companyid) @@ -602,20 +602,20 @@ namespace ASC.Api.CRM var company = DaoFactory.ContactDao.GetByID(companyid); if (company == null || !CRMSecurity.CanAccessTo(company)) throw new ItemNotFoundException(); - return ToListContactWrapper(DaoFactory.ContactDao.GetMembers(companyid).Where(CRMSecurity.CanAccessTo).ToList()); + return ToListContactWrapper(DaoFactory.ContactDao.GetMembers(companyid)); } /// - /// Adds the selected person to the company with the ID specified in the request + /// Adds the selected person to the company with the ID specified in the request. /// /// Company ID /// Person ID - /// Add person to company + /// Add a person to the company /// Contacts /// /// /// - /// Person + /// Person /// [Create(@"contact/company/{companyid:[0-9]+}/person")] public PersonWrapper AddPeopleToCompany(int companyid, int personid) @@ -634,16 +634,16 @@ namespace ASC.Api.CRM } /// - /// Deletes the selected person from the company with the ID specified in the request + /// Deletes the selected person from the company with the ID specified in the request. /// /// Company ID /// Person ID - /// Delete person from company + /// Delete a person from the company /// Contacts /// /// /// - /// Person + /// Person /// [Delete(@"contact/company/{companyid:[0-9]+}/person")] public PersonWrapper DeletePeopleFromCompany(int companyid, int personid) @@ -662,18 +662,18 @@ namespace ASC.Api.CRM } /// - /// Creates the person with the parameters (first name, last name, description, etc.) specified in the request + /// Creates a person with the parameters (first name, last name, description, etc.) specified in the request. /// /// First name /// Last name - /// Post + /// Job title /// Company ID /// Person description text - /// Person privacy: 0 - not shared, 1 - shared for read/write, 2 - shared for read only - /// List of managers for the person - /// User field list + /// Person privacy: 0 - not shared, 1 - shared for reading/writing, 2 - shared for reading only + /// List of person managers + /// Custom field list /// Contact photo (upload using multipart/form-data) - /// Create person + /// Create a person /// Contacts /// Person /// @@ -738,15 +738,15 @@ namespace ASC.Api.CRM } /// - /// Changes the photo for the contact with the ID specified in the request + /// Changes a photo for the contact with the ID specified in the request. /// /// Contact ID /// Contact photo (upload using multipart/form-data) - /// Change contact photo + /// Change a contact photo /// Contacts /// /// - /// Path to contact photo + /// Path to the contact photo /// [Update(@"contact/{contactid:[0-9]+}/changephoto")] public string ChangeContactPhoto(int contactid, IEnumerable photo) @@ -775,15 +775,15 @@ namespace ASC.Api.CRM } /// - /// Changes the photo for the contact with the ID specified in the request + /// Changes a photo for the contact with the ID specified in the request by URL. /// /// Contact ID - /// contact photo url - /// Change contact photo + /// Contact photo URL + /// Change a contact photo by URL /// Contacts /// /// - /// Path to contact photo + /// Path to the contact photo /// [Update(@"contact/{contactid:[0-9]+}/changephotobyurl")] public string ChangeContactPhoto(int contactid, string photourl) @@ -797,17 +797,17 @@ namespace ASC.Api.CRM } /// - /// Merge two selected contacts + /// Merges two contacts specified in the request. /// - /// the first contact ID for merge - /// the second contact ID for merge + /// The first contact ID to merge + /// The second contact ID to merge /// Merge contacts /// Contacts /// /// /// /// - /// Contact + /// Contact /// [Update(@"contact/merge")] public ContactWrapper MergeContacts(int fromcontactid, int tocontactid) @@ -831,19 +831,19 @@ namespace ASC.Api.CRM } /// - /// Updates the selected person with the parameters (first name, last name, description, etc.) specified in the request + /// Updates the selected person with the parameters (first name, last name, description, etc.) specified in the request. /// /// Person ID - /// First name - /// Last name - /// Post - /// Company ID - /// Person description text - /// Person privacy: 0 - not shared, 1 - shared for read/write, 2 - shared for read only - /// List of persons managers - /// User field list - /// Contact photo (upload using multipart/form-data) - /// Update person + /// New first name + /// New last name + /// New job title + /// New company ID + /// New person description text + /// New person privacy: 0 - not shared, 1 - shared for reading/writing, 2 - shared for reading only + /// New list of person managers + /// New custom field list + /// New contact photo (upload using multipart/form-data) + /// Update a person /// Contacts /// Person /// @@ -908,16 +908,16 @@ namespace ASC.Api.CRM } /// - /// Creates the company with the parameters specified in the request + /// Creates a company with the parameters specified in the request. /// /// Company name /// Company description text - /// Linked person list - /// Company privacy: 0 - not shared, 1 - shared for read/write, 2 - shared for read only - /// List of managers for the company - /// User field list + /// List of persons linked to the company + /// Company privacy: 0 - not shared, 1 - shared for reading/writing, 2 - shared for reading only + /// List of company managers + /// Custom field list /// Contact photo (upload using multipart/form-data) - /// Create company + /// Create a company /// Contacts /// Company /// @@ -983,14 +983,14 @@ namespace ASC.Api.CRM } /// - /// Quickly creates the list of companies + /// Creates a list of companies with the names specified in the request. /// /// - /// Quick company list creation + /// Create companies /// /// Company name /// Contacts - /// Contact list + /// List of contacts /// [Create(@"contact/company/quick")] public IEnumerable CreateCompany(IEnumerable companyName) @@ -1027,20 +1027,20 @@ namespace ASC.Api.CRM } /// - /// Quickly creates the list of persons with the first and last names specified in the request + /// Creates a list of persons with the first and last names specified in the request. /// /// - /// Quick person list creation + /// Create persons /// /// Pairs: user first name, user last name /// /// /// /// Contacts - /// Contact list + /// List of contacts /// [Create(@"contact/person/quick")] public IEnumerable CreatePerson(IEnumerable> data) @@ -1080,19 +1080,19 @@ namespace ASC.Api.CRM } /// - /// Updates the selected company with the parameters specified in the request + /// Updates the selected company with the parameters specified in the request. /// /// Company ID - /// Company name - /// Company description text - /// Company privacy: 0 - not shared, 1 - shared for read/write, 2 - shared for read only - /// List of company managers - /// User field list - /// Update company + /// New company name + /// New company description text + /// New company privacy: 0 - not shared, 1 - shared for reading/writnig, 2 - shared for reading only + /// New list of company managers + /// New custom field list + /// Update a company /// Contacts /// /// - /// Company + /// Company /// [Update(@"contact/company/{companyid:[0-9]+}")] public CompanyWrapper UpdateCompany( @@ -1137,16 +1137,16 @@ namespace ASC.Api.CRM } /// - /// Updates the selected contact status + /// Updates a status of the contact with the ID specified in the request. /// /// Contact ID - /// Contact status ID - /// Update status in contact by id + /// New contact status ID + /// Update a contact status by ID /// Contacts /// /// /// - /// Company + /// Company /// [Update(@"contact/{contactid:[0-9]+}/status")] public ContactWrapper UpdateContactStatus(int contactid, int contactStatusid) @@ -1176,16 +1176,16 @@ namespace ASC.Api.CRM } /// - /// Updates status of the selected company and all its participants + /// Updates a status of the selected company and all its participants. /// /// Company ID - /// Contact status ID - /// Update company and participants status + /// New contact status ID + /// Update a status of a company and its participants /// Contacts /// /// /// - /// Company + /// Company /// [Update(@"contact/company/{companyid:[0-9]+}/status")] public ContactWrapper UpdateCompanyAndParticipantsStatus(int companyid, int contactStatusid) @@ -1226,16 +1226,16 @@ namespace ASC.Api.CRM } /// - /// Updates status of the selected person, related company and all its participants + /// Updates a status of the selected person, related company and all its participants. /// /// Person ID - /// Contact status ID - /// Update person, related company and participants status + /// New contact status ID + /// Update a status of a person, related company and its participants /// Contacts /// /// /// - /// Person + /// Person /// [Update(@"contact/person/{personid:[0-9]+}/status")] public ContactWrapper UpdatePersonAndItsCompanyStatus(int personid, int contactStatusid) @@ -1297,14 +1297,15 @@ namespace ASC.Api.CRM } /// - /// Get access rights to the contact with the ID specified in the request + /// Returns access rights to the contact with the ID specified in the request. /// + /// Contact ID /// Get contact access rights /// Contacts /// /// /// - /// User list + /// List of contacts [Read(@"contact/{contactid:[0-9]+}/access")] public IEnumerable GetContactAccessList(int contactid) { @@ -1323,18 +1324,18 @@ namespace ASC.Api.CRM } /// - /// Sets access rights for other users to the contact with the ID specified in the request + /// Sets access rights to the contact with the ID specified in the request. /// /// Contact ID /// Contact privacy: private or not /// List of managers - /// Set contact access rights + /// Set access rights to the contact /// Contacts /// /// /// /// - /// Contact + /// Contact /// [Update(@"contact/{contactid:[0-9]+}/access")] public ContactWrapper SetAccessToContact(int contactid, bool isShared, IEnumerable managerList) @@ -1378,17 +1379,17 @@ namespace ASC.Api.CRM } /// - /// Sets access rights for other users to the list of contacts with the IDs specified in the request + /// Sets access rights to the list of contacts with the IDs specified in the request. /// - /// Contact ID list + /// List of contact IDs /// Company privacy: shared or not /// List of managers - /// Set contact access rights + /// Set access rights to the contacts by IDs /// Contacts /// /// /// - /// Contact list + /// List of contacts /// [Update(@"contact/access")] public IEnumerable SetAccessToBatchContact(IEnumerable contactid, bool isShared, IEnumerable managerList) @@ -1407,22 +1408,22 @@ namespace ASC.Api.CRM } /// - /// Sets access rights for the selected user to the list of contacts with the parameters specified in the request + /// Sets access rights to the list of contacts with the parameters specified in the request. /// /// Contact privacy: private or not /// List of managers - /// Tag + /// Contact tags /// Contact stage ID (warmth) /// Contact type ID - /// + /// Contact list view /// Start date /// End date - /// Set contact access rights + /// Set access rights to the contacts by parameters /// Contacts /// /// /// - /// Contact list + /// List of contacts /// [Update(@"contact/filter/access")] public IEnumerable SetAccessToBatchContact( @@ -1468,15 +1469,15 @@ namespace ASC.Api.CRM } /// - /// Deletes the contact with the ID specified in the request from the portal + /// Deletes a contact with the ID specified in the request from the portal. /// - /// Delete contact + /// Delete a contact /// Contacts /// Contact ID /// /// /// - /// Contact + /// Contact /// [Delete(@"contact/{contactid:[0-9]+}")] public ContactWrapper DeleteContact(int contactid) @@ -1493,15 +1494,15 @@ namespace ASC.Api.CRM } /// - /// Deletes the group of contacts with the IDs specified in the request + /// Deletes a group of contacts with the IDs specified in the request. /// - /// Contact ID list + /// List of contact IDs /// /// - /// Delete contact group + /// Delete contacts by IDs /// Contacts /// - /// Contact list + /// List of contacts /// [Update(@"contact")] public IEnumerable DeleteBatchContacts(IEnumerable contactids) @@ -1515,15 +1516,16 @@ namespace ASC.Api.CRM } /// - /// Returns the list of 30 contacts in the CRM module with prefix + /// Returns a list of 30 contacts from the CRM module with a prefix specified in the request. /// - /// - /// searchType - /// - /// + /// Get contacts by prefix + /// Contact prefix + /// Contact search type + /// Contact entity type + /// Contact entity ID /// Contacts /// - /// Contact list + /// List of contacts /// /// false [Read(@"contact/byprefix")] @@ -1582,15 +1584,16 @@ namespace ASC.Api.CRM /// - /// Returns the list contacts in the CRM module with contact information + /// Returns a list of contacts from the CRM module with the contact information specified in the request. /// /// Contact information type - /// Data - /// Category + /// Contact data + /// Contact category /// Contact importance: primary or not + /// Get contacts by contact information /// Contacts /// - /// Contact list + /// List of contacts /// [Read(@"contact/bycontactinfo")] public IEnumerable GetContactsByContactInfo(ContactInfoType? infoType, String data, int? category, bool? isPrimary) @@ -1605,12 +1608,13 @@ namespace ASC.Api.CRM } /// - /// + /// Returns a certain number of tweets created by a user with the ID specified in the request. /// - /// - /// + /// Contact ID + /// Number of tweets + /// Get user tweets /// Contacts - /// + /// List of tweets [Read(@"contact/{contactid:[0-9]+}/tweets")] public List GetUserTweets(int contactid, int count) { @@ -1636,7 +1640,7 @@ namespace ASC.Api.CRM { try { - messages.AddRange(twitterProvider.GetUserTweets(twitterAccount.ID, twitterAccount.Data, MessageCount)); + messages.AddRange(twitterProvider.GetUserTweets(twitterAccount.Data, MessageCount)); } catch (ResourceNotFoundException ex) { @@ -1669,11 +1673,12 @@ namespace ASC.Api.CRM /// - /// + /// Returns a list of twitter profiles by the search text specified in the request. /// - /// + /// Search text + /// Get twitter profiles /// Contacts - /// + /// List of twitter profiles [Read(@"contact/twitterprofile")] public List FindTwitterProfiles(string searchText) { @@ -1698,13 +1703,14 @@ namespace ASC.Api.CRM } /// - /// + /// Deletes an avatar of the contact with the ID specified in the request. /// - /// - /// - /// + /// Contact ID + /// Contact type ID + /// Defines if an avatar is already uploaded or not + /// Delete a contact avatar /// Contacts - /// + /// Default photo [Delete(@"contact/{contactid:[0-9]+}/avatar")] public string DeleteContactAvatar(int contactId, string contactType, bool uploadOnly) { @@ -1733,11 +1739,12 @@ namespace ASC.Api.CRM } /// - /// + /// Returns a list of the social media images for the contact with the ID specified in the request. /// - /// + /// Contact ID + /// Get contact social media images by contact ID /// Contacts - /// + /// List of social media images [Read(@"contact/{contactid:[0-9]+}/socialmediaavatar")] public List GetContactSMImages(int contactId) { @@ -1745,11 +1752,12 @@ namespace ASC.Api.CRM } /// - /// + /// Returns a list of the contact social media images for the social networks specified in the request. /// - /// + /// List of contact social networks + /// Get contact social media images by networks /// Contacts - /// + /// List of social media images [Create(@"contact/socialmediaavatar")] public List GetContactSMImagesByNetworks(List socialNetworks) { @@ -1768,15 +1776,16 @@ namespace ASC.Api.CRM } /// - /// + /// Uploads an avatar of the contact with the ID specified in the request from the social network. /// - /// - /// - /// - /// - /// + /// Contact ID + /// Contact social network + /// User identity + /// Defines if an avatar is already uploaded or not + /// Temporary directory name + /// Upload an avatar from social network /// Contacts - /// + /// Avatar [Update(@"contact/{contactid:[0-9]+}/avatar")] public ContactPhotoManager.PhotoData UploadUserAvatarFromSocialNetwork(int contactId, SocialNetworks socialNetwork, string userIdentity, bool uploadOnly, string tmpDirName) { @@ -1801,6 +1810,17 @@ namespace ASC.Api.CRM return null; } + /// + /// Sends a mail through SMTP to contacts with the IDs specified in the request. + /// + /// File IDs + /// Contact IDs + /// Mail subject + /// Mail body + /// Defines if a mail will be stored in the history or not + /// Send a mail + /// Contacts + /// Mail /// false [Create(@"contact/mailsmtp/send")] public IProgressItem SendMailSMTPToContacts(List fileIDs, List contactIds, String subject, String body, bool storeInHistory) @@ -1813,6 +1833,14 @@ namespace ASC.Api.CRM return MailSender.Start(fileIDs, contactIds, subject, body, storeInHistory); } + /// + /// Returns a preview of a mail sent through SMTP to contact with the ID specified in the request. + /// + /// Mail template + /// Contact ID + /// Get a mail preview + /// Contacts + /// Mail preview /// false [Create(@"contact/mailsmtp/preview")] public string GetMailSMTPToContactsPreview(string template, int contactId) @@ -1824,6 +1852,13 @@ namespace ASC.Api.CRM return manager.Apply(template, contactId); } + + /// + /// Returns a status of a mail sent through SMTP to the current contact. + /// + /// Get a mail status + /// Contacts + /// Mail status /// false [Read(@"contact/mailsmtp/status")] public IProgressItem GetMailSMTPToContactsStatus() @@ -1831,6 +1866,12 @@ namespace ASC.Api.CRM return MailSender.GetStatus(); } + /// + /// Cancels the mail sending through SMTP to the current contacts. + /// + /// Cancel mail sending + /// Contacts + /// Mail status /// false [Update(@"contact/mailsmtp/cancel")] public IProgressItem CancelMailSMTPToContacts() @@ -1840,6 +1881,13 @@ namespace ASC.Api.CRM return progressItem; } + /// + /// Sets the creation date of a contact with the ID specified in the request. + /// + /// Contact ID + /// Contact creation date + /// Set the contact creation date + /// Contacts /// false [Update(@"contact/{contactid:[0-9]+}/creationdate")] public void SetContactCreationDate(int contactId, ApiDateTime creationDate) @@ -1853,6 +1901,12 @@ namespace ASC.Api.CRM dao.SetContactCreationDate(contactId, creationDate); } + /// + /// Sets the last modified date of a contact with the ID specified in the request. + /// + /// Contact ID + /// Contact last modified date + /// Set the contact last modified date /// false [Update(@"contact/{contactid:[0-9]+}/lastmodifeddate")] public void SetContactLastModifedDate(int contactId, ApiDateTime lastModifedDate) diff --git a/module/ASC.Api/ASC.Api.CRM/CRMApi.CurrencyRates.cs b/module/ASC.Api/ASC.Api.CRM/CRMApi.CurrencyRates.cs index 84fa2b3f4..30d53e902 100644 --- a/module/ASC.Api/ASC.Api.CRM/CRMApi.CurrencyRates.cs +++ b/module/ASC.Api/ASC.Api.CRM/CRMApi.CurrencyRates.cs @@ -33,12 +33,12 @@ namespace ASC.Api.CRM public const decimal MaxRateValue = (decimal)99999999.99; /// - /// Get the list of currency rates + /// Returns a list of all the currency rates. /// - /// Get currency rates list + /// Get all currency rates /// Common /// - /// List of currency rates + /// List of currency rates /// [Read(@"currency/rates")] public IEnumerable GetCurrencyRates() @@ -47,12 +47,13 @@ namespace ASC.Api.CRM } /// - /// Get currency rate by id + /// Returns a currency rate by ID. /// - /// Get currency rate + /// Currency rate ID + /// Get a currency rate by ID /// Common /// - /// Currency rate + /// Currency rate /// /// [Read(@"currency/rates/{id:[0-9]+}")] @@ -66,12 +67,14 @@ namespace ASC.Api.CRM } /// - /// Get currency rate by currencies + /// Returns a currency rate by currencies. /// - /// Get currency rate + /// Original currency + /// Converted currency + /// Get a currency rate by currencies /// Common /// - /// Currency rate + /// Currency rate /// /// [Read(@"currency/rates/{fromCurrency}/{toCurrency}")] @@ -86,11 +89,14 @@ namespace ASC.Api.CRM } /// - /// Create new currency rate object + /// Creates a new currency rate with the parameters specified in the request. /// - /// + /// Original currency + /// Converted currency + /// Currency rate + /// Create a currency rate /// Common - /// + /// Currency rate [Create(@"currency/rates")] public CurrencyRateWrapper CreateCurrencyRate(string fromCurrency, string toCurrency, decimal rate) { @@ -112,11 +118,15 @@ namespace ASC.Api.CRM } /// - /// Update currency rate object + /// Updates a currency rate with the parameters specified in the request. /// - /// + /// Currency ID + /// New original currency + /// New converted currency + /// New currency rate + /// Update a currency rate /// Common - /// + /// Updated currency rate [Update(@"currency/rates/{id:[0-9]+}")] public CurrencyRateWrapper UpdateCurrencyRate(int id, string fromCurrency, string toCurrency, decimal rate) { @@ -143,11 +153,13 @@ namespace ASC.Api.CRM } /// - /// Set currency rates + /// Sets rates to the currency specified in the request. /// - /// + /// Currency (abbreviation) + /// List of currency rates + /// Set currency rates /// Common - /// + /// Currency information [Create(@"currency/setrates")] public List SetCurrencyRates(String currency, List rates) { @@ -184,11 +196,12 @@ namespace ASC.Api.CRM } /// - /// Add currency rates + /// Adds currency rates specified in the request. /// - /// + /// List of currency rates + /// Add currency rates /// Common - /// + /// Currency information [Create(@"currency/addrates")] public List AddCurrencyRates(List rates) { @@ -226,11 +239,12 @@ namespace ASC.Api.CRM } /// - /// Delete currency rate object + /// Deletes a currency rate with the ID specified in the request. /// - /// + /// Currency rate ID + /// Delete a currency rate /// Common - /// + /// Currency rate [Delete(@"currency/rates/{id:[0-9]+}")] public CurrencyRateWrapper DeleteCurrencyRate(int id) { diff --git a/module/ASC.Api/ASC.Api.CRM/CRMApi.CustomFields.cs b/module/ASC.Api/ASC.Api.CRM/CRMApi.CustomFields.cs index 339568570..9142cb2e8 100644 --- a/module/ASC.Api/ASC.Api.CRM/CRMApi.CustomFields.cs +++ b/module/ASC.Api/ASC.Api.CRM/CRMApi.CustomFields.cs @@ -35,13 +35,13 @@ namespace ASC.Api.CRM public partial class CRMApi { /// - /// Returns the list of descriptions for all existing user fields + /// Returns a list of descriptions for all the existing custom fields. /// - /// Type - /// Get user field list - /// User fields + /// Entity type + /// Get custom fields + /// Custom fields /// - /// User field list + /// List of custom fields /// /// [Read(@"{entityType:(contact|person|company|opportunity|case)}/customfield/definitions")] @@ -51,13 +51,13 @@ namespace ASC.Api.CRM } /// - /// Returns the list of all user field values using the entity type and entity ID specified in the request + /// Returns a list of all the custom fields for the entity type and ID specified in the request. /// - /// Type - /// ID - /// Get user field values - /// User fields - /// + /// Entity type + /// Entity ID + /// Get entity custom fields + /// Custom fields + /// List of entity custom fields [Read(@"{entityType:(contact|person|company|opportunity|case)}/{entityid:[0-9]+}/customfield")] public IEnumerable GetCustomFieldForSubject(string entityType, int entityid) { @@ -65,16 +65,16 @@ namespace ASC.Api.CRM } /// - /// Sets the new user field value using the entity type, ID, field ID and value specified in the request + /// Sets the selected custom field to the entity with type and ID specified in the request. /// - /// Type - /// ID + /// Entity type + /// Entity ID /// Field ID - /// Field Value - /// Set user field value - /// User fields + /// Field value + /// Set an entity custom field + /// Custom fields /// - /// User field + /// Custom field /// [Create(@"{entityType:(contact|person|company|opportunity|case)}/{entityid:[0-9]+}/customfield/{fieldid:[0-9]+}")] public CustomFieldBaseWrapper SetEntityCustomFieldValue(string entityType, int entityid, int fieldid, string fieldValue) @@ -92,61 +92,61 @@ namespace ASC.Api.CRM } /// - /// Creates a new user field with the parameters (entity type, field title, type, etc.) specified in the request + /// Creates a new custom field with the parameters (entity type, field title, type, etc.) specified in the request. /// - /// Entity type + /// Entity type /// Field title /// - /// User field value + /// Custom field type /// /// Field position - /// Mask - /// Create user field - /// User fields + /// Mask + /// Create a custom field + /// Custom fields /// - /// User field + /// Custom field /// /// /// - /// Updates the selected user field with the parameters (entity type, field title, type, etc.) specified in the request + /// Updates the selected custom field with the parameters (entity type, field title, type, etc.) specified in the request. /// - /// User field id - /// Entity type - /// Field title + /// Custom field ID + /// New entity type + /// New field title /// - /// User field value + /// remark="Allowed values: 0 (TextField), 1 (TextArea), 2 (SelectBox), 3 (CheckBox), 4 (Heading) or 5 (Date)"> + /// New custom field type /// - /// Field position - /// Mask - /// Updates the selected user field - /// User fields + /// New field position + /// New mask + /// Update a custom field + /// Custom fields /// - /// User field + /// Updated custom field /// /// /// /// /// @@ -250,16 +250,16 @@ namespace ASC.Api.CRM } /// - /// Deletes the user field with the ID specified in the request + /// Deletes a custom field with the ID specified in the request. /// - /// Type + /// Entity type /// Field ID - /// Delete user field - /// User fields + /// Delete a custom field + /// Custom fields /// /// /// - /// User field + /// Custom field /// [Delete(@"{entityType:(contact|person|company|opportunity|case)}/customfield/{fieldid:[0-9]+}")] public CustomFieldWrapper DeleteCustomField(string entityType, int fieldid) @@ -282,13 +282,14 @@ namespace ASC.Api.CRM } /// - /// Updates user fields order + /// Updates the custom field order with the custom field list specified in the request. /// - /// User field ID list - /// Entity type - /// User fields + /// List of custom field IDs + /// Entity type + /// Update the custom field order + /// Custom fields /// - /// User fields + /// Custom fields in the new order /// /// /// diff --git a/module/ASC.Api/ASC.Api.CRM/CRMApi.Deals.cs b/module/ASC.Api/ASC.Api.CRM/CRMApi.Deals.cs index def977081..e57199b3c 100644 --- a/module/ASC.Api/ASC.Api.CRM/CRMApi.Deals.cs +++ b/module/ASC.Api/ASC.Api.CRM/CRMApi.Deals.cs @@ -37,13 +37,13 @@ namespace ASC.Api.CRM public partial class CRMApi { /// - /// Returns the detailed information about the opportunity with the ID specified in the request + /// Returns the detailed information about an opportunity with the ID specified in the request. /// /// Opportunity ID /// - /// Opportunity + /// Opportunity /// - /// Get opportunity by ID + /// Get an opportunity by ID /// Opportunities /// /// @@ -59,14 +59,14 @@ namespace ASC.Api.CRM } /// - /// Updates the selected opportunity to the stage with the ID specified in the request + /// Updates the selected opportunity to the stage with the ID specified in the request. /// /// Opportunity ID - /// Opportunity stage ID + /// New opportunity stage ID /// - /// Opportunity + /// Opportunity with the updated stage /// - /// Update opportunity stage + /// Update an opportunity stage by ID /// Opportunities /// /// @@ -92,17 +92,17 @@ namespace ASC.Api.CRM } /// - /// Sets access rights for the selected opportunity with the parameters specified in the request + /// Sets access rights to the selected opportunity with the parameters specified in the request. /// /// Opportunity ID /// Opportunity privacy: private or not - /// List of users with access - /// Set rights to opportunity + /// List of users with access rights + /// Set access rights to the opportunity /// Opportunities /// /// /// - /// Opportunity + /// Opportunity /// [Update(@"opportunity/{opportunityid:[0-9]+}/access")] public OpportunityWrapper SetAccessToDeal(int opportunityid, bool isPrivate, IEnumerable accessList) @@ -154,24 +154,24 @@ namespace ASC.Api.CRM } /// - /// Sets access rights for other users to the list of all opportunities matching the parameters specified in the request + /// Sets access rights to the list of all the opportunities matching the parameters specified in the request. /// - /// Opportunity responsible + /// Opportunity responsible ID /// Opportunity stage ID - /// Tags + /// Opportunity tags /// Contact ID /// Participation status: take into account opportunities where the contact is a participant or not /// Start date /// End date /// Opportunity stage type /// Opportunity privacy: private or not - /// List of users with access - /// Set opportunity access rights + /// List of users with access rights + /// Set access rights to the filtered opportunities /// Opportunities /// /// /// - /// Opportunity list + /// List of opportunities /// [Update(@"opportunity/filter/access")] public IEnumerable SetAccessToBatchDeal( @@ -213,17 +213,17 @@ namespace ASC.Api.CRM } /// - /// Sets access rights for other users to the list of opportunities with the IDs specified in the request + /// Sets access rights to the list of opportunities with the IDs specified in the request. /// - /// Opportunity ID list + /// List of opportunity IDs /// Opportunity privacy: private or not - /// List of users with access - /// Set opportunity access rights + /// List of users with access rights + /// Set access rights to the opportunities by IDs /// Opportunities /// /// /// - /// Opportunity list + /// List of opportunities /// [Update(@"opportunity/access")] public IEnumerable SetAccessToBatchDeal(IEnumerable opportunityid, bool isPrivate, IEnumerable accessList) @@ -251,15 +251,15 @@ namespace ASC.Api.CRM /// - /// Deletes the group of opportunities with the IDs specified in the request + /// Deletes a group of opportunities with the IDs specified in the request. /// - /// Opportunity ID list + /// List of opportunity IDs /// /// - /// Delete opportunity group + /// Delete opportunities by IDs /// Opportunities /// - /// Opportunity list + /// List of opportunities /// [Update(@"opportunity")] public IEnumerable DeleteBatchDeals(IEnumerable opportunityids) @@ -273,11 +273,11 @@ namespace ASC.Api.CRM } /// - /// Deletes the list of all opportunities matching the parameters specified in the request + /// Deletes a list of all the opportunities matching the parameters specified in the request. /// - /// Opportunity responsible + /// Opportunity responsible ID /// Opportunity stage ID - /// Tags + /// Opportunity tags /// Contact ID /// Participation status: take into account opportunities where the contact is a participant or not /// Start date @@ -285,10 +285,10 @@ namespace ASC.Api.CRM /// Opportunity stage type /// /// - /// Delete opportunity group + /// Delete filtered opportunities /// Opportunities /// - /// Opportunity list + /// List of opportunities /// [Delete(@"opportunity/filter")] public IEnumerable DeleteBatchDeals( @@ -319,20 +319,20 @@ namespace ASC.Api.CRM } /// - /// Returns the list of all opportunities matching the parameters specified in the request + /// Returns a list of all the opportunities matching the parameters specified in the request. /// - /// Opportunity responsible + /// Opportunity responsible ID /// Opportunity stage ID - /// Tags + /// Opportunity tags /// Contact ID /// Participation status: take into account opportunities where the contact is a participant or not /// Start date /// End date /// Opportunity stage type - /// Get opportunity list + /// Get filtered opportunities /// Opportunities /// - /// Opportunity list + /// List of opportunities /// [Read(@"opportunity/filter")] public IEnumerable GetDeals( @@ -432,15 +432,15 @@ namespace ASC.Api.CRM } /// - /// Deletes the opportunity with the ID specified in the request + /// Deletes an opportunity with the ID specified in the request. /// /// Opportunity ID - /// Delete opportunity + /// Delete an opportunity /// Opportunities /// /// /// - /// Opportunity + /// Opportunity /// [Delete(@"opportunity/{opportunityid:[0-9]+}")] public OpportunityWrapper DeleteDeal(int opportunityid) @@ -456,29 +456,29 @@ namespace ASC.Api.CRM } /// - /// Creates the opportunity with the parameters specified in the request + /// Creates an opportunity with the parameters specified in the request. /// - /// Create opportunity - /// Opportunity primary contact - /// Participants + /// Create an opportunity + /// Opportunity primary contact ID + /// Opportunity participants /// Opportunity title /// Opportunity description - /// Opportunity responsible - /// Bid - /// Amount of transaction - /// Currency (Abbreviation) - /// Period + /// Opportunity responsible ID + /// Bid type + /// Amount of transactions + /// Currency (abbreviation) + /// Amount per period /// Stage ID /// Opportunity success probability /// Actual opportunity closure date /// Expected opportunity closure date - /// User field list + /// Custom field list /// Opportunity privacy: private or not - /// List of users with access to the opportunity - /// Notify users in accessList about the opportunity + /// List of users with access rights to the opportunity + /// Notifies users from the access list about the opportunity /// Opportunities /// - /// Opportunity + /// Opportunity /// /// [Create(@"opportunity")] @@ -552,30 +552,30 @@ namespace ASC.Api.CRM } /// - /// Updates the selected opportunity with the parameters specified in the request + /// Updates the selected opportunity with the parameters specified in the request. /// - /// Update opportunity + /// Update an opportunity ///Opportunity ID - ///Opportunity primary contact - /// Participants - /// Opportunity title - /// Opportunity description - /// Opportunity responsible - /// Bid - /// Amount of transaction - /// Currency (Abbreviation) - /// Period - /// Stage ID - /// Opportunity success probability - /// Actual opportunity closure date - /// Expected opportunity closure date - /// User field list - /// Opportunity privacy: private or not - /// List of users with access to the opportunity - /// Notify users in accessList about the opportunity + ///New opportunity primary contact ID + /// New opportunity participants + /// New opportunity title + /// New opportunity description + /// New opportunity responsible ID + /// New bid type + /// New amount of transactions + /// New currency (abbreviation) + /// New amount per period + /// New stage ID + /// New opportunity success probability + /// New actual opportunity closure date + /// New expected opportunity closure date + /// New custom field list + /// New opportunity privacy: private or not + /// New list of users with access rights to the opportunity + /// Notifies users from the access list about the opportunity /// Opportunities /// - /// Opportunity + /// Updated opportunity /// /// [Update(@"opportunity/{opportunityid:[0-9]+}")] @@ -650,12 +650,12 @@ namespace ASC.Api.CRM } /// - /// Returns the list of all contacts associated with the opportunity with the ID specified in the request + /// Returns a list of all the contacts related to the opportunity with the ID specified in the request. /// /// Opportunity ID - /// Get all opportunity contacts + /// Get opportunity contacts /// Opportunities - /// Contact list + /// List of contacts /// /// [Read(@"opportunity/{opportunityid:[0-9]+}/contact")] @@ -676,15 +676,15 @@ namespace ASC.Api.CRM } /// - /// Adds the selected contact to the opportunity with the ID specified in the request + /// Adds the selected contact to the opportunity with the ID specified in the request. /// /// Opportunity ID /// Contact ID - /// Add opportunity contact + /// Add an opportunity contact /// Opportunities /// /// - /// Participant + /// Contact /// [Create(@"opportunity/{opportunityid:[0-9]+}/contact/{contactid:[0-9]+}")] public ContactWrapper AddMemberToDeal(int opportunityid, int contactid) @@ -708,16 +708,16 @@ namespace ASC.Api.CRM } /// - /// Deletes the selected contact from the opportunity with the ID specified in the request + /// Deletes the selected contact from the opportunity with the ID specified in the request. /// /// Opportunity ID /// Contact ID - /// Delete opportunity contact + /// Delete an opportunity contact /// Opportunities /// /// /// - /// Participant + /// Contact /// [Delete(@"opportunity/{opportunityid:[0-9]+}/contact/{contactid:[0-9]+}")] public ContactWrapper DeleteMemberFromDeal(int opportunityid, int contactid) @@ -741,14 +741,14 @@ namespace ASC.Api.CRM } /// - /// Returns the list of 30 opportunities in the CRM module with prefix + /// Returns a list of 30 opportunities in the CRM module with a prefix specified in the request. /// - /// - /// - /// + /// Opportunity prefix + /// Contact ID + /// Internal search or not /// Opportunities /// - /// Opportunities list + /// List of opportunities /// /// false [Read(@"opportunity/byprefix")] @@ -783,13 +783,13 @@ namespace ASC.Api.CRM } /// - /// Returns the list of all contact opportunities + /// Returns a list of all the opportunities for the contact with the ID specified in the request. /// /// Contact ID - /// Get opportunity list + /// Get contact opportunities /// Opportunities /// - /// Opportunity list + /// List of opportunities /// [Read(@"opportunity/bycontact/{contactid:[0-9]+}")] public IEnumerable GetDeals(int contactid) @@ -798,6 +798,13 @@ namespace ASC.Api.CRM return ToListOpportunityWrapper(deals); } + /// + /// Sets the opportunity creation date specified in the request. + /// + /// Opportunity ID + /// Opportunity creation date + /// Set the opportunity creation date + /// Opportunities /// false [Update(@"opportunity/{opportunityid:[0-9]+}/creationdate")] public void SetDealCreationDate(int opportunityid, ApiDateTime creationDate) @@ -811,6 +818,13 @@ namespace ASC.Api.CRM dao.SetDealCreationDate(opportunityid, creationDate); } + /// + /// Sets the opportunity last modified date specified in the request. + /// + /// Opportunity ID + /// Opportunity last modified date + /// Set the opportunity last modified date + /// Opportunities /// false [Update(@"opportunity/{opportunityid:[0-9]+}/lastmodifeddate")] public void SetDealLastModifedDate(int opportunityid, ApiDateTime lastModifedDate) diff --git a/module/ASC.Api/ASC.Api.CRM/CRMApi.Invoices.cs b/module/ASC.Api/ASC.Api.CRM/CRMApi.Invoices.cs index ebe1b251c..82bd70b83 100644 --- a/module/ASC.Api/ASC.Api.CRM/CRMApi.Invoices.cs +++ b/module/ASC.Api/ASC.Api.CRM/CRMApi.Invoices.cs @@ -38,10 +38,10 @@ namespace ASC.Api.CRM public partial class CRMApi { /// - /// Returns the detailed information about the invoice with the ID specified in the request + /// Returns the detailed information about an invoice with the ID specified in the request. /// /// Invoice ID - /// Get invoice by ID + /// Get an invoice by ID /// Invoices /// Invoice [Read(@"invoice/{invoiceid:[0-9]+}")] @@ -61,9 +61,9 @@ namespace ASC.Api.CRM } /// - /// Returns the detailed information about the invoice sample + /// Returns the detailed information about an invoice sample. /// - /// Get invoice sample + /// Get an invoice sample /// Invoices /// Invoice [Read(@"invoice/sample")] @@ -85,12 +85,12 @@ namespace ASC.Api.CRM } /// - /// Returns the json data of the invoice with the ID specified in the request + /// Returns the JSON data of an invoice with the ID specified in the request. /// /// Invoice ID - /// Get invoice json data + /// Get the invoice JSON data /// Invoices - /// Json Data + /// Invoice JSON data [Read(@"invoice/jsondata/{invoiceid:[0-9]+}")] public string GetInvoiceJsonData(int invoiceid) { @@ -106,19 +106,19 @@ namespace ASC.Api.CRM } /// - /// Returns the list of invoices matching the creteria specified in the request + /// Returns a list of invoices matching the parameters specified in the request. /// /// Invoice status - /// Invoice issue date from - /// Invoice issue date to - /// Invoice due date from - /// Invoice due date to + /// Invoice start issue date + /// Invoice end issue date + /// Invoice start due date + /// Invoice end due date /// Invoice entity type /// Invoice entity ID - /// Invoice currency - /// Get invoice list + /// Invoice currency (abbreviation) + /// Get invoices /// Invoices - /// Invoice list + /// List of invoices [Read(@"invoice/filter")] public IEnumerable GetInvoices( InvoiceStatus? status, @@ -216,13 +216,13 @@ namespace ASC.Api.CRM } /// - /// Returns the list of all invoices associated with the entity with the ID and type specified in the request + /// Returns a list of all the invoices related to the entity with the ID and type specified in the request. /// /// Invoice entity type /// Invoice entity ID /// Get entity invoices /// Invoices - /// Invoice list + /// List of invoices [Read(@"{entityType:(contact|person|company|opportunity)}/invoicelist/{entityid:[0-9]+}")] public IEnumerable GetEntityInvoices(String entityType, int entityid) { @@ -232,13 +232,13 @@ namespace ASC.Api.CRM } /// - /// Updates the status of invoices with the IDs specified in the request + /// Updates a status of invoices with the IDs specified in the request. /// - /// Invoice ID list - /// Status - /// Update invoice group status + /// List of invoice IDs + /// New invoice status + /// Update an invoice group status /// Invoices - /// KeyValuePair of Invoices and InvoiceItems + /// Invoice information [Update(@"invoice/status/{status:[\w\d-]+}")] public KeyValuePair, IEnumerable> UpdateInvoiceBatchStatus( int[] invoiceids, @@ -342,10 +342,10 @@ namespace ASC.Api.CRM } /// - /// Delete the invoice with the ID specified in the request + /// Deletes an invoice with the ID specified in the request. /// /// Invoice ID - /// Delete invoice + /// Delete an invoice /// Invoices /// Invoice [Delete(@"invoice/{invoiceid:[0-9]+}")] @@ -361,12 +361,12 @@ namespace ASC.Api.CRM } /// - /// Deletes the group of invoices with the IDs specified in the request + /// Deletes a group of invoices with the IDs specified in the request. /// - /// Invoice ID list - /// Delete invoice group + /// List of invoice IDs + /// Delete an invoice group /// Invoices - /// Invoice list + /// List of invoices [Delete(@"invoice")] public IEnumerable DeleteBatchInvoices(IEnumerable invoiceids) { @@ -379,7 +379,7 @@ namespace ASC.Api.CRM } /// - /// Creates the invoice with the parameters (contactId, consigneeId, etc.) specified in the request + /// Creates an invoice with the parameters (contact ID, consignee ID, etc.) specified in the request. /// /// Invoice number /// Invoice issue date @@ -391,13 +391,13 @@ namespace ASC.Api.CRM /// Invoice delivery address ID /// Invoice due date /// Invoice language - /// Invoice currency + /// Invoice currency (abbreviation) /// Invoice exchange rate /// Invoice purchase order number /// Invoice terms /// Invoice description - /// Invoice lines list - /// Create invoice + /// List of invoice lines + /// Create an invoice /// Invoices /// Invoice /// @@ -426,8 +426,7 @@ namespace ASC.Api.CRM /// }] /// } /// - /// where invoiceItemID, invoiceTax1ID, invoiceTax2ID - ids of the real existing invoice item and invoice taxes, - /// contactId - id of the existing contact + /// where invoiceItemID, invoiceTax1ID, invoiceTax2ID - IDs of the real existing invoice item and invoice taxes, contactId - ID of the existing contact. /// /// ]]> /// @@ -546,27 +545,27 @@ namespace ASC.Api.CRM } /// - /// Updates the selected invoice with the parameters (contactId, consigneeId, etc.) specified in the request + /// Updates the selected invoice with the parameters (contact ID, consignee ID, etc.) specified in the request. /// /// Invoice ID - /// Invoice issue date - /// Invoice template type - /// Invoice contact ID - /// Invoice consignee ID - /// Invoice entity ID - /// Invoice billing address ID - /// Invoice delivery address ID - /// Invoice due date - /// Invoice language - /// Invoice currency - /// Invoice exchange rate - /// Invoice purchase order number - /// Invoice terms - /// Invoice description - /// Invoice lines list - /// Update invoice + /// New invoice issue date + /// New invoice template type + /// New invoice contact ID + /// New invoice consignee ID + /// New invoice entity ID + /// New invoice billing address ID + /// New invoice delivery address ID + /// New invoice due date + /// New invoice language + /// New invoice currency (abbreviation) + /// New invoice exchange rate + /// New invoice purchase order number + /// New invoice terms + /// New invoice description + /// New list of invoice lines + /// Update an invoice /// Invoices - /// Invoice + /// Updated invoice /// /// /// @@ -672,12 +670,12 @@ namespace ASC.Api.CRM } /// - /// Returns the pdf file associated with the invoice with the ID specified in the request + /// Returns the pdf file related to an invoice with the ID specified in the request. /// /// Invoice ID - /// Get invoice pdf file + /// Get the invoice pdf file /// Invoices - /// File + /// Pdf file [Read(@"invoice/{invoiceid:[0-9]+}/pdf")] public FileWrapper GetInvoicePdfExistOrCreate(int invoiceid) { @@ -695,14 +693,14 @@ namespace ASC.Api.CRM } /// - /// Returns information about the generation of the pdf file of the invoice + /// Returns information about the generation of the invoice pdf file. /// /// Invoice ID - /// Storage Url + /// Storage URL /// Revision ID - /// Check invoice pdf file + /// Get invoice converter data /// Invoices - /// ConverterData + /// Converter data [Create(@"invoice/converter/data")] public ConverterData GetInvoiceConverterData(int invoiceId, string storageUrl, string revisionId) { @@ -754,12 +752,12 @@ namespace ASC.Api.CRM } /// - /// Returns the existence of the invoice with the Number specified in the request + /// Returns the existence of an invoice with the number specified in the request. /// /// Invoice number /// Check invoice existence by number /// Invoices - /// IsExist + /// Invoice existence [Read(@"invoice/bynumber/exist")] public Boolean GetInvoiceByNumberExistence(string number) { @@ -768,10 +766,10 @@ namespace ASC.Api.CRM } /// - /// Returns the detailed information about the invoice with the Number specified in the request + /// Returns the detailed information about an invoice with the number specified in the request. /// /// Invoice number - /// Get invoice by number + /// Get an invoice by number /// Invoices /// Invoice [Read(@"invoice/bynumber")] @@ -790,13 +788,13 @@ namespace ASC.Api.CRM } /// - /// Returns the list of invoice items matching the creteria specified in the request + /// Returns a list of invoice items matching the parameters specified in the request. /// - /// Status - /// InventoryStock - /// Get invoice item list + /// Invoice status + /// Inventory stock + /// Get filtered invoices /// Invoices - /// InvoiceItem list + /// List of invoice items [Read(@"invoiceitem/filter")] public IEnumerable GetInvoiceItems(int status, bool? inventoryStock) { @@ -869,12 +867,12 @@ namespace ASC.Api.CRM } /// - /// Returns the detailed information about the invoice item with the ID specified in the request + /// Returns the detailed information about an invoice item with the ID specified in the request. /// - /// Invoice Item ID - /// Get invoice item by ID + /// Invoice item ID + /// Get an invoice item by ID /// Invoices - /// Invoice Item + /// Invoice item [Read(@"invoiceitem/{invoiceitemid:[0-9]+}")] public InvoiceItemWrapper GetInvoiceItemByID(int invoiceitemid) { @@ -887,20 +885,20 @@ namespace ASC.Api.CRM } /// - /// Creates the invoice line with the parameters (invoiceId, invoiceItemId, etc.) specified in the request + /// Creates an invoice line with the parameters (invoice ID, invoice item ID, etc.) specified in the request. /// /// Invoice ID /// Invoice item ID /// First invoice tax ID /// Second invoice tax ID - /// Sort Order + /// Sort order /// Description /// Quantity /// Price /// Discount - /// Create invoice line + /// Create an invoice line /// Invoices - /// InvoiceLine + /// Invoice line [Create(@"invoiceline")] public InvoiceLineWrapper CreateInvoiceLine( int invoiceId, @@ -944,21 +942,21 @@ namespace ASC.Api.CRM } /// - /// Updates the selected invoice line with the parameters (invoiceId, invoiceItemId, etc.) specified in the request + /// Updates the selected invoice line with the parameters (invoice ID, invoice item ID, etc.) specified in the request. /// - /// Line ID + /// Invoice line ID /// Invoice ID /// Invoice item ID - /// First invoice tax ID - /// Second invoice tax ID - /// Sort Order - /// Description - /// Quantity - /// Price - /// Discount - /// Update invoice line + /// New first invoice tax ID + /// New second invoice tax ID + /// New sort order + /// New description + /// New quantity + /// New price + /// New discount + /// Update an invoice line /// Invoices - /// InvoiceLine + /// Updated invoice line [Update(@"invoiceline/{id:[0-9]+}")] public InvoiceLineWrapper UpdateInvoiceLine( int id, @@ -1005,12 +1003,12 @@ namespace ASC.Api.CRM } /// - /// Deletes the invoice line with the ID specified in the request + /// Deletes an invoice line with the ID specified in the request. /// - /// Line ID - /// Delete invoice line + /// Invoice line ID + /// Delete an invoice line /// Invoices - /// Line ID + /// Invoice line ID [Delete(@"invoiceline/{id:[0-9]+}")] public int DeleteInvoiceLine(int id) { @@ -1058,19 +1056,19 @@ namespace ASC.Api.CRM } /// - /// Creates the invoice item with the parameters (title, description, price, etc.) specified in the request + /// Creates an invoice item with the parameters (title, description, price, etc.) specified in the request. /// - /// Item title - /// Item description - /// Item price - /// Item stock keeping unit - /// Item stock quantity - /// Track inventory + /// Invoice item title + /// Invoice item description + /// Invoice item price + /// Invoice item stock keeping unit + /// Invoice item stock quantity + /// Invoice item track inventory /// Item first invoice tax ID /// Item second invoice tax ID - /// Create invoice item + /// Create an invoice item /// Invoices - /// InvoiceItem + /// Invoice item [Create(@"invoiceitem")] public InvoiceItemWrapper CreateInvoiceItem( string title, @@ -1108,20 +1106,20 @@ namespace ASC.Api.CRM } /// - /// Updates the selected invoice item with the parameters (title, description, price, etc.) specified in the request + /// Updates the selected invoice item with the parameters (title, description, price, etc.) specified in the request. /// - /// Item ID - /// Item title - /// Item description - /// Item price - /// Item stock keeping unit - /// Item stock quantity - /// Track inventory - /// Item first invoice tax ID - /// Item second invoice tax ID - /// Update invoice item + /// Invoice item ID + /// New invoice item title + /// New invoice item description + /// New invoice item price + /// New invoice item stock keeping unit + /// New invoice item stock quantity + /// New invoice item track inventory + /// New item first invoice tax ID + /// New item second invoice tax ID + /// Update an invoice item /// Invoices - /// InvoiceItem + /// Updated invoice item [Update(@"invoiceitem/{id:[0-9]+}")] public InvoiceItemWrapper UpdateInvoiceItem(int id, string title, @@ -1162,12 +1160,12 @@ namespace ASC.Api.CRM } /// - /// Deletes the invoice item with the ID specified in the request + /// Deletes an invoice item with the ID specified in the request. /// - /// Item ID - /// Delete invoice item + /// Invoice item ID + /// Delete an invoice item /// Invoices - /// InvoiceItem + /// Invoice item [Delete(@"invoiceitem/{id:[0-9]+}")] public InvoiceItemWrapper DeleteInvoiceItem(int id) { @@ -1186,12 +1184,12 @@ namespace ASC.Api.CRM } /// - /// Deletes the group of invoice items with the IDs specified in the request + /// Deletes a group of invoice items with the IDs specified in the request. /// - /// Item ID list - /// Delete Invoice item group + /// List of invoice item IDs + /// Delete invoice items /// Invoices - /// InvoiceItem list + /// List of invoice items [Delete(@"invoiceitem")] public IEnumerable DeleteBatchItems(IEnumerable ids) { @@ -1210,11 +1208,11 @@ namespace ASC.Api.CRM } /// - /// Returns the list of invoice taxes + /// Returns a list of invoice taxes. /// - /// Get invoice taxes list + /// Get invoice taxes /// Invoices - /// InvoiceTax list + /// List of invoice taxes [Read(@"invoice/tax")] public IEnumerable GetInvoiceTaxes() { @@ -1222,14 +1220,14 @@ namespace ASC.Api.CRM } /// - /// Creates the invoice tax with the parameters (name, description, rate) specified in the request + /// Creates an invoice tax with the parameters (name, description, rate) specified in the request. /// /// Tax name /// Tax description /// Tax rate - /// Create invoice tax + /// Create an invoice tax /// Invoices - /// InvoiceTax + /// Invoice tax [Create(@"invoice/tax")] public InvoiceTaxWrapper CreateInvoiceTax( string name, @@ -1258,15 +1256,15 @@ namespace ASC.Api.CRM } /// - /// Updates the selected invoice tax with the parameters (name, description, rate) specified in the request + /// Updates the selected invoice tax with the parameters (name, description, rate) specified in the request. /// /// Tax ID - /// Tax name - /// Tax description - /// Tax rate - /// Update invoice tax + /// New tax name + /// New tax description + /// New tax rate + /// Update an invoice tax /// Invoices - /// InvoiceTax + /// Updated invoice tax [Update(@"invoice/tax/{id:[0-9]+}")] public InvoiceTaxWrapper UpdateInvoiceTax( int id, @@ -1298,12 +1296,12 @@ namespace ASC.Api.CRM } /// - /// Delete the invoice tax with the ID specified in the request + /// Deletes an invoice tax with the ID specified in the request. /// /// Tax ID - /// Delete invoice tax + /// Delete an invoice tax /// Invoices - /// InvoiceTax + /// Invoice tax [Delete(@"invoice/tax/{id:[0-9]+}")] public InvoiceTaxWrapper DeleteInvoiceTax(int id) { @@ -1322,11 +1320,11 @@ namespace ASC.Api.CRM } /// - /// Get default invoice settings + /// Returns the default invoice settings. /// - /// Get default invoice settings + /// Get the default invoice settings /// Invoices - /// InvoiceSetting + /// Default invoice settings [Read(@"invoice/settings")] public InvoiceSetting GetSettings() { @@ -1334,14 +1332,14 @@ namespace ASC.Api.CRM } /// - /// Save default invoice number + /// Saves the default settings for invoice number specified in the request. /// - /// Is autogenerated - /// Prefix - /// Number - /// Save default invoice number + /// Defines if the default invoice number is autogenerated or not + /// Invoice prefix + /// Invoice number + /// Save the default settings for invoice number /// Invoices - /// InvoiceSetting + /// Invoice settings [Update(@"invoice/settings/name")] public InvoiceSetting SaveNumberSettings(bool autogenerated, string prefix, string number) { @@ -1366,12 +1364,12 @@ namespace ASC.Api.CRM } /// - /// Save default invoice terms + /// Saves the default settings for invoice terms specified in the request. /// - /// Terms - /// Save default invoice terms + /// Invoice terms + /// Save the default setting for invoice terms /// Invoices - /// InvoiceSetting + /// Invoice settings [Update(@"invoice/settings/terms")] public InvoiceSetting SaveTermsSettings(string terms) { @@ -1387,6 +1385,13 @@ namespace ASC.Api.CRM return result; } + /// + /// Sets the creation date to the invoice with the ID specified in the request. + /// + /// Invoice ID + /// Invoice creation date + /// Set the invoice creation date + /// Invoices /// false [Update(@"invoice/{invoiceid:[0-9]+}/creationdate")] public void SetInvoiceCreationDate(int invoiceid, ApiDateTime creationDate) @@ -1400,6 +1405,13 @@ namespace ASC.Api.CRM dao.SetInvoiceCreationDate(invoiceid, creationDate); } + /// + /// Sets the last modified date to the invoice with the ID specified in the request. + /// + /// Invoice ID + /// Invoice last modified date + /// Set the invoice last modified date + /// Invoices /// false [Update(@"invoice/{invoiceid:[0-9]+}/lastmodifeddate")] public void SetInvoiceLastModifedDate(int invoiceid, ApiDateTime lastModifedDate) diff --git a/module/ASC.Api/ASC.Api.CRM/CRMApi.ListItem.cs b/module/ASC.Api/ASC.Api.CRM/CRMApi.ListItem.cs index 025ed0027..951332aa8 100644 --- a/module/ASC.Api/ASC.Api.CRM/CRMApi.ListItem.cs +++ b/module/ASC.Api/ASC.Api.CRM/CRMApi.ListItem.cs @@ -32,18 +32,18 @@ namespace ASC.Api.CRM public partial class CRMApi { /// - /// Creates an opportunity stage with the parameters (title, description, success probability, etc.) specified in the request + /// Creates an opportunity stage with the parameters (title, description, success probability, etc.) specified in the request. /// - /// Title - /// Description - /// Color - /// Success probability - /// Stage type - /// Create opportunity stage + /// Stage title + /// Stage description + /// Stage color + /// Stage success probability + /// Stage type + /// Create an opportunity stage /// Opportunities /// /// - /// Opportunity stage + /// Opportunity stage /// [Create(@"opportunity/stage")] public DealMilestoneWrapper CreateDealMilestone( @@ -75,20 +75,20 @@ namespace ASC.Api.CRM } /// - /// Updates the selected opportunity stage with the parameters (title, description, success probability, etc.) specified in the request + /// Updates the selected opportunity stage with the parameters (title, description, success probability, etc.) specified in the request. /// /// Opportunity stage ID - /// Title - /// Description - /// Color - /// Success probability - /// Stage type - /// Update opportunity stage + /// New stage title + /// New stage description + /// New stage color + /// New stage success probability + /// New stage type + /// Update an opportunity stage by parameters /// Opportunities /// /// /// - /// Opportunity stage + /// Updated opportunity stage /// [Update(@"opportunity/stage/{id:[0-9]+}")] public DealMilestoneWrapper UpdateDealMilestone( @@ -125,16 +125,16 @@ namespace ASC.Api.CRM } /// - /// Updates the selected opportunity stage with the color specified in the request + /// Updates the selected opportunity stage with the color specified in the request. /// /// Opportunity stage ID - /// Color - /// Update opportunity stage color + /// New stage color + /// Update an opportunity stage color /// Opportunities /// /// /// - /// Opportunity stage + /// Opportunity stage with the updated color /// [Update(@"opportunity/stage/{id:[0-9]+}/color")] public DealMilestoneWrapper UpdateDealMilestoneColor(int id, string color) @@ -155,15 +155,15 @@ namespace ASC.Api.CRM } /// - /// Updates the available opportunity stages order with the list specified in the request + /// Updates the available order of opportunity stages with a list specified in the request. /// /// - /// Update opportunity stages order + /// Update the order of opportunity stages /// - /// Opportunity stage ID list + /// List of opportunity stage IDs /// Opportunities /// - /// Opportunity stages + /// Opportunity stages in the new order /// /// /// @@ -186,15 +186,15 @@ namespace ASC.Api.CRM } /// - /// Deletes the opportunity stage with the ID specified in the request + /// Deletes an opportunity stage with the ID specified in the request. /// - /// Delete opportunity stage + /// Delete an opportunity stage /// Opportunities /// Opportunity stage ID /// /// /// - /// Opportunity stage + /// Opportunity stage /// [Delete(@"opportunity/stage/{id:[0-9]+}")] public DealMilestoneWrapper DeleteDealMilestone(int id) @@ -215,13 +215,13 @@ namespace ASC.Api.CRM } /// - /// Creates a new history category with the parameters (title, description, etc.) specified in the request + /// Creates a new history category with the parameters (title, description, etc.) specified in the request. /// - ///Title - ///Description - ///Order - ///Image name - ///Create history category + ///History category title + ///History category description + ///History category order + ///Image name of the history category + ///Create a history category /// History ///History category /// @@ -247,16 +247,16 @@ namespace ASC.Api.CRM } /// - /// Updates the selected history category with the parameters (title, description, etc.) specified in the request + /// Updates the selected history category with the parameters (title, description, etc.) specified in the request. /// ///History category ID - ///Title - ///Description - ///Order - ///Image name - ///Update history category + ///New history category title + ///New history category description + ///New history category order + ///New image name of the history category + ///Update a history category ///History - ///History category + ///Updated history category /// /// [Update(@"history/category/{id:[0-9]+}")] @@ -285,16 +285,16 @@ namespace ASC.Api.CRM } /// - /// Updates the icon of the selected history category + /// Updates an icon of a history category with the ID specified in the request. /// /// History category ID - /// icon name - /// Update history category icon + /// New icon name of the history category + /// Update a history category icon /// History /// /// /// - /// History category + /// History category with the updated icon /// [Update(@"history/category/{id:[0-9]+}/icon")] public HistoryCategoryWrapper UpdateHistoryCategoryIcon(int id, string imageName) @@ -315,15 +315,15 @@ namespace ASC.Api.CRM } /// - /// Updates the history categories order with the list specified in the request + /// Updates the order of the history categories with a list specified in the request. /// /// - /// Update history categories order + /// Update the order of history categories /// - /// History category title list + /// List of history category titles /// History /// - /// History categories + /// History categories in the new order /// /// /// @@ -344,9 +344,9 @@ namespace ASC.Api.CRM } /// - /// Deletes the selected history category with the ID specified in the request + /// Deletes a history category with the ID specified in the request. /// - /// Delete history category + /// Delete a history category /// History /// History category ID /// @@ -378,19 +378,16 @@ namespace ASC.Api.CRM } /// - /// Creates a new task category with the parameters (title, description, etc.) specified in the request + /// Creates a new task category with the parameters (title, description, etc.) specified in the request. /// - ///Title - ///Description - ///Order - ///Image name - ///Create task category + ///Task category title + ///Task category description + ///Task category order + ///Image name of task category + ///Create a task category ///Tasks ///Task category /// - /// - /// Task category - /// [Create(@"task/category")] public TaskCategoryWrapper CreateTaskCategory(string title, string description, string imageName, int sortOrder) { @@ -411,22 +408,19 @@ namespace ASC.Api.CRM } /// - /// Updates the selected task category with the parameters (title, description, etc.) specified in the request + /// Updates the selected task category with the parameters (title, description, etc.) specified in the request. /// ///Task category ID - ///Title - ///Description - ///Order - ///Image name - ///Update task category + ///New task category title + ///New task category description + ///New task category order + ///New image name of task category + ///Update a task category ///Tasks - ///Task category + ///Updated task category /// /// /// - /// - /// Task category - /// [Update(@"task/category/{id:[0-9]+}")] public TaskCategoryWrapper UpdateTaskCategory(int id, string title, string description, string imageName, int sortOrder) { @@ -453,16 +447,16 @@ namespace ASC.Api.CRM } /// - /// Updates the icon of the task category with the ID specified in the request + /// Updates an icon of the task category with the ID specified in the request. /// /// Task category ID - /// icon name - /// Update task category icon + /// New icon name of task category + /// Update a task category icon /// Tasks /// /// /// - /// Task category + /// Task category with the updated icon /// [Update(@"task/category/{id:[0-9]+}/icon")] public TaskCategoryWrapper UpdateTaskCategoryIcon(int id, string imageName) @@ -483,15 +477,15 @@ namespace ASC.Api.CRM } /// - /// Updates the task categories order with the list specified in the request + /// Updates the order of the task categories with a list specified in the request. /// /// - /// Update task categories order + /// Update the order of task categories /// - /// Task category title list + /// List of task category titles /// Tasks /// - /// Task categories + /// Task categories in the new order /// /// /// @@ -512,12 +506,13 @@ namespace ASC.Api.CRM } /// - /// Deletes the task category with the ID specified in the request + /// Deletes a task category with the ID specified in the request. /// - /// Delete task category + /// Delete a task category /// Tasks /// Task category ID - /// Task category ID for replace in task with current category stage + /// Task category ID to replace the deleted category in the tasks with the current task category + /// Task category /// /// /// @@ -544,19 +539,16 @@ namespace ASC.Api.CRM } /// - /// Creates a new contact status with the parameters (title, description, etc.) specified in the request + /// Creates a new contact status with the parameters (title, description, etc.) specified in the request. /// - ///Title - ///Description - ///Color - ///Order + ///Contact status title + ///Contact status description + ///Contact status color + ///Contact status sort order ///Contact status - /// Create contact status + /// Create a contact status /// Contacts /// - /// - /// Contact status - /// [Create(@"contact/status")] public ContactStatusWrapper CreateContactStatus(string title, string description, string color, int sortOrder) { @@ -577,22 +569,19 @@ namespace ASC.Api.CRM } /// - /// Updates the selected contact status with the parameters (title, description, etc.) specified in the request + /// Updates the selected contact status with the parameters (title, description, etc.) specified in the request. /// ///Contact status ID - ///Title - ///Description - ///Color - ///Order - ///Contact status - /// Update contact status + ///New contact status title + ///New contact status description + ///New contact status color + ///New contact status sort order + ///Updated contact status + /// Update a contact status /// Contacts /// /// /// - /// - /// Contact status - /// [Update(@"contact/status/{id:[0-9]+}")] public ContactStatusWrapper UpdateContactStatus(int id, string title, string description, string color, int sortOrder) { @@ -619,16 +608,16 @@ namespace ASC.Api.CRM } /// - /// Updates the color of the selected contact status with the new color specified in the request + /// Updates a color of the selected contact status with a new color specified in the request. /// /// Contact status ID - /// Color - /// Update contact status color + /// New contact status color + /// Update a contact status color /// Contacts /// /// /// - /// Contact status + /// Contact status with the new color /// [Update(@"contact/status/{id:[0-9]+}/color")] public ContactStatusWrapper UpdateContactStatusColor(int id, string color) @@ -649,15 +638,15 @@ namespace ASC.Api.CRM } /// - /// Updates the contact statuses order with the list specified in the request + /// Updates the order of the contact statuses with a list specified in the request. /// /// - /// Update contact statuses order + /// Update the order of contact statuses /// - /// Contact status title list + /// List of contact status titles /// Contacts /// - /// Contact statuses + /// Contact statuses in the new order /// /// /// @@ -678,16 +667,16 @@ namespace ASC.Api.CRM } /// - /// Deletes the contact status with the ID specified in the request + /// Deletes a contact status with the ID specified in the request. /// - /// Delete contact status + /// Delete a contact status /// Contacts /// Contact status ID /// /// /// /// - /// Contact status + /// Contact status /// [Delete(@"contact/status/{contactStatusid:[0-9]+}")] public ContactStatusWrapper DeleteContactStatus(int contactStatusid) @@ -714,11 +703,11 @@ namespace ASC.Api.CRM } /// - /// Returns the status of the contact for the ID specified in the request + /// Returns a contact status with the ID specified in the request. /// /// Contact status ID /// Contact status - /// Get contact status + /// Get a contact status /// Contacts /// /// @@ -734,17 +723,14 @@ namespace ASC.Api.CRM } /// - /// Creates a new contact type with the parameters (title, etc.) specified in the request + /// Creates a new contact type with the parameters (title, etc.) specified in the request. /// - ///Title - ///Order + ///Contact type title + ///Contact type sort order ///Contact type - /// Create contact type + /// Create a contact type /// Contacts /// - /// - /// Contact type - /// [Create(@"contact/type")] public ContactTypeWrapper CreateContactType(string title, int sortOrder) { @@ -764,20 +750,17 @@ namespace ASC.Api.CRM } /// - /// Updates the selected contact type with the parameters (title, description, etc.) specified in the request + /// Updates the selected contact type with the parameters (title, description, etc.) specified in the request. /// ///Contact type ID - ///Title - ///Order - ///Contact type - /// Update contact type + ///New contact type title + ///New contact type order + ///Updated contact type + /// Update a contact type /// Contacts /// /// /// - /// - /// Contact type - /// [Update(@"contact/type/{id:[0-9]+}")] public ContactTypeWrapper UpdateContactType(int id, string title, int sortOrder) { @@ -802,15 +785,15 @@ namespace ASC.Api.CRM } /// - /// Updates the contact types order with the list specified in the request + /// Updates the order of the contact types with a list specified in the request. /// /// - /// Update contact types order + /// Update the order of contact types /// - /// Contact type title list + /// List of contact type titles /// Contacts /// - /// Contact types + /// Contact types in the new order /// /// /// @@ -831,16 +814,16 @@ namespace ASC.Api.CRM } /// - /// Deletes the contact type with the ID specified in the request + /// Deletes a contact type with the ID specified in the request. /// - /// Delete contact type + /// Delete a contact type /// Contacts /// Contact type ID /// /// /// /// - /// Contact type + /// Contact type /// [Delete(@"contact/type/{contactTypeid:[0-9]+}")] public ContactTypeWrapper DeleteContactType(int contactTypeid) @@ -867,11 +850,11 @@ namespace ASC.Api.CRM } /// - /// Returns the type of the contact for the ID specified in the request + /// Returns a contact type with the ID specified in the request. /// /// Contact type ID /// Contact type - /// Get contact type + /// Get a contact type /// Contacts /// /// @@ -887,11 +870,11 @@ namespace ASC.Api.CRM } /// - /// Returns the stage of the opportunity with the ID specified in the request + /// Returns an opportunity stage with the ID specified in the request. /// /// Opportunity stage ID /// Opportunity stage - /// Get opportunity stage + /// Get an opportunity stage /// Opportunities /// /// @@ -907,11 +890,11 @@ namespace ASC.Api.CRM } /// - /// Returns the category of the task with the ID specified in the request + /// Returns a task category with the ID specified in the request. /// /// Task category ID /// Task category - /// Get task category + /// Get a task category /// Tasks /// /// @@ -927,12 +910,12 @@ namespace ASC.Api.CRM } /// - /// Returns the list of all history categories available on the portal + /// Returns a list of all the history categories available on the portal. /// /// Get all history categories /// History /// - /// List of all history categories + /// List of all the history categories /// [Read(@"history/category")] public IEnumerable GetHistoryCategoryWrapper() @@ -950,12 +933,12 @@ namespace ASC.Api.CRM } /// - /// Returns the list of all task categories available on the portal + /// Returns a list of all the task categories available on the portal. /// /// Get all task categories /// Tasks /// - /// List of all task categories + /// List of all the task categories /// [Read(@"task/category")] public IEnumerable GetTaskCategories() @@ -973,12 +956,12 @@ namespace ASC.Api.CRM } /// - /// Returns the list of all contact statuses available on the portal + /// Returns a list of all the contact statuses available on the portal. /// /// Get all contact statuses /// Contacts /// - /// List of all contact statuses + ///List of all the contact statuses /// [Read(@"contact/status")] public IEnumerable GetContactStatuses() @@ -996,12 +979,12 @@ namespace ASC.Api.CRM } /// - /// Returns the list of all contact types available on the portal + /// Returns a list of all the contact types available on the portal. /// /// Get all contact types /// Contacts /// - /// List of all contact types + /// List of all the contact types /// [Read(@"contact/type")] public IEnumerable GetContactTypes() @@ -1020,12 +1003,12 @@ namespace ASC.Api.CRM } /// - /// Returns the list of all opportunity stages available on the portal + /// Returns a list of all the opportunity stages available on the portal. /// /// Get all opportunity stages /// Opportunities /// - /// List of all opportunity stages + /// List of all the opportunity stages /// [Read(@"opportunity/stage")] public IEnumerable GetDealMilestones() diff --git a/module/ASC.Api/ASC.Api.CRM/CRMApi.RelationshipEvent.cs b/module/ASC.Api/ASC.Api.CRM/CRMApi.RelationshipEvent.cs index 2312b5691..08dbf8f5c 100644 --- a/module/ASC.Api/ASC.Api.CRM/CRMApi.RelationshipEvent.cs +++ b/module/ASC.Api/ASC.Api.CRM/CRMApi.RelationshipEvent.cs @@ -44,20 +44,20 @@ namespace ASC.Api.CRM public partial class CRMApi { /// - /// Returns the list of all events matching the parameters specified in the request + /// Returns a list of all the events matching the parameters specified in the request. /// /// - /// Get event list + /// Get filtered events /// /// History /// Related entity type /// Related entity ID - /// Task category ID + /// Event category ID /// Event author - /// Earliest task due date - /// Latest task due date + /// Earliest event due date + /// Latest event due date /// - /// Event list + /// Event list /// [Read(@"history/filter")] public IEnumerable GetHistory( @@ -151,17 +151,17 @@ namespace ASC.Api.CRM } /// - /// Deletes the event with the ID specified in the request and all the files associated with this event + /// Deletes an event with the ID specified in the request and all the files associated with this event. /// /// - /// Delete event and related files + /// Delete an event and related files /// /// History /// Event ID /// /// /// - /// Event + /// Event /// [Delete(@"history/{id:[0-9]+}")] public RelationshipEventWrapper DeleteHistory(int id) @@ -182,16 +182,16 @@ namespace ASC.Api.CRM } /// - /// Creates a text (.txt) file in the selected folder with the title and contents sent in the request + /// Creates a text (.txt) file in the selected folder with the title and contents specified in the request. /// - /// Create txt + /// Create a text file /// Files - /// Entity type - /// Entity ID + /// Related entity type + /// Related entity ID /// File title /// File contents /// - /// File info + /// File information /// [Create(@"{entityType:(contact|opportunity|case)}/{entityid:[0-9]+}/files/text")] public FileWrapper CreateTextFile(string entityType, int entityid, string title, string content) @@ -224,27 +224,27 @@ namespace ASC.Api.CRM } /// - /// Upload file + /// Uploads a file to the CRM module with the parameters specified in the request. /// - /// Upload file + /// Upload a file /// Files /// /// - ///
  1. Single file upload. You should set Content-Type & Content-Disposition header to specify filename and content type, and send file in request body
  2. - ///
  3. Using standart multipart/form-data method
  4. + ///
  5. Single file upload. You should set the Content-Type & Content-Disposition headers to specify file name and content type, and send a file in the request body.
  6. + ///
  7. Using standart multipart/form-data method.
  8. /// ]]> ///
    - /// Entity type - /// Entity ID - /// Request Input stream - /// Content-Type Header - /// Content-Disposition Header + /// Related entity type + /// Related entity ID + /// Request input stream + /// Content-Type header + /// Content-Disposition header /// List of files when posted as multipart/form-data - /// If True, upload documents in original formats as well + /// Defines if the documents in the original formats are also uploaded or not /// - /// File info + /// File informatin /// [Create(@"{entityType:(contact|opportunity|case)}/{entityid:[0-9]+}/files/upload")] public FileWrapper UploadFileInCRM( @@ -289,10 +289,10 @@ namespace ASC.Api.CRM } /// - /// Creates the event with the parameters specified in the request + /// Creates an event with the parameters specified in the request. /// /// - /// Create event + /// Create an event /// /// History /// Contact ID @@ -300,16 +300,16 @@ namespace ASC.Api.CRM /// Related entity ID /// /// /// - /// Contents - /// Category ID + /// Event contents + /// Event category ID /// Event creation date - /// List of IDs of the files associated with the event - /// User field list + /// List of file IDs for the current event + /// List of users who will be notified about the event /// - /// Created event + /// Created event /// [Create(@"history")] public RelationshipEventWrapper AddHistoryTo( @@ -431,16 +431,16 @@ namespace ASC.Api.CRM } /// - /// Associates the selected file(s) with the entity with the ID or type specified in the request + /// Attaches the selected file(s) to the entity specified in the request. /// /// - /// Associate file with entity + /// Attach files to the entity /// /// Entity type /// Entity ID /// List of IDs of the files /// Files - /// Entity with the file attached + /// Entity with the file(s) attached [Create(@"{entityType:(contact|opportunity|case)}/{entityid:[0-9]+}/files")] public RelationshipEventWrapper AttachFiles(string entityType, int entityid, IEnumerable fileids) { @@ -480,12 +480,12 @@ namespace ASC.Api.CRM } /// - /// Returns the ID for the root folder used to store the files for the CRM module + /// Returns the ID for the root folder used to store the files for the CRM module. /// /// Get root folder ID /// Files /// - /// Root folder ID + /// Root folder ID /// [Read(@"files/root")] public object GetRootFolderID() @@ -494,14 +494,14 @@ namespace ASC.Api.CRM } /// - /// Returns the list of all files for the entity with the ID or type specified in the request + /// Returns a list of all the files for the entity with the ID and type specified in the request. /// /// Entity type /// Entity ID - /// Get file list + /// Get entity files /// Files /// - /// File list + /// List of files /// [Read(@"{entityType:(contact|opportunity|case)}/{entityid:[0-9]+}/files")] public IEnumerable GetFiles(string entityType, int entityid) @@ -523,15 +523,15 @@ namespace ASC.Api.CRM } /// - /// Deletes the file with the ID specified in the request + /// Deletes a file with the ID specified in the request. /// - /// Delete file + /// Delete a file /// Files /// File ID /// /// /// - /// File Info + /// File information /// [Delete(@"files/{fileid:[0-9]+}")] public FileWrapper DeleteCRMFile(int fileid) diff --git a/module/ASC.Api/ASC.Api.CRM/CRMApi.Reports.cs b/module/ASC.Api/ASC.Api.CRM/CRMApi.Reports.cs index d3b3170f7..c7000ff95 100644 --- a/module/ASC.Api/ASC.Api.CRM/CRMApi.Reports.cs +++ b/module/ASC.Api/ASC.Api.CRM/CRMApi.Reports.cs @@ -31,7 +31,7 @@ namespace ASC.Api.CRM { public partial class CRMApi { - /// Returns a list of all user report files + /// Returns a list of all the user report files. /// Get report files /// Reports /// Report files @@ -61,13 +61,13 @@ namespace ASC.Api.CRM return files.ConvertAll(file => new FileWrapper(file)).OrderByDescending(file => file.Id); } - /// Delete the report file with the ID specified in the request + /// Deletes a report file with the ID specified in the request. /// File ID - /// Delete report file + /// Delete a report file /// Reports /// if user can't create reports - /// if fileid les than 0 - /// if file not found + /// if file ID is less than 0 + /// if file is not found [Delete(@"report/file/{fileid:[0-9]+}")] public void DeleteFile(int fileid) { @@ -83,7 +83,7 @@ namespace ASC.Api.CRM DaoFactory.ReportDao.DeleteFile(fileid); } - /// Get the state of the report generation task + /// Returns a state of the report generation task. /// Get report generation state /// Reports /// Report state @@ -97,8 +97,8 @@ namespace ASC.Api.CRM return DocbuilderReportsUtility.Status(ReportOrigin.CRM); } - /// Terminate the report generation task - /// Terminate report generation + /// Terminates the report generation task. + /// Terminate the report generation /// Reports /// if user can't create reports [Read(@"report/terminate")] @@ -110,13 +110,13 @@ namespace ASC.Api.CRM DocbuilderReportsUtility.Terminate(ReportOrigin.CRM); } - /// Check data availability for a report + /// Checks report data for the parameters specified in the request. /// Report type /// Time period /// Managers /// Check report data /// Reports - /// Object + /// Report information /// if user can't create reports [Create(@"report/check")] public object CheckReportData(ReportType type, ReportTimePeriod timePeriod, Guid[] managers) @@ -131,11 +131,11 @@ namespace ASC.Api.CRM }; } - /// Run the report generation task + /// Runs the report generation task with the parameters specified in the request. /// Report type /// Time period /// Managers - /// Generate report + /// Generate a report /// Reports /// Report state /// if user can't create reports diff --git a/module/ASC.Api/ASC.Api.CRM/CRMApi.Tag.cs b/module/ASC.Api/ASC.Api.CRM/CRMApi.Tag.cs index 77de46cc2..70287b120 100644 --- a/module/ASC.Api/ASC.Api.CRM/CRMApi.Tag.cs +++ b/module/ASC.Api/ASC.Api.CRM/CRMApi.Tag.cs @@ -34,14 +34,14 @@ namespace ASC.Api.CRM public partial class CRMApi { /// - /// Returns the list of all tags associated with the entity with the ID and type specified in the request + /// Returns a list of all the tags related to the entity with the ID and type specified in the request. /// - /// Entity type + /// Entity type /// Entity ID /// Get entity tags /// Tags /// - /// Tag + /// Tag /// /// [Read(@"{entityType:(contact|opportunity|case)}/tag/{entityid:[0-9]+}")] @@ -76,13 +76,13 @@ namespace ASC.Api.CRM } /// - /// Returns the list of all tags for the contact with the ID specified in the request + /// Returns a list of all the tags for the contact with the ID specified in the request. /// /// Contact ID - /// Get all contact tags + /// Get contact tags /// Tags /// - /// List of contact tags + /// List of contact tags /// /// [Read(@"contact/{contactid:[0-9]+}/tag")] @@ -96,14 +96,14 @@ namespace ASC.Api.CRM } /// - /// Creates the tag for the selected entity with the tag name specified in the request + /// Creates a tag for the selected entity with a tag name specified in the request. /// - /// Entity type + /// Entity type /// Tag name - /// Create tag + /// Create a tag /// Tags /// - /// Tag + /// Tag /// /// [Create(@"{entityType:(contact|opportunity|case)}/tag")] @@ -122,13 +122,13 @@ namespace ASC.Api.CRM } /// - /// Returns the list of all tags associated with the entity type specified in the request + /// Returns a list of all the tags associated with the entity type specified in the request. /// - /// Entity type - /// Get tags for entity type + /// Entity type + /// Get tags for entity type /// Tags /// - /// Tag + /// Tag /// /// [Read(@"{entityType:(contact|opportunity|case)}/tag")] @@ -150,16 +150,16 @@ namespace ASC.Api.CRM } /// - /// Adds a group of tags to the entity with the ID specified in the request + /// Adds a tag to the entities with the IDs specified in the request. /// - /// Add tag group to entity + /// Add a tag to the entities /// Tags - /// Tag type - /// Entity ID + /// Entity type + /// List of entity IDs /// Tag name /// /// - /// Tag + /// Tag /// [Create(@"{entityType:(contact|opportunity|case)}/taglist")] public string AddTagToBatch(string entityType, IEnumerable entityid, string tagName) @@ -175,20 +175,20 @@ namespace ASC.Api.CRM } /// - /// Adds the selected tag to the group of contacts with the parameters specified in the request + /// Adds the selected tag to the group of contacts with the parameters specified in the request. /// - /// Add tag to contact group + /// Add a tag to a contact group /// Tags - /// Tag + /// Contact tags /// Contact stage ID (warmth) /// Contact type ID - /// + /// Contact list view /// Start date /// End date /// Tag name /// /// - /// Tag + /// Tag /// [Create(@"contact/filter/taglist")] public string AddTagToBatchContacts( @@ -222,13 +222,13 @@ namespace ASC.Api.CRM } /// - /// Adds the selected tag to the group of opportunities with the parameters specified in the request + /// Adds the selected tag to the group of opportunities with the parameters specified in the request. /// - /// Add tag to opportunity group + /// Add a tag to an opportunity group /// Tags - /// Opportunity responsible + /// Opportunity responsible ID /// Opportunity stage ID - /// Tags + /// Opportunity tags /// Contact ID /// Participation status: take into account opportunities where the contact is a participant or not /// Start date @@ -237,7 +237,7 @@ namespace ASC.Api.CRM /// Tag name /// /// - /// Tag + /// Tag /// [Create(@"opportunity/filter/taglist")] public string AddTagToBatchDeals( @@ -271,17 +271,17 @@ namespace ASC.Api.CRM } /// - /// Adds the selected tag to the group of cases with the parameters specified in the request + /// Adds the selected tag to the group of cases with the parameters specified in the request. /// - /// Add tag to case group + /// Add a tag to a case group /// Tags /// Contact ID /// Case status - /// Tags + /// Case tags /// Tag name /// /// - /// Tag + /// Tag /// [Create(@"case/filter/taglist")] public string AddTagToBatchCases(int contactid, bool? isClosed, IEnumerable tags, string tagName) @@ -300,11 +300,11 @@ namespace ASC.Api.CRM } /// - /// Deletes all the unused tags from the entities with the type specified in the request + /// Deletes all the unused tags from the entities with the type specified in the request. /// /// Delete unused tags /// Tags - /// Entity type + /// Entity type /// Tags [Delete(@"{entityType:(contact|opportunity|case)}/tag/unused")] public IEnumerable DeleteUnusedTag(string entityType) @@ -321,16 +321,16 @@ namespace ASC.Api.CRM } /// - /// Adds the selected tag to the entity with the type and ID specified in the request + /// Adds the selected tag to the entity with the type and ID specified in the request. /// - /// Add tag + /// Add a tag to the entity /// Tags - /// Entity type + /// Entity type /// Entity ID /// Tag name /// /// - /// Tag + /// Tag /// [Create(@"{entityType:(contact|opportunity|case)}/{entityid:[0-9]+}/tag")] public string AddTagTo(string entityType, int entityid, string tagName) @@ -352,10 +352,10 @@ namespace ASC.Api.CRM } /// - /// Adds the selected tag to the entity (company or person) specified in the request and to all related contacts + /// Adds the selected tag to the entity (company or person) specified in the request and to all the related contacts. /// - /// Add tag - /// Entity type + /// Add a tag to the entity and related contacts + /// Entity type /// Entity ID /// Tag name /// Tags @@ -363,7 +363,7 @@ namespace ASC.Api.CRM /// /// /// - /// Tag + /// Tag /// [Create(@"{entityType:(company|person)}/{entityid:[0-9]+}/tag/group")] public string AddContactTagToGroup(string entityType, int entityid, string tagName) @@ -442,16 +442,16 @@ namespace ASC.Api.CRM } /// - /// Deletes the selected tag from the entity with the type specified in the request + /// Deletes the selected tag from the entities with the type specified in the request. /// - /// Delete tag + /// Delete a tag from the entities /// Entity type /// Tag name /// Tags /// /// /// - /// Tag + /// Tag /// [Delete(@"{entityType:(contact|opportunity|case)}/tag")] public string DeleteTag(string entityType, string tagName) @@ -472,16 +472,16 @@ namespace ASC.Api.CRM } /// - /// Deletes the selected tag from the entity with the type and ID specified in the request + /// Deletes the selected tag from the entity with the type and ID specified in the request. /// - /// Remove tag + /// Delete a tag from the entity /// Tags /// Entity type /// Entity ID /// Tag name /// /// - /// Tag + /// Tag /// [Delete(@"{entityType:(contact|opportunity|case)}/{entityid:[0-9]+}/tag")] public string DeleteTagFrom(string entityType, int entityid, string tagName) @@ -505,10 +505,10 @@ namespace ASC.Api.CRM } /// - /// Deletes the selected tag from the entity (company or person) specified in the request and from all related contacts + /// Deletes the selected tag from the entity (company or person) specified in the request and from all the related contacts. /// - /// Delete tag - /// Entity type + /// Delete a tag from the entity and related contacts + /// Entity type /// Entity ID /// Tag name /// Tags @@ -516,7 +516,7 @@ namespace ASC.Api.CRM /// /// /// - /// Tag + /// Tag /// [Delete(@"{entityType:(company|person)}/{entityid:[0-9]+}/tag/group")] public string DeleteContactTagFromGroup(string entityType, int entityid, string tagName) diff --git a/module/ASC.Api/ASC.Api.CRM/CRMApi.TaskTemplate.cs b/module/ASC.Api/ASC.Api.CRM/CRMApi.TaskTemplate.cs index cfe724094..ef474a3de 100644 --- a/module/ASC.Api/ASC.Api.CRM/CRMApi.TaskTemplate.cs +++ b/module/ASC.Api/ASC.Api.CRM/CRMApi.TaskTemplate.cs @@ -30,14 +30,14 @@ namespace ASC.Api.CRM public partial class CRMApi { /// - /// Creates a new task template container with the type and title specified in the request + /// Creates a new task template container with the type and title specified in the request. /// - /// Type - /// Title - /// Create task template container - /// Task Templates + /// Entity type + /// Template container title + /// Create a task template container + /// Task templates /// - /// Task template container + /// Task template container /// /// /// false @@ -57,13 +57,13 @@ namespace ASC.Api.CRM } /// - /// Returns the complete list of all the task template containers available on the portal + /// Returns a complete list of all the task template containers available on the portal. /// - /// Type - /// Get task template container list - /// Task Templates + /// Entity type + /// Get task template containers + /// Task templates /// - /// Task template container list + /// List of task template containers /// /// false [Read(@"{entityType:(contact|person|company|opportunity|case)}/tasktemplatecontainer")] @@ -73,13 +73,13 @@ namespace ASC.Api.CRM } /// - /// Deletes the task template container with the ID specified in the request + /// Deletes a task template container with the ID specified in the request. /// /// Task template container ID - /// Delete task template container - /// Task Templates + /// Delete a task template container + /// Task templates /// - /// Deleted task template container + /// Deleted task template container /// /// /// @@ -98,14 +98,14 @@ namespace ASC.Api.CRM } /// - /// Updates the task template container with the ID specified in the request + /// Updates a task template container with the ID specified in the request. /// /// Task template container ID - /// Title - /// Update task template container - /// Task Templates + /// New template container title + /// Update a task template container + /// Task templates /// - /// Task template container + /// Updated task template container /// /// /// @@ -126,13 +126,13 @@ namespace ASC.Api.CRM } /// - /// Returns the detailed information on the task template container with the ID specified in the request + /// Returns the detailed information on the task template container with the ID specified in the request. /// /// Task template container ID - /// Get task template container by ID - /// Task Templates + /// Get a task template container by ID + /// Task templates /// - /// Task template container + /// Task template container /// /// /// @@ -149,13 +149,13 @@ namespace ASC.Api.CRM } /// - /// Returns the list of all tasks in the container with the ID specified in the request + /// Returns a list of all the tasks in the container with the ID specified in the request. /// /// Task template container ID - /// Get task template list by contaier ID - /// Task Templates + /// Get container tasks + /// Task templates /// - /// Task template list + /// List of task templates /// /// /// @@ -172,18 +172,18 @@ namespace ASC.Api.CRM } /// - /// Creates a new task template with the parameters specified in the request in the container with the selected ID + /// Creates a new task template with the parameters specified in the request in the container with the selected ID. /// /// Task template container ID - /// Title - /// Description - /// Responsible ID - /// Category ID - /// Responsible notification: notify or not - /// Ticks offset - /// - /// Create task template - /// Task Templates + /// Task template title + /// Task template description + /// Task template responsible ID + /// Task template category ID + /// Responsible notification: notifies or not + /// Tick offset + /// Defines if the deadline is fixed or not + /// Create a task template + /// Task templates /// Task template /// /// @@ -223,19 +223,19 @@ namespace ASC.Api.CRM } /// - /// Updates the selected task template with the parameters specified in the request in the container with the selected ID + /// Updates the selected task template with the parameters specified in the request. /// /// Task template ID - /// Task template container ID - /// Title - /// Description - /// Responsible ID - /// Category ID - /// Responsible notification: notify or not - /// Ticks offset - /// - /// Update task template - /// Task Templates + /// New task template container ID + /// New task template title + /// New task template description + /// New task template responsible ID + /// New task template category ID + /// Responsible notification: notifies or not + /// New tick offset + /// Defines if the deadline is fixed or not + /// Update a task template + /// Task templates /// Task template /// /// @@ -280,11 +280,11 @@ namespace ASC.Api.CRM } /// - /// Deletes the task template with the ID specified in the request + /// Deletes a task template with the ID specified in the request. /// /// Task template ID - /// Delete task template - /// Task Templates + /// Delete a task template + /// Task templates /// Task template /// /// @@ -305,11 +305,11 @@ namespace ASC.Api.CRM } /// - /// Return the task template with the ID specified in the request + /// Returns a task template with the ID specified in the request. /// /// Task template ID - /// Get task template by ID - /// Task Templates + /// Get a task template by ID + /// Task templates /// Task template /// /// diff --git a/module/ASC.Api/ASC.Api.CRM/CRMApi.Tasks.cs b/module/ASC.Api/ASC.Api.CRM/CRMApi.Tasks.cs index 9f983640b..7e7ed0380 100644 --- a/module/ASC.Api/ASC.Api.CRM/CRMApi.Tasks.cs +++ b/module/ASC.Api/ASC.Api.CRM/CRMApi.Tasks.cs @@ -34,7 +34,7 @@ namespace ASC.Api.CRM public partial class CRMApi { /// - /// Returns the detailed information about the task with the ID specified in the request + /// Returns the detailed information about a task with the ID specified in the request. /// /// Task ID /// Task @@ -59,20 +59,20 @@ namespace ASC.Api.CRM } /// - /// Returns the list of tasks matching the creteria specified in the request + /// Returns a list of tasks matching the parameters specified in the request. /// - /// Task responsible + /// Task responsible ID /// Task category ID - /// Show open or closed tasks only + /// Task status /// Earliest task due date /// Latest task due date /// Related entity type /// Related entity ID /// - /// Get task list + /// Get tasks /// Tasks /// - /// Task list + /// List of all tasks /// [Read(@"task/filter")] public IEnumerable GetAllTasks( @@ -182,14 +182,14 @@ namespace ASC.Api.CRM } /// - /// Open anew the task with the ID specified in the request + /// Reopens a task with the ID specified in the request. /// - /// Resume task + /// Reopen a task /// Tasks /// Task ID /// /// - /// Task + /// Task /// [Update(@"task/{taskid:[0-9]+}/reopen")] public TaskWrapper ReOpenTask(int taskid) @@ -205,14 +205,14 @@ namespace ASC.Api.CRM } /// - /// Close the task with the ID specified in the request + /// Closes a task with the ID specified in the request. /// - /// Close task + /// Close a task /// Tasks /// Task ID /// /// - /// Task + /// Task /// [Update(@"task/{taskid:[0-9]+}/close")] public TaskWrapper CloseTask(int taskid) @@ -228,9 +228,9 @@ namespace ASC.Api.CRM } /// - /// Delete the task with the ID specified in the request + /// Deletes a task with the ID specified in the request. /// - /// Delete task + /// Delete a task /// Tasks /// Task ID /// @@ -253,7 +253,7 @@ namespace ASC.Api.CRM } /// - /// Creates the task with the parameters (title, description, due date, etc.) specified in the request + /// Creates a task with the parameters (title, description, due date, etc.) specified in the request. /// /// Task title /// Task description @@ -263,10 +263,10 @@ namespace ASC.Api.CRM /// Contact ID /// Related entity type /// Related entity ID - /// Notify the responsible about the task - /// Time period in minutes for reminder to the responsible about the task + /// Notifies the responsible about the task + /// Time period in minutes to remind the responsible of the task /// - /// Create task + /// Create a task /// Tasks /// Task [Create(@"task")] @@ -343,20 +343,20 @@ namespace ASC.Api.CRM } /// - /// Creates the group of the same task with the parameters (title, description, due date, etc.) specified in the request for several contacts + /// Creates a group of the same task with the parameters (title, description, due date, etc.) specified in the request for several contacts. /// /// Task title /// Task description /// Task due date /// Task responsible ID /// Task category ID - /// contact ID list + /// List of contact IDs /// Related entity type /// Related entity ID - /// Notify the responsible about the task - /// Time period in minutes for reminder to the responsible about the task + /// Notifies the responsible about the task + /// Time period in minutes to remind the responsible of the task /// - /// Create task list + /// Create a task group /// Tasks /// Tasks /// false @@ -454,20 +454,20 @@ namespace ASC.Api.CRM /// - /// Updates the selected task with the parameters (title, description, due date, etc.) specified in the request + /// Updates the selected task with the parameters (title, description, due date, etc.) specified in the request. /// /// Task ID - /// Task title - /// Task description - /// Task due date - /// Task responsible ID - /// Task category ID - /// Contact ID - /// Related entity type - /// Related entity ID - /// Notify or not - /// Time period in minutes for reminder to the responsible about the task - /// Update task + /// New task title + /// New task description + /// New task due date + /// New task responsible ID + /// New task category ID + /// New contact ID + /// New related entity type + /// New related entity ID + /// Notifies the responsible about the task + /// New time period in minutes to remind the responsible of the task + /// Update a task /// Tasks /// Task [Update(@"task/{taskid:[0-9]+}")] @@ -542,6 +542,13 @@ namespace ASC.Api.CRM return ToTaskWrapper(task); } + /// + /// Sets the creation date to the task with the ID specified in the request. + /// + /// Task ID + /// Task creation date + /// Set the task creation date + /// Tasks /// false [Update(@"task/{taskid:[0-9]+}/creationdate")] public void SetTaskCreationDate(int taskId, ApiDateTime creationDate) @@ -555,6 +562,13 @@ namespace ASC.Api.CRM dao.SetTaskCreationDate(taskId, creationDate); } + /// + /// Sets the last modified date to the task with the ID specified in the request. + /// + /// Task ID + /// Task last modified date + /// Set the task last modified date + /// Tasks /// false [Update(@"task/{taskid:[0-9]+}/lastmodifeddate")] public void SetTaskLastModifedDate(int taskId, ApiDateTime lastModifedDate) diff --git a/module/ASC.Api/ASC.Api.CRM/CRMApi.Utils.cs b/module/ASC.Api/ASC.Api.CRM/CRMApi.Utils.cs index db8c7b15d..4d16744be 100644 --- a/module/ASC.Api/ASC.Api.CRM/CRMApi.Utils.cs +++ b/module/ASC.Api/ASC.Api.CRM/CRMApi.Utils.cs @@ -35,12 +35,12 @@ namespace ASC.Api.CRM public partial class CRMApi { /// - /// Returns the list of all currencies currently available on the portal + /// Returns a list of all the currencies currently available on the portal. /// - /// Get currency list + /// Get available currencies /// Common /// - /// List of available currencies + /// List of available currencies /// [Read(@"settings/currency")] public IEnumerable GetAvaliableCurrency() @@ -49,15 +49,15 @@ namespace ASC.Api.CRM } /// - /// Returns the result of convertation from one currency to another + /// Returns a result of converting one currency into another. /// /// Amount to convert - /// Old currency key - /// New currency key - /// Get the result of convertation + /// Currency to be converted + /// Currency into which the original currency will be converted + /// Convert a currency /// Common /// - /// Decimal result of convertation + /// Decimal result of converting /// [Read(@"settings/currency/convert")] public Decimal ConvertAmount(Decimal amount, String fromcurrency, String tocurrency) @@ -66,13 +66,13 @@ namespace ASC.Api.CRM } /// - /// Returns the summary table with rates for selected currency + /// Returns a summary table with the rates for the currency specified in the request. /// - /// Currency (Abbreviation) - /// Get the summary table + /// Currency (abbreviation) + /// Get currency rates /// Common /// - /// Dictionary of currencies and rates + /// Dictionary of currency rates /// /// [Read(@"settings/currency/summarytable")] @@ -94,13 +94,13 @@ namespace ASC.Api.CRM } /// - /// + /// Updates the contact status setting with the parameter specified in the request. /// - /// Change contact status group auto - /// + /// Defines if the contact status setting is changed automatically or not + /// Update the contact status setting /// Contacts /// - /// ChangeContactStatusGroupAuto setting value (true, false or null) + /// Updated contact status setting value (true, false or null) /// /// [Update(@"contact/status/settings")] @@ -116,13 +116,13 @@ namespace ASC.Api.CRM } /// - /// + /// Updates the setting for writing mails to the history with the parameter specified in the request. /// - /// Write mail to history auto - /// + /// Defines if the mails are written to the history automatically or not + /// Update the setting for writing mails to the history /// Contacts /// - /// WriteMailToHistoryAuto setting value (true or false) + /// Updated setting for writing mails to the history (true or false) /// /// [Update(@"contact/mailtohistory/settings")] @@ -137,13 +137,13 @@ namespace ASC.Api.CRM } /// - /// + /// Updates the setting for adding tags to the contact with the parameter specified in the request. /// - /// add tag to contact group auto - /// + /// Defines if a tag is added to the contact automatically or not + /// Update the setting for adding tags to the contact /// Contacts /// - /// AddTagToContactGroupAuto setting value (true, false or null) + /// Setting for adding tags to contact (true, false or null) /// /// [Update(@"contact/tag/settings")] @@ -160,12 +160,14 @@ namespace ASC.Api.CRM /// - /// Set IsConfiguredPortal tenant setting and website contact form key specified in the request + /// Sets the tenant setting for portal configuration and website contact form key specified in the request. /// - /// Set tenant settings + /// Defines if the tenant setting for portal configuration is set or not + /// Website contact form key + /// Set the tenant setting /// Common /// - /// IsConfiguredPortal setting value (true or false) + /// Tenant setting for portal configuration value (true or false) /// [Update(@"settings")] public Boolean SetIsPortalConfigured(Boolean? configured, Guid? webFormKey) @@ -179,12 +181,12 @@ namespace ASC.Api.CRM } /// - /// Save organisation company name + /// Updates a company name with the one specified in the request. /// - /// Organisation company name - /// Save organisation company name + /// New company name + /// Update a company name /// Organisation - /// Organisation company name + /// Updated company name /// [Update(@"settings/organisation/base")] public String UpdateOrganisationSettingsCompanyName(String companyName) @@ -205,16 +207,16 @@ namespace ASC.Api.CRM } /// - /// Save organisation company address + /// Updates the company address with the one specified in the request. /// - /// Organisation company street/building/apartment address - /// City - /// State - /// Zip - /// Country - /// Save organisation company address + /// New company street/building/apartment + /// New company city + /// New company state + /// New company zip + /// New company country + /// Update the company address /// Organisation - /// Returns a JSON object with the organization company address details + /// Updated company address /// [Update(@"settings/organisation/address")] public String UpdateOrganisationSettingsCompanyAddress(String street, String city, String state, String zip, String country) @@ -248,10 +250,10 @@ namespace ASC.Api.CRM } /// - /// Save organisation logo + /// Updates the organisation logo setting with the parameter specified in the request. /// - /// Reset organisation logo - /// Save organisation logo + /// Resets organisation logo or not + /// Update the organisation logo setting /// Organisation /// Organisation logo ID /// @@ -288,12 +290,12 @@ namespace ASC.Api.CRM } /// - /// Get organisation logo in base64 format (if 'id' is 0 then take current logo) + /// Returns an organisation logo with the ID specified in the request in the base64 format. /// - /// organisation logo id - /// Get organisation logo + /// Organisation logo ID + /// Get an organisation logo /// Organisation - /// Organisation logo content in base64 + /// Organisation logo in the base64 format /// [Read(@"settings/organisation/logo")] public String GetOrganisationSettingsLogo(int id) @@ -315,11 +317,11 @@ namespace ASC.Api.CRM } /// - /// Change Website Contact Form key + /// Updates the website contact form key. /// - /// Change web form key + /// Update web form key /// Common - /// Web form key + /// Updated web form key /// [Update(@"settings/webformkey/change")] public string ChangeWebToLeadFormKey() @@ -336,12 +338,12 @@ namespace ASC.Api.CRM } /// - /// Change default CRM currency + /// Updates the default CRM currency with the currency specified in the request. /// - /// Currency (Abbreviation) - /// Change currency + /// Currency (abbreviation) + /// Update a currency /// Common - /// currency + /// Updated currency /// /// [Update(@"settings/currency")] @@ -363,6 +365,14 @@ namespace ASC.Api.CRM return ToCurrencyInfoWrapper(cur); } + /// + /// Starts the import from the csv file specified in the request. + /// + /// Entity type + /// URI to the csv file + /// JSON settings in the string format + /// Start import from csv file + /// Common /// false [Create(@"{entityType:(contact|opportunity|case|task)}/import/start")] public string StartImportFromCSV(string entityType, string csvFileURI, string jsonSettings) @@ -392,6 +402,13 @@ namespace ASC.Api.CRM return ""; } + /// + /// Returns a status of import from the csv file. + /// + /// Entity type + /// Get import status + /// Common + /// Import status /// false [Read(@"{entityType:(contact|opportunity|case|task)}/import/status")] public IProgressItem GetImportFromCSVStatus(string entityType) @@ -420,6 +437,15 @@ namespace ASC.Api.CRM return ImportFromCSV.GetStatus(entityTypeObj); } + /// + /// Returns a sample row from the imported csv file specified in the request. + /// + /// URI to the csv file + /// Sample row index + /// JSON settings in the string format + /// Get a sample row + /// Common + /// Sample row /// false [Read(@"import/samplerow")] public String GetImportFromCSVSampleRow(string csvFileURI, int indexRow, string jsonSettings) @@ -433,6 +459,14 @@ namespace ASC.Api.CRM return ImportFromCSV.GetRow(CSVFileStream, indexRow, jsonSettings); } + /// + /// Processes the fake upload of the csv file specified in the request. + /// + /// URI to the csv file + /// JSON settings in the string format + /// Process fake upload + /// Common + /// Uploaded file /// false [Create(@"import/uploadfake")] public FileUploadResult ProcessUploadFake(string csvFileURI, string jsonSettings) @@ -440,6 +474,12 @@ namespace ASC.Api.CRM return new ImportFromCSVManager().ProcessUploadFake(csvFileURI, jsonSettings); } + /// + /// Returns a status of export to the csv file. + /// + /// Get export status + /// Common + /// Export status /// false [Read(@"export/status")] public IProgressItem GetExportStatus() @@ -448,6 +488,12 @@ namespace ASC.Api.CRM return ExportToCsv.GetStatus(false); } + /// + /// Cancels the export to the csv file. + /// + /// Cancel export to csv file + /// Common + /// Export status /// false [Update(@"export/cancel")] public IProgressItem CancelExport() @@ -457,6 +503,12 @@ namespace ASC.Api.CRM return ExportToCsv.GetStatus(false); } + /// + /// Starts the export to the csv file. + /// + /// Start export to csv file + /// Common + /// Export data operation /// false [Create(@"export/start")] public IProgressItem StartExport() @@ -468,6 +520,12 @@ namespace ASC.Api.CRM return ExportToCsv.Start(null, string.Format("{0}_{1}.zip", CRMSettingResource.Export, DateTime.UtcNow.Ticks)); } + /// + /// Returns a status of partial export to the csv file. + /// + /// Get status of partial export + /// Common + /// Partial export status /// false [Read(@"export/partial/status")] public IProgressItem GetPartialExportStatus() @@ -475,6 +533,12 @@ namespace ASC.Api.CRM return ExportToCsv.GetStatus(true); } + /// + /// Cancels the partial export to the csv file. + /// + /// Cancel partial export to csv file + /// Common + /// Partial export status /// false [Update(@"export/partial/cancel")] public IProgressItem CancelPartialExport() @@ -483,6 +547,14 @@ namespace ASC.Api.CRM return ExportToCsv.GetStatus(true); } + /// + /// Starts the partial export to the csv file. + /// + /// Entity type + /// Filter string in the base64 format + /// Start partial export to csv file + /// Common + /// Export data operation /// false [Create(@"export/partial/{entityType:(contact|opportunity|case|task|invoiceitem)}/start")] public IProgressItem StartPartialExport(string entityType, string base64FilterString) diff --git a/module/ASC.Api/ASC.Api.CRM/CRMApi.Voip.cs b/module/ASC.Api/ASC.Api.CRM/CRMApi.Voip.cs index 3006ec35a..279cccba6 100644 --- a/module/ASC.Api/ASC.Api.CRM/CRMApi.Voip.cs +++ b/module/ASC.Api/ASC.Api.CRM/CRMApi.Voip.cs @@ -47,11 +47,13 @@ namespace ASC.Api.CRM #region Numbers /// - /// + /// Returns all the available phone numbers matching the parameters specified in the request. /// - /// + /// Number type + /// ISO country code + /// Get available phone numbers /// Voip - /// + /// Phone numbers /// /// [Read(@"voip/numbers/available")] @@ -64,11 +66,11 @@ namespace ASC.Api.CRM } /// - /// + /// Returns a list of all the unlinked phone numbers. /// - /// + /// Get unlinked phone numbers /// Voip - /// + /// List of unlinked phone numbers /// [Read(@"voip/numbers/unlinked")] public IEnumerable GetUnlinkedPhoneNumbers() @@ -82,11 +84,11 @@ namespace ASC.Api.CRM } /// - /// + /// Returns all the existing phone numbers. /// - /// + /// Get all phone numbers /// Voip - /// + /// Existing phone numbers /// [Read(@"voip/numbers/existing")] public IEnumerable GetExistingPhoneNumbers() @@ -96,11 +98,12 @@ namespace ASC.Api.CRM return DaoFactory.VoipDao.GetNumbers(); } /// - /// + /// Buys a phone number specified in the request. /// - /// + /// Phone number + /// Buy a phone number /// Voip - /// + /// Phone number /// [Create(@"voip/numbers")] public VoipPhone BuyNumber(string number) @@ -117,11 +120,12 @@ namespace ASC.Api.CRM } /// - /// + /// Links a new phone number with the ID specified in the request to the voip provider. /// - /// + /// Phone number ID + /// Link a phone number /// Voip - /// + /// Phone number /// [Create(@"voip/numbers/link")] public VoipPhone LinkNumber(string id) @@ -163,11 +167,12 @@ namespace ASC.Api.CRM } /// - /// + /// Deletes a phone number with the ID specified in the request. /// - /// + /// Phone number ID + /// Delete a phone number /// Voip - /// + /// Phone number /// [Delete(@"voip/numbers/{numberId:\w+}")] public VoipPhone DeleteNumber(string numberId) @@ -185,11 +190,12 @@ namespace ASC.Api.CRM } /// - /// + /// Returns a phone number with the ID specified in the request. /// - /// + /// Phone number ID + /// Get a phone number /// Voip - /// + /// Phone number /// [Read(@"voip/numbers/{numberId:\w+}")] public VoipPhone GetNumber(string numberId) @@ -198,11 +204,11 @@ namespace ASC.Api.CRM } /// - /// + /// Returns the current phone number. /// - /// + /// Get the current phone number /// Voip - /// + /// Current phone number [Read(@"voip/numbers/current")] public VoipPhone GetCurrentNumber() { @@ -210,11 +216,11 @@ namespace ASC.Api.CRM } /// - /// + /// Returns a token for the current phone number. /// - /// + /// Get a token /// Voip - /// + /// Token [Read(@"voip/token")] public string GetToken() { @@ -222,11 +228,20 @@ namespace ASC.Api.CRM } /// - /// + /// Updates the settings of the phone number with the ID specified in the request. /// - /// + /// Phone number ID + /// New phone number greeting audio + /// New phone number audio for holding up + /// New phone number waiting URL + /// New phone number voice mail + /// New phone number working hours + /// Defines if a phone number allows making the outgoing calls or not + /// Defines if the phone number allows recording the calls or not + /// New phone number alias + /// Update phone number settings /// Voip - /// + /// Updated phone number settings /// [Update(@"voip/numbers/{numberId:\w+}/settings")] public VoipPhone UpdateSettings(string numberId, string greeting, string holdUp, string wait, string voiceMail, WorkingHours workingHours, bool? allowOutgoingCalls, bool? record, string alias) @@ -271,11 +286,13 @@ namespace ASC.Api.CRM } /// - /// + /// Updates the voip settings with the parameters specified in the request. /// - /// + /// Connection waiting queue + /// Defines if a phone number will be paused or not + /// Update voip settings /// Voip - /// + /// Updated voip settings /// [Update(@"voip/numbers/settings")] public object UpdateSettings(Queue queue, bool pause) @@ -318,11 +335,11 @@ namespace ASC.Api.CRM } /// - /// + /// Returns the voip settings. /// - /// + /// Get voip settings /// Voip - /// + /// Voip settings /// [Read(@"voip/numbers/settings")] @@ -343,11 +360,11 @@ namespace ASC.Api.CRM } /// - /// + /// Returns the links to the voip uploaded files. /// - /// + /// Get the links to the uploaded files /// Voip - /// + /// Links to the voip uploaded files /// [Read(@"voip/uploads")] public IEnumerable GetUploadedFilesUri() @@ -390,11 +407,13 @@ namespace ASC.Api.CRM } /// - /// + /// Deletes an uploaded file with the name specified in the request. /// - /// + /// Audio type + /// Uploaded file name + /// Delete an uploaded file /// Voip - /// + /// Uploaded file /// /// [Delete(@"voip/uploads")] @@ -462,11 +481,12 @@ namespace ASC.Api.CRM #region Operators /// - /// + /// Returns the operators of the phone number with the ID specified in the request. /// - /// + /// Phone number ID + /// Get operators /// Voip - /// + /// Phone number operators /// [Read(@"voip/numbers/{numberId:\w+}/oper")] public IEnumerable GetOperators(string numberId) @@ -475,11 +495,13 @@ namespace ASC.Api.CRM } /// - /// + /// Adds the operators to the phone number with the ID specified in the request. /// - /// + /// Phone number ID + /// Phone number operators + /// Add operators /// Voip - /// + /// Added phone number operators /// /// [Update(@"voip/numbers/{numberId:\w+}/oper")] @@ -505,11 +527,13 @@ namespace ASC.Api.CRM } /// - /// + /// Deletes an operator from the phone number with the ID specified in the request. /// - /// + /// Phone number ID + /// Phone number operator GUID + /// Delete an operator /// Voip - /// + /// Phone number operator /// [Delete(@"voip/numbers/{numberId:\w+}/oper")] public Guid DeleteOperator(string numberId, Guid oper) @@ -534,11 +558,17 @@ namespace ASC.Api.CRM } /// - /// + /// Updates a phone number operator with the parameters specified in the request. /// - /// + /// Phone number operator ID + /// New operator status + /// Defines if an operator allows making the outgoing calls or not + /// Defines if an operator allows recording calls or not + /// New operator answer type + /// New redirect phone number + /// Update an operator /// Voip - /// + /// Updated operator /// [Update(@"voip/opers/{operatorId}")] public Agent UpdateOperator(Guid operatorId, AgentStatus? status, bool? allowOutgoingCalls, bool? record, AnswerType? answerType, string redirectToNumber) @@ -590,11 +620,13 @@ namespace ASC.Api.CRM #region Calls /// - /// + /// Makes a call to the phone number specified in the request. /// - /// + /// Phone number to call + /// Contact ID + /// Make a call /// Voip - /// + /// Phone call information /// [Create(@"voip/call")] public VoipCallWrapper MakeCall(string to, string contactId) @@ -618,11 +650,12 @@ namespace ASC.Api.CRM } /// - /// + /// Answers a phone call with the ID specified in the request. /// - /// + /// Phone call ID + /// Answer a call /// Voip - /// + /// Phone call information [Create(@"voip/call/{callId:\w+}/answer")] public VoipCallWrapper AnswerCall(string callId) { @@ -634,11 +667,12 @@ namespace ASC.Api.CRM } /// - /// + /// Rejects a phone call with the ID specified in the request. /// - /// + /// Phone call ID + /// Reject a call /// Voip - /// + /// Phone call information [Create(@"voip/call/{callId:\w+}/reject")] public VoipCallWrapper RejectCall(string callId) { @@ -650,11 +684,13 @@ namespace ASC.Api.CRM } /// - /// + /// Redirects a phone call with the ID specified in the request. /// - /// + /// Phone call ID + /// Redirect phone number + /// Redirect a call /// Voip - /// + /// Phone call information [Create(@"voip/call/{callId:\w+}/redirect")] public VoipCallWrapper ReditectCall(string callId, string to) { @@ -679,11 +715,18 @@ namespace ASC.Api.CRM } /// - /// + /// Saves a call with the parameters specified in the request. /// - /// + /// Phone call ID + /// Phone number that is calling + /// Phone number to call + /// Phone number ID that answered a call + /// Phone call status + /// Contact ID + /// Phone call price + /// Save a call /// Voip - /// + /// Phone call information [Create(@"voip/call/{callId:\w+}")] public VoipCallWrapper SaveCall(string callId, string from, string to, Guid answeredBy, VoipCallStatus? status, string contactId, decimal? price) { @@ -752,11 +795,11 @@ namespace ASC.Api.CRM } /// - /// + /// Saves a price for the call with the ID specified in the request. /// - /// + /// Phone call ID + /// Save a call price /// Voip - /// [Create(@"voip/price/{callId:\w+}")] public void SavePrice(string callId) { @@ -764,11 +807,17 @@ namespace ASC.Api.CRM } /// - /// + /// Returns a list of the calls matching the parameters specified in the request. /// - /// + /// Phone call type + /// Phone number that is calling + /// Phone number to call + /// Call agent + /// Call client + /// Contact ID + /// Get calls /// Voip - /// + /// List of calls [Read(@"voip/call")] public IEnumerable GetCalls(string callType, ApiDateTime from, ApiDateTime to, Guid? agent, int? client, int? contactID) { @@ -816,11 +865,11 @@ namespace ASC.Api.CRM } /// - /// + /// Returns a list of all the missed calls. /// - /// + /// Get missed calls /// Voip - /// + /// List of missed calls [Read(@"voip/call/missed")] public IEnumerable GetMissedCalls() { @@ -854,11 +903,12 @@ namespace ASC.Api.CRM } /// - /// + /// Returns the detailed information about a phone call with the ID specified in the request. /// - /// + /// Phone call ID + /// Get a call /// Voip - /// + /// Phone call information [Read(@"voip/call/{callId:\w+}")] public VoipCallWrapper GetCall(string callId) { diff --git a/module/ASC.Api/ASC.Api.CRM/Wrappers/ContactInfoWrapper.cs b/module/ASC.Api/ASC.Api.CRM/Wrappers/ContactInfoWrapper.cs index a95eaf21f..846b24ac1 100644 --- a/module/ASC.Api/ASC.Api.CRM/Wrappers/ContactInfoWrapper.cs +++ b/module/ASC.Api/ASC.Api.CRM/Wrappers/ContactInfoWrapper.cs @@ -18,6 +18,7 @@ using System; using System.Runtime.Serialization; +using ASC.Common.Logging; using ASC.CRM.Core; using ASC.Web.CRM.Classes; @@ -38,15 +39,22 @@ namespace ASC.Api.CRM.Wrappers public Address(ContactInfo contactInfo) { if (contactInfo.InfoType != ContactInfoType.Address) throw new ArgumentException(); - - City = JObject.Parse(contactInfo.Data)["city"].Value(); - Country = JObject.Parse(contactInfo.Data)["country"].Value(); - State = JObject.Parse(contactInfo.Data)["state"].Value(); - Street = JObject.Parse(contactInfo.Data)["street"].Value(); - Zip = JObject.Parse(contactInfo.Data)["zip"].Value(); - Category = contactInfo.Category; - CategoryName = contactInfo.CategoryToString(); - IsPrimary = contactInfo.IsPrimary; + try + { + var jResult = JObject.Parse(contactInfo.Data); + City = jResult["city"].Value(); + Country = jResult["country"].Value(); + State = jResult["state"].Value(); + Street = jResult["street"].Value(); + Zip = jResult["zip"].Value(); + Category = contactInfo.Category; + CategoryName = contactInfo.CategoryToString(); + IsPrimary = contactInfo.IsPrimary; + } + catch (Exception ex) + { + LogManager.GetLogger("ASC.Api").Error(ex); + } } public static bool TryParse(ContactInfo contactInfo, out Address res) @@ -65,8 +73,9 @@ namespace ASC.Api.CRM.Wrappers res.IsPrimary = contactInfo.IsPrimary; return true; } - catch (Exception) + catch (Exception ex) { + LogManager.GetLogger("ASC.Api").Error(ex); res = null; return false; } diff --git a/module/ASC.Api/ASC.Api.Calendar/ASC.Api.Calendar.csproj b/module/ASC.Api/ASC.Api.Calendar/ASC.Api.Calendar.csproj index 9ee608e34..6c241ff55 100644 --- a/module/ASC.Api/ASC.Api.Calendar/ASC.Api.Calendar.csproj +++ b/module/ASC.Api/ASC.Api.Calendar/ASC.Api.Calendar.csproj @@ -65,9 +65,13 @@ + + + + @@ -318,6 +322,10 @@ {E7BE6CE8-F6B0-4B9B-831B-BA0C85C8D130} ASC.Web.Studio + + {8c534af7-5696-4e68-9ff4-ffc311893c10} + ASC.Web.Files + {49f07fff-98a5-47d2-a9e9-a46b98c41245} ASC.Api.Core @@ -338,7 +346,7 @@ 4.1.11 - 12.0.3 + 13.0.1 diff --git a/module/ASC.Api/ASC.Api.Calendar/Attachments/AttachmentEngine.cs b/module/ASC.Api/ASC.Api.Calendar/Attachments/AttachmentEngine.cs new file mode 100644 index 000000000..57b523eef --- /dev/null +++ b/module/ASC.Api/ASC.Api.Calendar/Attachments/AttachmentEngine.cs @@ -0,0 +1,170 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using ASC.Common.Logging; +using ASC.Files.Core; +using ASC.Web.Files.Api; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Services.WCFService; +using ASC.Web.Files.Utils; + +using File = ASC.Files.Core.File; +using FileShare = ASC.Files.Core.Security.FileShare; + +namespace ASC.Api.Calendar.Attachments +{ + public class AttachmentEngine + { + private static readonly ILog Logger = LogManager.GetLogger("ASC.Api.Calendar.AttachmentEngine"); + public const string Module = "calendar"; + public const string Bunch = "event"; + public const string Temp = "temp"; + + public static object GetTmpFolderId() + { + return FilesIntegration.RegisterBunch(Module, Bunch, Temp); + } + + public static object GetFolderId(string eventId) + { + return FilesIntegration.RegisterBunch(Module, Bunch, eventId); + } + + public static IEnumerable GetFolderIds(IEnumerable eventIds) + { + return FilesIntegration.RegisterBunchFolders(Module, Bunch, eventIds); + } + + public static IEnumerable GetFiles(string eventId) + { + using (var dao = FilesIntegration.GetFileDao()) + { + var folderId = GetFolderId(eventId); + var fileIds = dao.GetFiles(folderId); + return dao.GetFiles(fileIds); + } + } + + public static File GetFile(object fileId) + { + using (var dao = FilesIntegration.GetFileDao()) + { + return dao.GetFile(fileId); + } + } + + public static File SaveFile(File file, Stream stream) + { + using (var dao = FilesIntegration.GetFileDao()) + { + return dao.SaveFile(file, stream); + } + } + + public static void MoveFile(object fileId, object folderId) + { + using (var dao = FilesIntegration.GetFileDao()) + { + dao.MoveFile(fileId, folderId); + } + } + + public static void DeleteFile(object fileId) + { + using (var dao = FilesIntegration.GetFileDao()) + { + dao.DeleteFile(fileId); + } + } + + public static void DeleteFolder(string eventId) + { + var folderId = GetFolderId(eventId); + DeleteFolders(new[] { folderId }); + } + + public static void DeleteFolders(IEnumerable eventIds) + { + var folderIds = GetFolderIds(eventIds); + DeleteFolders(folderIds); + } + + private static void DeleteFolders(IEnumerable folderIds) + { + try + { + var items = new ItemList(folderIds.Select(folderId => "folder_" + folderId)); + Global.FileStorageService.DeleteItems("delete", items, true, false, true); + } + catch (Exception ex) + { + Logger.Error("ERROR: " + ex.Message); + } + } + + public static void RegisterFileSecurityProvider() + { + FilesIntegration.RegisterFileSecurityProvider(Module, Bunch, new SecurityAdapterProvider()); + } + + public static Uri ShareFileAndGetUri(string fileId) + { + var file = GetFile(fileId); + + var objectId = "file_" + file.ID; + var sharedInfo = Global.FileStorageService.GetSharedInfo(new ItemList { objectId }).Find(r => r.SubjectId == FileConstant.ShareLinkId); + if (sharedInfo == null || sharedInfo.Share == FileShare.Restrict || sharedInfo.Share == FileShare.None) + { + var list = new ItemList + { + new AceWrapper + { + SubjectId = FileConstant.ShareLinkId, + SubjectGroup = true, + Share = FileShare.Read + } + }; + var aceCollection = new AceCollection + { + Entries = new ItemList { objectId }, + Aces = list + }; + Global.FileStorageService.SetAceObject(aceCollection, false); + } + + var uri = new Uri(GetUriString(file)); + return uri; + } + + public static Uri GetUri(object fileId) + { + var file = GetFile(fileId); + var uriString = GetUriString(file); + return new Uri(uriString); + } + + public static string GetUriString(File file) + { + return FileShareLink.GetLink(file); + } + } +} \ No newline at end of file diff --git a/module/ASC.Api/ASC.Api.Calendar/Attachments/SecurityAdapter.cs b/module/ASC.Api/ASC.Api.Calendar/Attachments/SecurityAdapter.cs new file mode 100644 index 000000000..bc8c74649 --- /dev/null +++ b/module/ASC.Api/ASC.Api.Calendar/Attachments/SecurityAdapter.cs @@ -0,0 +1,139 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System; +using System.Collections.Generic; + +using ASC.Core; +using ASC.Files.Core; +using ASC.Files.Core.Security; + +namespace ASC.Api.Calendar.Attachments +{ + public class SecurityAdapter : IFileSecurity + { + private BusinessObjects.Calendar calendarObj; + private BusinessObjects.Event eventObj; + + public SecurityAdapter() + { + } + + public SecurityAdapter(string data) + { + if (int.TryParse(data, out int id)) + { + using (var provider = new BusinessObjects.DataProvider()) + { + eventObj = provider.GetEventById(id); + if (eventObj != null) { + calendarObj = provider.GetCalendarById(Convert.ToInt32(eventObj.CalendarId)); + } + } + } + } + + public SecurityAdapter(BusinessObjects.Event eventObj, BusinessObjects.Calendar calendarObj) + { + this.eventObj = eventObj; + this.calendarObj = calendarObj; + } + + public bool CanRead(FileEntry entry, Guid userId) + { + if (entry == null) return false; + + if (userId == FileConstant.ShareLinkId) + { + return true; + } + + if (eventObj != null) + { + return eventObj.OwnerId == userId || eventObj.SharingOptions.PublicItems.Find(x => x.Id == userId) != null; + } + + return false; + } + + public bool CanCustomFilterEdit(FileEntry file, Guid userId) + { + return false; + } + + public bool CanComment(FileEntry entry, Guid userId) + { + return false; + } + + public bool CanFillForms(FileEntry entry, Guid userId) + { + return false; + } + + public bool CanReview(FileEntry entry, Guid userId) + { + return false; + } + + public bool CanCreate(FileEntry entry, Guid userId) + { + return false; + } + + public bool CanDelete(FileEntry entry, Guid userId) + { + if (eventObj != null) + { + return eventObj.OwnerId == userId + || SecurityContext.PermissionResolver.Check(CoreContext.Authentication.GetAccountByID(userId), eventObj, + null, CalendarAccessRights.FullAccessAction) + || SecurityContext.PermissionResolver.Check(CoreContext.Authentication.GetAccountByID(userId), calendarObj, + null, CalendarAccessRights.FullAccessAction); + } + + return false; + } + + public bool CanEdit(FileEntry entry, Guid userId) + { + return false; + } + + public IEnumerable WhoCanRead(FileEntry entry) + { + var list = new List { FileConstant.ShareLinkId }; + + if (eventObj != null) + { + list.Add(eventObj.OwnerId); + + foreach (var item in eventObj.SharingOptions.PublicItems) + { + list.Add(item.Id); + } + } + + return list; + } + + public bool CanDownload(FileEntry entry, Guid userId) + { + return CanRead(entry, userId); + } + } +} \ No newline at end of file diff --git a/module/ASC.Api/ASC.Api.Calendar/Attachments/SecurityAdapterProvider.cs b/module/ASC.Api/ASC.Api.Calendar/Attachments/SecurityAdapterProvider.cs new file mode 100644 index 000000000..7a131260d --- /dev/null +++ b/module/ASC.Api/ASC.Api.Calendar/Attachments/SecurityAdapterProvider.cs @@ -0,0 +1,56 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System; +using System.Collections.Generic; + +using ASC.Api.Calendar.BusinessObjects; +using ASC.Files.Core.Security; + +namespace ASC.Api.Calendar.Attachments +{ + public class SecurityAdapterProvider : IFileSecurityProvider + { + public IFileSecurity GetFileSecurity(string data) + { + return new SecurityAdapter(data); + } + + public Dictionary GetFileSecurity(Dictionary data) + { + var result = new Dictionary(); + using (var provider = new DataProvider()) + { + foreach (var item in data) + { + if (int.TryParse(item.Value, out int id)) + { + var eventObj = provider.GetEventById(id); + if(eventObj != null) + { + var calendarObj = provider.GetCalendarById(Convert.ToInt32(eventObj.CalendarId)); + result.Add(item.Key, new SecurityAdapter(eventObj, calendarObj)); + continue; + } + } + result.Add(item.Key, new SecurityAdapter()); + } + } + return result; + } + } +} diff --git a/module/ASC.Api/ASC.Api.Calendar/BusinessObjects/DataProvider.cs b/module/ASC.Api/ASC.Api.Calendar/BusinessObjects/DataProvider.cs index df092f880..d9a288872 100644 --- a/module/ASC.Api/ASC.Api.Calendar/BusinessObjects/DataProvider.cs +++ b/module/ASC.Api/ASC.Api.Calendar/BusinessObjects/DataProvider.cs @@ -23,6 +23,7 @@ using System.Threading.Tasks; using System.Web; using ASC.Api.Calendar.ExternalCalendars; +using ASC.Api.Calendar.Attachments; using ASC.Api.Calendar.iCalParser; using ASC.Common.Data; using ASC.Common.Data.Sql; @@ -45,6 +46,7 @@ namespace ASC.Api.Calendar.BusinessObjects private const string _eventTable = "calendar_events evt"; private const string _todoTable = "calendar_todos td"; private const string _eventItemTable = "calendar_event_item evt_itm"; + private ILog Log = LogManager.GetLogger("ASC.Calendar"); private static string eventUidDomain; @@ -393,7 +395,8 @@ namespace ASC.Api.Calendar.BusinessObjects int calendarId; using (var tr = db.BeginTransaction()) { - + description = StringUtils.NormalizeStringForMySql(description); + name = StringUtils.NormalizeStringForMySql(name); calendarId = db.ExecuteScalar(new SqlInsert("calendar_calendars") .InColumnValue("id", 0) .InColumnValue("tenant", @@ -611,7 +614,7 @@ namespace ASC.Api.Calendar.BusinessObjects { using (var tr = db.BeginTransaction()) { - var eventsData = db.ExecuteList( new SqlQuery("calendar_events e") + var eventsData = db.ExecuteList(new SqlQuery("calendar_events e") .Select(cc.SelectQuery) .Where("e.calendar_id", calendarId) .Where("e.tenant", tenant.TenantId)); @@ -632,13 +635,13 @@ namespace ASC.Api.Calendar.BusinessObjects } catch (Exception ex) { - LogManager.GetLogger("ASC.Calendar").Error(ex); + Log.Error(ex); } }, TaskCreationOptions.LongRunning); updateNotifications.ConfigureAwait(false); updateNotifications.Start(); - + } else @@ -704,7 +707,7 @@ namespace ASC.Api.Calendar.BusinessObjects } catch (Exception ex) { - LogManager.GetLogger("ASC.Calendar").Error(ex); + Log.Error(ex); } }, TaskCreationOptions.LongRunning); @@ -729,7 +732,7 @@ namespace ASC.Api.Calendar.BusinessObjects } catch (Exception ex) { - LogManager.GetLogger("ASC.Calendar").Error(ex); + Log.Error(ex); } db.ExecuteNonQuery(new SqlDelete("calendar_calendars").Where("id", calendarId)); @@ -833,6 +836,8 @@ namespace ASC.Api.Calendar.BusinessObjects using (var tr = db.BeginTransaction()) { + description = StringUtils.NormalizeStringForMySql(description); + name = StringUtils.NormalizeStringForMySql(name); todoId = db.ExecuteScalar(new SqlInsert("calendar_todos") .InColumnValue("id", 0) .InColumnValue("tenant", @@ -885,6 +890,7 @@ namespace ASC.Api.Calendar.BusinessObjects .Select(cc.SelectQuery) .Where(Exp.In(tdId.Name, todoIds)) + .Where("owner_id", userId) .Where("tenant", tenantId)); } else @@ -892,6 +898,7 @@ namespace ASC.Api.Calendar.BusinessObjects data = db.ExecuteList(new SqlQuery(_todoTable) .Select(cc.SelectQuery) + .Where("owner_id", userId) .Where(Exp.In(tdId.Name, todoIds))); } @@ -1018,6 +1025,7 @@ namespace ASC.Api.Calendar.BusinessObjects var eUid = cc.RegistryColumn("evt.uid"); var eStatus = cc.RegistryColumn("evt.status"); var eTimeZone = cc.RegistryColumn("evt.time_zone"); + var eHasAttachments = cc.RegistryColumn("evt.has_attachments"); var data = new List(); @@ -1088,7 +1096,8 @@ namespace ASC.Api.Calendar.BusinessObjects RecurrenceRule = eRRule.Parse(r), Uid = eUid.Parse(r), Status = (EventStatus)eStatus.Parse(r), - TimeZone = eTimeZone.Parse(r) + TimeZone = eTimeZone.Parse(r), + HasAttachments = eHasAttachments.Parse(r) }; events.Add(ev); } @@ -1195,12 +1204,14 @@ namespace ASC.Api.Calendar.BusinessObjects string uid, EventStatus status, DateTime createDate, - TimeZoneInfo timeZone) + TimeZoneInfo timeZone, + bool hasAttachments) { int eventId; using (var tr = db.BeginTransaction()) { - + description = StringUtils.NormalizeStringForMySql(description); + name = StringUtils.NormalizeStringForMySql(name); eventId = db.ExecuteScalar(new SqlInsert("calendar_events") .InColumnValue("id", 0) .InColumnValue("tenant", @@ -1222,6 +1233,7 @@ namespace ASC.Api.Calendar.BusinessObjects .InColumnValue("uid", GetEventUid(uid)) .InColumnValue("status", (int)status) .InColumnValue("time_zone", timeZone?.Id) + .InColumnValue("has_attachments", hasAttachments) .Identity(0, 0, true)); foreach (var item in publicItems) @@ -1255,7 +1267,8 @@ namespace ASC.Api.Calendar.BusinessObjects List publicItems, EventStatus status, DateTime createDate, - TimeZoneInfo timeZone + TimeZoneInfo timeZone, + bool hasAttachments ) { using (var tr = db.BeginTransaction()) @@ -1272,6 +1285,7 @@ namespace ASC.Api.Calendar.BusinessObjects .Set("rrule", rrule.ToString()) .Set("status", (int)status) .Set("time_zone", timeZone?.Id) + .Set("has_attachments", hasAttachments) .Where(Exp.Eq("id", eventId)); if (ownerId.Equals(SecurityContext.CurrentAccount.ID)) diff --git a/module/ASC.Api/ASC.Api.Calendar/CalendarApi.cs b/module/ASC.Api/ASC.Api.Calendar/CalendarApi.cs index 4de781599..b370409c2 100644 --- a/module/ASC.Api/ASC.Api.Calendar/CalendarApi.cs +++ b/module/ASC.Api/ASC.Api.Calendar/CalendarApi.cs @@ -32,6 +32,7 @@ using System.Threading.Tasks; using System.Web; using ASC.Api.Attributes; +using ASC.Api.Calendar.Attachments; using ASC.Api.Calendar.BusinessObjects; using ASC.Api.Calendar.ExternalCalendars; using ASC.Api.Calendar.iCalParser; @@ -45,12 +46,14 @@ using ASC.Common.Caching; using ASC.Common.Data; using ASC.Common.Data.Sql; using ASC.Common.Logging; +using ASC.Common.Radicale; using ASC.Common.Security; using ASC.Common.Utils; using ASC.Core; using ASC.Security.Cryptography; using ASC.Specific; using ASC.Web.Core.Calendars; +using ASC.Web.Core.Files; using ASC.Web.Studio.Core; using ASC.Web.Studio.Utility; @@ -63,11 +66,15 @@ using SecurityContext = ASC.Core.SecurityContext; namespace ASC.Api.Calendar { + /// + /// Access to the calendars. + /// public class iCalApiContentResponse : IApiContentResponce { private readonly Stream _stream; private readonly string _fileName; + public iCalApiContentResponse(Stream stream, string fileName) { _stream = stream; @@ -180,14 +187,15 @@ namespace ASC.Api.Calendar #region Calendars & Subscriptions /// - /// Returns the list of all dates which contain the events from the displayed calendars + /// Returns a list of all the event days from one period till another. /// /// - /// Calendar events + /// Get event days /// /// Period start date /// Period end date - /// Date list + /// Events + /// List of dates /// false [Read("eventdays/{startDate}/{endDate}")] public List GetEventDays(ApiDateTime startDate, ApiDateTime endDate) @@ -258,18 +266,27 @@ namespace ASC.Api.Calendar } /// - /// Returns the list of calendars and subscriptions with the events for the current user for the selected period + /// Returns a list of calendars with the events for the current user in the selected period. /// /// - /// Calendars and subscriptions + /// Get calendars /// /// Period start date /// Period end date - /// List of calendars and subscriptions with events + /// Calendars and subscriptions + /// List of calendars with events [Read("calendars/{startDate}/{endDate}")] public List LoadCalendars(ApiDateTime startDate, ApiDateTime endDate) { - var result = LoadInternalCalendars(); + int newCalendarsCount; + var calendars = _dataProvider.LoadCalendarsForUser(SecurityContext.CurrentAccount.ID, out newCalendarsCount); + + var isFirstEntry = !calendars.Exists(c => !c.IsiCalStream() && + !c.Id.Equals(SharedEventsCalendar.CalendarId, StringComparison.InvariantCultureIgnoreCase) && + c.OwnerId.Equals(SecurityContext.CurrentAccount.ID)); + + var result = LoadInternalCalendars(calendars); + //external if (!IsPersonal) @@ -308,15 +325,45 @@ namespace ASC.Api.Calendar result.ForEach(c => c.Events = c.UserCalendar.GetEventWrappers(SecurityContext.CurrentAccount.ID, startDate, endDate)); } + if (isFirstEntry) + { + var tenant = CoreContext.TenantManager.GetCurrentTenant(); + var myUri = HttpContext.Current.Request.GetUrlRewriter(); + var updateNotifications = new Task(() => + { + CoreContext.TenantManager.SetCurrentTenant(tenant); + try + { + var firstCal = result.FindAll(c => !c.IsSubscription).FirstOrDefault(); + var firstCalResult = GetCalendarCalDavUrl(firstCal.Id, myUri).Result; + var extCalendars = CalendarManager.Instance.GetCalendarsForUser(SecurityContext.CurrentAccount.ID); + foreach (var calendar in extCalendars) + { + var extCalResult = GetCalendarCalDavUrl(calendar.Id, myUri).Result; + } + } + catch (Exception e) + { + Logger.Error(e.Message); + } + + }, TaskCreationOptions.LongRunning); + + updateNotifications.ConfigureAwait(false); + updateNotifications.Start(); + + } + return result; + } - private List LoadInternalCalendars() + private List LoadInternalCalendars(List userCalendars = null) { var result = new List(); int newCalendarsCount; //internal - var calendars = _dataProvider.LoadCalendarsForUser(SecurityContext.CurrentAccount.ID, out newCalendarsCount); + var calendars = userCalendars != null ? userCalendars : _dataProvider.LoadCalendarsForUser(SecurityContext.CurrentAccount.ID, out newCalendarsCount); var userTimeZone = CoreContext.TenantManager.GetCurrentTenant().TimeZone; @@ -349,16 +396,16 @@ namespace ASC.Api.Calendar result.Add(new CalendarWrapper(firstCal)); } - return result; } /// - /// Returns the list of all subscriptions available to the user + /// Returns a list of all the subscriptions available to the current user. /// /// - /// Subscription list + /// Get subscriptions /// + /// Calendars and subscriptions /// List of subscriptions [Read("subscriptions")] public List LoadSubscriptions() @@ -399,12 +446,13 @@ namespace ASC.Api.Calendar } /// - /// Updates the subscription state either subscribing or unsubscribing the user to/from it + /// Updates the subscription states either subscribing or unsubscribing the user to/from it. /// /// - /// Update subscription + /// Update the subscription states /// - /// Updated subscription states + /// New subscription states + /// Calendars and subscriptions /// false [Update("subscriptions/manage")] public void ManageSubscriptions(IEnumerable states) @@ -431,12 +479,13 @@ namespace ASC.Api.Calendar } /// - /// Returns the detailed information about the calendar with the ID specified in the request + /// Returns the detailed information about a calendar with the ID specified in the request. /// /// - /// Calendar by ID + /// Get a calendar by ID /// /// Calendar ID + /// Calendars and subscriptions /// Calendar [Read("{calendarId}")] public CalendarWrapper GetCalendarById(string calendarId) @@ -475,10 +524,10 @@ namespace ASC.Api.Calendar } /// - /// Creates the new calendar with the parameters (name, description, color, etc.) specified in the request + /// Creates a new calendar with the parameters (name, description, color, etc.) specified in the request. /// /// - /// Create calendar + /// Create a calendar /// /// Calendar name /// Calendar description @@ -487,11 +536,12 @@ namespace ASC.Api.Calendar /// Calendar time zone /// Event alert type, in case alert type is set by default /// Calendar sharing options with other users - /// iCal url - /// Calendar for todo list + /// iCal URL + /// Defines if this calendar is for the todo list + /// Calendars and subscriptions /// Created calendar [Create("")] - public CalendarWrapper CreateCalendar(string name, string description, string textColor, string backgroundColor, string timeZone, EventAlertType alertType, List sharingOptions, string iCalUrl, int isTodo = 0) + public async Task CreateCalendar(string name, string description, string textColor, string backgroundColor, string timeZone, EventAlertType alertType, List sharingOptions, string iCalUrl, int isTodo = 0) { var sharingOptionsList = sharingOptions ?? new List(); var timeZoneInfo = TimeZoneConverter.GetTimeZone(timeZone); @@ -510,14 +560,9 @@ namespace ASC.Api.Calendar var currentUserEmail = CheckUserEmail(currentUser) ? CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID).Email : null; var currentUserName = currentUserEmail != null ? currentUserEmail.ToLower() + "@" + myUri.Host : null; var tenant = CoreContext.TenantManager.GetCurrentTenant(); - var caldavTask = new Task(() => - { - CoreContext.TenantManager.SetCurrentTenant(tenant); - CreateCalDavCalendar(name, description, backgroundColor, calDavGuid.ToString(), myUri, currentUserName, currentUserEmail); - - }); - caldavTask.Start(); + await CreateCalDavCalendar(name, description, backgroundColor, calDavGuid.ToString(), myUri, currentUserName, currentUserEmail).ConfigureAwait(false); + CoreContext.TenantManager.SetCurrentTenant(tenant.TenantId); var cal = _dataProvider.CreateCalendar( SecurityContext.CurrentAccount.ID, name, description, textColor, backgroundColor, timeZoneInfo, alertType, null, sharingOptionsList.Select(o => o as SharingOptions.PublicItem).ToList(), @@ -549,7 +594,7 @@ namespace ASC.Api.Calendar { var cals = DDayICalParser.DeserializeCalendar(tempReader); - ImportEvents(Convert.ToInt32(cal.Id), cals); + await ImportEvents(Convert.ToInt32(cal.Id), cals).ConfigureAwait(false); } } } @@ -562,13 +607,9 @@ namespace ASC.Api.Calendar return new CalendarWrapper(cal); } - private string HexFromRGB(int r, int g, int b) - { - return String.Format("#{0:X2}{1:X2}{2:X2}", r, g, b); - } - private void UpdateSharedCalDavCalendar(DataProvider dataProvider, string name, string description, string backgroundColor, string calDavGuid, Uri myUri, List sharingOptionsList, List events, string calendarId, string calendarGuid, int tenantId, DateTime updateDate = default(DateTime), + private async Task UpdateSharedCalDavCalendarAsync(DataProvider dataProvider, string name, string description, string backgroundColor, string calDavGuid, Uri myUri, List sharingOptionsList, List events, string calendarId, string calendarGuid, int tenantId, DateTime updateDate = default(DateTime), VTimeZone calendarVTimeZone = null, TimeZoneInfo calendarTimeZone = null) { @@ -580,17 +621,21 @@ namespace ASC.Api.Calendar var parseCalendar = DDayICalParser.DeserializeCalendar(calendarIcs); var calendar = parseCalendar.FirstOrDefault(); + foreach (var sharingParam in sharingOptionsList) { var fullAccess = sharingParam.actionId == AccessOption.FullAccessOption.Id || sharingParam.actionId == AccessOption.OwnerOption.Id; + + CoreContext.TenantManager.SetCurrentTenant(tenantId); if (sharingParam.isGroup) { + var updateGroupTask = new List(); var users = CoreContext.UserManager.GetUsersByGroup(sharingParam.itemId); foreach (var userGroup in users) { - UpdateCalDavCalendar(name, description, backgroundColor, calDavGuid, myUri, userGroup.Email.ToLower(), true); + updateGroupTask.Add(UpdateCalDavCalendar(name, description, backgroundColor, calDavGuid, myUri, userGroup.Email.ToLower(), true)); foreach (var e in events) { @@ -606,17 +651,19 @@ namespace ASC.Api.Calendar var ics = DDayICalParser.SerializeCalendar(calendar); - UpdateSharedEvent(userGroup, evt.Uid, fullAccess, myUri, ics, calendarGuid, updateDate, calendarVTimeZone, calendarTimeZone); + updateGroupTask.Add(UpdateSharedEvent(userGroup, evt.Uid, fullAccess, myUri, ics, calendarGuid, updateDate, calendarVTimeZone, calendarTimeZone)); } } + await Task.WhenAll(updateGroupTask).ConfigureAwait(false); } else { var user = CoreContext.UserManager.GetUsers(sharingParam.itemId); + var updateUserTasks = new List(); if (!CheckUserEmail(user)) return; - UpdateCalDavCalendar(name, description, backgroundColor, calDavGuid, myUri, user.Email.ToLower(), true); + updateUserTasks.Add(UpdateCalDavCalendar(name, description, backgroundColor, calDavGuid, myUri, user.Email.ToLower(), true)); foreach (var sharedEvent in events) { @@ -632,8 +679,10 @@ namespace ASC.Api.Calendar var ics = DDayICalParser.SerializeCalendar(calendar); - UpdateSharedEvent(user, evt.Uid, fullAccess, myUri, ics, calendarGuid, updateDate, calendarVTimeZone, calendarTimeZone); + updateUserTasks.Add(UpdateSharedEvent(user, evt.Uid, fullAccess, myUri, ics, calendarGuid, updateDate, calendarVTimeZone, calendarTimeZone)); } + + await Task.WhenAll(updateUserTasks).ConfigureAwait(false); } } } @@ -644,182 +693,34 @@ namespace ASC.Api.Calendar } - private string UpdateCalDavCalendar(string name, string description, string backgroundColor, string calDavGuid, Uri myUri, string email, bool isSharedCalendar = false) - { - var currentUserName = email.ToLower() + "@" + myUri.Host; - var calDavServerUrl = myUri.Scheme + "://" + myUri.Host + "/caldav"; - - var requestUrl = calDavServerUrl + "/" + HttpUtility.UrlEncode(currentUserName) + "/" + calDavGuid + (isSharedCalendar ? "-shared" : ""); - - name = (name ?? "").Trim(); - if (String.IsNullOrEmpty(name)) - throw new Exception(Resources.CalendarApiResource.ErrorEmptyName); - - description = (description ?? "").Trim(); - backgroundColor = (backgroundColor ?? "").Trim(); - - Logger.Info("RADICALE REWRITE URL: " + myUri); - - string[] numbers = Regex.Split(backgroundColor, @"\D+"); - var color = numbers.Length > 4 ? HexFromRGB(int.Parse(numbers[1]), int.Parse(numbers[2]), int.Parse(numbers[3])) : "#000000"; - var data = "" + - "" + - "" + - "" + - "" + name + "" + - "" + color + "" + - "" + description + "" + - ""; - try - { - var webRequest = (HttpWebRequest)WebRequest.Create(requestUrl); - webRequest.Method = "PROPPATCH"; - webRequest.ContentType = "text/calendar; charset=utf-8"; - - var authorization = isSharedCalendar ? GetSystemAuthorization() : GetUserAuthorization(email); - webRequest.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(authorization))); - - var encoding = new UTF8Encoding(); - byte[] bytes = encoding.GetBytes(data); - webRequest.ContentLength = bytes.Length; - using (var writeStream = webRequest.GetRequestStream()) - { - writeStream.Write(bytes, 0, bytes.Length); - } - - using (var webResponse = webRequest.GetResponse()) - using (var reader = new StreamReader(webResponse.GetResponseStream())) - { - return requestUrl; - } - } - catch (Exception ex) - { - Logger.Error(ex); - return ""; - } - } - - private string CreateCalDavCalendar(string name, string description, string backgroundColor, string calDavGuid, Uri myUri, string currentUserName, string email, bool isSharedCalendar = false) - { - name = (name ?? "").Trim(); - if (String.IsNullOrEmpty(name)) - throw new Exception(Resources.CalendarApiResource.ErrorEmptyName); - - description = (description ?? "").Trim(); - backgroundColor = (backgroundColor ?? "").Trim(); - - var calDavServerUrl = myUri.Scheme + "://" + myUri.Host + "/caldav"; - - Logger.Info("RADICALE REWRITE URL: " + myUri); - - string[] numbers = Regex.Split(backgroundColor, @"\D+"); - var color = numbers.Length > 4 ? HexFromRGB(int.Parse(numbers[1]), int.Parse(numbers[2]), int.Parse(numbers[3])) : "#000000"; - - var data = "" + - "" + - "" + - "" + - "" + - "" + name + "" + - "" + color + "" + - "" + description + ""; - - var requestUrl = calDavServerUrl + "/" + HttpUtility.UrlEncode(currentUserName) + "/" + calDavGuid + (isSharedCalendar ? "-shared" : ""); - - try - { - var webRequest = (HttpWebRequest)WebRequest.Create(requestUrl); - webRequest.Method = "MKCOL"; - webRequest.ContentType = "text/plain;charset=UTF-8"; - - var authorization = isSharedCalendar ? GetSystemAuthorization() : GetUserAuthorization(email); - webRequest.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(authorization))); - - var encoding = new UTF8Encoding(); - byte[] bytes = encoding.GetBytes(data); - webRequest.ContentLength = bytes.Length; - using (Stream writeStream = webRequest.GetRequestStream()) - { - writeStream.Write(bytes, 0, bytes.Length); - } - - using (var webResponse = webRequest.GetResponse()) - using (var reader = new StreamReader(webResponse.GetResponseStream())) - { - reader.ReadToEnd(); - return calDavServerUrl + "/" + currentUserName + "/" + calDavGuid + (isSharedCalendar ? "-shared" : ""); - } - } - catch (Exception ex) - { - Logger.Error(ex); - return ""; - } - } - - private static string GetUserCaldavCalendar(string calUrl, string email) - { - var webRequest = (HttpWebRequest)WebRequest.Create(calUrl); - webRequest.Method = "GET"; - webRequest.ContentType = "text/calendar; charset=utf-8"; - - var authorization = GetUserAuthorization(email); - webRequest.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(authorization))); - - try - { - using (var webResponse = webRequest.GetResponse()) - using (var reader = new StreamReader(webResponse.GetResponseStream())) - { - string ics = reader.ReadToEnd(); - if (!string.IsNullOrEmpty(ics)) - { - return ics; - } - return ""; - - } - } - catch (WebException exception) - { - if (exception.Status == WebExceptionStatus.ProtocolError && exception.Response != null) - { - var resp = (HttpWebResponse)exception.Response; - if (resp.StatusCode == HttpStatusCode.NotFound) - { - return "NotFound"; - } - } - Logger.Info("ERROR. Get calendar CalDav url: " + exception.Message); - return ""; - } - } /// - /// Updates the selected calendar with the parameters (name, description, color, etc.) specified in the request for the current user and access rights for other users + /// Updates the selected calendar with the parameters (name, description, color, etc.) specified in the request. /// /// - /// Update calendar + /// Update a calendar /// /// Calendar ID - /// Calendar new name - /// Calendar new description - /// Event text color - /// Event background color - /// Calendar time zone - /// Event alert type, in case alert type is set by default - /// Display type: show or hide events in calendar - /// Calendar sharing options with other users - /// iCal url + /// New calendar name + /// New calendar description + /// New event text color + /// New event background color + /// New calendar time zone + /// New event alert type, in case alert type is set by default + /// Display type: show or hide events in the calendar + /// New calendar sharing options with other users + /// New iCal URL + /// Calendars and subscriptions /// Updated calendar [Update("{calendarId}")] - public CalendarWrapper UpdateCalendar(string calendarId, string name, string description, string textColor, string backgroundColor, string timeZone, EventAlertType alertType, bool hideEvents, List sharingOptions, string iCalUrl = "") + public async Task UpdateCalendar(string calendarId, string name, string description, string textColor, string backgroundColor, string timeZone, EventAlertType alertType, bool hideEvents, List sharingOptions, string iCalUrl = "") { TimeZoneInfo timeZoneInfo = TimeZoneConverter.GetTimeZone(timeZone); int calId; + var currentTenantId = CoreContext.TenantManager.GetCurrentTenant().TenantId; + var myUri = HttpContext.Current.Request.GetUrlRewriter(); if (!string.IsNullOrEmpty(iCalUrl)) { try @@ -836,7 +737,7 @@ namespace ASC.Api.Calendar { var cals = DDayICalParser.DeserializeCalendar(tempReader); - ImportEvents(Convert.ToInt32(calendarId), cals); + await ImportEvents(Convert.ToInt32(calendarId), cals).ConfigureAwait(false); } } } @@ -847,7 +748,7 @@ namespace ASC.Api.Calendar } - + CoreContext.TenantManager.SetCurrentTenant(currentTenantId); if (int.TryParse(calendarId, out calId)) { var oldCal = _dataProvider.GetCalendarById(calId); @@ -900,7 +801,7 @@ namespace ASC.Api.Calendar description = oldCal.Description; } - var myUri = HttpContext.Current.Request.GetUrlRewriter(); + var _email = CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID).Email; var cal = _dataProvider.UpdateCalendar(calId, name, description, @@ -910,102 +811,10 @@ namespace ASC.Api.Calendar var oldSharingList = new List(); var owner = CoreContext.UserManager.GetUsers(cal.OwnerId); - if (SecurityContext.CurrentAccount.ID != cal.OwnerId) - { - if (CheckUserEmail(owner)) - { - UpdateCalDavCalendar(name, description, backgroundColor, oldCal.calDavGuid, myUri, owner.Email); - } - } - else - { - UpdateCalDavCalendar(name, description, backgroundColor, oldCal.calDavGuid, myUri, _email); - } - var pic = PublicItemCollection.GetForCalendar(oldCal); - if (pic.Items.Count > 1) - { - oldSharingList.AddRange(from publicItem in pic.Items - where publicItem.ItemId != owner.ID.ToString() - select new SharingParam - { - Id = Guid.Parse(publicItem.ItemId), - isGroup = publicItem.IsGroup, - actionId = publicItem.SharingOption.Id - }); - } - if (sharingOptionsList.Count > 0) - { - var tenant = CoreContext.TenantManager.GetCurrentTenant(); - var events = cal.LoadEvents(SecurityContext.CurrentAccount.ID, DateTime.MinValue, DateTime.MaxValue); - var calendarObjViewSettings = cal != null && cal.ViewSettings != null ? cal.ViewSettings.FirstOrDefault() : null; - var targetCalendar = DDayICalParser.ConvertCalendar(cal != null ? cal.GetUserCalendar(calendarObjViewSettings) : null); - - var caldavTask = new Task(() => - { - using (var dataProvider = new DataProvider()) - { - UpdateSharedCalDavCalendar(dataProvider, name, description, backgroundColor, oldCal.calDavGuid, myUri, sharingOptionsList, events, calendarId, cal.calDavGuid, tenant.TenantId, DateTime.Now, targetCalendar.TimeZones[0], cal.TimeZone); - } - }, TaskCreationOptions.LongRunning); - - caldavTask.ConfigureAwait(false); - caldavTask.Start(); - } - - oldSharingList.RemoveAll(c => sharingOptionsList.Contains(sharingOptionsList.Find((x) => x.Id == c.Id))); - - if (oldSharingList.Count > 0) - { - var currentTenantId = TenantProvider.CurrentTenantID; - var caldavHost = myUri.Host; - - var replaceSharingEventTask = new Task(() => - { - try - { - CoreContext.TenantManager.SetCurrentTenant(currentTenantId); - - foreach (var sharingOption in oldSharingList) - { - if (!sharingOption.IsGroup) - { - var user = CoreContext.UserManager.GetUsers(sharingOption.itemId); - if (CheckUserEmail(user)) - { - var currentUserName = user.Email.ToLower() + "@" + caldavHost; - var userEmail = user.Email; - RemoveCaldavCalendar(currentUserName, userEmail, cal.calDavGuid, myUri, user.ID != cal.OwnerId); - } - } - else - { - var users = CoreContext.UserManager.GetUsersByGroup(sharingOption.itemId); - foreach (var user in users) - { - if (CheckUserEmail(user)) - { - var userEmail = user.Email; - var currentUserName = userEmail.ToLower() + "@" + caldavHost; - RemoveCaldavCalendar(currentUserName, userEmail, cal.calDavGuid, myUri, user.ID != cal.OwnerId); - } - } - } - } - } - catch (Exception ex) - { - LogManager.GetLogger("ASC.Calendar").Error(ex.Message); - } - - }, TaskCreationOptions.LongRunning); - replaceSharingEventTask.ConfigureAwait(false); - replaceSharingEventTask.Start(); - } - if (cal != null) { //clear old rights - CoreContext.AuthorizationManager.RemoveAllAces(cal); + CoreContext.AuthorizationManager.RemoveAllAces(cal); // TODO: an understandable error related to tenant availability foreach (var opt in sharingOptionsList) if (String.Equals(opt.actionId, AccessOption.FullAccessOption.Id, StringComparison.InvariantCultureIgnoreCase)) @@ -1013,8 +822,56 @@ namespace ASC.Api.Calendar //notify CalendarNotifyClient.NotifyAboutSharingCalendar(cal, oldCal); + + if (SecurityContext.CurrentAccount.ID != cal.OwnerId) + { + if (CheckUserEmail(owner)) + { + await UpdateCalDavCalendar(name, description, backgroundColor, oldCal.calDavGuid, myUri, owner.Email); + } + } + else + { + await UpdateCalDavCalendar(name, description, backgroundColor, oldCal.calDavGuid, myUri, _email); + } + var pic = PublicItemCollection.GetForCalendar(oldCal); + if (pic.Items.Count > 1) + { + oldSharingList.AddRange(from publicItem in pic.Items + where publicItem.ItemId != owner.ID.ToString() + select new SharingParam + { + Id = Guid.Parse(publicItem.ItemId), + isGroup = publicItem.IsGroup, + actionId = publicItem.SharingOption.Id + }); + } + if (sharingOptionsList.Count > 0) + { + var tenant = CoreContext.TenantManager.GetCurrentTenant(); + var events = cal.LoadEvents(SecurityContext.CurrentAccount.ID, DateTime.MinValue, DateTime.MaxValue); + var calendarObjViewSettings = cal != null && cal.ViewSettings != null ? cal.ViewSettings.FirstOrDefault() : null; + var targetCalendar = DDayICalParser.ConvertCalendar(cal != null ? cal.GetUserCalendar(calendarObjViewSettings) : null); + + using (var dataProvider = new DataProvider()) + { + await UpdateSharedCalDavCalendarAsync(dataProvider, name, description, backgroundColor, oldCal.calDavGuid, myUri, sharingOptionsList, events, calendarId, cal.calDavGuid, tenant.TenantId, DateTime.Now, targetCalendar.TimeZones[0], cal.TimeZone) + .ConfigureAwait(false); + } + + + } + + oldSharingList.RemoveAll(c => sharingOptionsList.Contains(sharingOptionsList.Find((x) => x.Id == c.Id))); + + if (oldSharingList.Count > 0) + { + await ReplaceUpdateCalDavSharingEvent(oldSharingList, myUri, cal).ConfigureAwait(false); + } + return new CalendarWrapper(cal); } + return null; } } @@ -1024,19 +881,24 @@ namespace ASC.Api.Calendar } + + + + /// - /// Change the calendar display parameters specified in the request for the current user + /// Updates the calendar display parameters specified in the request for the current user. /// /// - /// Update calendar user view + /// Update the calendar view /// /// Calendar ID - /// Calendar name - /// Event text color - /// Event background color - /// Calendar time zone - /// Event alert type, in case alert type is set by default + /// New calendar name + /// New event text color + /// New event background color + /// New calendar time zone + /// New event alert type, in case alert type is set by default /// Display type: show or hide events in calendar + /// Calendars and subscriptions /// Updated calendar [Update("{calendarId}/view")] public CalendarWrapper UpdateCalendarView(string calendarId, string name, string textColor, string backgroundColor, string timeZone, EventAlertType alertType, bool hideEvents) @@ -1064,14 +926,15 @@ namespace ASC.Api.Calendar } /// - /// Deletes the calendar with the ID specified in the request + /// Deletes a calendar with the ID specified in the request. /// /// - /// Delete calendar + /// Delete a calendar /// /// Calendar ID + /// Calendars and subscriptions [Delete("{calendarId}")] - public void RemoveCalendar(int calendarId) + public async Task RemoveCalendar(int calendarId) { var cal = _dataProvider.GetCalendarById(calendarId); var events = cal.LoadEvents(SecurityContext.CurrentAccount.ID, DateTime.MinValue, DateTime.MaxValue); @@ -1083,12 +946,15 @@ namespace ASC.Api.Calendar CoreContext.AuthorizationManager.RemoveAllAces(cal); var caldavGuid = _dataProvider.RemoveCalendar(calendarId); + var eventIds = events.Select(x => x.Id); + AttachmentEngine.DeleteFolders(eventIds); + var myUri = HttpContext.Current.Request.GetUrlRewriter(); var currentTenantId = TenantProvider.CurrentTenantID; var caldavHost = myUri.Host; if (caldavGuid != Guid.Empty) { - LogManager.GetLogger("ASC.Calendar").Info("RADICALE REWRITE URL: " + myUri); + Logger.Info("RADICALE REWRITE URL: " + myUri); var currentUser = CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID); var currentUserEmail = CheckUserEmail(currentUser) ? CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID).Email : null; @@ -1096,27 +962,22 @@ namespace ASC.Api.Calendar if (currentUserEmail != null) { - var caldavTask = new Task(() => + try { - try - { - CoreContext.TenantManager.SetCurrentTenant(currentTenantId); - RemoveCaldavCalendar(currentUserName, currentUserEmail, caldavGuid.ToString(), myUri); - } - catch (Exception ex) - { - LogManager.GetLogger("ASC.Calendar").Error(ex.Message); - } - - }, TaskCreationOptions.LongRunning); - caldavTask.ConfigureAwait(false); - caldavTask.Start(); + await RemoveCaldavCalendar(currentUserEmail, caldavGuid.ToString(), myUri).ConfigureAwait(false); + } + catch (Exception ex) + { + LogManager.GetLogger("ASC.Calendar").Error(ex.Message); + } } } var sharingList = new List(); + CoreContext.TenantManager.SetCurrentTenant(currentTenantId); if (pic.Items.Count > 1) { + sharingList.AddRange(from publicItem in pic.Items where publicItem.ItemId != SecurityContext.CurrentAccount.ID.ToString() select new SharingParam @@ -1127,373 +988,263 @@ namespace ASC.Api.Calendar }); } + await ReplaceRemoveSharingEventTask(sharingList, myUri, cal, events).ConfigureAwait(false); - var replaceSharingEventTask = new Task(() => + } + + #endregion + + + #region Caldav/methods + + private async Task UpdateCalDavCalendar(string name, string description, string backgroundColor, string calDavGuid, Uri myUri, string email, bool isSharedCalendar = false) + { + var CalDavCalendar = new CalDavCalendar(calDavGuid, isSharedCalendar); + + name = (name ?? "").Trim(); + if (String.IsNullOrEmpty(name)) + throw new Exception(Resources.CalendarApiResource.ErrorEmptyName); + + description = (description ?? "").Trim(); + backgroundColor = (backgroundColor ?? "").Trim(); + + var authorization = isSharedCalendar ? CalDavCalendar.GetSystemAuthorization() : GetUserAuthorization(email); + + + await CalDavCalendar.Update(name, description, backgroundColor, myUri, email.ToLower(), authorization).ConfigureAwait(false); + } + + private async Task CreateCalDavCalendar(string name, string description, string backgroundColor, string calDavGuid, Uri myUri, string currentUserName, string email, bool isSharedCalendar = false) + { + var CalDavCalendar = new CalDavCalendar(calDavGuid, isSharedCalendar); + + name = (name ?? "").Trim(); + if (string.IsNullOrEmpty(name)) + throw new Exception(Resources.CalendarApiResource.ErrorEmptyName); + + description = (description ?? "").Trim(); + backgroundColor = (backgroundColor ?? "").Trim(); + + var authorization = isSharedCalendar ? CalDavCalendar.GetSystemAuthorization() : GetUserAuthorization(email); + return await CalDavCalendar.CreateAsync(name, description, backgroundColor, myUri, currentUserName, authorization).ConfigureAwait(false); + + + } + + private static async Task RemoveCaldavCalendar(string email, string calDavGuid, Uri myUri, bool isShared = false) + { + + try + { + var calDavCalendar = new CalDavCalendar(calDavGuid, isShared); + var requestUrl = calDavCalendar.GetRadicaleUrl(myUri.ToString(), email, isShared, isRedirectUrl: true, entityId: calDavGuid); + var authorization = isShared ? calDavCalendar.GetSystemAuthorization() : GetUserAuthorization(email); + var davRequest = new DavRequest() + { + Url = requestUrl, + Authorization = authorization + }; + await RadicaleClient.RemoveAsync(davRequest).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.Error(ex); + } + } + + private async Task CreateCaldavEvents(string calDavGuid, Uri myUri, string currentUserEmail, BaseCalendar icalendar, string calendarIcs, int tenantId) + { + var parseCalendar = DDayICalParser.DeserializeCalendar(calendarIcs); + var calendar = parseCalendar.FirstOrDefault(); + CoreContext.TenantManager.SetCurrentTenant(tenantId); + + var calendarId = icalendar.Id; + var ddayCalendar = new Ical.Net.Calendar(); + try + { + if (calendar != null) + { + var events = calendar.Events; + var updateCaldavEventTasks = new List(); + foreach (var evt in events) + { + var uid = evt.Uid; + if (evt.Created != null) + evt.Created = DDayICalParser.ToUtc(evt.Created) != DateTime.MinValue ? evt.Created : new CalDateTime(DateTime.Now); + string[] split = uid.Split(new Char[] { '@' }); + ddayCalendar = DDayICalParser.ConvertCalendar(icalendar); + ddayCalendar.Events.Clear(); + ddayCalendar.Events.Add(evt); + + var ics = DDayICalParser.SerializeCalendar(ddayCalendar); + updateCaldavEventTasks.Add(UpdateCaldavEventTask(ics, split[0], true, calDavGuid, myUri, currentUserEmail, + DateTime.Now, ddayCalendar.TimeZones[0], icalendar.TimeZone)); + + } + var todos = icalendar.GetTodoWrappers(SecurityContext.CurrentAccount.ID, new ApiDateTime(DateTime.MinValue, icalendar.TimeZone), new ApiDateTime(DateTime.MaxValue, icalendar.TimeZone)); + foreach (var td in todos) + { + ddayCalendar = DDayICalParser.ConvertCalendar(icalendar); + ddayCalendar.Todos.Clear(); + + var todo = new Ical.Net.CalendarComponents.Todo + { + Summary = td.Name, + Description = td.Description, + Start = td.Start != DateTime.MinValue ? new CalDateTime(td.Start) : null, + Completed = td.Completed != DateTime.MinValue ? new CalDateTime(td.Completed) : null, + }; + + ddayCalendar.Todos.Add(todo); + + var ics = DDayICalParser.SerializeCalendar(ddayCalendar); + var uid = td.Uid; + string[] split = uid.Split(new Char[] { '@' }); + updateCaldavEventTasks.Add(UpdateCaldavEventTask(ics, split[0], true, calDavGuid, myUri, currentUserEmail, + DateTime.Now, ddayCalendar.TimeZones[0], icalendar.TimeZone)); + } + await Task.WhenAll(updateCaldavEventTasks).ConfigureAwait(false); + } + + } + catch (Exception exception) + { + Logger.Error("ERROR. Create caldav events: " + exception.Message); + } + + } + + private static async Task UpdateCaldavEventTask( + string ics, + string uid, + bool sendToRadicale, + string guid, + Uri myUri, + string userEmail, + DateTime updateDate = default(DateTime), + VTimeZone calendarVTimeZone = null, + TimeZoneInfo calendarTimeZone = null, + bool isDelete = false, + bool isShared = false + ) + { + if (sendToRadicale) { try { - CoreContext.TenantManager.SetCurrentTenant(currentTenantId); - - foreach (var sharingOption in sharingList) + if (guid != null && guid != "") { - if (!sharingOption.IsGroup) + + var calDavServerUrl = myUri.Scheme + "://" + myUri.Host + "/caldav"; + var caldavHost = myUri.Host; + + Logger.Info("RADICALE REWRITE URL: " + myUri); + + if (userEmail == null) return; + + var currentUserName = userEmail.ToLower() + "@" + caldavHost; + + int indexOfChar = ics.IndexOf("BEGIN:VTIMEZONE"); + int indexOfCharEND = ics.IndexOf("END:VTIMEZONE"); + + if (indexOfChar != -1) { - var user = CoreContext.UserManager.GetUsers(sharingOption.itemId); - if (CheckUserEmail(user)) + ics = ics.Remove(indexOfChar, indexOfCharEND + 14 - indexOfChar); + if (ics.IndexOf("BEGIN:VTIMEZONE") > -1) await UpdateCaldavEventTask(ics, uid, true, guid, myUri, userEmail).ConfigureAwait(false); + } + + + var icsCalendars = DDayICalParser.DeserializeCalendar(ics); + var icsCalendar = icsCalendars == null ? null : icsCalendars.FirstOrDefault(); + var icsEvents = icsCalendar == null ? null : icsCalendar.Events; + var icsEvent = icsEvents == null ? null : icsEvents.FirstOrDefault(); + + var icsTodos = icsCalendar == null ? null : icsCalendar.Todos; + var icsTodo = icsTodos == null ? null : icsTodos.FirstOrDefault(); + + if (calendarTimeZone != null && calendarVTimeZone != null) + { + if (icsEvent != null && !icsEvent.IsAllDay) { - RemoveCaldavCalendar(user.Email + "@" + caldavHost, user.Email, cal.calDavGuid, myUri, user.ID != cal.OwnerId); + icsEvent.Created = null; + + //var tz = TimeZoneConverter.GetTimeZone(calendarVTimeZone.TzId); + + //if (icsEvent.DtStart.TzId != calendarVTimeZone.TzId) + //{ + // var _DtStart = DDayICalParser.ToUtc(icsEvent.Start).Add(tz.GetUtcOffset(icsEvent.Start.Value)); + // icsEvent.DtStart = new CalDateTime(_DtStart.Year, _DtStart.Month, _DtStart.Day, _DtStart.Hour, _DtStart.Minute, _DtStart.Second, calendarVTimeZone.TzId); + //} + + //if (icsEvent.DtEnd.TzId != calendarVTimeZone.TzId) + //{ + // var _DtEnd = DDayICalParser.ToUtc(icsEvent.End).Add(tz.GetUtcOffset(icsEvent.End.Value)); + // icsEvent.DtEnd = new CalDateTime(_DtEnd.Year, _DtEnd.Month, _DtEnd.Day, _DtEnd.Hour, _DtEnd.Minute, _DtEnd.Second, calendarVTimeZone.TzId); + //} + + //foreach (var periodList in icsEvent.ExceptionDates) + //{ + // periodList.Parameters.Add("TZID", calendarVTimeZone.TzId); + //} + + } + + //icsCalendar.TimeZones.Clear(); + //icsCalendar.TimeZones.Add(calendarVTimeZone); + + } + if (icsEvent != null) + { + icsEvent.Uid = uid; + if (!isDelete) + { + icsEvent.ExceptionDates.Clear(); } } - else - { - var users = CoreContext.UserManager.GetUsersByGroup(sharingOption.itemId); - foreach (var user in users) - { - if (CheckUserEmail(user)) - { - RemoveCaldavCalendar(user.Email + "@" + caldavHost, user.Email, cal.calDavGuid, myUri, user.ID != cal.OwnerId); - } - } - } - } - foreach (var evt in events) - { - if (evt.SharingOptions.PublicItems.Count > 0) - { - var permissions = PublicItemCollection.GetForEvent(evt); - var so = permissions.Items - .Where(x => x.SharingOption.Id != AccessOption.OwnerOption.Id) - .Select(x => new SharingParam - { - Id = x.Id, - actionId = x.SharingOption.Id, - isGroup = x.IsGroup - }).ToList(); - var uid = evt.Uid; - string[] split = uid.Split(new Char[] { '@' }); - foreach (var sharingOption in so) - { - var fullAccess = sharingOption.actionId == AccessOption.FullAccessOption.Id; - if (!sharingOption.IsGroup) - { - var user = CoreContext.UserManager.GetUsers(sharingOption.itemId); - if (CheckUserEmail(user)) - { - deleteEvent(fullAccess ? split[0] + "_write" : split[0], SharedEventsCalendar.CalendarId, user.Email, myUri, user.ID != evt.OwnerId); - } - } + if (icsTodo != null) + { + icsTodo.Uid = uid; + } + + ics = DDayICalParser.SerializeCalendar(icsCalendar); + + try + { + var calDavCalendar = new CalDavCalendar(guid, isShared); + var authorization = isShared ? calDavCalendar.GetSystemAuthorization() : GetUserAuthorization(userEmail); + var requestUrl = calDavCalendar.GetRadicaleUrl(myUri.ToString(), userEmail.ToLower(), isShared, false, true, guid, uid); + await calDavCalendar.UpdateItem(requestUrl, authorization, ics).ConfigureAwait(false); + } + catch (WebException ex) + { + if (ex.Status == WebExceptionStatus.ProtocolError && ex.Response != null) + { + var resp = (HttpWebResponse)ex.Response; + if (resp.StatusCode == HttpStatusCode.NotFound || resp.StatusCode == HttpStatusCode.Conflict) + Logger.Debug("ERROR: " + ex.Message); else - { - var users = CoreContext.UserManager.GetUsersByGroup(sharingOption.itemId); - foreach (var user in users) - { - if (CheckUserEmail(user)) - { - var eventUid = user.ID == evt.OwnerId - ? split[0] - : fullAccess ? split[0] + "_write" : split[0]; - - deleteEvent(eventUid, SharedEventsCalendar.CalendarId, user.Email, myUri, true); - } - } - } + Logger.Error("ERROR: " + ex.Message); } + else + { + Logger.Error("ERROR: " + ex.Message); + } + } + catch (Exception ex) + { + Logger.Error("ERROR: " + ex.Message); } } } catch (Exception ex) { - LogManager.GetLogger("ASC.Calendar").Error(ex.Message); + Logger.Error("ERROR: " + ex.Message); } - - }, TaskCreationOptions.LongRunning); - replaceSharingEventTask.ConfigureAwait(false); - replaceSharingEventTask.Start(); - - } - - private static void RemoveCaldavCalendar(string currentUserName, string email, string calDavGuid, Uri myUri, bool isShared = false) - { - var calDavServerUrl = myUri.Scheme + "://" + myUri.Host + "/caldav"; - var requestUrl = calDavServerUrl + "/" + HttpUtility.UrlEncode(currentUserName) + "/" + (isShared ? calDavGuid + "-shared" : calDavGuid); - - try - { - var webRequest = (HttpWebRequest)WebRequest.Create(requestUrl); - webRequest.Method = "DELETE"; - webRequest.ContentType = "text/xml; charset=utf-8"; - - var authorization = isShared ? GetSystemAuthorization() : GetUserAuthorization(email); - webRequest.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(authorization))); - - using (var webResponse = webRequest.GetResponse()) - using (var reader = new StreamReader(webResponse.GetResponseStream())) { } - } - catch (Exception ex) - { - LogManager.GetLogger("ASC.Calendar").Error(ex); } } - #endregion - - #region ICal/import - - /// - /// Returns the link for the iCal associated with the calendar with the ID specified in the request - /// - /// - /// Get iCal link - /// - /// Calendar ID - /// iCal link - [Read("{calendarId}/icalurl")] - public string GetCalendariCalUrl(string calendarId) - { - var sig = Signature.Create(SecurityContext.CurrentAccount.ID, calendarId); - var path = UrlPath.ResolveUrl(() => new CalendarApi().GetCalendariCalStream(calendarId, sig)); - return new Uri(_context.RequestContext.HttpContext.Request.GetUrlRewriter(), VirtualPathUtility.ToAbsolute("~/" + path)).ToString(); - } - /// - /// Returns the link for the CalDav associated with the calendar with the ID specified in the request - /// - /// - /// Get CalDav link - /// - /// Calendar ID - /// CalDav link - [Read("{calendarId}/caldavurl")] - public string GetCalendarCalDavUrl(string calendarId) - { - var myUri = HttpContext.Current.Request.GetUrlRewriter(); - - var calDavServerUrl = myUri.Scheme + "://" + myUri.Host + "/caldav"; - var caldavHost = myUri.Host; - - var userId = SecurityContext.CurrentAccount.ID; - var user = CoreContext.UserManager.GetUsers(userId); - if (!CheckUserEmail(user)) return ""; - var userName = CoreContext.UserManager.GetUsers(userId).Email.ToLower(); - - var curCaldavUserName = userName + "@" + caldavHost; - - if (calendarId == "todo_calendar") - { - var todoCalendars = _dataProvider.LoadTodoCalendarsForUser(SecurityContext.CurrentAccount.ID); - var userTimeZone = CoreContext.TenantManager.GetCurrentTenant().TimeZone; - var todoCal = new CalendarWrapper(new BusinessObjects.Calendar()); - - if (todoCalendars.Count == 0) - { - todoCal = CreateCalendar("Todo_calendar", "", BusinessObjects.Calendar.DefaultTextColor, BusinessObjects.Calendar.DefaultTodoBackgroundColor, userTimeZone.ToString(), EventAlertType.FifteenMinutes, null, null, 1); - - if (todoCal != null) - { - using (var db = DbManager.FromHttpContext("calendar")) - { - using (var tr = db.BeginTransaction()) - { - try - { - var dataCaldavGuid = - db.ExecuteList(new SqlQuery("calendar_calendars") - .Select("caldav_guid") - .Where("id", todoCal.Id)) - .Select(r => r[0]) - .ToArray(); - var caldavGuid = dataCaldavGuid[0] != null - ? Guid.Parse(dataCaldavGuid[0].ToString()) - : Guid.Empty; - - var sharedCalUrl = new Uri(new Uri(calDavServerUrl), "/caldav/" + curCaldavUserName + "/" + caldavGuid).ToString(); - var calendar = GetUserCaldavCalendar(sharedCalUrl, userName); - if (calendar != "") - { - if (calendar == "NotFound") - { - return CreateCalDavCalendar( - "Todo_calendar", - "", - BusinessObjects.Calendar.DefaultTodoBackgroundColor, - caldavGuid.ToString(), - myUri, - curCaldavUserName, - userName - ); - } - return sharedCalUrl; - } - else - { - return ""; - } - - } - catch (Exception exception) - { - Logger.Error("ERROR: " + exception.Message); - return ""; - } - } - } - } - else - { - return ""; - } - } - else - { - var sharedCalUrl = new Uri(new Uri(calDavServerUrl), "/caldav/" + curCaldavUserName + "/" + todoCalendars[0].calDavGuid).ToString(); - var calendar = GetUserCaldavCalendar(sharedCalUrl, userName); - if (calendar != "") - { - if (calendar == "NotFound") - { - return CreateCalDavCalendar( - "Todo_calendar", - "", - BusinessObjects.Calendar.DefaultTodoBackgroundColor, - todoCalendars[0].calDavGuid.ToString(), - myUri, - curCaldavUserName, - userName - ); - } - return sharedCalUrl; - } - else - { - return ""; - } - } - } - - if (calendarId == BirthdayReminderCalendar.CalendarId || - calendarId == SharedEventsCalendar.CalendarId || - calendarId == "crm_calendar" || - calendarId.Contains("Project_")) - { - - if (SecurityContext.IsAuthenticated) - { - var sharedCalendar = GetCalendarById(calendarId); - - var currentCaldavUserName = userName + "@" + caldavHost; - var sharedCalUrl = new Uri(new Uri(calDavServerUrl), "/caldav/" + currentCaldavUserName + "/" + calendarId + "-shared").ToString(); - - var calendar = GetUserCaldavCalendar(sharedCalUrl, userName); - - if (calendar != "") - { - if (calendar == "NotFound") - { - sharedCalUrl = CreateCalDavCalendar( - sharedCalendar.UserCalendar.Name, - sharedCalendar.UserCalendar.Description, - sharedCalendar.TextColor, - calendarId, - myUri, - currentCaldavUserName, - userName, - true - ); - } - if (sharedCalUrl != "") - { - var calendarIcs = GetCalendariCalString(_dataProvider, calendarId, true); - - var tenant = CoreContext.TenantManager.GetCurrentTenant(); - var caldavTask = new Task(() => CreateCaldavSharedEvents(calendarId, calendarIcs, myUri, userName, sharedCalendar.UserCalendar, SecurityContext.CurrentAccount, tenant.TenantId)); - caldavTask.Start(); - - return sharedCalUrl; - } - } - } - return ""; - } - - var cal = _dataProvider.GetCalendarById(Convert.ToInt32(calendarId)); - var ownerId = cal.OwnerId; - - CoreContext.TenantManager.SetCurrentTenant(cal.TenantId); - - var isShared = ownerId != SecurityContext.CurrentAccount.ID; - var calDavGuid = cal.calDavGuid; - if (calDavGuid == "" || calDavGuid == Guid.Empty.ToString()) - { - calDavGuid = Guid.NewGuid().ToString(); - _dataProvider.UpdateCalendarGuid(Convert.ToInt32(cal.Id), Guid.Parse(calDavGuid)); - } - - var calUrl = new Uri(new Uri(calDavServerUrl), "/caldav/" + curCaldavUserName + "/" + calDavGuid + (isShared ? "-shared" : "")).ToString(); - - Logger.Info("RADICALE REWRITE URL: " + myUri); - - var caldavCalendar = GetUserCaldavCalendar(calUrl, userName); - - if (caldavCalendar != "") - { - if (caldavCalendar == "NotFound") - { - return SyncCaldavCalendar(calendarId, cal.Name, cal.Description, cal.Context.HtmlBackgroundColor, Guid.Parse(calDavGuid), myUri, curCaldavUserName, userName, isShared, cal.SharingOptions); - } - return calUrl; - } - return ""; - } - - private string SyncCaldavCalendar(string calendarId, - string name, - string description, - string backgroundColor, - Guid calDavGuid, - Uri myUri, - string curCaldavUserName, - string email, - bool isShared = false, - SharingOptions sharingOptions = null) - { - var calendarUrl = CreateCalDavCalendar(name, description, backgroundColor, calDavGuid.ToString(), myUri, curCaldavUserName, email, isShared); - - BaseCalendar icalendar; - int calId; - - var viewSettings = _dataProvider.GetUserViewSettings(SecurityContext.CurrentAccount.ID, new List { calendarId }); - - if (int.TryParse(calendarId, out calId)) - { - icalendar = _dataProvider.GetCalendarById(calId); - if (icalendar != null) - { - icalendar = icalendar.GetUserCalendar(viewSettings.FirstOrDefault()); - } - } - else - { - //external - icalendar = CalendarManager.Instance.GetCalendarForUser(SecurityContext.CurrentAccount.ID, calendarId); - if (icalendar != null) - { - icalendar = icalendar.GetUserCalendar(viewSettings.FirstOrDefault()); - } - } - - if (icalendar == null) return ""; - - var calendarIcs = GetCalendariCalString(_dataProvider, icalendar.Id, true); - - var tenant = CoreContext.TenantManager.GetCurrentTenant(); - var caldavTask = isShared ? new Task(() => CreateCaldavSharedEvents(calDavGuid.ToString(), calendarIcs, myUri, email, icalendar, SecurityContext.CurrentAccount, tenant.TenantId)) - : new Task(() => CreateCaldavEvents(calDavGuid.ToString(), myUri, email, icalendar, calendarIcs, tenant.TenantId)); - caldavTask.Start(); - - return calendarUrl; - } - - private void CreateCaldavSharedEvents(string calendarId, string calendarIcs, Uri myUri, string currentUserEmail, BaseCalendar icalendar, Common.Security.Authentication.IAccount currentUser, int tenantId) + private async Task CreateCaldavSharedEvents(string calendarId, string calendarIcs, Uri myUri, string currentUserEmail, BaseCalendar icalendar, Common.Security.Authentication.IAccount currentUser, int tenantId) { var parseCalendar = DDayICalParser.DeserializeCalendar(calendarIcs); var calendar = parseCalendar.FirstOrDefault(); @@ -1502,6 +1253,7 @@ namespace ASC.Api.Calendar { if (calendar != null) { + var updateCaldavEventTasks = new List(); var events = new List(); var isFullAccess = false; if (calendarId != BirthdayReminderCalendar.CalendarId && calendarId != "crm_calendar" && !calendarId.Contains("Project_")) @@ -1553,11 +1305,11 @@ namespace ASC.Api.Calendar var ics = DDayICalParser.SerializeCalendar(calendar); var eventUid = isFullAccess ? e.Uid + "_write" : e.Uid; - updateCaldavEvent(ics, eventUid, true, calendarId, + updateCaldavEventTasks.Add(UpdateCaldavEventTask(ics, eventUid, true, calendarId, myUri, currentUserEmail, DateTime.Now, - calendar.TimeZones[0], icalendar.TimeZone, false, true); - + calendar.TimeZones[0], icalendar.TimeZone, false, true)); } + await Task.WhenAll(updateCaldavEventTasks).ConfigureAwait(false); } } catch (Exception exception) @@ -1566,194 +1318,114 @@ namespace ASC.Api.Calendar } } - private void CreateCaldavEvents(string calDavGuid, Uri myUri, string currentUserEmail, BaseCalendar icalendar, string calendarIcs, int tenantId) + private async Task ReplaceUpdateCalDavSharingEvent(List list, Uri myUri, BusinessObjects.Calendar cal) { - var parseCalendar = DDayICalParser.DeserializeCalendar(calendarIcs); - var calendar = parseCalendar.FirstOrDefault(); - CoreContext.TenantManager.SetCurrentTenant(tenantId); - - var calendarId = icalendar.Id; - var ddayCalendar = new Ical.Net.Calendar(); try { - if (calendar != null) + var removeCaldavCalendarTasks = new List(); + foreach (var sharingOption in list) { - var events = calendar.Events; - foreach (var evt in events) + if (!sharingOption.IsGroup) { - var uid = evt.Uid; - if (evt.Created != null) - evt.Created = DDayICalParser.ToUtc(evt.Created) != DateTime.MinValue ? evt.Created : new CalDateTime(DateTime.Now); - string[] split = uid.Split(new Char[] { '@' }); - ddayCalendar = DDayICalParser.ConvertCalendar(icalendar); - ddayCalendar.Events.Clear(); - ddayCalendar.Events.Add(evt); - - var ics = DDayICalParser.SerializeCalendar(ddayCalendar); - updateCaldavEvent(ics, split[0], true, calDavGuid, myUri, currentUserEmail, - DateTime.Now, ddayCalendar.TimeZones[0], - icalendar.TimeZone); - } - - var todos = icalendar.GetTodoWrappers(SecurityContext.CurrentAccount.ID, new ApiDateTime(DateTime.MinValue, icalendar.TimeZone), new ApiDateTime(DateTime.MaxValue, icalendar.TimeZone)); - foreach (var td in todos) - { - ddayCalendar = DDayICalParser.ConvertCalendar(icalendar); - ddayCalendar.Todos.Clear(); - - var todo = new Ical.Net.CalendarComponents.Todo + var user = CoreContext.UserManager.GetUsers(sharingOption.itemId); + if (CheckUserEmail(user)) { - Summary = td.Name, - Description = td.Description, - Start = td.Start != DateTime.MinValue ? new CalDateTime(td.Start) : null, - Completed = td.Completed != DateTime.MinValue ? new CalDateTime(td.Completed) : null, - }; - - ddayCalendar.Todos.Add(todo); - - var ics = DDayICalParser.SerializeCalendar(ddayCalendar); - var uid = td.Uid; - string[] split = uid.Split(new Char[] { '@' }); - updateCaldavEvent(ics, split[0], true, calDavGuid, myUri, currentUserEmail, DateTime.Now, ddayCalendar.TimeZones[0], icalendar.TimeZone); - } - } - - } - catch (Exception exception) - { - Logger.Error("ERROR. Create caldav events: " + exception.Message); - } - - } - - /// - /// Run caldav event update function - /// - /// - /// Update CalDav Event - /// - /// changes of event - /// - /// false - [Read("change_to_storage", false)] //NOTE: this method doesn't requires auth!!! - public void ChangeOfCalendarStorage(string change, string key) - { - var authInterval = TimeSpan.FromHours(1); - var checkKeyResult = EmailValidationKeyProvider.ValidateEmailKey(change + ConfirmType.Auth, key, authInterval); - if (checkKeyResult != EmailValidationKeyProvider.ValidationResult.Ok) throw new SecurityException("Access Denied."); - - var urlRewriter = HttpContext.Current.Request.GetUrlRewriter(); - var caldavUser = change.Split('/')[0]; - var portalName = caldavUser.Split('@')[2]; - - if (change != null && portalName != null) - { - var calDavUrl = new Uri(urlRewriter.Scheme + "://" + portalName); - var caldavTask = new Task(() => UpdateCalDavEvent(change, calDavUrl)); - caldavTask.Start(); - } - } - - /// - /// Run caldav event delete function - /// - /// - /// Delete CalDav Event - /// - /// event info - /// - /// false - [Read("caldav_delete_event", false)] //NOTE: this method doesn't requires auth!!! - public void CaldavDeleteEvent(string eventInfo, string key) - { - var authInterval = TimeSpan.FromHours(1); - var checkKeyResult = EmailValidationKeyProvider.ValidateEmailKey(eventInfo + ConfirmType.Auth, key, authInterval); - if (checkKeyResult != EmailValidationKeyProvider.ValidationResult.Ok) throw new SecurityException("Access Denied."); - - if (eventInfo != null) - { - var myUri = HttpContext.Current.Request.GetUrlRewriter(); - var calEvent = eventInfo.Split('/')[2].Replace("_write", ""); ; - var eventGuid = calEvent.Split('.')[0]; - - var updateEventGuid = updatedEvents.Find((x) => x == eventGuid); - if (updateEventGuid == null) - { - Task.Run(() => DeleteCalDavEvent(eventInfo, myUri)); - } - else - { - updatedEvents.Remove(updateEventGuid); - } - } - } - - private void DeleteCalDavEvent(string eventInfo, Uri myUri) - { - Thread.Sleep(1000); - using (_dataProvider = new DataProvider()) - { - var caldavGuid = eventInfo.Split('/')[1].Replace("-shared", ""); - var calEvent = eventInfo.Split('/')[2].Replace("_write", ""); ; - var eventGuid = calEvent.Split('.')[0]; - - var currentUserId = Guid.Empty; - if (SecurityContext.IsAuthenticated) - { - currentUserId = SecurityContext.CurrentAccount.ID; - SecurityContext.Logout(); - } - try - { - if (caldavGuid != SharedEventsCalendar.CalendarId) - { - var calendar = _dataProvider.GetCalendarIdByCaldavGuid(caldavGuid); - - var calendarId = Convert.ToInt32(calendar[0][0]); - var ownerId = Guid.Parse(calendar[0][1].ToString()); - - - CoreContext.TenantManager.SetCurrentTenant(Convert.ToInt32(calendar[0][2])); - SecurityContext.AuthenticateMe(ownerId); - - var existEvent = _dataProvider.GetEventIdByUid(eventGuid + "%", calendarId); - if (existEvent != null) - { - RemoveEvent(Convert.ToInt32(existEvent.Id), null, EventRemoveType.AllSeries, myUri, true); - } - else - { - var existTodo = _dataProvider.GetTodoByUid(eventGuid + "%"); - if (existTodo != null) - { - RemoveTodo(Convert.ToInt32(existTodo.Id), true); - } + var currentUserName = user.Email.ToLower() + "@" + myUri.Host; + var userEmail = user.Email; + removeCaldavCalendarTasks.Add(RemoveCaldavCalendar(userEmail, cal.calDavGuid, myUri, user.ID != cal.OwnerId)); } } else { - var existEvent = _dataProvider.GetEventIdOnlyByUid(eventGuid + "%"); - if (existEvent != null) + var users = CoreContext.UserManager.GetUsersByGroup(sharingOption.itemId); + foreach (var user in users) { - CoreContext.TenantManager.SetCurrentTenant(existEvent.TenantId); - SecurityContext.AuthenticateMe(existEvent.OwnerId); - - RemoveEvent(Convert.ToInt32(existEvent.Id), null, EventRemoveType.AllSeries, myUri, true); + if (CheckUserEmail(user)) + { + var userEmail = user.Email; + var currentUserName = userEmail.ToLower() + "@" + myUri.Host; + removeCaldavCalendarTasks.Add(RemoveCaldavCalendar(userEmail, cal.calDavGuid, myUri, user.ID != cal.OwnerId)); + } } } } - finally - { - SecurityContext.Logout(); - if (currentUserId != Guid.Empty) - { - SecurityContext.AuthenticateMe(currentUserId); - } - } + + await Task.WhenAll(removeCaldavCalendarTasks).ConfigureAwait(false); + } + catch (Exception ex) + { + LogManager.GetLogger("ASC.Calendar").Error(ex.Message); } } - private void UpdateCalDavEvent(string change, Uri calDavUrl) + private async Task ReplaceRemoveSharingEventTask(List list, Uri myUri, BusinessObjects.Calendar cal, List events) + { + try + { + var currentTenant = CoreContext.TenantManager.GetCurrentTenant(); + await ReplaceUpdateCalDavSharingEvent(list, myUri, cal).ConfigureAwait(false); + + CoreContext.TenantManager.SetCurrentTenant(currentTenant.TenantId); + + + var deleteEventTasks = new List(); + foreach (var evt in events) + { + if (evt.SharingOptions.PublicItems.Count > 0) + { + var permissions = PublicItemCollection.GetForEvent(evt); + var so = permissions.Items + .Where(x => x.SharingOption.Id != AccessOption.OwnerOption.Id) + .Select(x => new SharingParam + { + Id = x.Id, + actionId = x.SharingOption.Id, + isGroup = x.IsGroup + }).ToList(); + var uid = evt.Uid; + string[] split = uid.Split(new Char[] { '@' }); + foreach (var sharingOption in so) + { + var fullAccess = sharingOption.actionId == AccessOption.FullAccessOption.Id; + + if (!sharingOption.IsGroup) + { + var user = CoreContext.UserManager.GetUsers(sharingOption.itemId); + if (CheckUserEmail(user)) + { + deleteEventTasks.Add(deleteEvent(fullAccess ? split[0] + "_write" : split[0], SharedEventsCalendar.CalendarId, user.Email, myUri, user.ID != evt.OwnerId)); + } + } + else + { + var users = CoreContext.UserManager.GetUsersByGroup(sharingOption.itemId); + foreach (var user in users) + { + if (CheckUserEmail(user)) + { + var eventUid = user.ID == evt.OwnerId + ? split[0] + : fullAccess ? split[0] + "_write" : split[0]; + + deleteEventTasks.Add(deleteEvent(eventUid, SharedEventsCalendar.CalendarId, user.Email, myUri, true)); + } + } + } + } + await Task.WhenAll(deleteEventTasks).ConfigureAwait(false); + + } + } + } + catch (Exception ex) + { + LogManager.GetLogger("ASC.Calendar").Error(ex.Message); + } + } + + + + private async Task UpdateCalDavEvent(string change, Uri calDavUrl) { try { @@ -1769,7 +1441,7 @@ namespace ASC.Api.Calendar var caldavGuid = changeData[1]; var eventGuid = changeData[2].Split('.')[0]; - var sharedPostfixIndex = caldavGuid.IndexOf("-shared"); + var sharedPostfixIndex = caldavGuid.IndexOf("-readonly"); var calendarId = 0; var ownerId = new Guid(); @@ -1785,10 +1457,11 @@ namespace ASC.Api.Calendar eventGuid = eventGuid.Remove(fullAccessPostfixIndex, eventGuid.Length - fullAccessPostfixIndex); } } - if (caldavGuid == BirthdayReminderCalendar.CalendarId || - caldavGuid == SharedEventsCalendar.CalendarId || - caldavGuid == "crm_calendar") + caldavGuid == "crm_calendar") { + return; + } + else if (caldavGuid == SharedEventsCalendar.CalendarId) { var userName = changeData[0]; var userData = userName.Split('@'); @@ -1846,138 +1519,135 @@ namespace ASC.Api.Calendar { SecurityContext.AuthenticateMe(ownerId); - var webRequest = (HttpWebRequest)WebRequest.Create(eventURl); - webRequest.Method = "GET"; - webRequest.ContentType = "text/calendar; charset=utf-8"; - var _email = CoreContext.UserManager.GetUsers(ownerId).Email; - var authorization = sharedPostfixIndex != -1 ? GetSystemAuthorization() : GetUserAuthorization(_email); - webRequest.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(authorization))); + var calDavCal = new CalDavCalendar(caldavGuid, true); + var authorization = sharedPostfixIndex != -1 ? calDavCal.GetSystemAuthorization() : GetUserAuthorization(_email); + + Logger.Info(String.Format("UpdateCalDavEvent eventURl: {0}", eventURl)); - using (var webResponse = webRequest.GetResponse()) - using (var reader = new StreamReader(webResponse.GetResponseStream())) + string ics = calDavCal.GetCollection(eventURl, authorization).Result.Data; + Logger.Info(String.Format("UpdateCalDavEvent: {0}", ics)); + var existEvent = _dataProvider.GetEventIdByUid(eventGuid + "%", calendarId); // new function + var existCalendar = _dataProvider.GetCalendarById(calendarId); + + var calendars = DDayICalParser.DeserializeCalendar(ics); + var _calendar = calendars == null ? null : calendars.FirstOrDefault(); + var eventObj = _calendar == null || _calendar.Events == null ? null : _calendar.Events.FirstOrDefault(); + if (eventObj != null && existCalendar.IsTodo == 0) { - string ics = reader.ReadToEnd(); - Logger.Info(String.Format("UpdateCalDavEvent: {0}", ics)); - var existEvent = _dataProvider.GetEventIdByUid(eventGuid + "%", calendarId); // new function - var existCalendar = _dataProvider.GetCalendarById(calendarId); + var name = eventObj.Summary; + var description = eventObj.Description ?? " "; - var calendars = DDayICalParser.DeserializeCalendar(ics); - var _calendar = calendars == null ? null : calendars.FirstOrDefault(); - var eventObj = _calendar == null || _calendar.Events == null ? null : _calendar.Events.FirstOrDefault(); - if (eventObj != null && existCalendar.IsTodo == 0) + var alarm = eventObj.Alarms == null ? null : eventObj.Alarms.FirstOrDefault(); + var alertType = EventAlertType.Default; + if (alarm != null) { - var name = eventObj.Summary; - var description = eventObj.Description ?? " "; - - var alarm = eventObj.Alarms == null ? null : eventObj.Alarms.FirstOrDefault(); - var alertType = EventAlertType.Default; - if (alarm != null) + if (alarm.Trigger.Duration != null) { - if (alarm.Trigger.Duration != null) + var alarmMinutes = alarm.Trigger.Duration.Value.Minutes; + var alarmHours = alarm.Trigger.Duration.Value.Hours; + var alarmDays = alarm.Trigger.Duration.Value.Days; + switch (alarmMinutes) { - var alarmMinutes = alarm.Trigger.Duration.Value.Minutes; - var alarmHours = alarm.Trigger.Duration.Value.Hours; - var alarmDays = alarm.Trigger.Duration.Value.Days; - switch (alarmMinutes) - { - case -5: - alertType = EventAlertType.FiveMinutes; - break; - case -15: - alertType = EventAlertType.FifteenMinutes; - break; - case -30: - alertType = EventAlertType.HalfHour; - break; - } - switch (alarmHours) - { - case -1: - alertType = EventAlertType.Hour; - break; - case -2: - alertType = EventAlertType.TwoHours; - break; - } - if (alarmDays == -1) - alertType = EventAlertType.Day; + case -5: + alertType = EventAlertType.FiveMinutes; + break; + case -15: + alertType = EventAlertType.FifteenMinutes; + break; + case -30: + alertType = EventAlertType.HalfHour; + break; } - } - - //var utcStartDate = eventObj.IsAllDay ? eventObj.Start.Value : DDayICalParser.ToUtc(eventObj.Start); - //var utcEndDate = eventObj.IsAllDay ? eventObj.End.Value : DDayICalParser.ToUtc(eventObj.End); - - //var rrule = RecurrenceRule.Parse(GetRRuleString(eventObj)); - //var status = DDayICalParser.ConvertEventStatus(eventObj.Status); - - if (existEvent != null) - { - var eventId = int.Parse(existEvent.Id); - - var cal = new Ical.Net.Calendar(); - - var permissions = PublicItemCollection.GetForEvent(existEvent); - var sharingOptions = permissions.Items - .Where(x => x.SharingOption.Id != AccessOption.OwnerOption.Id) - .Select(x => new SharingParam - { - Id = x.Id, - actionId = x.SharingOption.Id, - isGroup = x.IsGroup - }).ToList(); - - //eventObj.Start = new CalDateTime(DateTime.SpecifyKind(utcStartDate, DateTimeKind.Utc), TimeZoneInfo.Utc.Id); - //eventObj.End = new CalDateTime(DateTime.SpecifyKind(utcEndDate, DateTimeKind.Utc), TimeZoneInfo.Utc.Id); - //eventObj.Created = new CalDateTime(DateTime.SpecifyKind(eventObj.Created != null ? eventObj.Created.Value : DateTime.Now, DateTimeKind.Utc), TimeZoneInfo.Utc.Id); - - - //cal.Events.Add(eventObj); - //ics = DDayICalParser.SerializeCalendar(cal); - UpdateEvent(eventId, calendarId.ToString(), ics, alertType, - sharingOptions, true, ownerId.ToString()); - } - else - { - AddEvent(calendarId, ics, alertType, null, eventGuid); + switch (alarmHours) + { + case -1: + alertType = EventAlertType.Hour; + break; + case -2: + alertType = EventAlertType.TwoHours; + break; + } + if (alarmDays == -1) + alertType = EventAlertType.Day; } } - var todoObj = _calendar == null || _calendar.Todos == null ? null : _calendar.Todos.FirstOrDefault(); - if (todoObj != null && existCalendar.IsTodo == 1) + + //var utcStartDate = eventObj.IsAllDay ? eventObj.Start.Value : DDayICalParser.ToUtc(eventObj.Start); + //var utcEndDate = eventObj.IsAllDay ? eventObj.End.Value : DDayICalParser.ToUtc(eventObj.End); + + //var rrule = RecurrenceRule.Parse(GetRRuleString(eventObj)); + //var status = DDayICalParser.ConvertEventStatus(eventObj.Status); + + if (existEvent != null) { - var todoName = todoObj.Summary; - var todoDescription = todoObj.Description ?? " "; - var todoUtcStartDate = todoObj.Start != null ? DDayICalParser.ToUtc(todoObj.Start) : DateTime.MinValue; - var todoCompleted = todoObj.Completed != null ? DDayICalParser.ToUtc(todoObj.Completed) : DateTime.MinValue; + var eventId = int.Parse(existEvent.Id); - var existTodo = _dataProvider.GetTodoIdByUid(eventGuid + "%", calendarId); + var cal = new Ical.Net.Calendar(); - if (existTodo != null) - { - var todoId = int.Parse(existTodo.Id); + var permissions = PublicItemCollection.GetForEvent(existEvent); + var sharingOptions = permissions.Items + .Where(x => x.SharingOption.Id != AccessOption.OwnerOption.Id) + .Select(x => new SharingParam + { + Id = x.Id, + actionId = x.SharingOption.Id, + isGroup = x.IsGroup + }).ToList(); + + //eventObj.Start = new CalDateTime(DateTime.SpecifyKind(utcStartDate, DateTimeKind.Utc), TimeZoneInfo.Utc.Id); + //eventObj.End = new CalDateTime(DateTime.SpecifyKind(utcEndDate, DateTimeKind.Utc), TimeZoneInfo.Utc.Id); + //eventObj.Created = new CalDateTime(DateTime.SpecifyKind(eventObj.Created != null ? eventObj.Created.Value : DateTime.Now, DateTimeKind.Utc), TimeZoneInfo.Utc.Id); - UpdateTodo( - calendarId, - todoName, - todoDescription, - todoUtcStartDate, - existTodo.Uid, - todoCompleted); - } - else - { - CreateTodo(calendarId, - todoName, - todoDescription, - todoUtcStartDate, - eventGuid, - todoCompleted); - } + //cal.Events.Add(eventObj); + //ics = DDayICalParser.SerializeCalendar(cal); + await UpdateEvent(eventId, calendarId.ToString(), ics, alertType, + sharingOptions, true, ownerId.ToString()).ConfigureAwait(false); + } + else + { + await AddEvent(calendarId, ics, alertType, null, eventGuid).ConfigureAwait(false); } } + var todoObj = _calendar == null || _calendar.Todos == null ? null : _calendar.Todos.FirstOrDefault(); + if (todoObj != null && existCalendar.IsTodo == 1) + { + var todoName = todoObj.Summary; + var todoDescription = todoObj.Description ?? " "; + var todoUtcStartDate = todoObj.Start != null ? DDayICalParser.ToUtc(todoObj.Start) : DateTime.MinValue; + var todoCompleted = todoObj.Completed != null ? DDayICalParser.ToUtc(todoObj.Completed) : DateTime.MinValue; + + var existTodo = _dataProvider.GetTodoIdByUid(eventGuid + "%", calendarId); + + if (existTodo != null) + { + var todoId = int.Parse(existTodo.Id); + + + UpdateTodo( + calendarId, + todoName, + todoDescription, + todoUtcStartDate, + existTodo.Uid, + todoCompleted); + } + else + { + CreateTodo(calendarId, + todoName, + todoDescription, + todoUtcStartDate, + eventGuid, + todoCompleted); + } + } + + + } finally @@ -2010,15 +1680,674 @@ namespace ASC.Api.Calendar Logger.Error(ex); } } + + private DavRequest GetDavRequest(string email, string calendarId, Uri myUri, string calendarGuid, string eventUid) + { + var calDavServerUrl = myUri.Scheme + "://" + myUri.Host + "/caldav"; + var currentUserName = email.ToLower() + "@" + myUri.Host; + var requestDeleteUrl = calDavServerUrl + "/" + HttpUtility.UrlEncode(currentUserName) + "/" + calendarGuid + "/" + eventUid + ".ics"; + var calDavCalendar = new CalDavCalendar(calendarId, false); + var authorization = calDavCalendar.GetSystemAuthorization(); + var davRequest = new DavRequest() + { + Url = requestDeleteUrl, + Authorization = authorization + }; + + return davRequest; + } + + private async Task SharingEventTask(string old_ics, Uri myUri, BusinessObjects.Calendar calendarObj, Ical.Net.Calendar targetCalendar, + TimeZoneInfo calendarObjTimeZone, List sharingOptions, string[] uidData, string calendarId, + DateTime createDate, List calendarCharingList, BusinessObjects.Calendar cal, bool isShared, string eventUid, string oldCalendarGuid) + { + + var currentUserEmail = CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID).Email.ToLower(); + var calDavGuid = calendarObj != null ? calendarObj.calDavGuid : ""; + + try + { + var updateCaldavEventTasks = new List(); + //event sharing ptions + foreach (var sharingOption in sharingOptions) + { + if (!sharingOption.IsGroup) + { + var user = CoreContext.UserManager.GetUsers(sharingOption.itemId); + if (CheckUserEmail(user)) + { + + if (oldCalendarGuid != "") + { + var davRequest = GetDavRequest(user.Email, calendarId, myUri, oldCalendarGuid, eventUid); + updateCaldavEventTasks.Add(RadicaleClient.RemoveAsync(davRequest)); + } + + updateCaldavEventTasks.Add(ReplaceSharingEvent(user, sharingOption.actionId, uidData[0], myUri, old_ics, + calendarId, createDate, targetCalendar.TimeZones[0], + calendarObj.TimeZone)); + } + } + else + { + var users = CoreContext.UserManager.GetUsersByGroup(sharingOption.itemId); + foreach (var user in users) + { + if (CheckUserEmail(user)) + { + if (oldCalendarGuid != "") + { + var davRequest = GetDavRequest(user.Email, calendarId, myUri, oldCalendarGuid, eventUid); + updateCaldavEventTasks.Add(RadicaleClient.RemoveAsync(davRequest)); + } + + updateCaldavEventTasks.Add(ReplaceSharingEvent(user, sharingOption.actionId, uidData[0], myUri, old_ics, + calendarId, createDate, targetCalendar.TimeZones[0], + calendarObjTimeZone)); + } + } + } + } + + //calendar sharing options + foreach (var sharingOption in calendarCharingList) + { + if (!sharingOption.IsGroup) + { + var user = CoreContext.UserManager.GetUsers(sharingOption.itemId); + if (CheckUserEmail(user)) + { + if (oldCalendarGuid != "") + { + var davRequest = GetDavRequest(user.Email, calendarId, myUri, oldCalendarGuid, eventUid); + updateCaldavEventTasks.Add(RadicaleClient.RemoveAsync(davRequest)); + } + updateCaldavEventTasks.Add(ReplaceSharingEvent(user, sharingOption.actionId, uidData[0], myUri, old_ics, + calendarId, createDate, targetCalendar.TimeZones[0], + calendarObj.TimeZone, cal.calDavGuid)); + } + } + else + { + var users = CoreContext.UserManager.GetUsersByGroup(sharingOption.itemId); + foreach (var user in users) + { + if (CheckUserEmail(user)) + { + if (oldCalendarGuid != "") + { + var davRequest = GetDavRequest(user.Email, calendarId, myUri, oldCalendarGuid, eventUid); + updateCaldavEventTasks.Add(RadicaleClient.RemoveAsync(davRequest)); + } + updateCaldavEventTasks.Add(ReplaceSharingEvent(user, sharingOption.actionId, uidData[0], myUri, old_ics, + calendarId, createDate, targetCalendar.TimeZones[0], + calendarObjTimeZone, cal.calDavGuid)); + } + } + } + } + if (!isShared) + { + if (oldCalendarGuid != "") + { + var davRequest = GetDavRequest(currentUserEmail, calendarId, myUri, oldCalendarGuid, eventUid); + updateCaldavEventTasks.Add(RadicaleClient.RemoveAsync(davRequest)); + } + updateCaldavEventTasks.Add(UpdateCaldavEventTask(old_ics, eventUid, true, calDavGuid, myUri, currentUserEmail, + createDate, targetCalendar.TimeZones[0], calendarObjTimeZone, false, isShared)); + } + else + { + var owner = CoreContext.UserManager.GetUsers(calendarObj.OwnerId); + if (CheckUserEmail(owner)) + { + if (oldCalendarGuid != "") + { + var davRequest = GetDavRequest(owner.Email, calendarId, myUri, oldCalendarGuid, eventUid); + updateCaldavEventTasks.Add(RadicaleClient.RemoveAsync(davRequest)); + } + updateCaldavEventTasks.Add(UpdateCaldavEventTask(old_ics, uidData[0], true, calendarObj.calDavGuid, myUri, owner.Email, + createDate, targetCalendar.TimeZones[0], calendarObjTimeZone)); + } + } + + await Task.WhenAll(updateCaldavEventTasks).ConfigureAwait(false); + } + catch (Exception ex) + { + LogManager.GetLogger("ASC.Calendar").Error(ex.Message); + } + + } + + + private async Task UpdateCaldavTask(string old_ics, string currentEventUid, BusinessObjects.Calendar calendarObj, Uri myUri, Ical.Net.Calendar targetCalendar, + TimeZoneInfo calendarObjTimeZone, List sharingList, string[] split, List sharingOptions) + { + + var calDavGuid = calendarObj != null ? calendarObj.calDavGuid : ""; + var userId = SecurityContext.CurrentAccount.ID; + var currentUserEmail = CoreContext.UserManager.GetUsers(userId).Email.ToLower(); + + var updateCaldavEventTasks = new List(); + updateCaldavEventTasks.Add(UpdateCaldavEventTask(old_ics, currentEventUid, true, calDavGuid, myUri, + currentUserEmail, DateTime.Now, + targetCalendar.TimeZones[0], calendarObjTimeZone, false, userId != calendarObj.OwnerId)); + + //calendar sharing list + foreach (var sharingOption in sharingList) + { + var fullAccess = sharingOption.actionId == AccessOption.FullAccessOption.Id; + + if (!sharingOption.IsGroup) + { + var user = CoreContext.UserManager.GetUsers(sharingOption.itemId); + if (CheckUserEmail(user)) + { + var sharedEventUid = user.ID == calendarObj.OwnerId + ? split[0] + : fullAccess ? split[0] + "_write" : split[0]; + + updateCaldavEventTasks.Add(UpdateCaldavEventTask(old_ics, sharedEventUid, true, calDavGuid, myUri, + user.Email, DateTime.Now, targetCalendar.TimeZones[0], + calendarObjTimeZone, false, user.ID != calendarObj.OwnerId)); + } + } + else + { + var users = CoreContext.UserManager.GetUsersByGroup(sharingOption.itemId); + + foreach (var user in users) + { + if (CheckUserEmail(user)) + { + var sharedEventUid = user.ID == calendarObj.OwnerId + ? split[0] + : fullAccess ? split[0] + "_write" : split[0]; + updateCaldavEventTasks.Add(UpdateCaldavEventTask(old_ics, sharedEventUid, true, calDavGuid, myUri, + user.Email, DateTime.Now, targetCalendar.TimeZones[0], + calendarObjTimeZone, false, user.ID != calendarObj.OwnerId)); + } + } + } + } + //event sharing list + foreach (var sharingOption in sharingOptions) + { + var fullAccess = sharingOption.actionId == AccessOption.FullAccessOption.Id; + + if (!sharingOption.IsGroup) + { + var user = CoreContext.UserManager.GetUsers(sharingOption.itemId); + if (CheckUserEmail(user)) + { + var sharedEventUid = user.ID == calendarObj.OwnerId + ? split[0] + : fullAccess ? split[0] + "_write" : split[0]; + + updateCaldavEventTasks.Add(UpdateCaldavEventTask(old_ics, sharedEventUid, true, SharedEventsCalendar.CalendarId, myUri, + user.Email, DateTime.Now, targetCalendar.TimeZones[0], + calendarObjTimeZone, false, user.ID != calendarObj.OwnerId)); + } + + } + else + { + var users = CoreContext.UserManager.GetUsersByGroup(sharingOption.itemId); + + foreach (var user in users) + { + if (CheckUserEmail(user)) + { + var sharedEventUid = user.ID == calendarObj.OwnerId + ? split[0] + : fullAccess ? split[0] + "_write" : split[0]; + + updateCaldavEventTasks.Add(UpdateCaldavEventTask(old_ics, sharedEventUid, true, SharedEventsCalendar.CalendarId, myUri, + user.Email, DateTime.Now, targetCalendar.TimeZones[0], + calendarObjTimeZone, false, user.ID != calendarObj.OwnerId)); + } + } + } + } + await Task.WhenAll(updateCaldavEventTasks).ConfigureAwait(false); + } + + #endregion + + #region ICal/import + /// - /// Returns the feed for the iCal associated with the calendar by its ID and signagure specified in the request + /// Returns a link to the iCal related to the calendar with the ID specified in the request. /// - /// Get iCal feed + /// + /// Get iCal link + /// + /// Calendar ID + /// Calendars and subscriptions + /// iCal link + [Read("{calendarId}/icalurl")] + public string GetCalendariCalUrl(string calendarId) + { + var sig = Signature.Create(SecurityContext.CurrentAccount.ID, calendarId); + var path = UrlPath.ResolveUrl(() => new CalendarApi().GetCalendariCalStream(calendarId, sig)); + return new Uri(_context.RequestContext.HttpContext.Request.GetUrlRewriter(), VirtualPathUtility.ToAbsolute("~/" + path)).ToString(); + } + /// + /// Returns a link to the CalDav related to the calendar with the ID specified in the request. + /// + /// + /// Get CalDav link + /// + /// Calendar ID + /// Current URI + /// Calendars and subscriptions + /// CalDav link + [Read("{calendarId}/caldavurl")] + public async Task GetCalendarCalDavUrl(string calendarId, Uri uri = null) + { + var myUri = uri != null ? uri : HttpContext.Current.Request.GetUrlRewriter(); + + var calDavServerUrl = myUri.Scheme + "://" + myUri.Host + "/caldav"; + var caldavHost = myUri.Host; + var currentTenantId = CoreContext.TenantManager.GetCurrentTenant().TenantId; + var userId = SecurityContext.CurrentAccount.ID; + var user = CoreContext.UserManager.GetUsers(userId); + if (!CheckUserEmail(user)) + return new DavResponse() + { + Completed = false, + Error = "Invalid user email" + }; + var userName = CoreContext.UserManager.GetUsers(userId).Email.ToLower(); + + var curCaldavUserName = userName + "@" + caldavHost; + using (_dataProvider = new DataProvider()) + { + if (calendarId == "todo_calendar") + { + var todoCalendars = _dataProvider.LoadTodoCalendarsForUser(SecurityContext.CurrentAccount.ID); + var userTimeZone = CoreContext.TenantManager.GetCurrentTenant().TimeZone; + var todoCal = new CalendarWrapper(new BusinessObjects.Calendar()); + + if (todoCalendars.Count == 0) + { + todoCal = CreateCalendar("Todo_calendar", "", BusinessObjects.Calendar.DefaultTextColor, BusinessObjects.Calendar.DefaultTodoBackgroundColor, userTimeZone.ToString(), EventAlertType.FifteenMinutes, null, null, 1).Result; + + if (todoCal != null) + { + using (var db = DbManager.FromHttpContext("calendar")) + { + using (var tr = db.BeginTransaction()) + { + try + { + var dataCaldavGuid = + db.ExecuteList(new SqlQuery("calendar_calendars") + .Select("caldav_guid") + .Where("id", todoCal.Id)) + .Select(r => r[0]) + .ToArray(); + var caldavGuid = dataCaldavGuid[0] != null + ? Guid.Parse(dataCaldavGuid[0].ToString()) + : Guid.Empty; + + //var caldavCal = new CalDavCalendar(caldavGuid.ToString(), false); + //var shara = caldavCal.GetRadicaleUrl(myUri.ToString(), userName, false, false, true, caldavGuid.ToString()); + var sharedCalUrl = new Uri(new Uri(calDavServerUrl), "/caldav/" + HttpUtility.UrlEncode(curCaldavUserName) + "/" + caldavGuid).ToString(); + + + var todoCalDavCreateResponse = new CalDavCalendar(caldavGuid.ToString(), false).GetCollection(sharedCalUrl, GetUserAuthorization(userName)).Result; + if (!todoCalDavCreateResponse.Completed && todoCalDavCreateResponse.StatusCode == 404) + { + return await CreateCalDavCalendar( + "Todo_calendar", + "", + BusinessObjects.Calendar.DefaultTodoBackgroundColor, + caldavGuid.ToString(), + myUri, + curCaldavUserName, + userName + ).ConfigureAwait(false); + } + todoCalDavCreateResponse.Data = sharedCalUrl; + return todoCalDavCreateResponse; + } + catch (Exception exception) + { + Logger.Error("ERROR: " + exception.Message); + return new DavResponse() + { + Completed = false, + Error = exception.Message + }; + } + } + } + } + else + { + return new DavResponse() + { + Completed = false, + Error = "Create calendar error" + }; + } + } + else + { + var sharedCalUrl = new Uri(new Uri(calDavServerUrl), "/caldav/" + HttpUtility.UrlEncode(curCaldavUserName) + "/" + todoCalendars[0].calDavGuid).ToString(); + var todoCalDavGetResponse = await new CalDavCalendar(todoCalendars[0].calDavGuid.ToString(), false).GetCollection(sharedCalUrl, GetUserAuthorization(userName)); + if (!todoCalDavGetResponse.Completed && todoCalDavGetResponse.StatusCode == 404) + { + return await CreateCalDavCalendar( + "Todo_calendar", + "", + BusinessObjects.Calendar.DefaultTodoBackgroundColor, + todoCalendars[0].calDavGuid.ToString(), + myUri, + curCaldavUserName, + userName + ).ConfigureAwait(false); + } + todoCalDavGetResponse.Data = sharedCalUrl; + return todoCalDavGetResponse; + } + } + + if (calendarId == BirthdayReminderCalendar.CalendarId || + calendarId == SharedEventsCalendar.CalendarId || + calendarId == "crm_calendar" || + calendarId.Contains("Project_")) + { + + if (SecurityContext.IsAuthenticated) + { + var sharedCalendar = GetCalendarById(calendarId); + + var currentCaldavUserName = userName + "@" + caldavHost; + var sharedCalUrl = new Uri(new Uri(calDavServerUrl), "/caldav/" + currentCaldavUserName + "/" + calendarId + "-readonly").ToString(); + + var calendarResponse = await new CalDavCalendar(calendarId, false).GetCollection(sharedCalUrl, GetUserAuthorization(userName)).ConfigureAwait(false); + CoreContext.TenantManager.SetCurrentTenant(currentTenantId); + if (!calendarResponse.Completed) + { + var sharedCalDavResponse = new DavResponse(); + if (calendarResponse.StatusCode == 404) + { + sharedCalDavResponse = await CreateCalDavCalendar( + sharedCalendar.UserCalendar.Name, + sharedCalendar.UserCalendar.Description, + sharedCalendar.TextColor, + calendarId, + myUri, + currentCaldavUserName, + userName, + true + ); + CoreContext.TenantManager.SetCurrentTenant(currentTenantId); + } + if (sharedCalDavResponse.Completed) + { + var calendarIcs = GetCalendariCalString(_dataProvider, calendarId, true); + + var tenant = CoreContext.TenantManager.GetCurrentTenant(); + await CreateCaldavSharedEvents(calendarId, calendarIcs, myUri, userName, sharedCalendar.UserCalendar, SecurityContext.CurrentAccount, tenant.TenantId).ConfigureAwait(false); + CoreContext.TenantManager.SetCurrentTenant(currentTenantId); + + + return sharedCalDavResponse; + } + } + else + { + calendarResponse.Data = sharedCalUrl; + return calendarResponse; + } + } + return new DavResponse() + { + Completed = false, + Error = "Authentication error" + }; + + } + + var cal = _dataProvider.GetCalendarById(Convert.ToInt32(calendarId)); + var ownerId = cal.OwnerId; + + var isShared = ownerId != SecurityContext.CurrentAccount.ID; + var calDavGuid = cal.calDavGuid; + if (calDavGuid == "" || calDavGuid == Guid.Empty.ToString()) + { + calDavGuid = Guid.NewGuid().ToString(); + _dataProvider.UpdateCalendarGuid(Convert.ToInt32(cal.Id), Guid.Parse(calDavGuid)); + } + + var calDavCalendar = new CalDavCalendar(calDavGuid.ToString(), isShared); + var calUrl = calDavCalendar.GetRadicaleUrl(myUri.ToString(), userName, isShared, false, true, calDavGuid.ToString()); + + Logger.Info("RADICALE REWRITE URL: " + myUri); + + var calDavResponse = calDavCalendar.GetCollection(calUrl, GetUserAuthorization(userName)).Result; + + if (!calDavResponse.Completed && calDavResponse.StatusCode == 404) + { + return SyncCaldavCalendar(calendarId, cal.Name, cal.Description, cal.Context.HtmlBackgroundColor, Guid.Parse(calDavGuid), myUri, curCaldavUserName, userName, isShared, cal.SharingOptions).Result; + } + calDavResponse.Data = calUrl; + return calDavResponse; + } + + + + } + + private async Task SyncCaldavCalendar(string calendarId, + string name, + string description, + string backgroundColor, + Guid calDavGuid, + Uri myUri, + string curCaldavUserName, + string email, + bool isShared = false, + SharingOptions sharingOptions = null) + { + var createCalDavResponse = CreateCalDavCalendar(name, description, backgroundColor, calDavGuid.ToString(), myUri, curCaldavUserName, email, isShared).Result; + + BaseCalendar icalendar; + int calId; + + var viewSettings = _dataProvider.GetUserViewSettings(SecurityContext.CurrentAccount.ID, new List { calendarId }); + + if (int.TryParse(calendarId, out calId)) + { + icalendar = _dataProvider.GetCalendarById(calId); + if (icalendar != null) + { + icalendar = icalendar.GetUserCalendar(viewSettings.FirstOrDefault()); + } + } + else + { + //external + icalendar = CalendarManager.Instance.GetCalendarForUser(SecurityContext.CurrentAccount.ID, calendarId); + if (icalendar != null) + { + icalendar = icalendar.GetUserCalendar(viewSettings.FirstOrDefault()); + } + } + + if (icalendar == null) + return new DavResponse() + { + Completed = false, + Error = "Calendar not found" + }; + + var calendarIcs = GetCalendariCalString(_dataProvider, icalendar.Id, true); + + var tenant = CoreContext.TenantManager.GetCurrentTenant(); + if (isShared) + { + await CreateCaldavSharedEvents(calDavGuid.ToString(), calendarIcs, myUri, email, icalendar, SecurityContext.CurrentAccount, tenant.TenantId).ConfigureAwait(false); + } + else + { + await CreateCaldavEvents(calDavGuid.ToString(), myUri, email, icalendar, calendarIcs, tenant.TenantId).ConfigureAwait(false); + } + + + + return createCalDavResponse; + } + + + + + + /// + /// Updates a calendar storage with a new one specified in the request. + /// + /// + /// Update a calendar storage + /// + /// New calendar storage + /// Email key + /// Calendars and subscriptions + /// false + [Read("change_to_storage", false)] //NOTE: This method doesn't require auth!!! + public async Task ChangeOfCalendarStorage(string change, string key) + { + var authInterval = TimeSpan.FromHours(1); + var checkKeyResult = EmailValidationKeyProvider.ValidateEmailKey(change + ConfirmType.Auth, key, authInterval); + if (checkKeyResult != EmailValidationKeyProvider.ValidationResult.Ok) throw new SecurityException("Access Denied."); + + var urlRewriter = HttpContext.Current.Request.GetUrlRewriter(); + var caldavUser = change.Split('/')[0]; + var portalName = caldavUser.Split('@')[2]; + + if (change != null && portalName != null) + { + var calDavUrl = new Uri(urlRewriter.Scheme + "://" + portalName); + + await UpdateCalDavEvent(change, calDavUrl).ConfigureAwait(false); + + } + } + + /// + /// Deletes the specified information from the CalDav event. + /// + /// + /// Delete the CalDav event information + /// + /// Event information that will be deleted + /// Email key + /// Events + /// false + [Read("caldav_delete_event", false)] //NOTE: This method doesn't require auth!!! + public async Task CaldavDeleteEvent(string eventInfo, string key) + { + var authInterval = TimeSpan.FromHours(1); + var checkKeyResult = EmailValidationKeyProvider.ValidateEmailKey(eventInfo + ConfirmType.Auth, key, authInterval); + if (checkKeyResult != EmailValidationKeyProvider.ValidationResult.Ok) throw new SecurityException("Access Denied."); + + if (eventInfo != null) + { + var myUri = HttpContext.Current.Request.GetUrlRewriter(); + var calEvent = eventInfo.Split('/')[2].Replace("_write", ""); + var eventGuid = calEvent.Split('.')[0]; + + var updateEventGuid = updatedEvents.Find((x) => x == eventGuid); + if (updateEventGuid == null) + { + await DeleteCalDavEvent(eventInfo, myUri).ConfigureAwait(false); + } + else + { + updatedEvents.Remove(updateEventGuid); + } + } + } + + private async Task DeleteCalDavEvent(string eventInfo, Uri myUri) + { + Thread.Sleep(1000); + using (_dataProvider = new DataProvider()) + { + var caldavGuid = eventInfo.Split('/')[1].Replace("-readonly", ""); + var calEvent = eventInfo.Split('/')[2].Replace("_write", ""); ; + var eventGuid = calEvent.Split('.')[0]; + + var currentUserId = Guid.Empty; + if (SecurityContext.IsAuthenticated) + { + currentUserId = SecurityContext.CurrentAccount.ID; + SecurityContext.Logout(); + } + try + { + if (caldavGuid != SharedEventsCalendar.CalendarId) + { + var calendar = _dataProvider.GetCalendarIdByCaldavGuid(caldavGuid); + + var calendarId = Convert.ToInt32(calendar[0][0]); + var ownerId = Guid.Parse(calendar[0][1].ToString()); + + + CoreContext.TenantManager.SetCurrentTenant(Convert.ToInt32(calendar[0][2])); + SecurityContext.CurrentUser = ownerId; + + var existEvent = _dataProvider.GetEventIdByUid(eventGuid + "%", calendarId); + if (existEvent != null) + { + await RemoveEvent(Convert.ToInt32(existEvent.Id), null, EventRemoveType.AllSeries, myUri, true).ConfigureAwait(false); + } + else + { + var existTodo = _dataProvider.GetTodoByUid(eventGuid + "%"); + if (existTodo != null) + { + await RemoveTodo(Convert.ToInt32(existTodo.Id), true).ConfigureAwait(false); + } + } + } + else + { + var existEvent = _dataProvider.GetEventIdOnlyByUid(eventGuid + "%"); + if (existEvent != null) + { + CoreContext.TenantManager.SetCurrentTenant(existEvent.TenantId); + SecurityContext.CurrentUser = existEvent.OwnerId; + + await RemoveEvent(Convert.ToInt32(existEvent.Id), null, EventRemoveType.AllSeries, myUri, true).ConfigureAwait(false); + } + } + } + finally + { + SecurityContext.Logout(); + if (currentUserId != Guid.Empty) + { + SecurityContext.CurrentUser = currentUserId; + } + } + } + } + + + /// + /// Returns the iCal feed associated with the calendar by its ID and signagure specified in the request. + /// + /// Get the iCal feed /// Calendar ID /// Signature - /// To get the feed you need to use the method returning the iCal feed link (it will generate the necessary signature) + /// To get the feed you need to use the method returning the iCal feed link (it will generate the necessary signature). + /// Calendars and subscriptions /// Calendar iCal feed - [Read("{calendarId}/ical/{signature}", false)] //NOTE: this method doesn't requires auth!!! + [Read("{calendarId}/ical/{signature}", false)] //NOTE: This method doesn't require auth!!! public iCalApiContentResponse GetCalendariCalStream(string calendarId, string signature) { try @@ -2059,7 +2388,7 @@ namespace ASC.Api.Calendar } try { - SecurityContext.AuthenticateMe(userId); + SecurityContext.CurrentUser = userId; var icalFormat = GetCalendariCalString(_dataProvider, calendarId); if (icalFormat != null) resp = new iCalApiContentResponse(new MemoryStream(Encoding.UTF8.GetBytes(icalFormat)), calendarId + ".ics"); @@ -2069,7 +2398,7 @@ namespace ASC.Api.Calendar SecurityContext.Logout(); if (currentUserId != Guid.Empty) { - SecurityContext.AuthenticateMe(currentUserId); + SecurityContext.CurrentUser = currentUserId; } } } @@ -2112,7 +2441,6 @@ namespace ASC.Api.Calendar icalendar = icalendar.GetUserCalendar(viewSettings.FirstOrDefault()); } } - if (icalendar == null) return null; var ddayCalendar = DDayICalParser.ConvertCalendar(icalendar); @@ -2128,7 +2456,6 @@ namespace ASC.Api.Calendar if (int.TryParse(e.Id, out evtId)) eventIds.Add(evtId); } - var eventsHystory = dataProvider.GetEventsHistory(eventIds.ToArray()); foreach (var e in events) @@ -2228,13 +2555,14 @@ namespace ASC.Api.Calendar } /// - /// Imports the events from the iCal files + /// Imports the events from the iCal files specified in the request. /// /// - /// Import iCal + /// Import events from the iCal files /// - /// iCal formatted files with the events to be imported - /// Returns the number of imported events + /// iCal formatted files with the events + /// Events + /// The number of imported events [Create("import")] public int ImportEvents(IEnumerable files) { @@ -2248,14 +2576,15 @@ namespace ASC.Api.Calendar } /// - /// Imports the events from the iCal files to the existing calendar + /// Imports the events from the iCal files to the calendar with the ID specified in the request. /// /// - /// Import iCal + /// Import iCal events to the calendar /// - /// ID for the calendar which serves as the future storage base for the imported events - /// iCal formatted files with the events to be imported - /// Returns the number of imported events + /// Calendar ID + /// iCal formatted files with the events + /// Events + /// The number of imported events [Create("{calendarId}/import")] public int ImportEvents(int calendarId, IEnumerable files) { @@ -2269,7 +2598,7 @@ namespace ASC.Api.Calendar { var cals = DDayICalParser.DeserializeCalendar(reader); - counter = ImportEvents(calendarId, cals); + counter = ImportEvents(calendarId, cals).Result; } } } @@ -2278,21 +2607,62 @@ namespace ASC.Api.Calendar } /// - /// Imports the events from the iCal files + /// Imports the events from the iCal files and attachments specified in the request. /// /// - /// Import iCal + /// Import events from the iCal files and attachments /// /// Calendar ID - /// iCal formatted string - /// Returns the number of imported events + /// iCal formatted files with the events and attachments + /// false + /// Events + /// The number of imported events + [Create("importFromAggregator")] + public async Task ImportEventsFromAggregator(int calendarId, IEnumerable files) + { + if (calendarId > 0) + { + if (files != null) + { + var fileIcs = files.FirstOrDefault(x => x.FileName.Equals("calendar.ics")); + if (fileIcs != null) + { + var docs = files.Where(x => x != fileIcs); + using (var reader = new StreamReader(fileIcs.InputStream)) + { + var cals = DDayICalParser.DeserializeCalendar(reader); + return await ImportEvents(calendarId, cals, docs).ConfigureAwait(false); + } + } + } + } + + var calendar = LoadInternalCalendars().First(x => (!x.IsSubscription && x.IsTodo != 1)); + if (int.TryParse(calendar.Id, out calendarId)) + { + return await ImportEventsFromAggregator(calendarId, files).ConfigureAwait(false); + } + + throw new Exception(string.Format("Can't parse {0} to int", calendar.Id)); + } + + /// + /// Imports the events in the iCal format to the calendar with the ID specified in the request. + /// + /// + /// Import ics + /// + /// Calendar ID + /// iCal formatted string with the events to be imported + /// Events + /// The number of imported events [Create("importIcs")] public int ImportEvents(int calendarId, string iCalString) { if (calendarId > 0) { var cals = DDayICalParser.DeserializeCalendar(iCalString); - return ImportEvents(calendarId, cals); + return ImportEvents(calendarId, cals).Result; } var calendar = LoadInternalCalendars().First(x => (!x.IsSubscription && x.IsTodo != 1)); @@ -2304,7 +2674,7 @@ namespace ASC.Api.Calendar } - private int ImportEvents(int calendarId, IEnumerable cals) + private async Task ImportEvents(int calendarId, IEnumerable cals, IEnumerable docs = null) { var counter = 0; @@ -2322,6 +2692,7 @@ namespace ASC.Api.Calendar x.Method == Ical.Net.CalendarMethods.Reply || x.Method == Ical.Net.CalendarMethods.Cancel).ToList(); + var updateCaldavEventTask = new List(); foreach (var calendar in calendars) { if (calendar.Events == null) continue; @@ -2333,6 +2704,21 @@ namespace ASC.Api.Calendar { if (eventObj == null) continue; + var hasAttachments = eventObj.Attachments != null && eventObj.Attachments.Any(); + + if (hasAttachments) + { + if (docs != null && docs.Count() > 0) + { + SaveAttachmentsAndChangeUri(docs, eventObj.Attachments); + } + else if (calendar.Method.Equals(Ical.Net.CalendarMethods.Cancel)) + { + eventObj.Attachments.Clear(); + hasAttachments = false; + } + } + var tmpCalendar = calendar.Copy(); tmpCalendar.Events.Clear(); tmpCalendar.Events.Add(eventObj); @@ -2369,21 +2755,17 @@ namespace ASC.Api.Calendar var currentUserEmail = CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID).Email.ToLower(); var currentTenantId = TenantProvider.CurrentTenantID; - var updateCaldavTask = new Task(() => + try { - try - { - CoreContext.TenantManager.SetCurrentTenant(currentTenantId); - updateCaldavEvent(ics, split[0], true, calDavGuid, myUri, currentUserEmail, DateTime.Now, tmpCalendar.TimeZones[0], existCalendarTimeZone); - } - catch(Exception ex) - { - LogManager.GetLogger("ASC.Calendar").Error(ex.Message); - } - - }, TaskCreationOptions.LongRunning); - updateCaldavTask.ConfigureAwait(false); - updateCaldavTask.Start(); + CoreContext.TenantManager.SetCurrentTenant(currentTenantId); + updateCaldavEventTask.Add(UpdateCaldavEventTask(ics, split[0], true, calDavGuid, myUri, currentUserEmail, DateTime.Now, tmpCalendar.TimeZones[0], existCalendarTimeZone)); + } + catch (Exception ex) + { + LogManager.GetLogger("ASC.Calendar").Error(ex.Message); + } + + } catch (Exception e) { @@ -2404,13 +2786,20 @@ namespace ASC.Api.Calendar eventObj.Uid, calendar.Method == Ical.Net.CalendarMethods.Cancel ? EventStatus.Cancelled : DDayICalParser.ConvertEventStatus(eventObj.Status), eventObj.Created != null ? eventObj.Created.Value : DateTime.Now, - eventTimeZone); + eventTimeZone, + hasAttachments); var eventId = result != null && result.Any() ? Int32.Parse(result.First().Id) : 0; if (eventId > 0) { _dataProvider.AddEventHistory(calendarId, eventObj.Uid, eventId, ics); + + if (hasAttachments) + { + SaveAttachments(eventObj.Attachments, eventId.ToString()); + } + counter++; } } @@ -2466,20 +2855,16 @@ namespace ASC.Api.Calendar if (currentUserEmail != null) { var currentTenantId = TenantProvider.CurrentTenantID; - var updateCaldavTask = new Task(() => + + try { - try - { - CoreContext.TenantManager.SetCurrentTenant(currentTenantId); - updateCaldavEvent(ics, split[0], true, calDavGuid, myUri, currentUserEmail, DateTime.Now, tmpCalendar.TimeZones[0], existCalendarTimeZone); - } - catch(Exception ex) - { - LogManager.GetLogger("ASC.Calendar").Error(ex.Message); - } - }, TaskCreationOptions.LongRunning); - updateCaldavTask.ConfigureAwait(false); - updateCaldavTask.Start(); + CoreContext.TenantManager.SetCurrentTenant(currentTenantId); + updateCaldavEventTask.Add(UpdateCaldavEventTask(ics, split[0], true, calDavGuid, myUri, currentUserEmail, DateTime.Now, tmpCalendar.TimeZones[0], existCalendarTimeZone)); + } + catch (Exception ex) + { + LogManager.GetLogger("ASC.Calendar").Error(ex.Message); + } } } catch (Exception e) @@ -2489,38 +2874,52 @@ namespace ASC.Api.Calendar //updateEvent(ics, split[0], calendarId.ToString(), true, DateTime.Now, tmpCalendar.TimeZones[0], existCalendar.TimeZone); - CreateEvent(eventHistory.CalendarId, - mergedEvent.Summary, - mergedEvent.Description, - utcStartDate, - utcEndDate, - RecurrenceRule.Parse(rrule), - EventAlertType.Default, - mergedEvent.IsAllDay, - sharingOptions, - mergedEvent.Uid, - DDayICalParser.ConvertEventStatus(mergedEvent.Status), - eventObj.Created != null ? eventObj.Created.Value : DateTime.Now, - eventTimeZone); + var result = CreateEvent(eventHistory.CalendarId, + mergedEvent.Summary, + mergedEvent.Description, + utcStartDate, + utcEndDate, + RecurrenceRule.Parse(rrule), + EventAlertType.Default, + mergedEvent.IsAllDay, + sharingOptions, + mergedEvent.Uid, + DDayICalParser.ConvertEventStatus(mergedEvent.Status), + eventObj.Created != null ? eventObj.Created.Value : DateTime.Now, + eventTimeZone, + hasAttachments); - counter++; + var eventId = result != null && result.Any() ? Int32.Parse(result.First().Id) : 0; + + if (eventId > 0) + { + if (hasAttachments) + { + SaveAttachments(eventObj.Attachments, eventId.ToString()); + } + + counter++; + } } } } + await Task.WhenAll(updateCaldavEventTask).ConfigureAwait(false); + return counter; } /// - /// Creates a calendar by the link to the external iCal feed + /// Creates a calendar by the link to the external iCal feed. /// /// - /// Create calendar + /// Create a calendar by iCal URL /// /// Link to the external iCal feed /// Calendar name - /// Event text name + /// Event text color /// Event background name + /// Calendars and subscriptions /// Created calendar [Create("calendarUrl")] public CalendarWrapper CreateCalendarStream(string iCalUrl, string name, string textColor, string backgroundColor) @@ -2553,20 +2952,21 @@ namespace ASC.Api.Calendar #region Events /// - /// Creates the new event in the default calendar with the parameters specified in the request + /// Creates a new event in the default calendar with the parameters specified in the request. /// /// - /// Create new event + /// Create a new event in the default calendar /// /// Event name /// Event description /// Event start date /// Event end date - /// Event recurrence type (RRULE string in iCal format) + /// Event recurrence type (RRULE string in the iCal format) /// Event notification type /// Event duration type: all day long or not /// Event sharing access parameters - /// Event list + /// Events + /// List of events [Create("event")] public List AddEvent(string name, string description, ApiDateTime startDate, ApiDateTime endDate, string repeatType, EventAlertType alertType, bool isAllDayLong, List sharingOptions) { @@ -2577,19 +2977,19 @@ namespace ASC.Api.Calendar { var cal = new Ical.Net.Calendar(); cal.Events.Add(DDayICalParser.CreateEvent(name, description, startDate.UtcTime, endDate.UtcTime, repeatType, isAllDayLong, EventStatus.Confirmed)); - return AddEvent(calendarId, DDayICalParser.SerializeCalendar(cal), alertType, sharingOptions); + return AddEvent(calendarId, DDayICalParser.SerializeCalendar(cal), alertType, sharingOptions).Result; } throw new Exception(string.Format("Can't parse {0} to int", calendar.Id)); } /// - /// Creates the new event in the selected calendar with the parameters specified in the request + /// Creates a new event in the selected calendar with the parameters specified in the request. /// /// - /// Create new event + /// Create a new event in the selected calendar /// - /// ID of the calendar where the event is created + /// Calendar ID /// Event name /// Event description /// Event start date @@ -2598,16 +2998,17 @@ namespace ASC.Api.Calendar /// Event notification type /// Event duration type: all day long or not /// Event sharing access parameters - /// Event list + /// Events + /// List of events [Create("{calendarId}/event")] public List AddEvent(int calendarId, string name, string description, ApiDateTime startDate, ApiDateTime endDate, string repeatType, EventAlertType alertType, bool isAllDayLong, List sharingOptions) { var cal = new Ical.Net.Calendar(); cal.Events.Add(DDayICalParser.CreateEvent(name, description, startDate.UtcTime, endDate.UtcTime, repeatType, isAllDayLong, EventStatus.Confirmed)); - return AddEvent(calendarId, DDayICalParser.SerializeCalendar(cal), alertType, sharingOptions); + return AddEvent(calendarId, DDayICalParser.SerializeCalendar(cal), alertType, sharingOptions).Result; } - private List CreateEvent(int calendarId, string name, string description, DateTime utcStartDate, DateTime utcEndDate, RecurrenceRule rrule, EventAlertType alertType, bool isAllDayLong, List sharingOptions, string uid, EventStatus status, DateTime createDate, TimeZoneInfo eventTimeZone) + private List CreateEvent(int calendarId, string name, string description, DateTime utcStartDate, DateTime utcEndDate, RecurrenceRule rrule, EventAlertType alertType, bool isAllDayLong, List sharingOptions, string uid, EventStatus status, DateTime createDate, TimeZoneInfo eventTimeZone, bool hasAttachments) { var sharingOptionsList = sharingOptions ?? new List(); @@ -2632,6 +3033,7 @@ namespace ASC.Api.Calendar sharingOptions, status, createDate, + hasAttachments, false, "", eventTimeZone); @@ -2655,7 +3057,8 @@ namespace ASC.Api.Calendar uid, status, createDate, - eventTimeZone); + eventTimeZone, + hasAttachments); if (evt != null) { @@ -2674,33 +3077,34 @@ namespace ASC.Api.Calendar } /// - /// Updates the existing event in the selected calendar with the parameters specified in the request + /// Updates the existing event in the selected calendar with the parameters specified in the request. /// /// - /// Update event + /// Update an event /// - /// ID of the calendar where the event belongs + /// Calendar ID /// Event ID - /// Event new name - /// Event new description - /// Event start date - /// Event end date - /// Event recurrence type (RRULE string in iCal format) - /// Event notification type - /// Event duration type: all day long or not - /// Event sharing access parameters - /// Event status - /// Updated event list + /// New event name + /// New event description + /// New event start date + /// New event end date + /// New event recurrence type (RRULE string in iCal format) + /// New event notification type + /// New event duration type: all day long or not + /// New event sharing access parameters + /// New event status + /// Events + /// Updated list of events [Update("{calendarId}/{eventId}")] public List Update(string calendarId, int eventId, string name, string description, ApiDateTime startDate, ApiDateTime endDate, string repeatType, EventAlertType alertType, bool isAllDayLong, List sharingOptions, EventStatus status) { var cal = new Ical.Net.Calendar(); cal.Events.Add(DDayICalParser.CreateEvent(name, description, startDate.UtcTime, endDate.UtcTime, repeatType, isAllDayLong, status)); - return UpdateEvent(eventId, calendarId, DDayICalParser.SerializeCalendar(cal), alertType, sharingOptions); + return UpdateEvent(eventId, calendarId, DDayICalParser.SerializeCalendar(cal), alertType, sharingOptions).Result; } - private List UpdateEvent(string calendarId, int eventId, string name, string description, ApiDateTime startDate, ApiDateTime endDate, string repeatType, EventAlertType alertType, bool isAllDayLong, List sharingOptions, EventStatus status, DateTime createDate, bool fromCalDavServer = false, string ownerId = "", TimeZoneInfo eventTimeZone = null) + private List UpdateEvent(string calendarId, int eventId, string name, string description, ApiDateTime startDate, ApiDateTime endDate, string repeatType, EventAlertType alertType, bool isAllDayLong, List sharingOptions, EventStatus status, DateTime createDate, bool hasAttachments, bool fromCalDavServer = false, string ownerId = "", TimeZoneInfo eventTimeZone = null) { var sharingOptionsList = sharingOptions ?? new List(); @@ -2737,11 +3141,12 @@ namespace ASC.Api.Calendar var rrule = RecurrenceRule.Parse(repeatType); var evt = _dataProvider.UpdateEvent(eventId, calId, oldEvent.OwnerId, name, description, startDate.UtcTime, endDate.UtcTime, rrule, alertType, isAllDayLong, - sharingOptionsList.Select(o => o as SharingOptions.PublicItem).ToList(), status, createDate, eventTimeZone); + sharingOptionsList.Select(o => o as SharingOptions.PublicItem).ToList(), status, createDate, eventTimeZone, hasAttachments); if (evt != null) { //clear old rights + CoreContext.TenantManager.SetCurrentTenant(TenantProvider.CurrentTenantID); CoreContext.AuthorizationManager.RemoveAllAces(evt); foreach (var opt in sharingOptionsList) @@ -2760,16 +3165,17 @@ namespace ASC.Api.Calendar /// - /// Creates the new task in the selected calendar with the parameters specified in the request + /// Creates a new task in the selected calendar with the parameters specified in the request. /// /// - /// Create new task + /// Create a new task /// - /// Task in iCal format - /// Task uid - /// Todo + /// Task in the iCal format + /// Task UID + /// Tasks + /// Task [Create("icstodo")] - public List AddTodo(string ics, string todoUid = null) + public async Task> AddTodo(string ics, string todoUid = null) { var old_ics = ics; @@ -2781,7 +3187,7 @@ namespace ASC.Api.Calendar if (todoCalendars.Count == 0) { - todoCal = CreateCalendar("Todo_calendar", "", BusinessObjects.Calendar.DefaultTextColor, BusinessObjects.Calendar.DefaultTodoBackgroundColor, userTimeZone.ToString(), EventAlertType.FifteenMinutes, null, null, 1); + todoCal = CreateCalendar("Todo_calendar", "", BusinessObjects.Calendar.DefaultTextColor, BusinessObjects.Calendar.DefaultTodoBackgroundColor, userTimeZone.ToString(), EventAlertType.FifteenMinutes, null, null, 1).Result; } var calendarId = Convert.ToInt32(todoCalendars.Count == 0 ? todoCal.Id : todoCalendars.FirstOrDefault().Id); @@ -2846,20 +3252,14 @@ namespace ASC.Api.Calendar if (currentUserEmail != null) { var currentTenantId = TenantProvider.CurrentTenantID; - var updateCaldavTask = new Task(() => + try { - try - { - CoreContext.TenantManager.SetCurrentTenant(currentTenantId); - updateCaldavEvent(old_ics, split[0], true, calDavGuid, myUri, currentUserEmail); - } - catch (Exception ex) - { - LogManager.GetLogger("ASC.Calendar").Error(ex.Message); - } - }, TaskCreationOptions.LongRunning); - updateCaldavTask.ConfigureAwait(false); - updateCaldavTask.Start(); + await UpdateCaldavEventTask(old_ics, split[0], true, calDavGuid, myUri, currentUserEmail).ConfigureAwait(false); + } + catch (Exception ex) + { + LogManager.GetLogger("ASC.Calendar").Error(ex.Message); + } } } catch (Exception e) @@ -2872,18 +3272,19 @@ namespace ASC.Api.Calendar } /// - /// Updates the existing task with the parameters specified in the request + /// Updates the existing task with the parameters specified in the request. /// /// - /// Update task + /// Update a task /// /// Task ID - /// ID of the calendar where the task belongs - /// Task in iCal format - /// bool flag says that request from caldav server + /// Calendar ID + /// Task in the iCal format + /// Defines if the request is from the CalDav server or not + /// Tasks /// Updated task [Update("icstodo")] - public List UpdateTodo(string calendarId, string ics, string todoId, bool fromCalDavServer = false) + public async Task> UpdateTodo(string calendarId, string ics, string todoId, bool fromCalDavServer = false) { var todo = _dataProvider.GetTodoById(Convert.ToInt32(todoId)); if (todo == null) @@ -2927,6 +3328,19 @@ namespace ASC.Api.Calendar todoObj.Uid = todo.Uid; + + + var completed = todoObj.Completed == null ? DateTime.MinValue : DDayICalParser.ToUtc(todoObj.Completed); + var utcStartDate = todoObj.DtStart != null ? DDayICalParser.ToUtc(todoObj.DtStart) : DateTime.MinValue; + + var result = UpdateTodo( + int.Parse(calendarId), + todoObj.Summary, + todoObj.Description, + utcStartDate, + todoObj.Uid, + completed); + if (!fromCalDavServer) { try @@ -2943,20 +3357,14 @@ namespace ASC.Api.Calendar if (currentUserEmail != null) { var currentTenantId = TenantProvider.CurrentTenantID; - var updateCaldavTask = new Task(() => + try { - try - { - CoreContext.TenantManager.SetCurrentTenant(currentTenantId); - updateCaldavEvent(old_ics, split[0], true, calDavGuid, myUri, currentUserEmail); - } - catch (Exception ex) - { - LogManager.GetLogger("ASC.Calendar").Error(ex.Message); - } - }, TaskCreationOptions.LongRunning); - updateCaldavTask.ConfigureAwait(false); - updateCaldavTask.Start(); + await UpdateCaldavEventTask(old_ics, split[0], true, calDavGuid, myUri, currentUserEmail).ConfigureAwait(false); + } + catch (Exception ex) + { + LogManager.GetLogger("ASC.Calendar").Error(ex.Message); + } } } catch (Exception e) @@ -2966,46 +3374,37 @@ namespace ASC.Api.Calendar } - var completed = todoObj.Completed == null ? DateTime.MinValue : DDayICalParser.ToUtc(todoObj.Completed); - var utcStartDate = todoObj.DtStart != null ? DDayICalParser.ToUtc(todoObj.DtStart) : DateTime.MinValue; - - var result = UpdateTodo( - int.Parse(calendarId), - todoObj.Summary, - todoObj.Description, - utcStartDate, - todoObj.Uid, - completed); return result; } /// - /// Deletes task + /// Deletes a task with the ID specified in the request. /// /// - /// Delete task + /// Delete a task /// /// Task ID - /// Bool flag says that request from caldav server + /// Defines if the request is from the CalDav server or not + /// Tasks [Delete("todos/{todoId}")] - public void RemoveTodo(int todoId, bool fromCaldavServer = false) + public async Task RemoveTodo(int todoId, bool fromCaldavServer = false) { var todo = _dataProvider.GetTodoById(todoId); var uid = todo.Uid; string[] split = uid.Split(new Char[] { '@' }); + _dataProvider.RemoveTodo(todoId); + if (!fromCaldavServer) { var email = CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID).Email; var myUri = HttpContext.Current.Request.GetUrlRewriter(); - deleteEvent(split[0], todo.CalendarId, email, myUri); + await deleteEvent(split[0], todo.CalendarId, email, myUri).ConfigureAwait(false); } - _dataProvider.RemoveTodo(todoId); - } private List UpdateTodo(int calendarId, string name, string description, DateTime utcStartDate, string uid, DateTime completed) { @@ -3065,13 +3464,24 @@ namespace ASC.Api.Calendar } return null; } + /// + /// Adds an event in the ics format to the calendar specified in the request. + /// + /// + /// Add the iCal event + /// + /// Calendar GUID + /// Event GUID + /// Event in the iCal format + /// Events + /// Event [Create("outsideevent")] - public void AddEventOutside(string calendarGuid, string eventGuid, string ics) + public async Task AddEventOutside(string calendarGuid, string eventGuid, string ics) { - if (calendarGuid.IndexOf("-shared") > 0) + if (calendarGuid.IndexOf("-readonly") > 0) { - var caldavGuid = calendarGuid.Replace("-shared", ""); + var caldavGuid = calendarGuid.Replace("-readonly", ""); var calendarTmp = _dataProvider.GetCalendarIdByCaldavGuid(caldavGuid); var calendarId = Convert.ToInt32(calendarTmp[0][0]); @@ -3080,7 +3490,7 @@ namespace ASC.Api.Calendar if (eventData == null) { - AddEvent(calendarId, ics, EventAlertType.Never, new List()); + await AddEvent(calendarId, ics, EventAlertType.Never, new List()).ConfigureAwait(false); } else { @@ -3099,22 +3509,23 @@ namespace ASC.Api.Calendar isGroup = publicItem.IsGroup }); } - UpdateEvent(Convert.ToInt32(eventData.Id), calendarId.ToString(), ics, EventAlertType.Never, eventCharingList); + await UpdateEvent(Convert.ToInt32(eventData.Id), calendarId.ToString(), ics, EventAlertType.Never, eventCharingList).ConfigureAwait(false); } } } } /// - /// Deletes the project calendar with the ID specified in the request + /// Deletes a project calendar with the ID specified in the request. /// /// - /// Delete project calendar + /// Delete a project calendar /// /// Calendar ID /// Project team + /// Calendars and subscriptions [Delete("caldavprojcal")] - public void DeleteCaldavCalendar(string calendarId, List team = null) + public async Task DeleteCaldavCalendar(string calendarId, List team = null) { try { @@ -3122,35 +3533,28 @@ namespace ASC.Api.Calendar var caldavHost = myUri.Host; var currentTenantId = TenantProvider.CurrentTenantID; - var deleteCaldavCalendarTask = new Task(() => + try { - try + foreach (var teamMember in team) { - CoreContext.TenantManager.SetCurrentTenant(currentTenantId); - foreach (var teamMember in team) + var currentUser = CoreContext.UserManager.GetUsers(Guid.Parse(teamMember)); + + if (CheckUserEmail(currentUser)) { - var currentUser = CoreContext.UserManager.GetUsers(Guid.Parse(teamMember)); + var currentUserName = currentUser.Email.ToLower() + "@" + caldavHost; - if (CheckUserEmail(currentUser)) - { - var currentUserName = currentUser.Email.ToLower() + "@" + caldavHost; - - RemoveCaldavCalendar( - currentUserName, - currentUser.Email, - calendarId, - myUri, - true); - } + await RemoveCaldavCalendar( + currentUser.Email, + calendarId, + myUri, + true).ConfigureAwait(false); } } - catch (Exception ex) - { - LogManager.GetLogger("ASC.Calendar").Error(ex.Message); - } - }, TaskCreationOptions.LongRunning); - deleteCaldavCalendarTask.ConfigureAwait(false); - deleteCaldavCalendarTask.Start(); + } + catch (Exception ex) + { + LogManager.GetLogger("ASC.Calendar").Error(ex.Message); + } } catch (Exception ex) { @@ -3159,16 +3563,17 @@ namespace ASC.Api.Calendar } /// - /// Deletes the whole caldav event from the calendar + /// Deletes the whole CalDav event from the calendar. /// /// - /// Delete event + /// Delete the CalDav event /// - /// ID of the calendar where the event belongs - /// Event uid + /// Calendar ID + /// Event UID /// Task responsibles + /// Events [Delete("caldavevent")] - public void DeleteCaldavEvent(string calendarId, string uid, List responsibles = null) + public async Task DeleteCaldavEvent(string calendarId, string uid, List responsibles = null) { try { @@ -3177,39 +3582,14 @@ namespace ASC.Api.Calendar if (responsibles == null || responsibles.Count == 0) { var currentUserEmail = CoreContext.UserManager.GetUsers(currentUserId).Email; - deleteEvent(uid, calendarId, currentUserEmail, myUri, true); + await deleteEvent(uid, calendarId, currentUserEmail, myUri, true).ConfigureAwait(false); } else if (responsibles.Count > 0) { - var currentTenantId = TenantProvider.CurrentTenantID; - var deleteCaldavEventTask = new Task(() => - { - try - { - CoreContext.TenantManager.SetCurrentTenant(currentTenantId); - foreach (var responsibleSid in responsibles) - { - var currentUser = CoreContext.UserManager.GetUsers(Guid.Parse(responsibleSid)); - if (CheckUserEmail(currentUser)) - { - deleteEvent( - uid, - calendarId, - currentUser.Email, - myUri, - true - ); - } - } - } - catch (Exception ex) - { - LogManager.GetLogger("ASC.Calendar").Error(ex.Message); - } + var currentTenantId = CoreContext.TenantManager.GetCurrentTenant().TenantId; + + await DeleteCaldavEventTask(responsibles, myUri, uid, calendarId, currentTenantId).ConfigureAwait(false); - }, TaskCreationOptions.LongRunning); - deleteCaldavEventTask.ConfigureAwait(false); - deleteCaldavEventTask.Start(); } } catch (Exception ex) @@ -3219,6 +3599,35 @@ namespace ASC.Api.Calendar } + public async Task DeleteCaldavEventTask(List responsibles, Uri myUri, string uid, string calendarId, int currentTenantId) + { + try + { + foreach (var responsibleSid in responsibles) + { + CoreContext.TenantManager.SetCurrentTenant(currentTenantId); + var currentUser = CoreContext.UserManager.GetUsers(Guid.Parse(responsibleSid)); + if (CheckUserEmail(currentUser)) + { + await deleteEvent( + uid, + calendarId, + currentUser.Email, + myUri, + true + ).ConfigureAwait(false); + } + } + } + catch (Exception ex) + { + LogManager.GetLogger("ASC.Calendar").Error(ex.Message); + } + } + + + + private Ical.Net.Calendar getEventIcs(int alert, CalendarWrapper calendar, CalendarEvent evt, string calendarId) { var ddayCalendar = DDayICalParser.ConvertCalendar(calendar.UserCalendar); @@ -3245,15 +3654,16 @@ namespace ASC.Api.Calendar } /// - /// Returns the offset or difference between the time in specified time zone and Coordinated Universal Time (UTC) for a particular dates + /// Returns the offset or difference between the time in the specified time zone and Coordinated Universal Time (UTC) for the particular dates. /// /// - /// Returns the UTC offset for a particular dates + /// Get the UTC offset /// - /// Time zone id + /// Time zone ID /// Start date to determine the offset /// End date to determine the offset - /// UTC offset in minutes + /// Calendars and subscriptions + /// The UTC offset in minutes [Create("utcoffset")] public object GetUtcOffsets(string timeZone, ApiDateTime startDate, ApiDateTime endDate) { @@ -3268,24 +3678,25 @@ namespace ASC.Api.Calendar } /// - /// Create/update the existing caldav event in the selected calendar + /// Updates the existing CalDav event in the calendar with the ID specified in the request. /// /// - /// Update caldav event + /// Update the CalDav event /// - /// ID of the calendar where the event belongs - /// Event uid + /// Calendar ID + /// Event UID /// Event notification type /// Task responsibles + /// Events [Update("caldavevent")] - public void UpdateCaldavEvent(string calendarId, string uid, int alert = 0, List responsibles = null) + public async Task UpdateCaldavEvent(string calendarId, string uid, int alert = 0, List responsibles = null) { try { if (responsibles.Count > 0) { var myUri = HttpContext.Current.Request.GetUrlRewriter(); - var currentTenantId = TenantProvider.CurrentTenantID; + var currentTenantId = CoreContext.TenantManager.GetCurrentTenant().TenantId; var currentUserId = Guid.Empty; @@ -3296,9 +3707,12 @@ namespace ASC.Api.Calendar } try { + + var updateCaldavEventTasks = new List(); foreach (var responsibleSid in responsibles) { - SecurityContext.AuthenticateMe(Guid.Parse(responsibleSid)); + CoreContext.TenantManager.SetCurrentTenant(currentTenantId); + SecurityContext.CurrentUser = Guid.Parse(responsibleSid); var calendarIcs = GetCalendariCalString(_dataProvider, calendarId, true); var parseCalendar = DDayICalParser.DeserializeCalendar(calendarIcs); @@ -3320,36 +3734,28 @@ namespace ASC.Api.Calendar } } var serializeIcs = DDayICalParser.SerializeCalendar(ddayCalendar); - var updateEventTask = new Task(() => + + + var user = CoreContext.UserManager.GetUsers(Guid.Parse(responsibleSid)); + if (CheckUserEmail(user) && sharedCalendar != null) { - try - { - CoreContext.TenantManager.SetCurrentTenant(currentTenantId); - var user = CoreContext.UserManager.GetUsers(Guid.Parse(responsibleSid)); - if (CheckUserEmail(user) && sharedCalendar != null) - { - updateCaldavEvent( - serializeIcs, - uid, - true, - calendarId, - myUri, - user.Email, - DateTime.Now, - ddayCalendar.TimeZones[0], - sharedCalendar.UserCalendar.TimeZone, false, true - ); - } - } - catch (Exception ex) - { - Logger.Error(String.Format("Error: {0}", ex.Message)); - } - }, TaskCreationOptions.LongRunning); - updateEventTask.ConfigureAwait(false); - updateEventTask.Start(); + updateCaldavEventTasks.Add(UpdateCaldavEventTask( + serializeIcs, + uid, + true, + calendarId, + myUri, + user.Email, + DateTime.Now, + ddayCalendar.TimeZones[0], + sharedCalendar.UserCalendar.TimeZone, false, true)); + } + SecurityContext.Logout(); } + + await Task.WhenAll(updateCaldavEventTasks).ConfigureAwait(false); + CoreContext.TenantManager.SetCurrentTenant(currentTenantId); } } catch (Exception ex) @@ -3361,7 +3767,7 @@ namespace ASC.Api.Calendar SecurityContext.Logout(); if (currentUserId != Guid.Empty) { - SecurityContext.AuthenticateMe(currentUserId); + SecurityContext.CurrentUser = currentUserId; } } @@ -3374,19 +3780,20 @@ namespace ASC.Api.Calendar } /// - /// Creates the new event in the selected calendar with the parameters specified in the request + /// Creates a new iCal event in the selected calendar with the parameters specified in the request. /// /// - /// Create new event + /// Create a new iCal event /// - /// ID of the calendar where the event is created - /// Event in iCal format + /// Calendar ID + /// Event in the iCal format /// Event notification type /// Event sharing access parameters - /// Event uid + /// Event UID + /// Events /// Event [Create("icsevent")] - public List AddEvent(int calendarId, string ics, EventAlertType alertType, List sharingOptions, string eventUid = null) + public async Task> AddEvent(int calendarId, string ics, EventAlertType alertType, List sharingOptions, string eventUid = null) { var old_ics = ics; if (calendarId <= 0) @@ -3438,6 +3845,8 @@ namespace ASC.Api.Calendar eventUid = eventUid == null ? null : string.Format("{0}@{1}", eventUid, DataProvider.EventUidDomain); + var hasAttachments = eventObj.Attachments != null && eventObj.Attachments.Any(); + var result = CreateEvent(calendarId, eventObj.Summary, eventObj.Description, @@ -3450,7 +3859,8 @@ namespace ASC.Api.Calendar DataProvider.GetEventUid(eventUid), EventStatus.Confirmed, eventObj.Created != null ? eventObj.Created.Value : DateTime.Now, - eventTimeZone); + eventTimeZone, + hasAttachments); if (result == null || !result.Any()) return null; @@ -3460,6 +3870,11 @@ namespace ASC.Api.Calendar eventObj.Sequence = 0; eventObj.Status = Ical.Net.EventStatus.Confirmed; + if (hasAttachments) + { + SaveAttachments(eventObj.Attachments, evt.Id); + } + targetCalendar.Method = Ical.Net.CalendarMethods.Request; targetCalendar.Events.Clear(); targetCalendar.Events.Add(eventObj); @@ -3497,131 +3912,50 @@ namespace ASC.Api.Calendar { currentEventUid = currentEventUid + "_write"; } - var updateCaldavTask = new Task(() => - { - try - { - CoreContext.TenantManager.SetCurrentTenant(currentTenantId); - updateCaldavEvent(old_ics, currentEventUid, true, calDavGuid, myUri, - currentUserEmail, DateTime.Now, - targetCalendar.TimeZones[0], calendarObjTimeZone, false, userId != calendarObj.OwnerId); + _dataProvider.AddEventHistory(calendarId, evt.Uid, int.Parse(evt.Id), ics); - //calendar sharing list - foreach (var sharingOption in sharingList) - { - var fullAccess = sharingOption.actionId == AccessOption.FullAccessOption.Id; + await UpdateCaldavTask(ics, currentEventUid, calendarObj, myUri, targetCalendar, calendarObjTimeZone, sharingList, split, sharingOptions).ConfigureAwait(false); - if (!sharingOption.IsGroup) - { - var user = CoreContext.UserManager.GetUsers(sharingOption.itemId); - if (CheckUserEmail(user)) - { - var sharedEventUid = user.ID == calendarObj.OwnerId - ? split[0] - : fullAccess ? split[0] + "_write" : split[0]; - updateCaldavEvent(old_ics, sharedEventUid, true, calDavGuid, myUri, - user.Email, DateTime.Now, targetCalendar.TimeZones[0], - calendarObjTimeZone, false, user.ID != calendarObj.OwnerId); - } - } - else - { - var users = CoreContext.UserManager.GetUsersByGroup(sharingOption.itemId); - - foreach (var user in users) - { - if (CheckUserEmail(user)) - { - var sharedEventUid = user.ID == calendarObj.OwnerId - ? split[0] - : fullAccess ? split[0] + "_write" : split[0]; - updateCaldavEvent(old_ics, sharedEventUid, true, calDavGuid, myUri, - user.Email, DateTime.Now, targetCalendar.TimeZones[0], - calendarObjTimeZone, false, user.ID != calendarObj.OwnerId); - } - } - } - } - //event sharing list - foreach (var sharingOption in sharingOptions) - { - var fullAccess = sharingOption.actionId == AccessOption.FullAccessOption.Id; - - if (!sharingOption.IsGroup) - { - var user = CoreContext.UserManager.GetUsers(sharingOption.itemId); - if (CheckUserEmail(user)) - { - var sharedEventUid = user.ID == calendarObj.OwnerId - ? split[0] - : fullAccess ? split[0] + "_write" : split[0]; - - updateCaldavEvent(old_ics, sharedEventUid, true, SharedEventsCalendar.CalendarId, myUri, - user.Email, DateTime.Now, targetCalendar.TimeZones[0], - calendarObjTimeZone, false, user.ID != calendarObj.OwnerId); - } - - } - else - { - var users = CoreContext.UserManager.GetUsersByGroup(sharingOption.itemId); - - foreach (var user in users) - { - if (CheckUserEmail(user)) - { - var sharedEventUid = user.ID == calendarObj.OwnerId - ? split[0] - : fullAccess ? split[0] + "_write" : split[0]; - - updateCaldavEvent(old_ics, sharedEventUid, true, SharedEventsCalendar.CalendarId, myUri, - user.Email, DateTime.Now, targetCalendar.TimeZones[0], - calendarObjTimeZone, false, user.ID != calendarObj.OwnerId); - } - } - } - } - } - catch (Exception ex) - { - LogManager.GetLogger("ASC.Calendar").Error(ex.Message); - } - - }, TaskCreationOptions.LongRunning); - updateCaldavTask.ConfigureAwait(false); - updateCaldavTask.Start(); } + catch (Exception e) { Logger.Error(e.Message); } - _dataProvider.AddEventHistory(calendarId, evt.Uid, int.Parse(evt.Id), ics); return result; } /// - /// Updates the existing event in the selected calendar with the parameters specified in the request + /// Updates the existing iCal event in the selected calendar with the ID specified in the request. /// /// - /// Update event + /// Update the iCal event /// /// Event ID - /// ID of the calendar where the event belongs - /// Event in iCal format - /// Event notification type - /// Event sharing access parameters - /// bool flag says that request from caldav server - /// Event owner id + /// Calendar ID + /// Event in the iCal format + /// New event notification type + /// New event sharing access parameters + /// Defines if the request is from the CalDav server or not + /// New event owner ID + /// Events /// Updated event [Update("icsevent")] - public List UpdateEvent(int eventId, string calendarId, string ics, EventAlertType alertType, List sharingOptions, bool fromCalDavServer = false, string ownerId = "") + public async Task> UpdateEvent(int eventId, string calendarId, string ics, EventAlertType alertType, List sharingOptions, bool fromCalDavServer = false, string ownerId = "") { var evt = _dataProvider.GetEventById(eventId); + + int oldCalendarId; + if (int.TryParse(evt.CalendarId, out oldCalendarId)) + { + oldCalendarId = int.Parse(evt.CalendarId); + } + var old_ics = ics; if (evt == null) throw new Exception(Resources.CalendarApiResource.ErrorItemNotFound); @@ -3694,6 +4028,36 @@ namespace ASC.Api.Calendar eventObj.Sequence = sequence; //eventObj.ExceptionDates.Clear(); + var hasAttachments = eventObj.Attachments != null && eventObj.Attachments.Any(); + + if (hasAttachments) + { + var currentAttachments = AttachmentEngine.GetFiles(evt.Id).Select(x => x.ID.ToString()); + var actualAttachments = eventObj.Attachments.Select(x => GetFileIdFromUriQuery(x.Uri)); + + var newAttachments = actualAttachments.Except(currentAttachments); + var removedAttachments = currentAttachments.Except(actualAttachments); + + var baseUrl = CommonLinkUtility.GetFullAbsolutePath(FilesLinkUtility.FilesBaseAbsolutePath); + foreach (var attachment in eventObj.Attachments) + { + if (!attachment.Uri.AbsoluteUri.StartsWith(baseUrl)) + { + continue; + } + var fileId = GetFileIdFromUriQuery(attachment.Uri); + if (newAttachments.Contains(fileId)) + { + SaveAttachment(attachment, evt.Id); + } + } + + foreach (var fileId in removedAttachments) + { + AttachmentEngine.DeleteFile(fileId); + } + } + targetCalendar.Method = fromCalDavServer ? calendar.Method : Ical.Net.CalendarMethods.Request; targetCalendar.Events.Clear(); targetCalendar.Events.Add(eventObj); @@ -3724,9 +4088,27 @@ namespace ASC.Api.Calendar var utcEndDate = eventObj.IsAllDay ? eventObj.End.Value : DDayICalParser.ToUtc(eventObj.End); var createDate = mergedEvent.Created != null ? mergedEvent.Created.Value : DateTime.Now; + if (eventObj.IsAllDay && utcStartDate.Date < utcEndDate.Date) utcEndDate = utcEndDate.AddDays(-1); + + var resultEvent = UpdateEvent(calendarId, + eventId, + eventObj.Summary, + eventObj.Description, + new ApiDateTime(utcStartDate, TimeZoneInfo.Utc), + new ApiDateTime(utcEndDate, TimeZoneInfo.Utc), + rrule, + alertType, + eventObj.IsAllDay, + sharingOptions, + DDayICalParser.ConvertEventStatus(mergedEvent.Status), createDate, + hasAttachments, + fromCalDavServer, + ownerId, + eventTimeZone); + if (!fromCalDavServer) { try @@ -3770,85 +4152,8 @@ namespace ASC.Api.Calendar }); } - var sharingEventTask = new Task(() => - { - try - { - CoreContext.TenantManager.SetCurrentTenant(currentTenantId); - //event sharing ptions - foreach (var sharingOption in sharingOptions) - { - if (!sharingOption.IsGroup) - { - var user = CoreContext.UserManager.GetUsers(sharingOption.itemId); - if (CheckUserEmail(user)) - { - ReplaceSharingEvent(user, sharingOption.actionId, uidData[0], myUri, old_ics, - calendarId, createDate, targetCalendar.TimeZones[0], - calendarObj.TimeZone); - } - } - else - { - var users = CoreContext.UserManager.GetUsersByGroup(sharingOption.itemId); - foreach (var user in users) - { - if (CheckUserEmail(user)) - { - ReplaceSharingEvent(user, sharingOption.actionId, uidData[0], myUri, old_ics, - calendarId, createDate, targetCalendar.TimeZones[0], - calendarObjTimeZone); - } - } - } - } - //calendar sharing options - foreach (var sharingOption in calendarCharingList) - { - if (!sharingOption.IsGroup) - { - var user = CoreContext.UserManager.GetUsers(sharingOption.itemId); - if (CheckUserEmail(user)) - { - ReplaceSharingEvent(user, sharingOption.actionId, uidData[0], myUri, old_ics, - calendarId, createDate, targetCalendar.TimeZones[0], - calendarObj.TimeZone, cal.calDavGuid); - } - } - else - { - var users = CoreContext.UserManager.GetUsersByGroup(sharingOption.itemId); - foreach (var user in users) - { - if (CheckUserEmail(user)) - { - ReplaceSharingEvent(user, sharingOption.actionId, uidData[0], myUri, old_ics, - calendarId, createDate, targetCalendar.TimeZones[0], - calendarObjTimeZone, cal.calDavGuid); - } - } - } - } - if (!isShared) - { - updateCaldavEvent(old_ics, eventUid, true, calDavGuid, myUri, currentUserEmail, createDate, targetCalendar.TimeZones[0], calendarObjTimeZone, false, isShared); - } - else - { - var owner = CoreContext.UserManager.GetUsers(calendarObj.OwnerId); - if (CheckUserEmail(owner)) - { - updateCaldavEvent(old_ics, uidData[0], true, calendarObj.calDavGuid, myUri, owner.Email, createDate, targetCalendar.TimeZones[0], calendarObjTimeZone); - } - } - } - catch (Exception ex) - { - LogManager.GetLogger("ASC.Calendar").Error(ex.Message); - } - }, TaskCreationOptions.LongRunning); - sharingEventTask.ConfigureAwait(false); - sharingEventTask.Start(); + await SharingEventTask(old_ics, myUri, calendarObj, targetCalendar, calendarObjTimeZone, sharingOptions, uidData, calendarId, + createDate, calendarCharingList, cal, isShared, eventUid, oldCalendarId != 0 && oldCalendarId.ToString() != calendarId ? _dataProvider.GetCalendarById(oldCalendarId).calDavGuid : "").ConfigureAwait(false); } catch (Exception e) @@ -3857,24 +4162,80 @@ namespace ASC.Api.Calendar } } - return UpdateEvent(calendarId, - eventId, - eventObj.Summary, - eventObj.Description, - new ApiDateTime(utcStartDate, TimeZoneInfo.Utc), - new ApiDateTime(utcEndDate, TimeZoneInfo.Utc), - rrule, - alertType, - eventObj.IsAllDay, - sharingOptions, - DDayICalParser.ConvertEventStatus(mergedEvent.Status), createDate, - fromCalDavServer, - ownerId, - eventTimeZone); + return resultEvent; } + private void SaveAttachment(Attachment attachment, string eventId) + { + var query = HttpUtility.ParseQueryString(attachment.Uri.Query); + var fileId = query["fileId"]; - private static void UpdateSharedEvent(Core.Users.UserInfo userSharingInfo, string guid, bool fullAccess, + if (!string.IsNullOrEmpty(query["tmp"])) + { + var folderID = AttachmentEngine.GetFolderId(eventId); + AttachmentEngine.MoveFile(fileId, folderID); + + attachment.Uri = AttachmentEngine.GetUri(fileId); + } + else + { + if (string.IsNullOrEmpty(query["doc"])) + { + attachment.Uri = AttachmentEngine.ShareFileAndGetUri(fileId); + } + } + } + + private string GetFileIdFromUriQuery(Uri uri) + { + var query = HttpUtility.ParseQueryString(uri.Query); + return query["fileid"]; + } + + private void SaveAttachmentsAndChangeUri(IEnumerable docs, IList attachments) + { + var folderId = AttachmentEngine.GetTmpFolderId(); + + foreach (var attachment in attachments) + { + if (attachment.Uri.AbsoluteUri.StartsWith("cid:", StringComparison.OrdinalIgnoreCase)) + { + var cid = attachment.Uri.AbsoluteUri.Replace("cid:", ""); + var doc = docs.FirstOrDefault(x => x.FileName.StartsWith(cid)); + if (doc != null) + { + var cidFileName = doc.FileName.Split('/'); + var fileName = cidFileName[1]; + var document = new Files.Core.File + { + Title = fileName, + FolderID = folderId, + ContentLength = doc.ContentLength, + ThumbnailStatus = Files.Core.Thumbnail.NotRequired + }; + document = AttachmentEngine.SaveFile(document, doc.InputStream); + + var uriString = AttachmentEngine.GetUriString(document) + "&tmp=true"; + attachment.Uri = new Uri(uriString); + attachment.Parameters.Add("FILENAME", document.Title); + } + } + } + } + + private void SaveAttachments(IList attachments, string eventId) + { + var baseUrl = CommonLinkUtility.GetFullAbsolutePath(FilesLinkUtility.FilesBaseAbsolutePath); + foreach (var attachment in attachments) + { + if (attachment.Uri.AbsoluteUri.StartsWith(baseUrl)) + { + SaveAttachment(attachment, eventId); + } + } + } + + private static async Task UpdateSharedEvent(Core.Users.UserInfo userSharingInfo, string guid, bool fullAccess, Uri myUri, string oldIcs, string calendarId, @@ -3903,22 +4264,20 @@ namespace ASC.Api.Calendar var currentUserName = userSharingInfo.Email.ToLower() + "@" + caldavHost; - var requestDeleteUrl = calDavServerUrl + "/" + HttpUtility.UrlEncode(currentUserName) + "/" + calendarId + "-shared" + "/" + oldEventUid + ".ics"; + var requestDeleteUrl = calDavServerUrl + "/" + HttpUtility.UrlEncode(currentUserName) + "/" + calendarId + "-readonly" + "/" + oldEventUid + ".ics"; updatedEvents.Add(guid); try { - var webRequest = (HttpWebRequest)WebRequest.Create(requestDeleteUrl); - webRequest.Method = "DELETE"; - - var authorization = GetSystemAuthorization(); - webRequest.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(authorization))); - - using (var webResponse = webRequest.GetResponse()) - using (var reader = new StreamReader(webResponse.GetResponseStream())) + var calDavCalendar = new CalDavCalendar(calendarId, false); + var authorization = calDavCalendar.GetSystemAuthorization(); + var davRequest = new DavRequest() { - } + Url = requestDeleteUrl, + Authorization = authorization + }; + await RadicaleClient.RemoveAsync(davRequest).ConfigureAwait(false); } catch (WebException ex) { @@ -3941,11 +4300,11 @@ namespace ASC.Api.Calendar } finally { - updateCaldavEvent(oldIcs, eventUid, true, calendarId, myUri, - userSharingInfo.Email, updateDate, calendarVTimeZone, calendarTimeZone, false, true); + await UpdateCaldavEventTask(oldIcs, eventUid, true, calendarId, myUri, + userSharingInfo.Email, updateDate, calendarVTimeZone, calendarTimeZone, false, true).ConfigureAwait(false); } } - private static void ReplaceSharingEvent( + private static async Task ReplaceSharingEvent( ASC.Core.Users.UserInfo user, string actionId, string guid, @@ -3980,21 +4339,19 @@ namespace ASC.Api.Calendar var currentUserName = user.Email.ToLower() + "@" + caldavHost; var requestDeleteUrl = calDavServerUrl + "/" + HttpUtility.UrlEncode(currentUserName) + "/" + - (calGuid ?? SharedEventsCalendar.CalendarId) + (actionId != AccessOption.OwnerOption.Id ? "-shared" : "") + "/" + oldEventUid + + (calGuid ?? SharedEventsCalendar.CalendarId) + (actionId != AccessOption.OwnerOption.Id ? "-readonly" : "") + "/" + oldEventUid + ".ics"; try { - var webRequest = (HttpWebRequest)WebRequest.Create(requestDeleteUrl); - webRequest.Method = "DELETE"; - - var authorization = GetSystemAuthorization(); - webRequest.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(authorization))); - - using (var webResponse = webRequest.GetResponse()) - using (var reader = new StreamReader(webResponse.GetResponseStream())) + var calDavCalendar = new CalDavCalendar(calendarId, false); + var authorization = calDavCalendar.GetSystemAuthorization(); + var davRequest = new DavRequest() { - } + Url = requestDeleteUrl, + Authorization = authorization + }; + await RadicaleClient.RemoveAsync(davRequest).ConfigureAwait(false); } catch (WebException ex) { @@ -4017,161 +4374,17 @@ namespace ASC.Api.Calendar } finally { - updateCaldavEvent(oldIcs, eventUid, true, + await UpdateCaldavEventTask(oldIcs, eventUid, true, (calGuid ?? SharedEventsCalendar.CalendarId), myUri, - user.Email, updateDate, calendarVTimeZone, calendarTimeZone, false, actionId != AccessOption.OwnerOption.Id); + user.Email, updateDate, calendarVTimeZone, calendarTimeZone, false, actionId != AccessOption.OwnerOption.Id).ConfigureAwait(false); } } } - private static void updateCaldavEvent( - string ics, - string uid, - bool sendToRadicale, - string guid, - Uri myUri, - string userEmail, - DateTime updateDate = default(DateTime), - VTimeZone calendarVTimeZone = null, - TimeZoneInfo calendarTimeZone = null, - bool isDelete = false, - bool isShared = false - ) - { - if (sendToRadicale) - { - try - { - if (guid != null && guid != "") - { - var calDavServerUrl = myUri.Scheme + "://" + myUri.Host + "/caldav"; - var caldavHost = myUri.Host; - - Logger.Info("RADICALE REWRITE URL: " + myUri); - - if (userEmail == null) return; - - var currentUserName = userEmail.ToLower() + "@" + caldavHost; - - int indexOfChar = ics.IndexOf("BEGIN:VTIMEZONE"); - int indexOfCharEND = ics.IndexOf("END:VTIMEZONE"); - - if (indexOfChar != -1) - { - ics = ics.Remove(indexOfChar, indexOfCharEND + 14 - indexOfChar); - if (ics.IndexOf("BEGIN:VTIMEZONE") > -1) updateCaldavEvent(ics, uid, true, guid, myUri, userEmail); - } - - var requestUrl = calDavServerUrl + "/" + HttpUtility.UrlEncode(currentUserName) + "/" + guid + (isShared ? "-shared" : "") + - "/" + HttpUtility.UrlEncode(uid) + ".ics"; - - var icsCalendars = DDayICalParser.DeserializeCalendar(ics); - var icsCalendar = icsCalendars == null ? null : icsCalendars.FirstOrDefault(); - var icsEvents = icsCalendar == null ? null : icsCalendar.Events; - var icsEvent = icsEvents == null ? null : icsEvents.FirstOrDefault(); - - var icsTodos = icsCalendar == null ? null : icsCalendar.Todos; - var icsTodo = icsTodos == null ? null : icsTodos.FirstOrDefault(); - - if (calendarTimeZone != null && calendarVTimeZone != null) - { - if (icsEvent != null && !icsEvent.IsAllDay) - { - icsEvent.Created = null; - - //var tz = TimeZoneConverter.GetTimeZone(calendarVTimeZone.TzId); - - //if (icsEvent.DtStart.TzId != calendarVTimeZone.TzId) - //{ - // var _DtStart = DDayICalParser.ToUtc(icsEvent.Start).Add(tz.GetUtcOffset(icsEvent.Start.Value)); - // icsEvent.DtStart = new CalDateTime(_DtStart.Year, _DtStart.Month, _DtStart.Day, _DtStart.Hour, _DtStart.Minute, _DtStart.Second, calendarVTimeZone.TzId); - //} - - //if (icsEvent.DtEnd.TzId != calendarVTimeZone.TzId) - //{ - // var _DtEnd = DDayICalParser.ToUtc(icsEvent.End).Add(tz.GetUtcOffset(icsEvent.End.Value)); - // icsEvent.DtEnd = new CalDateTime(_DtEnd.Year, _DtEnd.Month, _DtEnd.Day, _DtEnd.Hour, _DtEnd.Minute, _DtEnd.Second, calendarVTimeZone.TzId); - //} - - //foreach (var periodList in icsEvent.ExceptionDates) - //{ - // periodList.Parameters.Add("TZID", calendarVTimeZone.TzId); - //} - - } - if (icsEvent != null) - { - icsEvent.Uid = uid; - if (!isDelete) - { - icsEvent.ExceptionDates.Clear(); - } - } - - //icsCalendar.TimeZones.Clear(); - //icsCalendar.TimeZones.Add(calendarVTimeZone); - - ics = DDayICalParser.SerializeCalendar(icsCalendar); - } - - if (icsTodo != null) - { - icsTodo.Uid = uid; - ics = DDayICalParser.SerializeCalendar(icsCalendar); - } - - - try - { - var webRequest = (HttpWebRequest)WebRequest.Create(requestUrl); - webRequest.Method = "PUT"; - webRequest.ContentType = "text/calendar; charset=utf-8"; - - var authorization = isShared ? GetSystemAuthorization() : GetUserAuthorization(userEmail); - webRequest.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(authorization))); - - var encoding = new UTF8Encoding(); - byte[] bytes = encoding.GetBytes(ics); - webRequest.ContentLength = bytes.Length; - using (var writeStream = webRequest.GetRequestStream()) - { - writeStream.Write(bytes, 0, bytes.Length); - } - - using (var webResponse = webRequest.GetResponse()) - using (var reader = new StreamReader(webResponse.GetResponseStream())) { } - } - catch (WebException ex) - { - if (ex.Status == WebExceptionStatus.ProtocolError && ex.Response != null) - { - var resp = (HttpWebResponse)ex.Response; - if (resp.StatusCode == HttpStatusCode.NotFound || resp.StatusCode == HttpStatusCode.Conflict) - Logger.Debug("ERROR: " + ex.Message); - else - Logger.Error("ERROR: " + ex.Message); - } - else - { - Logger.Error("ERROR: " + ex.Message); - } - } - catch (Exception ex) - { - Logger.Error("ERROR: " + ex.Message); - } - } - } - catch (Exception ex) - { - Logger.Error("ERROR: " + ex.Message); - } - } - } - private static void updateEvent(string ics, string uid, string calendarId, bool sendToRadicale, DateTime updateDate = default(DateTime), Ical.Net.CalendarComponents.VTimeZone calendarVTimeZone = null, TimeZoneInfo calendarTimeZone = null) + private static async Task updateEvent(string ics, string uid, string calendarId, bool sendToRadicale, DateTime updateDate = default(DateTime), Ical.Net.CalendarComponents.VTimeZone calendarVTimeZone = null, TimeZoneInfo calendarTimeZone = null) { using (var db = DbManager.FromHttpContext("calendar")) { @@ -4210,7 +4423,7 @@ namespace ASC.Api.Calendar if (indexOfChar != -1) { ics = ics.Remove(indexOfChar, indexOfCharEND + 14 - indexOfChar); - if (ics.IndexOf("BEGIN:VTIMEZONE") > -1) updateEvent(ics, uid, calendarId, true); + if (ics.IndexOf("BEGIN:VTIMEZONE") > -1) await updateEvent(ics, uid, calendarId, true).ConfigureAwait(false); } var requestUrl = calDavServerUrl + "/" + HttpUtility.UrlEncode(currentUserName) + "/" + caldavGuid + @@ -4246,23 +4459,9 @@ namespace ASC.Api.Calendar try { - var webRequest = (HttpWebRequest)WebRequest.Create(requestUrl); - webRequest.Method = "PUT"; - webRequest.ContentType = "text/calendar; charset=utf-8"; - + var calDavCalendar = new CalDavCalendar(caldavGuid.ToString(), false); var authorization = GetUserAuthorization(_email); - webRequest.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(authorization))); - - var encoding = new UTF8Encoding(); - byte[] bytes = encoding.GetBytes(ics); - webRequest.ContentLength = bytes.Length; - using (var writeStream = webRequest.GetRequestStream()) - { - writeStream.Write(bytes, 0, bytes.Length); - } - - using (var webResponse = webRequest.GetResponse()) - using (var reader = new StreamReader(webResponse.GetResponseStream())) { } + await calDavCalendar.UpdateItem(requestUrl, authorization, ics).ConfigureAwait(false); } catch (WebException ex) { @@ -4303,32 +4502,34 @@ namespace ASC.Api.Calendar } /// - /// Deletes the whole event from the calendar (all events in the series) + /// Deletes the event series from the calendar. /// /// - /// Delete event series + /// Delete the event series /// /// Event ID + /// Events [Delete("events/{eventId}")] - public void RemoveEvent(int eventId) + public async Task RemoveEvent(int eventId) { - RemoveEvent(eventId, null, EventRemoveType.AllSeries); + await RemoveEvent(eventId, null, EventRemoveType.AllSeries).ConfigureAwait(false); } /// - /// Deletes one event from the series of recurrent events + /// Deletes one event from the series of recurrent events. /// /// - /// Delete event + /// Delete an event from event series /// /// Event ID /// Date to be deleted from the recurrent event /// Recurrent event deletion type - /// Bool flag says that request from caldav server - /// Current uri + /// Defines if the request is from the CalDav server or not + /// Current URI + /// Events /// Updated event series collection [Delete("events/{eventId}/custom")] - public List RemoveEvent(int eventId, ApiDateTime date, EventRemoveType type, Uri uri = null, bool fromCaldavServer = false) + public async Task> RemoveEvent(int eventId, ApiDateTime date, EventRemoveType type, Uri uri = null, bool fromCaldavServer = false) { var events = new List(); var evt = _dataProvider.GetEventById(eventId); @@ -4369,100 +4570,98 @@ namespace ASC.Api.Calendar var calendarId = evt.CalendarId; var myUri = HttpContext.Current != null ? HttpContext.Current.Request.GetUrlRewriter() : uri != null ? uri : new Uri("http://localhost"); var currentUserId = SecurityContext.CurrentAccount.ID; - var removeEventTask = new Task(() => - { - try - { - CoreContext.TenantManager.SetCurrentTenant(currentTenantId); - //calendar sharing list - foreach (var sharingOption in sharingList) - { - var fullAccess = sharingOption.actionId == AccessOption.FullAccessOption.Id; - if (!sharingOption.IsGroup) - { - var user = CoreContext.UserManager.GetUsers(sharingOption.itemId); - if (CheckUserEmail(user)) - { - deleteEvent(fullAccess ? split[0] + "_write" : split[0], calendarId, user.Email, myUri, user.ID != cal.OwnerId); - } - } - else - { - var users = CoreContext.UserManager.GetUsersByGroup(sharingOption.itemId); - foreach (var user in users) - { - if (CheckUserEmail(user)) - { - var eventUid = user.ID == evt.OwnerId - ? split[0] - : fullAccess ? split[0] + "_write" : split[0]; - deleteEvent(eventUid, calendarId, user.Email, myUri, true); - } - - } - } - } - //event sharing list - foreach (var sharingOption in so) - { - var fullAccess = sharingOption.actionId == AccessOption.FullAccessOption.Id; - - if (!sharingOption.IsGroup) - { - var user = CoreContext.UserManager.GetUsers(sharingOption.itemId); - - deleteEvent(fullAccess ? split[0] + "_write" : split[0], SharedEventsCalendar.CalendarId, user.Email, myUri, user.ID != evt.OwnerId); - } - else - { - var users = CoreContext.UserManager.GetUsersByGroup(sharingOption.itemId); - foreach (var user in users) - { - var eventUid = user.ID == evt.OwnerId - ? split[0] - : fullAccess ? split[0] + "_write" : split[0]; - - deleteEvent(eventUid, SharedEventsCalendar.CalendarId, user.Email, myUri, true); - } - } - } - if (currentUserId == evt.OwnerId) - { - var owner = CoreContext.UserManager.GetUsers(evt.OwnerId); - deleteEvent(split[0], evt.CalendarId, owner.Email, myUri); - } - if (calendarId != BirthdayReminderCalendar.CalendarId && - calendarId != SharedEventsCalendar.CalendarId && - calendarId != "crm_calendar" && - !calendarId.Contains("Project_")) - { - if (currentUserId == cal.OwnerId) - { - var owner = CoreContext.UserManager.GetUsers(currentUserId); - - deleteEvent(split[0], evt.CalendarId, owner.Email, myUri); - } - } - } - catch (Exception ex) - { - LogManager.GetLogger("ASC.Calendar").Error(ex.Message); - } - }, TaskCreationOptions.LongRunning); - removeEventTask.ConfigureAwait(false); if (evt.OwnerId.Equals(SecurityContext.CurrentAccount.ID) || CheckPermissions(evt, CalendarAccessRights.FullAccessAction, true) || CheckPermissions(cal, CalendarAccessRights.FullAccessAction, true)) { if (type == EventRemoveType.AllSeries || evt.RecurrenceRule.Freq == Frequency.Never) { + CoreContext.TenantManager.SetCurrentTenant(currentTenantId); _dataProvider.RemoveEvent(eventId); + AttachmentEngine.DeleteFolder(eventId.ToString()); var ownerId = SecurityContext.CurrentAccount.ID != cal.OwnerId ? cal.OwnerId : SecurityContext.CurrentAccount.ID; var email = CoreContext.UserManager.GetUsers(ownerId).Email; - deleteEvent(split[0], evt.CalendarId, email, myUri); - removeEventTask.Start(); + await deleteEvent(split[0], evt.CalendarId, email, myUri).ConfigureAwait(false); + try + { + CoreContext.TenantManager.SetCurrentTenant(currentTenantId); + //calendar sharing list + foreach (var sharingOption in sharingList) + { + var fullAccess = sharingOption.actionId == AccessOption.FullAccessOption.Id; + + if (!sharingOption.IsGroup) + { + var user = CoreContext.UserManager.GetUsers(sharingOption.itemId); + if (CheckUserEmail(user)) + { + await deleteEvent(fullAccess ? split[0] + "_write" : split[0], calendarId, user.Email, myUri, user.ID != cal.OwnerId).ConfigureAwait(false); + } + } + else + { + var users = CoreContext.UserManager.GetUsersByGroup(sharingOption.itemId); + foreach (var user in users) + { + if (CheckUserEmail(user)) + { + var eventUid = user.ID == evt.OwnerId + ? split[0] + : fullAccess ? split[0] + "_write" : split[0]; + await deleteEvent(eventUid, calendarId, user.Email, myUri, true).ConfigureAwait(false); + } + + } + } + } + //event sharing list + foreach (var sharingOption in so) + { + var fullAccess = sharingOption.actionId == AccessOption.FullAccessOption.Id; + + if (!sharingOption.IsGroup) + { + var user = CoreContext.UserManager.GetUsers(sharingOption.itemId); + await deleteEvent(fullAccess ? split[0] + "_write" : split[0], SharedEventsCalendar.CalendarId, user.Email, myUri, user.ID != evt.OwnerId).ConfigureAwait(false); + } + else + { + var users = CoreContext.UserManager.GetUsersByGroup(sharingOption.itemId); + foreach (var user in users) + { + var eventUid = user.ID == evt.OwnerId + ? split[0] + : fullAccess ? split[0] + "_write" : split[0]; + + await deleteEvent(eventUid, SharedEventsCalendar.CalendarId, user.Email, myUri, true).ConfigureAwait(false); + } + } + } + if (currentUserId == evt.OwnerId) + { + var owner = CoreContext.UserManager.GetUsers(evt.OwnerId); + await deleteEvent(split[0], evt.CalendarId, owner.Email, myUri).ConfigureAwait(false); + } + if (calendarId != BirthdayReminderCalendar.CalendarId && + calendarId != SharedEventsCalendar.CalendarId && + calendarId != "crm_calendar" && + !calendarId.Contains("Project_")) + { + if (currentUserId == cal.OwnerId) + { + CoreContext.TenantManager.SetCurrentTenant(currentTenantId); + var owner = CoreContext.UserManager.GetUsers(currentUserId); + + await deleteEvent(split[0], evt.CalendarId, owner.Email, myUri).ConfigureAwait(false); + } + } + } + catch (Exception ex) + { + LogManager.GetLogger("ASC.Calendar").Error(ex.Message); + } return events; } @@ -4498,7 +4697,7 @@ namespace ASC.Api.Calendar evt = _dataProvider.UpdateEvent(int.Parse(evt.Id), int.Parse(evt.CalendarId), evt.OwnerId, evt.Name, evt.Description, evt.UtcStartDate, evt.UtcEndDate, evt.RecurrenceRule, evt.AlertType, evt.AllDayLong, - evt.SharingOptions.PublicItems, evt.Status, DateTime.Now, eventTimeZone); + evt.SharingOptions.PublicItems, evt.Status, DateTime.Now, eventTimeZone, evt.HasAttachments); if (!fromCaldavServer) { try @@ -4518,21 +4717,19 @@ namespace ASC.Api.Calendar var ics = DDayICalParser.SerializeCalendar(targetCalendar); - var updateCaldavTask = new Task(() => + { try { CoreContext.TenantManager.SetCurrentTenant(currentTenantId); - updateCaldavEvent(ics, split[0], true, calDavGuid, myUri, currentUserEmail, DateTime.Now, targetCalendar.TimeZones[0], calTz, true); + await UpdateCaldavEventTask(ics, split[0], true, calDavGuid, myUri, currentUserEmail, DateTime.Now, targetCalendar.TimeZones[0], calTz, true).ConfigureAwait(false); } catch (Exception ex) { LogManager.GetLogger("ASC.Calendar").Error(ex.Message); } - }, TaskCreationOptions.LongRunning); - updateCaldavTask.ConfigureAwait(false); - updateCaldavTask.Start(); + } } catch (Exception e) { @@ -4585,7 +4782,7 @@ namespace ASC.Api.Calendar return events; } - private static void deleteEvent(string uid, string calendarId, string email, Uri myUri, bool isShared = false) + private static async Task deleteEvent(string uid, string calendarId, string email, Uri myUri, bool isShared = false) { using (var db = DbManager.FromHttpContext("calendar")) { @@ -4593,7 +4790,7 @@ namespace ASC.Api.Calendar { try { - var сaldavGuid = ""; + var caldavGuid = ""; if (calendarId != BirthdayReminderCalendar.CalendarId && calendarId != SharedEventsCalendar.CalendarId && calendarId != "crm_calendar" && @@ -4605,33 +4802,30 @@ namespace ASC.Api.Calendar .Select(r => r[0]) .ToArray(); - сaldavGuid = dataCaldavGuid[0].ToString(); + caldavGuid = dataCaldavGuid[0].ToString(); } else { - сaldavGuid = calendarId; + caldavGuid = calendarId; } - if (сaldavGuid != "") + if (caldavGuid != "") { - var calDavServerUrl = myUri.Scheme + "://" + myUri.Host + "/caldav"; Logger.Info("RADICALE REWRITE URL: " + myUri); - var currentUserName = email + "@" + myUri.Host; - - var requestUrl = calDavServerUrl + "/" + HttpUtility.UrlEncode(currentUserName) + "/" + (isShared ? сaldavGuid + "-shared" : сaldavGuid) + "/" + uid + ".ics"; try { - var webRequest = (HttpWebRequest)WebRequest.Create(requestUrl); - webRequest.Method = "DELETE"; - - var authorization = isShared ? GetSystemAuthorization() : GetUserAuthorization(email); - webRequest.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(authorization))); - - using (var webResponse = webRequest.GetResponse()) - using (var reader = new StreamReader(webResponse.GetResponseStream())) { } + var calDavCalendar = new CalDavCalendar(calendarId, false); + var authorization = isShared ? calDavCalendar.GetSystemAuthorization() : GetUserAuthorization(email); + var requestUrl = calDavCalendar.GetRadicaleUrl(myUri.ToString(), email, isShared, false, true, caldavGuid, uid); + var davRequest = new DavRequest() + { + Url = requestUrl, + Authorization = authorization + }; + await RadicaleClient.RemoveAsync(davRequest).ConfigureAwait(false); } catch (WebException ex) { @@ -4660,17 +4854,18 @@ namespace ASC.Api.Calendar } } } - } + /// - /// Unsubscribes the current user from the event with the ID specified in the request + /// Unsubscribes the current user from the event with the ID specified in the request. /// /// - /// Unsubscribe from event + /// Unsubscribe from the event /// /// Event ID + /// Events [Delete("events/{eventId}/unsubscribe")] - public void UnsubscribeEvent(int eventId) + public async Task UnsubscribeEvent(int eventId) { var evt = _dataProvider.GetEventById(eventId); @@ -4681,20 +4876,21 @@ namespace ASC.Api.Calendar var email = CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID).Email; var fullAccess = CheckPermissions(evt, CalendarAccessRights.FullAccessAction, true); - deleteEvent(fullAccess ? split[0] + "_write" : split[0], SharedEventsCalendar.CalendarId, email, myUri, SecurityContext.CurrentAccount.ID != evt.OwnerId); + await deleteEvent(fullAccess ? split[0] + "_write" : split[0], SharedEventsCalendar.CalendarId, email, myUri, SecurityContext.CurrentAccount.ID != evt.OwnerId).ConfigureAwait(false); _dataProvider.UnsubscribeFromEvent(eventId, SecurityContext.CurrentAccount.ID); } } /// - /// Returns the event in ics format from history + /// Returns an event in the iCal format by its UID from the history. /// /// - /// Returns the event in ics format from history + /// Get ics by UID /// /// Event UID - /// Event History + /// Events + /// Event history [Read("events/{eventUid}/historybyuid")] public EventHistoryWrapper GetEventHistoryByUid(string eventUid) { @@ -4709,13 +4905,14 @@ namespace ASC.Api.Calendar } /// - /// Returns the event in ics format from history + /// Returns an event in the iCal format by its ID from the history. /// /// - /// Returns the event in ics format from history + /// Get ics by ID /// /// Event ID - /// Event History + /// Events + /// Event history [Read("events/{eventId}/historybyid")] public EventHistoryWrapper GetEventHistoryById(int eventId) { @@ -4909,12 +5106,13 @@ namespace ASC.Api.Calendar } /// - /// Returns the sharing access parameters to the calendar with the ID specified in the request + /// Returns the sharing access parameters to the calendar with the ID specified in the request. /// /// /// Get access parameters /// /// Calendar ID + /// Calendars and subscriptions /// Sharing access parameters [Read("{calendarId}/sharing")] public PublicItemCollection GetCalendarSharingOptions(int calendarId) @@ -4927,11 +5125,12 @@ namespace ASC.Api.Calendar } /// - /// Returns the default values for the sharing access parameters + /// Returns the default values for the sharing access parameters. /// /// - /// Get default access + /// Get default access parameters /// + /// Calendars and subscriptions /// Default sharing access parameters [Read("sharing")] public PublicItemCollection GetDefaultSharingOptions() @@ -4939,14 +5138,6 @@ namespace ASC.Api.Calendar return PublicItemCollection.GetDefault(); } - - private static string GetSystemAuthorization() - { - const string email = "admin@ascsystem"; - var currentAccountPaswd = InstanceCrypto.Encrypt(email); - return email + ":" + currentAccountPaswd; - } - private static string GetUserAuthorization(string email) { var user = CoreContext.UserManager.GetUserByEmail(email); diff --git a/module/ASC.Api/ASC.Api.Calendar/CalendarBootstrap.cs b/module/ASC.Api/ASC.Api.Calendar/CalendarBootstrap.cs new file mode 100644 index 000000000..2195d86e0 --- /dev/null +++ b/module/ASC.Api/ASC.Api.Calendar/CalendarBootstrap.cs @@ -0,0 +1,35 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using ASC.Api.Calendar.Attachments; +using ASC.Api.Interfaces; + +using ASC.Web.Files.Api; + +namespace ASC.Api.Calendar +{ + public class CalendarBootstrap : IApiBootstrapper + { + public void Configure() + { + if (!FilesIntegration.IsRegisteredFileSecurityProvider(AttachmentEngine.Module, AttachmentEngine.Bunch)) + { + FilesIntegration.RegisterFileSecurityProvider(AttachmentEngine.Module, AttachmentEngine.Bunch, new SecurityAdapterProvider()); + } + } + } +} \ No newline at end of file diff --git a/module/ASC.Api/ASC.Api.Calendar/Notification/CalendarNotifySource.cs b/module/ASC.Api/ASC.Api.Calendar/Notification/CalendarNotifySource.cs index cd5954d0b..0e93aac0a 100644 --- a/module/ASC.Api/ASC.Api.Calendar/Notification/CalendarNotifySource.cs +++ b/module/ASC.Api/ASC.Api.Calendar/Notification/CalendarNotifySource.cs @@ -16,6 +16,7 @@ using System; +using System.Linq; using ASC.Api.Calendar.BusinessObjects; using ASC.Common.Logging; @@ -102,6 +103,17 @@ namespace ASC.Api.Calendar.Notification : endDate.Add(data.TimeZone.GetOffset())); } + var attachmentText = string.Empty; + Func funcEventAttachmentResource = () => CalendarPatternResource.EventAttachments; + if (data.Event.HasAttachments) + { + var eventHistory = provider.GetEventHistory(int.Parse(data.Event.Id)); + if (eventHistory != null) + { + attachmentText = GetTagValueWithAttachmentText(eventHistory); + } + } + _notifyClient.SendNoticeAsync(CalendarNotifySource.EventAlert, null, r, @@ -114,6 +126,8 @@ namespace ASC.Api.Calendar.Notification (endDate > startDate) ? (endDate.ToShortDateString() + " " + endDate.ToShortTimeString()) : ""), + new TagActionValue("EventAttachmentsHeader", funcEventAttachmentResource), + new TagValue("EventAttachmentsBody", attachmentText), new TagValue("Priority", 1)); } } @@ -199,6 +213,23 @@ namespace ASC.Api.Calendar.Notification { return CommonLinkUtility.GetFullAbsolutePath(url); } + + private static string GetTagValueWithAttachmentText(EventHistory eventHistory) + { + var attachmentText = string.Empty; + var mergedCalendar = eventHistory.GetMerged(); + if (mergedCalendar != null && mergedCalendar.Events != null && mergedCalendar.Events.Any()) + { + var mergedEvent = mergedCalendar.Events.First(); + foreach (var attachment in mergedEvent.Attachments) + { + attachmentText += string.Format("{1}
    ", + attachment.Uri.ToString(), + attachment.Parameters.FirstOrDefault(x => x.Name.Equals("FILENAME")).Value); + } + } + return attachmentText; + } } diff --git a/module/ASC.Api/ASC.Api.Calendar/Notification/CalendarPatternResource.Designer.cs b/module/ASC.Api/ASC.Api.Calendar/Notification/CalendarPatternResource.Designer.cs index 030c267ad..4e8e5c74d 100644 --- a/module/ASC.Api/ASC.Api.Calendar/Notification/CalendarPatternResource.Designer.cs +++ b/module/ASC.Api/ASC.Api.Calendar/Notification/CalendarPatternResource.Designer.cs @@ -118,12 +118,16 @@ namespace ASC.Api.Calendar.Notification { ///$EventDescription ///#end ///  - ///  - ///^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. If you do not want to receive the event reminders, please manage your "subscription settings":"$RecipientSubscriptionConfigURL".^. + ///#if($EventAttachmentsBody!="") + ///$EventAttachmentsHeader + ///$EventAttachmentsBody + ///#end + /// + ///^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. If you do not want to receive the event reminders, please manage your "subscr [rest of string was truncated]";. /// - public static string EventAlertEmailPattern { + public static string EventAlertEmailPatternWithAttachments { get { - return ResourceManager.GetString("EventAlertEmailPattern", resourceCulture); + return ResourceManager.GetString("EventAlertEmailPatternWithAttachments", resourceCulture); } } @@ -134,11 +138,16 @@ namespace ASC.Api.Calendar.Notification { /// Event Description: ///  ///$EventDescription + ///#end + /// + ///#if($EventAttachmentsBody!="") + ///$EventAttachmentsHeader + ///$EventAttachmentsBody ///#end. /// - public static string EventAlertJabberPattern { + public static string EventAlertJabberPatternWithAttachments { get { - return ResourceManager.GetString("EventAlertJabberPattern", resourceCulture); + return ResourceManager.GetString("EventAlertJabberPatternWithAttachments", resourceCulture); } } @@ -150,5 +159,14 @@ namespace ASC.Api.Calendar.Notification { return ResourceManager.GetString("EventAlertSubject", resourceCulture); } } + + /// + /// Looks up a localized string similar to Attachments:. + /// + public static string EventAttachments { + get { + return ResourceManager.GetString("EventAttachments", resourceCulture); + } + } } } diff --git a/module/ASC.Api/ASC.Api.Calendar/Notification/CalendarPatternResource.az-Latn-AZ.resx b/module/ASC.Api/ASC.Api.Calendar/Notification/CalendarPatternResource.az-Latn-AZ.resx index 57c8d36e2..d6aab3a3b 100644 --- a/module/ASC.Api/ASC.Api.Calendar/Notification/CalendarPatternResource.az-Latn-AZ.resx +++ b/module/ASC.Api/ASC.Api.Calendar/Notification/CalendarPatternResource.az-Latn-AZ.resx @@ -60,22 +60,62 @@ #if($SharingType == "calendar") -h1.Təqvimə giriş verilib: $CalendarName - - -İstifadəçi "$UserName":"$UserLink" Sizə təqvimə giriş veri: $CalendarName - - -^Portalda qeydiyyatdan keçdiyiniz üçün bu mıktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Giriş aldığınız hadisələr haqqında məlumatlar almaq istəmirsinizsə, lütfən, "abunəlik ayarlarını":"$RecipientSubscriptionConfigURL" istəyinizə uyğun şəkildə dəyişin.^ -#end +h1.Təqvimə İcazə Verilir: $CalendarName + + +"$UserName":"$UserLink" sizə təqvimə giriş icazəsi verdi: $CalendarName + +^Siz bu e-məktubu alırsınız, çünki siz "${__VirtualRootPath}":"${__VirtualRootPath}" portalının qeydiyyatdan keçmiş istifadəçisisiniz. Sizinlə paylaşılan təqvimlər haqqında bildirişləri almaq istəmirsinizsə, lütfən, "abunəlik parametrlərinizi":"$RecipientSubscriptionConfigURL" idarə edin.^ +#son #if($SharingType == "event") -h1.Access Granted to Event: $EventName +h1.Tədbirə Giriş: $EventName -"$UserName":"$UserLink" has granted you the access to the event: $EventName +"$UserName":"$UserLink" sizə tədbirə giriş icazəsi verdi: $EventName -^Portalda qeydiyyatdan keçdiyiniz üçün bu mıktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Giriş aldığınız hadisələr haqqında məlumatlar almaq istəmirsinizsə, lütfən, "abunəlik ayarlarını":"$RecipientSubscriptionConfigURL" istəyinizə uyğun şəkildə dəyişin.^ +^Siz bu e-məktubu alırsınız, çünki siz "${__VirtualRootPath}":"${__VirtualRootPath}" portalının qeydiyyatdan keçmiş istifadəçisisiniz. Sizinlə paylaşılan tədbirlər haqqında bildirişlər almaq istəmirsinizsə, lütfən, "abunəlik parametrlərinizi" idarə edin:"$RecipientSubscriptionConfigURL".^ +#son + + + #if($SharingType == "təqvim") +Təqvim. Təqvimə giriş verildi:$CalendarName +#end +#if($SharingType == "hadisə") +Təqvim. Tədbirə giriş verildi:$EventName #end - \ No newline at end of file + + #if($SharingType == "təqvim") +Təqvim. Təqvimə icazə verildi:$CalendarName +#end +#if($SharingType == "tədbir") +Təqvim. Tədbirə giriş verildi: $EventName +#end + + + h1.Tədbir haqqında xatırlatma: $EventName + +$EventName hadisəsi $EventStartDate #if($EventEndDate!="")- $EventEndDate #end +#if($EventDescription!="") + +Hadisənin təsviri: + +$EventDescription +#end + +^Siz "${__VirtualRootPath}":"${__VirtualRootPath}" portalının qeydiyyatdan keçmiş üzvü olduğunuz üçün bu e-məktubu alırsınız. Tədbir xatırlatma bildirişini almaq istəmirsinizsə, "abunəlik parametrlərini":"$RecipientSubscriptionConfigURL" dəyişdirin.^ + + + $EventName tədbiri, üçün təyin edilir $EventStartDate #if($EventEndDate!="")- $EventEndDate #end +#if($EventDescription!="") + + Hadisənin təsviri: + +$EventDescription +#end + + + Təqvim. Tədbir haqqında xatırlatma: $EventName + + diff --git a/module/ASC.Api/ASC.Api.Calendar/Notification/CalendarPatternResource.resx b/module/ASC.Api/ASC.Api.Calendar/Notification/CalendarPatternResource.resx index 5e232f4e0..ab519203d 100644 --- a/module/ASC.Api/ASC.Api.Calendar/Notification/CalendarPatternResource.resx +++ b/module/ASC.Api/ASC.Api.Calendar/Notification/CalendarPatternResource.resx @@ -93,7 +93,7 @@ Calendar. Access granted to calendar: $CalendarName Calendar. Access granted to event: $EventName #end - + h1.Reminder about the Event: $EventName     @@ -105,19 +105,31 @@ The $EventName event is appointed for $EventStartDate #if($EventEndDate!="")- $E $EventDescription #end   -  +#if($EventAttachmentsBody!="") +$EventAttachmentsHeader +$EventAttachmentsBody +#end + ^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. If you do not want to receive the event reminders, please manage your "subscription settings":"$RecipientSubscriptionConfigURL".^ - + The $EventName event is appointed for $EventStartDate #if($EventEndDate!="")- $EventEndDate #end #if($EventDescription!="")    Event Description:   $EventDescription +#end + +#if($EventAttachmentsBody!="") +$EventAttachmentsHeader +$EventAttachmentsBody #end Calendar. Reminder about the event: $EventName + + Attachments: + \ No newline at end of file diff --git a/module/ASC.Api/ASC.Api.Calendar/Notification/calendar_patterns.xml b/module/ASC.Api/ASC.Api.Calendar/Notification/calendar_patterns.xml index bc3d379cd..5b92d9105 100644 --- a/module/ASC.Api/ASC.Api.Calendar/Notification/calendar_patterns.xml +++ b/module/ASC.Api/ASC.Api.Calendar/Notification/calendar_patterns.xml @@ -11,9 +11,9 @@ - + - + \ No newline at end of file diff --git a/module/ASC.Api/ASC.Api.Calendar/Resources/CalendarApiResource.az-Latn-AZ.resx b/module/ASC.Api/ASC.Api.Calendar/Resources/CalendarApiResource.az-Latn-AZ.resx index ae50c66ee..d23444412 100644 --- a/module/ASC.Api/ASC.Api.Calendar/Resources/CalendarApiResource.az-Latn-AZ.resx +++ b/module/ASC.Api/ASC.Api.Calendar/Resources/CalendarApiResource.az-Latn-AZ.resx @@ -70,6 +70,9 @@ Təqvimim + + Giriş rədd edildi + Ad boş ola bilməz @@ -79,6 +82,15 @@ Tam giriş + + iCal Təqvimləri + + + Başlıq yoxdur + + + Başlıq yoxdur + Sahib @@ -97,4 +109,7 @@ Mənimlə paylaşılan olaylar - \ No newline at end of file + + iCal üçün etibarsız istinad + + diff --git a/module/ASC.Api/ASC.Api.Calendar/Wrappers/EventWrapper.cs b/module/ASC.Api/ASC.Api.Calendar/Wrappers/EventWrapper.cs index 1dcd930c9..ab0064861 100644 --- a/module/ASC.Api/ASC.Api.Calendar/Wrappers/EventWrapper.cs +++ b/module/ASC.Api/ASC.Api.Calendar/Wrappers/EventWrapper.cs @@ -233,6 +233,15 @@ namespace ASC.Api.Calendar.Wrappers } } + [DataMember(Name = "hasAttachments", Order = 160)] + public bool HasAttachments + { + get + { + return _baseEvent.HasAttachments; + } + } + public static object GetSample() { @@ -252,7 +261,8 @@ namespace ASC.Api.Calendar.Wrappers title = "Event Name", objectId = "1", sourceId = "calendarID", - status = (int)EventStatus.Tentative + status = (int)EventStatus.Tentative, + hasAttachments = false }; } } diff --git a/module/ASC.Api/ASC.Api.Calendar/iCalParser/DDayICalParser.cs b/module/ASC.Api/ASC.Api.Calendar/iCalParser/DDayICalParser.cs index 6cd827782..e07e25792 100644 --- a/module/ASC.Api/ASC.Api.Calendar/iCalParser/DDayICalParser.cs +++ b/module/ASC.Api/ASC.Api.Calendar/iCalParser/DDayICalParser.cs @@ -30,6 +30,7 @@ namespace ASC.Api.Calendar.iCalParser { class DDayICalParser { + public static ILog Log = LogManager.GetLogger("ASC.Calendar"); public static Ical.Net.CalendarCollection DeserializeCalendar(string iCalCalendarString) { if (string.IsNullOrEmpty(iCalCalendarString)) return null; @@ -43,7 +44,7 @@ namespace ASC.Api.Calendar.iCalParser } catch (Exception ex) { - LogManager.GetLogger("ASC.Calendar").Error(ex); + Log.Error(ex); return null; } } @@ -56,7 +57,7 @@ namespace ASC.Api.Calendar.iCalParser } catch (Exception ex) { - LogManager.GetLogger("ASC.Calendar").Error(ex); + Log.Error(ex); return null; } } @@ -66,11 +67,12 @@ namespace ASC.Api.Calendar.iCalParser try { var serializer = new Ical.Net.Serialization.CalendarSerializer(); - return serializer.SerializeToString(calendar); + + return StringUtils.NormalizeStringForMySql(serializer.SerializeToString(calendar)); } catch (Exception ex) { - LogManager.GetLogger("ASC.Calendar").Error(ex); + Log.Error(ex); return null; } } @@ -91,7 +93,7 @@ namespace ASC.Api.Calendar.iCalParser } catch (Exception ex) { - LogManager.GetLogger("ASC.Calendar").Error(ex); + Log.Error(ex); return null; } } @@ -119,7 +121,7 @@ namespace ASC.Api.Calendar.iCalParser } catch (Exception ex) { - LogManager.GetLogger("ASC.Calendar").Error(ex); + Log.Error(ex); return null; } } @@ -140,7 +142,7 @@ namespace ASC.Api.Calendar.iCalParser } catch (Exception ex) { - LogManager.GetLogger("ASC.Calendar").Error(ex); + Log.Error(ex); return null; } } @@ -154,7 +156,7 @@ namespace ASC.Api.Calendar.iCalParser } catch (Exception ex) { - LogManager.GetLogger("ASC.Calendar").Error(ex); + Log.Error(ex); return null; } } @@ -168,7 +170,7 @@ namespace ASC.Api.Calendar.iCalParser } catch (Exception ex) { - LogManager.GetLogger("ASC.Calendar").Error(ex); + Log.Error(ex); return null; } } diff --git a/module/ASC.Api/ASC.Api.Client/ASC.Api.Client.csproj b/module/ASC.Api/ASC.Api.Client/ASC.Api.Client.csproj index 6146bb92a..3a5a864da 100644 --- a/module/ASC.Api/ASC.Api.Client/ASC.Api.Client.csproj +++ b/module/ASC.Api/ASC.Api.Client/ASC.Api.Client.csproj @@ -47,7 +47,7 @@ - 12.0.3 + 13.0.1 diff --git a/module/ASC.Api/ASC.Api.Community/ASC.Api.Community.csproj b/module/ASC.Api/ASC.Api.Community/ASC.Api.Community.csproj index e29626ec4..3ff939412 100644 --- a/module/ASC.Api/ASC.Api.Community/ASC.Api.Community.csproj +++ b/module/ASC.Api/ASC.Api.Community/ASC.Api.Community.csproj @@ -138,6 +138,11 @@ + + + 4.3.0 + + diff --git a/module/ASC.Api/ASC.Api.Community/Blogs/BlogApi.cs b/module/ASC.Api/ASC.Api.Community/Blogs/BlogApi.cs index e342fd994..6ec67801c 100644 --- a/module/ASC.Api/ASC.Api.Community/Blogs/BlogApi.cs +++ b/module/ASC.Api/ASC.Api.Community/Blogs/BlogApi.cs @@ -55,9 +55,9 @@ namespace ASC.Api.Community /// - /// Returns the list of all posts in blogs on the portal with the post title, date of creation and update, post text and author ID and display name + /// Returns a list of all the posts from the portal blogs with the post titles, dates of creation and update, post texts, authors. /// - ///All posts + ///Get posts ///List of all posts ///Blogs [Read("blog")] @@ -67,13 +67,13 @@ namespace ASC.Api.Community } /// - ///Creates a blog post with the specified title, content, tags and subscription to comments defined in the request body + ///Creates a blog post with the specified title, content, tags and subscription to comments defined in the request body. /// - ///Create post - ///New post title - ///New post text - ///Tag list separated with ',' - ///Subscribe to post comments + ///Create a post + ///Post title + ///Post text + ///List of tags separated with comma + ///Subscribes to the post comments ///Newly created post /// /// - ///Updates the specified post changing the post title, content or/and tags specified + ///Updates the selected post changing the post title, content or/and tags specified in the request. /// - ///Update post - ///post ID - ///new title - ///new post text - ///tag list separated with ',' + ///Update a post + ///Post ID + ///New title + ///New post text + ///New list of tags separated with comma ///Updated post /// /// - ///Deletes the selected post from blogs + ///Deletes a post with the ID specified in the request from blogs. /// - ///Delete post - ///post ID to delete - ///Nothing + ///Delete a post + ///Post ID /// ///Blogs [Delete("blog/{postid}")] @@ -162,11 +161,11 @@ namespace ASC.Api.Community } /// - ///Returns the list of all blog posts for the current user with the post title, date of creation and update, post text and author ID and display name + ///Returns a list of all the blog posts for the current user with the post titles, dates of creation and update, post texts, authors. /// ///Blogs - ///My posts - ///My post list + ///Get my posts + ///List of my posts [Read("blog/@self")] public IEnumerable GetMyPosts() { @@ -174,14 +173,14 @@ namespace ASC.Api.Community } /// - ///Returns a list of blog posts matching the search query with the post title, date of creation and update, post text and author + ///Returns a list of blog posts matching the search query specified in the request with the post titles, dates of creation and update, post texts, authors. /// ///Blogs /// ///Search posts /// - /// search query - ///Found post list + /// Search query + ///List of found posts [Read("blog/@search/{query}")] public IEnumerable GetSearchPosts(string query) { @@ -189,11 +188,11 @@ namespace ASC.Api.Community } /// - ///Returns a list of blog posts of the specified user with the post title, date of creation and update and post text + ///Returns a list of blog posts for the specified user with the post titles, dates of creation and update and post texts. /// - ///User posts + ///Get user posts ///Blogs - ///Username + ///User name ///List of user posts [Read("blog/user/{username}")] public IEnumerable GetUserPosts(string username) @@ -203,12 +202,12 @@ namespace ASC.Api.Community } /// - ///Returns a list of blog posts containing the specified tag with the post title, date of creation and update, post text and author + ///Returns a list of blog posts containing the tag specified in the request with the post titles, dates of creation and update, post texts, authors. /// - ///By tag + ///Get posts by tag ///Blogs - ///tag name - ///List of posts tagged with tag name + ///Tag name + ///List of posts with the specified tag name [Read("blog/tag/{tag}")] public IEnumerable GetPostsByTag(string tag) { @@ -216,10 +215,10 @@ namespace ASC.Api.Community } /// - ///Returns a list of all tags used with blog posts with the post title, date of creation and update, post text, author and all the tags used + ///Returns a list of all the tags used with blog posts with the number showing the tag usage. /// /// Blogs - /// Tags + /// Get tags ///List of tags [Read("blog/tag")] public IEnumerable GetTags() @@ -232,12 +231,12 @@ namespace ASC.Api.Community /// - /// Returns the detailed information for the blog post with the ID specified in the request + /// Returns the detailed information on the blog post with the ID specified in the request. /// - ///Specific post + ///Get a post ///Blogs - ///post ID - ///post + ///Post ID + ///Post information [Read("blog/{postid}")] public BlogPostWrapperFull GetPost(Guid postid) { @@ -245,12 +244,12 @@ namespace ASC.Api.Community } /// - /// Returns the list of all the comments for the blog post with the ID specified in the request + /// Returns a list of all the comments on the blog post with the ID specified in the request. /// ///Blogs - /// Get comments - ///post ID (GUID) - ///list of post comments + /// Get post comments + ///Post ID (GUID) + ///List of post comments [Read("blog/{postid}/comment")] public IEnumerable GetComments(Guid postid) { @@ -258,13 +257,13 @@ namespace ASC.Api.Community } /// - /// Adds a comment to the specified post with the comment text specified. The parent comment ID can be also specified if needed. + /// Adds a comment to the post with the ID specified in the request. The parent comment ID can be also specified if needed. /// - /// Add comment + /// Add a post comment /// Blogs - ///post ID - ///comment text - ///parent comment ID + ///Post ID + ///Comment text + ///Parent comment ID ///List of post comments /// /// /// /// - /// Send parentId=00000000-0000-0000-0000-000000000000 or don't send it at all if you want your comment to be on the root level + /// Send parentId=00000000-0000-0000-0000-000000000000 or don't send it at all if you want your comment to be on the root level. /// [Create("blog/{postid}/comment")] public BlogPostCommentWrapper AddComment(Guid postid, string content, Guid parentId) @@ -303,13 +302,13 @@ namespace ASC.Api.Community /// - /// Get comment preview with the content specified in the request + /// Returns a comment preview with the content specified in the request. /// - /// Get comment preview + /// Get a comment preview ///
    Comments
    /// Comment ID - /// Comment content - /// Comment info + /// Comment text in the HTML format + /// Comment information /// Blogs [Create("blog/comment/preview")] public CommentInfo GetBlogCommentPreview(string commentid, string htmltext) @@ -331,12 +330,12 @@ namespace ASC.Api.Community /// - ///Remove comment with the id specified in the request + /// Removes a comment with the ID specified in the request. /// - /// Remove comment + /// Remove a comment ///
    Comments
    /// Comment ID - /// Comment id + /// Comment ID /// Blogs [Delete("blog/comment/{commentid}")] public string RemoveBlogComment(string commentid) @@ -368,6 +367,15 @@ namespace ASC.Api.Community + /// + /// Adds a blog comment with the comment text specified in the request. The parent comment ID can be also specified if needed. + /// + /// Add a blog comment + ///
    Comments
    + /// Parent comment ID + /// Entity ID + /// Comment text + /// Comment information /// Blogs [Create("blog/comment")] public CommentInfo AddBlogComment(string parentcommentid, string entityid, string content) @@ -408,6 +416,14 @@ namespace ASC.Api.Community return GetCommentInfo(newComment, false); } + /// + /// Updates a blog comment specified in the request changing its content. + /// + /// Update a blog comment + ///
    Comments
    + /// Comment ID + /// New comment text + /// Updated blog comment /// Blogs [Update("blog/comment/{commentid}")] public string UpdateBlogComment(string commentid, string content) diff --git a/module/ASC.Api/ASC.Api.Community/Bookmarks/BookmarkApi.cs b/module/ASC.Api/ASC.Api.Community/Bookmarks/BookmarkApi.cs index e8fd70064..15032e5b3 100644 --- a/module/ASC.Api/ASC.Api.Community/Bookmarks/BookmarkApi.cs +++ b/module/ASC.Api/ASC.Api.Community/Bookmarks/BookmarkApi.cs @@ -45,10 +45,10 @@ namespace ASC.Api.Community } /// - ///Returns the list of all bookmarks on the portal with the bookmark titles, date of creation and update, bookmark text and author + ///Returns a list of all the portal bookmarks with the bookmark titles, dates of creation and update, bookmark texts and authors. /// /// - ///All bookmarks + ///Get bookmarks /// ///Bookmarks ///List of all bookmarks @@ -61,10 +61,10 @@ namespace ASC.Api.Community } /// - ///Returns the list of all bookmarks for the current user with the bookmark titles, date of creation and update, bookmark text and author + ///Returns a list of all the bookmarks for the current user with the bookmark titles, dates of creation and update, bookmark texts and author. /// /// - ///Added by me + ///Get my bookmarks /// ///Bookmarks ///List of bookmarks @@ -77,13 +77,13 @@ namespace ASC.Api.Community } /// - ///Returns a list of bookmarks matching the search query with the bookmark title, date of creation and update, bookmark description and author + ///Returns a list of bookmarks matching the search query specified in the request with the bookmark titles, dates of creation and update, bookmark descriptions and author. /// /// - ///Search + ///Search bookmarks /// ///Bookmarks - /// search query + /// Search query ///List of bookmarks [Read("bookmark/@search/{query}")] public IEnumerable SearchBookmarks(string query) @@ -94,10 +94,10 @@ namespace ASC.Api.Community } /// - ///Returns the list of favorite bookmarks for the current user with the bookmark titles, date of creation and update, bookmark text and author + ///Returns a list of favorite bookmarks for the current user with the bookmark titles, dates of creation and update, bookmark texts and author. /// /// - ///My favorite + ///Get my favorite bookmarks /// ///Bookmarks ///List of bookmarks @@ -110,10 +110,10 @@ namespace ASC.Api.Community } /// - ///Returns a list of all tags used for bookmarks with the number showing the tag usage + ///Returns a list of all the bookmark tags with a number specifying the tag usage. /// /// - ///All tags + ///Get all tags /// ///Bookmarks ///List of tags @@ -125,13 +125,13 @@ namespace ASC.Api.Community } /// - ///Returns the list of all bookmarks marked by the tag specified with the bookmark titles, date of creation and update, bookmark text and author + ///Returns a list of all the bookmarks marked by the tag specified in the request with the bookmark titles, dates of creation and update, bookmark texts and authors. /// /// - ///By tag + ///Get a bookmark by a tag /// ///Bookmarks - ///tag + ///Tag name ///List of bookmarks [Read("bookmark/bytag")] public IEnumerable GetBookmarksByTag(string tag) @@ -142,10 +142,10 @@ namespace ASC.Api.Community } /// - ///Returns the list of recenty added bookmarks with the bookmark titles, date of creation and update, bookmark text and author + ///Returns a list of recently added bookmarks with the bookmark titles, dates of creation and update, bookmark texts and authors. /// /// - ///Recently added + ///Get recent bookmarks /// ///Bookmarks ///List of bookmarks @@ -158,10 +158,10 @@ namespace ASC.Api.Community } /// - ///Returns the list of the bookmarks most popular on the current date with the bookmark titles, date of creation and update, bookmark text and author + ///Returns a list of the bookmarks most popular for the current date with the bookmark titles, dates of creation and update, bookmark texts and authors. /// /// - ///Top of day + ///Get top of day bookmarks /// ///Bookmarks ///List of bookmarks @@ -174,10 +174,10 @@ namespace ASC.Api.Community } /// - ///Returns the list of the bookmarks most popular in the current month with the bookmark titles, date of creation and update, bookmark text and author + ///Returns a list of the bookmarks most popular for the current month with the bookmark titles, dates of creation and update, bookmark texts and authors. /// /// - ///Top of month + ///Get top of month bookmarks /// ///Bookmarks ///List of bookmarks @@ -190,13 +190,13 @@ namespace ASC.Api.Community } /// - ///Returns the list of the bookmarks most popular on the current week with the bookmark titles, date of creation and update, bookmark text and author + ///Returns a list of the bookmarks most popular for the current week with the bookmark titles, dates of creation and update, bookmark texts and authors. /// /// - ///Top of week + ///Get top of week bookmarks /// ///Bookmarks - ///list of bookmarks + ///List of bookmarks [Read("bookmark/top/week")] public IEnumerable GetTopWeekBookmarks() { @@ -206,13 +206,13 @@ namespace ASC.Api.Community } /// - ///Returns the list of the bookmarks most popular in the current year with the bookmark titles, date of creation and update, bookmark text and author + ///Returns a list of the bookmarks most popular for the current year with the bookmark titles, dates of creation and update, bookmark texts and authors. /// /// - ///Top of year + ///Get top of year bookmarks /// ///Bookmarks - ///list of bookmarks + ///List of bookmarks [Read("bookmark/top/year")] public IEnumerable GetTopYearBookmarks() { @@ -222,14 +222,14 @@ namespace ASC.Api.Community } /// - /// Returns the list of all comments to the bookmark with the specified ID + /// Returns a list of all the comments on the bookmark with the ID specified in the request. /// /// - /// Get comments + /// Get bookmark comments /// ///Bookmarks ///Bookmark ID - ///list of bookmark comments + ///List of bookmark comments [Read("bookmark/{id}/comment")] public IEnumerable GetBookmarkComments(long id) { @@ -238,15 +238,15 @@ namespace ASC.Api.Community } /// - /// Adds a comment to the bookmark with the specified ID. The parent bookmark ID can be also specified if needed. + /// Adds a comment to the bookmark with the ID specified in the request. The parent bookmark ID can be also specified if needed. /// /// - /// Add comment + /// Add a bookmark comment by bookmark ID /// ///Bookmark ID - ///comment content - ///parent comment ID - ///list of bookmark comments + ///Comment text + ///Parent comment ID + ///List of bookmark comments /// /// /// /// - /// Send parentId=00000000-0000-0000-0000-000000000000 or don't send it at all if you want your comment to be on the root level + /// Send parentId=00000000-0000-0000-0000-000000000000 or doesn't send it at all if you want your comment to be on the root level. /// /// Bookmarks [Create("bookmark/{id}/comment")] @@ -284,13 +284,13 @@ namespace ASC.Api.Community } /// - /// Returns a detailed information on the bookmark with the specified ID + /// Returns the detailed information on the bookmark with the ID specified in the request. /// /// - /// Get bookmarks by ID + /// Get a bookmark by ID /// ///Bookmark ID - ///bookmark + ///Bookmark information ///Bookmarks [Read("bookmark/{id}")] public BookmarkWrapper GetBookmarkById(long id) @@ -299,16 +299,16 @@ namespace ASC.Api.Community } /// - /// Adds a bookmark with a specified title, description and tags + /// Adds a bookmark with the title, description and tags specified in the request. /// /// - /// Add bookmark + /// Add a bookmark /// - ///absolute url of bookmarking page - ///title to show - ///description - ///tags. separated by semicolon - ///newly added bookmark + ///Absolute URL to the bookmark page + ///Bookmark title + ///Bookmark description + ///Bookmark tags separated by semicolon + ///Newly added bookmark /// /// - /// Get comment preview with the content specified in the request + /// Returns a comment preview with the content specified in the request. /// - /// Get comment preview + /// Get a comment preview ///
    Comments
    /// Comment ID /// Comment content - /// Comment info + /// Comment information /// Bookmarks [Create("bookmark/comment/preview")] public CommentInfo GetBookmarkCommentPreview(string commentid, string htmltext) @@ -385,12 +385,12 @@ namespace ASC.Api.Community /// - ///Remove comment with the id specified in the request + ///Removes a comment with the ID specified in the request. /// - /// Remove comment + /// Remove a comment ///
    Comments
    /// Comment ID - /// Comment id + /// Comment ID /// Bookmarks [Delete("bookmark/comment/{commentid}")] public string RemoveBookmarkComment(string commentid) @@ -404,6 +404,16 @@ namespace ASC.Api.Community return null; } + /// + /// Adds a comment to the entity with the ID specified in the request. The parent bookmark ID can be also specified if needed. + /// + /// + /// Add a bookmark comment by entity ID + /// + ///Parent comment ID + ///Entity ID + ///Comment text + ///List of bookmark comments /// Bookmarks [Create("bookmark/comment")] public CommentInfo AddBookmarkComment(string parentcommentid, long entityid, string content) @@ -436,6 +446,13 @@ namespace ASC.Api.Community return BookmarkingConverter.ConvertComment(comment, new List()); } + /// + /// Updates the selected bookmark comment with the content specified in the request. + /// + /// Update a bookmark comment + /// Comment ID + /// New comment text + /// Updated bookmark /// Bookmarks [Update("bookmark/comment/{commentid}")] public string UpdateBookmarkComment(string commentid, string content) @@ -448,11 +465,11 @@ namespace ASC.Api.Community } /// - /// Removes bookmark from favourite. If after removing user bookmark raiting of this bookmark is 0, the bookmark will be removed completely. + /// Removes a bookmark from favorites. If after removing user bookmark, its rating is 0, the bookmark will be removed completely. /// - /// Removes bookmark from favourite + /// Remove a bookmark from favorites /// Bookmark ID - /// bookmark + /// Bookmark /// Bookmarks [Delete("bookmark/@favs/{id}")] public BookmarkWrapper RemoveBookmarkFromFavourite(long id) @@ -463,9 +480,9 @@ namespace ASC.Api.Community } /// - /// Removes bookmark + /// Removes a bookmark with the ID specified in the request. /// - /// Removes bookmark + /// Remove a bookmark /// Bookmark ID /// Bookmarks [Delete("bookmark/{id}")] diff --git a/module/ASC.Api/ASC.Api.Community/CommunityApi.cs b/module/ASC.Api/ASC.Api.Community/CommunityApi.cs index cafa8926b..225f7c2a4 100644 --- a/module/ASC.Api/ASC.Api.Community/CommunityApi.cs +++ b/module/ASC.Api/ASC.Api.Community/CommunityApi.cs @@ -21,7 +21,7 @@ using ASC.Api.Interfaces; namespace ASC.Api.Community { /// - ///Provides access to community data api + ///Provides access to the community data api. /// public partial class CommunityApi : IApiEntryPoint { @@ -29,7 +29,7 @@ namespace ASC.Api.Community /// - /// Starting entry point name + /// Starting entry point name. /// public string Name { diff --git a/module/ASC.Api/ASC.Api.Community/CommunityApiCommon.cs b/module/ASC.Api/ASC.Api.Community/CommunityApiCommon.cs index b1f786f0a..52fca8f57 100644 --- a/module/ASC.Api/ASC.Api.Community/CommunityApiCommon.cs +++ b/module/ASC.Api/ASC.Api.Community/CommunityApiCommon.cs @@ -20,7 +20,6 @@ using System.Web; using ASC.Api.Attributes; using ASC.Core; -using ASC.Web.Community.Birthdays; using ASC.Web.Studio.Utility.HtmlUtility; @@ -29,29 +28,20 @@ namespace ASC.Api.Community public partial class CommunityApi { /// - /// Subscribe or unsubscribe on birthday of user with the ID specified + /// Returns the preview information about the specified category from the community section. /// - /// Subscribe/unsubscribe on birthday - /// user ID - /// should be subscribed or unsubscribed - /// onRemind value - /// Birthday - [Create("birthday")] - public bool RemindAboutBirthday(Guid userid, bool onRemind) - { - BirthdaysNotifyClient.Instance.SetSubscription(SecurityContext.CurrentAccount.ID, userid, onRemind); - return onRemind; - } - + /// Get preview + /// Category title + /// Category content + /// Preview information [Create("preview")] public object GetPreview(string title, string content) { return new { title = HttpUtility.HtmlEncode(title), - content = HtmlUtility.GetFull(content) + content = HtmlUtility.GetFull(content, false) }; } - } } diff --git a/module/ASC.Api/ASC.Api.Community/Events/EventApi.cs b/module/ASC.Api/ASC.Api.Community/Events/EventApi.cs index 99ae53191..7c6250fa0 100644 --- a/module/ASC.Api/ASC.Api.Community/Events/EventApi.cs +++ b/module/ASC.Api/ASC.Api.Community/Events/EventApi.cs @@ -56,12 +56,12 @@ namespace ASC.Api.Community } /// - ///Returns the list of all events on the portal with the event titles, date of creation and update, event text and author + ///Returns a list of all the portal events with the event titles, dates of creation and update, event texts and authors. /// /// - ///All events + ///Get events /// - ///list of events + ///List of events ///Events [Read("event")] public IEnumerable GetEvents() @@ -73,15 +73,15 @@ namespace ASC.Api.Community /// - ///Creates a new event with the parameters (title, content, type) specified in the request + ///Creates a new event with the parameters (title, content, type) specified in the request. /// /// - ///Create event + ///Create an event /// - /// Title - /// Content - /// Type. One of (News|Order|Advert|Poll) - ///New created event + /// Event title + /// Event content + /// Event type (News|Order|Advert|Poll) + ///Newly created event ///Events [Create("event")] public EventWrapperFull CreateEvent(string content, string title, FeedType type) @@ -109,15 +109,15 @@ namespace ASC.Api.Community } /// - ///Updates the selected event changing the event title, content or/and event type specified + ///Updates the selected event changing the event title, content or/and event type specified in the request. /// /// - ///Update event + ///Update an event /// /// Feed ID - /// Title - /// Content - /// Type. One of (News|Order|Advert|Poll) + /// New event title + /// New event content + /// New event type (News|Order|Advert|Poll) ///List of events ///Events [Update("event/{feedid}")] @@ -143,11 +143,10 @@ namespace ASC.Api.Community } /// - ///Deletes the selected event + ///Deletes an event with the ID specified in the request. /// - ///Delete event + ///Delete an event ///Feed ID - ///Nothing /// ///Events [Delete("event/{feedid}")] @@ -170,10 +169,10 @@ namespace ASC.Api.Community } /// - ///Returns the list of all events for the current user with the event titles, date of creation and update, event text and author + ///Returns a list of all the events for the current user with the event titles, dates of creation and update, event texts and author. /// /// - ///My events + ///Get my events /// ///List of events ///Events @@ -186,12 +185,12 @@ namespace ASC.Api.Community } /// - ///Returns a list of events matching the search query with the event title, date of creation and update, event type and author + ///Returns a list of events matching the search query specified in the request with the event titles, dates of creation and update, event types and authors. /// /// - ///Search + ///Search events /// - /// search query + /// Search query ///List of events ///Events [Read("event/@search/{query}")] @@ -203,13 +202,13 @@ namespace ASC.Api.Community } /// - ///Returns the detailed information about the event with the specified ID + ///Returns the detailed information about an event with the ID specified in the request. /// /// - ///Specific event + ///Get an event /// ///Event ID - ///Event + ///Event information ///Events [Read("event/{feedid}")] public EventWrapperFull GetEvent(int feedid) @@ -219,12 +218,12 @@ namespace ASC.Api.Community } /// - ///Returns the detailed information about the comments on the event with the specified ID + ///Returns the detailed information about the comments on the event with the ID specified in the request. /// /// - ///Get comments + ///Get the event comments /// - ///Event id + ///Event ID ///List of comments ///Events [Read("event/{feedid}/comment")] @@ -236,15 +235,15 @@ namespace ASC.Api.Community } /// - ///Adds a comment to the event with the specified ID. The parent event ID can be also specified if needed. + ///Adds a comment to the event with the ID specified in the request. The parent event ID can be also specified if needed. /// /// - ///Add comment + ///Add an event comment by feed ID /// - ///Event ID - ///Comment content + ///Feed ID + ///Comment text ///Comment parent ID - ///Comments list + ///Comment /// /// /// /// - /// Send parentId=0 or don't send it at all if you want your comment to be on the root level + /// Send parentId=0 or doesn't send it at all if you want your comment to be on the root level. /// /// Events [Create("event/{feedid}/comment")] @@ -285,13 +284,13 @@ namespace ASC.Api.Community } /// - /// Sends a vote to a certain option in a poll-type event with the ID specified + /// Sends a vote to a certain option in a poll-type event with the ID specified in the request. /// /// - /// Vote for event + /// Vote for an event /// ///Event ID - ///Variants + ///Options ///Event ///Thrown if not a Poll ///General error @@ -306,7 +305,7 @@ namespace ASC.Api.Community /// ]]> ///
    /// - /// If event is not a poll, then you'll get an error + /// If an event is not a poll, then you'll get an error. /// /// Events [Create("event/{feedid}/vote")] @@ -328,14 +327,14 @@ namespace ASC.Api.Community /// - /// Subscribe or unsubscribe on comments of event with the ID specified + /// Subscribes to or unsubscribes from the comments of the event with the ID specified in the request. /// /// - /// Subscribe/unsubscribe on comments + /// Comment subscription /// - ///is already subscribed or unsubscribed + ///Subscribes to the event comments or unsubscribes from them ///Feed ID - ///Boolean value + ///Boolean value: true means that the user is subscribed to the event comments ///Events [Create("event/{feedid}/subscribe")] public bool SubscribeOnComments(bool isSubscribe, string feedid) @@ -362,13 +361,13 @@ namespace ASC.Api.Community /// - /// Get comment preview with the content specified in the request + /// Returns a comment preview with the content specified in the request. /// - /// Get comment preview + /// Get a comment preview ///
    Comments
    /// Comment ID - /// Comment content - /// Comment info + /// Comment text in the HTML format + /// Comment information /// Events [Create("event/comment/preview")] public CommentInfo GetEventCommentPreview(string commentid, string htmltext) @@ -398,12 +397,12 @@ namespace ASC.Api.Community /// - ///Remove comment with the id specified in the request + ///Removes a comment with the ID specified in the request. /// - /// Remove comment + /// Remove a comment ///
    Comments
    /// Comment ID - /// Comment info + /// Comment information /// Events [Delete("event/comment/{commentid}")] public string RemoveEventComment(string commentid) @@ -421,6 +420,16 @@ namespace ASC.Api.Community } + /// + ///Adds an event comment with the content specified in the request. The parent event ID can be also specified if needed. + /// + /// + ///Add an event comment by entity ID + /// + ///Comment parent ID + ///Entity ID + ///Comment text + ///Comment information /// Events [Create("event/comment")] public CommentInfo AddEventComment(string parentcommentid, string entityid, string content) @@ -441,6 +450,15 @@ namespace ASC.Api.Community return GetCommentInfo(comment); } + /// + ///Updates the selected event comment with the content specified in the request. + /// + /// + ///Update a comment + /// + ///Comment ID + ///New comment text + ///Updated comment /// Events [Update("event/comment/{commentid}")] public string UpdateComment(string commentid, string content) diff --git a/module/ASC.Api/ASC.Api.Community/Forums/ForumApi.cs b/module/ASC.Api/ASC.Api.Community/Forums/ForumApi.cs index fc9dd2887..c3c5db16e 100644 --- a/module/ASC.Api/ASC.Api.Community/Forums/ForumApi.cs +++ b/module/ASC.Api/ASC.Api.Community/Forums/ForumApi.cs @@ -53,10 +53,10 @@ namespace ASC.Api.Community } /// - ///Returns the list of all forums created on the portal with the topic/thread titles, date of creation and update, post text and author ID and display name + ///Returns a list of all the portal forums with the topic/thread titles, dates of creation and update, post texts and authors. /// /// - ///Forum list + ///Get forums /// ///List of forums ///Forums @@ -70,10 +70,10 @@ namespace ASC.Api.Community } /// - ///Returns the number of all forums created on the portal + ///Returns a number of all the portal forums. /// /// - ///Forums count + ///Count forums /// ///Number of forums ///false @@ -85,13 +85,13 @@ namespace ASC.Api.Community } /// - ///Returns the list of all thread topics in the forums on the portal with the thread title, date of creation and update, post text and author id and display name + ///Returns a list of all the thread topics with the topic titles, dates of creation and update, post texts and authors. /// /// - ///Thread topics + ///Get thread topics /// ///Thread ID - ///List of topics in thread + ///List of thread topics ///Forums [Read("forum/{threadid}")] public ForumThreadWrapperFull GetThreadTopics(int threadid) @@ -107,12 +107,12 @@ namespace ASC.Api.Community /// - ///Returns the list of all recently updated topics in the forums on the portal with the topic title, date of creation and update, post text and author + ///Returns a list of all the recently updated topics in the portal forums with the topic titles, dates of creation and update, post texts and authors. /// /// - ///Last updated topics + ///Get last updated topics /// - /// + ///List of last updated topics ///Forums [Read("forum/topic/recent")] public IEnumerable GetLastTopics() @@ -123,13 +123,13 @@ namespace ASC.Api.Community } /// - ///Returns the list of all posts in a selected thread in the forums on the portal with the thread title, date of creation and update, post text and author ID and display name + ///Returns a list of all the posts of the selected forum topic with the dates of creation and update, post texts and authors. /// /// - ///Posts + ///Get topic posts /// ///Topic ID - ///List of posts in topic + ///List of topic posts ///Forums [Read("forum/topic/{topicid}")] public ForumTopicWrapperFull GetTopicPosts(int topicid) @@ -146,10 +146,10 @@ namespace ASC.Api.Community } /// - /// Add thread to category + /// Adds a thread to the category with the ID specified in the request. /// /// - /// Add thread to category + /// Add a thread to a category /// /// Category ID (-1 for new category) /// Category name @@ -198,15 +198,15 @@ namespace ASC.Api.Community } /// - /// Adds a new topic to an existing thread with a subject, content and topic type specified + /// Adds a new topic to the existing thread with a subject, content and topic type specified in the request. /// /// - /// Add topic to thread + /// Add a topic to a thread /// /// Topic subject - /// ID of thread to add to + /// Thread ID /// Topic text - /// Type of topic + /// Topic type ///Added topic ///Forums [Create("forum/{threadid}")] @@ -219,15 +219,15 @@ namespace ASC.Api.Community } /// - /// Updates a topic in an existing thread changing the thread subject, making it sticky or closing it + /// Updates a topic with the ID specified in the request changing a topic subject, making it sticky or closing it. /// /// - /// Update topic in thread + /// Update a topic /// - /// ID of topic to update - /// Subject - /// Is sticky - /// Close topic + /// Topic ID + /// New subject + /// Makes a topic sticky + /// Closes a topic ///Updated topic ///Forums [Update("forum/topic/{topicid}")] @@ -237,10 +237,10 @@ namespace ASC.Api.Community return GetTopicPosts(topicid); } /// - /// Adds a post to an existing topic with a post subject and content specified in the request + /// Adds a post to the selected topic with a post subject and content specified in the request. /// /// - /// Add post to topic + /// Add a post to a topic /// ///Topic ID ///Parent post ID @@ -257,15 +257,15 @@ namespace ASC.Api.Community } /// - /// Updates a post in an existing topic changing the post subject or/and content + /// Updates a post in the selected topic changing the post subject or/and content specified in the request. /// /// - /// Update post in topic + /// Update a topic post /// ///Topic ID - ///ID of post to update - ///Post subject (required) - ///Post text + ///Post ID + ///New post subject (required) + ///New post text ///Updated post ///Forums [Update("forum/topic/{topicid}/{postid}")] @@ -276,13 +276,13 @@ namespace ASC.Api.Community } /// - ///Returns a list of topics matching the search query with the topic title, date of creation and update, post text and author + ///Returns a list of topics matching the search query with the topic titles, dates of creation and update, post texts and authors. /// /// - ///Search + ///Search topics /// ///Search query - ///list of topics + ///List of topics ///Forums [Read("forum/@search/{query}")] public IEnumerable SearchTopics(string query) @@ -295,14 +295,14 @@ namespace ASC.Api.Community /// - /// Deletes a selected post + /// Deletes a post with the ID specified in the request. /// /// - /// Delete post + /// Delete a post /// ///Post ID - /// ///Forums + ///Post [Delete("forum/post/{postid}")] public ForumTopicPostWrapper DeletePost(int postid) { @@ -322,14 +322,14 @@ namespace ASC.Api.Community } /// - /// Deletes a selected topic + /// Deletes a topic with the ID specified in the request. /// /// - /// Delete topic + /// Delete a topic /// ///Topic ID - /// ///Forums + ///Topic [Delete("forum/topic/{topicid}")] public ForumTopicWrapper DeleteTopic(int topicid) { @@ -346,14 +346,14 @@ namespace ASC.Api.Community } /// - /// Deletes a selected thread + /// Deletes a thread with the ID specified in the request. /// /// - /// Delete thread + /// Delete a thread /// ///Thread ID - /// ///Forums + ///Thread [Delete("forum/thread/{threadid}")] public ForumThreadWrapper DeleteThread(int threadid) { @@ -370,14 +370,14 @@ namespace ASC.Api.Community } /// - /// Deletes a selected thread category + /// Deletes a category with the ID specified in the request. /// /// - /// Delete category + /// Delete a category /// ///Category ID - /// ///Forums + ///Category [Delete("forum/category/{categoryid}")] public ForumCategoryWrapper DeleteThreadCategory(int categoryid) { diff --git a/module/ASC.Api/ASC.Api.Community/Wiki/WikiApi.cs b/module/ASC.Api/ASC.Api.Community/Wiki/WikiApi.cs index f7458d7d0..11f84a313 100644 --- a/module/ASC.Api/ASC.Api.Community/Wiki/WikiApi.cs +++ b/module/ASC.Api/ASC.Api.Community/Wiki/WikiApi.cs @@ -32,6 +32,8 @@ using ASC.Web.Studio.Utility.HtmlUtility; using ASC.Web.UserControls.Wiki; using ASC.Web.UserControls.Wiki.Data; +using Microsoft.Security.Application; + using File = ASC.Web.UserControls.Wiki.Data.File; namespace ASC.Api.Community @@ -41,26 +43,26 @@ namespace ASC.Api.Community private readonly WikiEngine _engine = new WikiEngine(); /// - /// Creates a new wiki page with the page name and content specified in the request + /// Creates a new wiki page with the page name and content specified in the request. /// - /// Create page + /// Create a page /// Page name /// Page content - /// page info + /// Page information /// Wiki [Create("wiki")] public PageWrapper CreatePage(string name, string body) { - return new PageWrapper(_engine.CreatePage(new Page { PageName = name, Body = body })); + return new PageWrapper(_engine.CreatePage(new Page { PageName = name, Body = Sanitizer.GetSafeHtmlFragment(body) })); } /// - /// Returns the list of all pages in wiki or pages in wiki category specified in the request + /// Returns a list of all the pages from wiki or wiki category specified in the request. /// - /// Pages + /// Get pages ///
    Pages
    /// Category name - /// + /// Pages /// Wiki [Read("wiki")] public IEnumerable GetPages(string category) @@ -71,13 +73,13 @@ namespace ASC.Api.Community } /// - /// Return the detailed information about the wiki page with the name and version specified in the request + /// Returns the detailed information about a wiki page with the name and version specified in the request. /// - /// Page + /// Get a page ///
    Pages
    /// Page name /// Page version - /// Page info + /// Page information /// Wiki [Read("wiki/{name}")] public PageWrapper GetPage(string name, int? version) @@ -92,12 +94,12 @@ namespace ASC.Api.Community } /// - /// Returns the list of history changes for the wiki page with the name specified in the request + /// Returns a list of history changes for a wiki page with the name specified in the request. /// - /// History + /// Get the page history ///
    Pages
    /// Page name - /// List of pages + /// List of history changes /// Wiki [Read("wiki/{page}/story")] public IEnumerable GetHistory(string page) @@ -106,11 +108,11 @@ namespace ASC.Api.Community } /// - /// Returns the list of wiki pages with the name matching the search query specified in the request + /// Returns a list of wiki pages with the name matching the search query specified in the request. /// - /// Search + /// Search pages by name ///
    Pages
    - /// Part of the page name + /// Search query /// List of pages /// Wiki [Read("wiki/search/byname/{name}")] @@ -120,11 +122,11 @@ namespace ASC.Api.Community } /// - /// Returns the list of wiki pages with the content matching the search query specified in the request + /// Returns a list of wiki pages with the content matching the search query specified in the request. /// - /// Search + /// Search pages by content ///
    Pages
    - /// Part of the page content + /// Search query /// List of pages /// Wiki [Read("wiki/search/bycontent/{content}")] @@ -134,24 +136,24 @@ namespace ASC.Api.Community } /// - /// Updates the wiki page with the name and content specified in the request + /// Updates a wiki page with the name and content specified in the request. /// - /// Update page + /// Update a page ///
    Pages
    - /// Page name - /// Page content - /// Page info + /// New page name + /// New page content + /// Page information /// Wiki [Update("wiki/{name}")] public PageWrapper UpdatePage(string name, string body) { - return new PageWrapper(_engine.UpdatePage(new Page { PageName = name, Body = body })); + return new PageWrapper(_engine.UpdatePage(new Page { PageName = name, Body = Sanitizer.GetSafeHtmlFragment(body) })); } /// - /// Deletes the wiki page with the name specified in the request + /// Deletes a wiki page with the name specified in the request. /// - /// Delete page + /// Delete a page ///
    Pages
    /// Page name /// Wiki @@ -162,14 +164,14 @@ namespace ASC.Api.Community } /// - /// Creates the comment to the selected wiki page with the content specified in the request + /// Creates a comment on the selected wiki page with the content specified in the request. /// - /// Create comment + /// Create a wiki comment ///
    Comments
    /// Page name - /// Comment content - /// Comment parent id - /// Comment info + /// Comment text + /// Comment parent ID + /// Comment information /// Wiki [Create("wiki/{page}/comment")] public CommentWrapper CreateComment(string page, string content, string parentId) @@ -179,9 +181,9 @@ namespace ASC.Api.Community } /// - /// Returns the list of all comments to the wiki page with the name specified in the request + /// Returns a list of all the comments to the wiki page with the name specified in the request. /// - /// All comments + /// Get the page comments ///
    Comments
    /// Page name /// List of comments @@ -193,10 +195,10 @@ namespace ASC.Api.Community } /// - /// Uploads the selected files to the wiki 'Files' section + /// Uploads the selected files to the wiki 'Files' section. /// /// Upload files - /// List of files to upload + /// List of files /// List of files /// Wiki [Create("wiki/file")] @@ -208,12 +210,12 @@ namespace ASC.Api.Community } /// - /// Returns the detailed file info about the file with the specified name in the wiki 'Files' section + /// Returns the detailed information about a file with the name specified in the request from the wiki 'Files' section. /// - /// File + /// Get a file ///
    Files
    /// File name - /// File info + /// File information /// Wiki [Read("wiki/file/{name}")] public FileWrapper GetFile(string name) @@ -222,9 +224,9 @@ namespace ASC.Api.Community } /// - /// Deletes the files with the specified name from the wiki 'Files' section + /// Deletes a file with the name specified in the request from the wiki 'Files' section. /// - /// Delete file + /// Delete a file /// File name /// Wiki [Delete("wiki/file/{name}")] @@ -234,13 +236,13 @@ namespace ASC.Api.Community } /// - /// Get comment preview with the content specified in the request + /// Returns a comment preview with the content specified in the request. /// - /// Get comment preview + /// Get a comment preview ///
    Comments
    /// Comment ID - /// Comment content - /// Comment info + /// Comment text in the HTML format + /// Comment information /// Wiki [Create("wiki/comment/preview")] public CommentInfo GetWikiCommentPreview(string commentid, string htmltext) @@ -257,12 +259,12 @@ namespace ASC.Api.Community } /// - ///Remove comment with the id specified in the request + ///Removes a comment with the ID specified in the request. /// - /// Remove comment + /// Remove a comment ///
    Comments
    /// Comment ID - /// Comment info + /// Comment ID /// Wiki [Delete("wiki/comment/{commentid}")] public string RemoveWikiComment(string commentid) @@ -273,6 +275,15 @@ namespace ASC.Api.Community + /// + /// Adds a comment to the selected entity with the content specified in the request. + /// + /// Add an entity comment + ///
    Comments
    + /// Comment parent ID + /// Entity ID + /// Comment text + /// Comment information /// Wiki [Create("wiki/comment")] public CommentInfo AddWikiComment(string parentcommentid, string entityid, string content) @@ -287,13 +298,13 @@ namespace ASC.Api.Community } /// - /// Updates the comment to the selected wiki page with the comment content specified in the request + /// Updates a comment on the selected wiki page with the content specified in the request. /// - /// Update comment + /// Update a comment ///
    Comments
    /// Comment ID - /// Comment content - /// + /// New comment text + /// Updated comment /// Wiki [Update("wiki/comment/{commentid}")] public string UpdateWikiComment(string commentid, string content) diff --git a/module/ASC.Api/ASC.Api.Documents/ASC.Api.Documents.csproj b/module/ASC.Api/ASC.Api.Documents/ASC.Api.Documents.csproj index 72ec18093..fbd63f246 100644 --- a/module/ASC.Api/ASC.Api.Documents/ASC.Api.Documents.csproj +++ b/module/ASC.Api/ASC.Api.Documents/ASC.Api.Documents.csproj @@ -22,7 +22,7 @@ ..\..\..\web\studio\ASC.Web.Studio\bin\ $(OutputPath)\ASC.Api.Documents.XML - 1591;1572;1573 + 1591;1572;1573 true @@ -104,7 +104,7 @@
    - 12.0.3 + 13.0.1 diff --git a/module/ASC.Api/ASC.Api.Documents/DocumentsApi.cs b/module/ASC.Api/ASC.Api.Documents/DocumentsApi.cs index ccd2f452d..00569e9dd 100644 --- a/module/ASC.Api/ASC.Api.Documents/DocumentsApi.cs +++ b/module/ASC.Api/ASC.Api.Documents/DocumentsApi.cs @@ -61,7 +61,7 @@ using SortedByType = ASC.Files.Core.SortedByType; namespace ASC.Api.Documents { /// - /// Provides access to documents + /// Provides access to documents. /// public class DocumentsApi : Interfaces.IApiEntryPoint { @@ -87,21 +87,16 @@ namespace ASC.Api.Documents /// - /// + /// Returns all the sections matching the parameters specified in the request. /// - /// List of Sections + /// Get sections /// User or group ID /// Filter type - /// - /// - /// - /// Sort by field name. - /// Sorting direction - /// The number of elements to be skipped in the beginning. Used for response data pagination. - /// Number of the elements returned. - /// Filter value. + /// Root folders with or without subfolders + /// Root folders with or without trash + /// Root folders with or without additional folders /// Folders - /// + /// Sections [Read("@root")] public IEnumerable GetRootFolders(Guid userIdOrGroupId, FilterType filterType, bool withsubfolders, bool withoutTrash, bool withoutAdditionalFolder) { @@ -148,7 +143,7 @@ namespace ASC.Api.Documents result.Add(Global.FolderCommon); } - if(Global.FolderProjects != null) + if (Global.FolderProjects != null) { result.Add(Global.FolderProjects); } @@ -170,16 +165,13 @@ namespace ASC.Api.Documents } /// - /// Returns the detailed list of files and folders located in the current user My section + /// Returns the detailed list of files and folders located in the "My documents" section. /// - /// Section My + /// Get my section /// Folders - /// Sort by field name. - /// Sorting direction - /// The number of elements to be skipped in the beginning. Used for response data pagination. - /// Number of the elements returned. - /// Filter value. - /// My folder contents + /// User or group ID + /// Filter type + /// My documents section contents [Read("@my")] public FolderContentWrapper GetMyFolder(Guid userIdOrGroupId, FilterType filterType) { @@ -187,16 +179,13 @@ namespace ASC.Api.Documents } /// - /// Returns the detailed list of files and folders located in the current user Projects section + /// Returns the detailed list of files and folders located in the "Projects" section. /// - /// Section Projects + /// Get project section /// Folders - /// Sort by field name. - /// Sorting direction - /// The number of elements to be skipped in the beginning. Used for response data pagination. - /// Number of the elements returned. - /// Filter value. - /// Projects folder contents + /// User or group ID + /// Filter type + /// Project section contents [Read("@projects")] public FolderContentWrapper GetProjectsFolder(Guid userIdOrGroupId, FilterType filterType) { @@ -205,16 +194,13 @@ namespace ASC.Api.Documents /// - /// Returns the detailed list of files and folders located in the Common section + /// Returns the detailed list of files and folders located in the "Common" section. /// - /// Section Common + /// Get common section /// Folders - /// Sort by field name. - /// Sorting direction - /// The number of elements to be skipped in the beginning. Used for response data pagination. - /// Number of the elements returned. - /// Filter value. - /// Common folder contents + /// User or group ID + /// Filter type + /// Common section contents [Read("@common")] public FolderContentWrapper GetCommonFolder(Guid userIdOrGroupId, FilterType filterType) { @@ -222,16 +208,13 @@ namespace ASC.Api.Documents } /// - /// Returns the detailed list of files and folders located in the Shared with Me section + /// Returns the detailed list of files and folders located in the "Shared with Me" section. /// - /// Section Shared + /// Get shared section /// Folders - /// Sort by field name. - /// Sorting direction - /// The number of elements to be skipped in the beginning. Used for response data pagination. - /// Number of the elements returned. - /// Filter value. - /// Shared folder contents + /// User or group ID + /// Filter type + /// Shared section contents [Read("@share")] public FolderContentWrapper GetShareFolder(Guid userIdOrGroupId, FilterType filterType) { @@ -239,18 +222,13 @@ namespace ASC.Api.Documents } /// - /// Returns the detailed list of recent files + /// Returns the detailed list of files located in the "Recent" section. /// - /// Section Recent + /// Get recent section /// Folders /// User or group ID /// Filter type - /// Sort by field name. - /// Sorting direction - /// The number of elements to be skipped in the beginning. Used for response data pagination. - /// Number of the elements returned. - /// Filter value. - /// Recent contents + /// Recent section contents [Read("@recent")] public FolderContentWrapper GetRecentFolder(Guid userIdOrGroupId, FilterType filterType) { @@ -258,18 +236,13 @@ namespace ASC.Api.Documents } /// - /// Returns the detailed list of favorites files + /// Returns the detailed list of files located in the "Favorites" section. /// - /// Section Favorite + /// Get favorite section /// Folders /// User or group ID /// Filter type - /// Sort by field name. - /// Sorting direction - /// The number of elements to be skipped in the beginning. Used for response data pagination. - /// Number of the elements returned. - /// Filter value. - /// Favorites contents + /// Favorite section contents [Read("@favorites")] public FolderContentWrapper GetFavoritesFolder(Guid userIdOrGroupId, FilterType filterType) { @@ -277,18 +250,13 @@ namespace ASC.Api.Documents } /// - /// Returns the detailed list of templates files + /// Returns the detailed list of files located in the "Templates" section. /// - /// Section Template + /// Get template section /// Folders /// User or group ID /// Filter type - /// Sort by field name. - /// Sorting direction - /// The number of elements to be skipped in the beginning. Used for response data pagination. - /// Number of the elements returned. - /// Filter value. - /// Templates contents + /// Template section contents [Read("@templates")] public FolderContentWrapper GetTemplatesFolder(Guid userIdOrGroupId, FilterType filterType) { @@ -296,16 +264,13 @@ namespace ASC.Api.Documents } /// - /// Returns the detailed list of files and folders located in the Recycle Bin + /// Returns the detailed list of files and folders located in the "Trash" section. /// - /// Section Trash + /// Get trash section /// Folders - /// Sort by field name. - /// Sorting direction - /// The number of elements to be skipped in the beginning. Used for response data pagination. - /// Number of the elements returned. - /// Filter value. - /// Trash folder contents + /// User or group ID + /// Filter type + /// Trash section contents [Read("@trash")] public FolderContentWrapper GetTrashFolder(Guid userIdOrGroupId, FilterType filterType) { @@ -313,20 +278,15 @@ namespace ASC.Api.Documents } /// - /// Returns the detailed list of files and folders located in the folder with the ID specified in the request + /// Returns the detailed list of files and folders located in the folder with the ID specified in the request. /// /// - /// Folder by ID + /// Get a folder by ID /// /// Folders /// Folder ID /// User or group ID /// Filter type - /// Sort by field name. - /// Sorting direction - /// The number of elements to be skipped in the beginning. Used for response data pagination. - /// Number of the elements returned. - /// Filter value. /// Folder contents [Read("{folderId}")] public FolderContentWrapper GetFolder(String folderId, Guid userIdOrGroupId, FilterType filterType) @@ -336,22 +296,22 @@ namespace ASC.Api.Documents } /// - /// Uploads the file specified with single file upload or standart multipart/form-data method to My section + /// Uploads a file specified in the request to the "My documents" section by single file uploading or standart multipart/form-data method. /// - /// Upload to My + /// Upload a file to my section /// Uploads + /// Request input stream + /// Content-Type header + /// Content-Disposition header + /// List of files when posted as multipart/form-data /// /// - ///
  9. Single file upload. You should set Content-Type & Content-Disposition header to specify filename and content type, and send file in request body
  10. - ///
  11. Using standart multipart/form-data method
  12. + ///
  13. Using single file upload. You should set the Content-Type & Content-Disposition headers to specify the file name and content type, and send the file to the request body.
  14. + ///
  15. Using standart multipart/form-data method.
  16. /// ]]> ///
    - /// Request Input stream - /// Content-Type Header - /// Content-Disposition Header - /// List of files when posted as multipart/form-data /// Uploaded file [Create("@my/upload")] public object UploadFileToMy(Stream file, ContentType contentType, ContentDisposition contentDisposition, IEnumerable files) @@ -360,22 +320,22 @@ namespace ASC.Api.Documents } /// - /// Uploads the file specified with single file upload or standart multipart/form-data method to Common section + /// Uploads a file specified in the request to the "Common" section by single file uploading or standart multipart/form-data method. /// - /// Upload to Common + /// Upload a file to the common section /// Uploads + /// Request input stream + /// Content-Type header + /// Content-Disposition header + /// List of files when posted as multipart/form-data /// /// - ///
  17. Single file upload. You should set Content-Type & Content-Disposition header to specify filename and content type, and send file in request body
  18. - ///
  19. Using standart multipart/form-data method
  20. + ///
  21. Using single file upload. You should set the Content-Type & Content-Disposition headers to specify the file name and content type, and send the file to the request body.
  22. + ///
  23. Using standart multipart/form-data method.
  24. /// ]]> ///
    - /// Request Input stream - /// Content-Type Header - /// Content-Disposition Header - /// List of files when posted as multipart/form-data /// Uploaded file [Create("@common/upload")] public object UploadFileToCommon(Stream file, ContentType contentType, ContentDisposition contentDisposition, IEnumerable files) @@ -385,26 +345,26 @@ namespace ASC.Api.Documents /// - /// Uploads the file specified with single file upload or standart multipart/form-data method to the selected folder + /// Uploads a file specified in the request to the selected folder by single file uploading or standart multipart/form-data method. /// - /// Upload file + /// Upload a file /// Uploads /// /// - ///
  25. Single file upload. You should set Content-Type & Content-Disposition header to specify filename and content type, and send file in request body
  26. - ///
  27. Using standart multipart/form-data method
  28. + ///
  29. Using single file upload. You should set the Content-Type & Content-Disposition headers to specify the file name and content type, and send the file to the request body.
  30. + ///
  31. Using standart multipart/form-data method.
  32. /// ]]> ///
    - /// Folder ID to upload to - /// Request Input stream - /// Content-Type Header - /// Content-Disposition Header + /// Folder ID + /// Request input stream + /// Content-Type header + /// Content-Disposition header /// List of files when posted as multipart/form-data - /// Create New If Exist - /// If True, upload documents in original formats as well - /// Keep status conversation after finishing + /// Creates a new file if it already exists or not + /// If true, uploads documents in the original formats as well + /// Keeps converting status after finishing or not /// Uploaded file [Create("{folderId}/upload")] public object UploadFile(string folderId, Stream file, ContentType contentType, ContentDisposition contentDisposition, IEnumerable files, bool? createNewIfExist, bool? storeOriginalFileFlag, bool keepConvertStatus = false) @@ -439,15 +399,15 @@ namespace ASC.Api.Documents } /// - /// Uploads the file specified with single file upload to Common section + /// Inserts a file specified in the request to the "My documents" section by single file uploading. /// - /// Insert to My - /// Request Input stream - /// Name of file which has to be uploaded - /// Create New If Exist - /// Keep status conversation after finishing + /// Insert a file to my section + /// Request input stream + /// File name + /// Creates a new file if it already exists or not + /// Keeps converting status after finishing or not /// Uploads - /// + /// Inserted file [Create("@my/insert")] public FileWrapper InsertFileToMy(Stream file, string title, bool? createNewIfExist, bool keepConvertStatus = false) { @@ -455,15 +415,15 @@ namespace ASC.Api.Documents } /// - /// Uploads the file specified with single file upload to Common section + /// Inserts a file specified in the request to the "Common" section by single file uploading. /// - /// Insert to Common - /// Request Input stream - /// Name of file which has to be uploaded - /// Create New If Exist - /// Keep status conversation after finishing + /// Insert a file to the common section + /// Request input stream + /// File name + /// Creates a new file if it already exists or not + /// Keeps converting status after finishing or not /// Uploads - /// + /// Inserted file [Create("@common/insert")] public FileWrapper InsertFileToCommon(Stream file, string title, bool? createNewIfExist, bool keepConvertStatus = false) { @@ -471,15 +431,16 @@ namespace ASC.Api.Documents } /// - /// Uploads the file specified with single file upload + /// Inserts a file specified in the request to the selected folder by single file uploading. /// - /// Folder ID to upload to - /// Request Input stream - /// Name of file which has to be uploaded - /// Create New If Exist - /// Keep status conversation after finishing + /// Insert a file + /// Folder ID + /// Request input stream + /// File name + /// Creates a new file if it already exists or not + /// Keeps converting status after finishing or not /// Uploads - /// + /// Inserted file [Create("{folderId}/insert")] public FileWrapper InsertFile(string folderId, Stream file, string title, bool? createNewIfExist, bool keepConvertStatus = false) { @@ -499,14 +460,16 @@ namespace ASC.Api.Documents } /// - /// Update file content + /// Updates the content of a file with the ID specified in the request. /// + /// Update file content /// Files - /// Stream of file + /// File stream /// File ID /// File extension - /// - /// + /// Encrypts a file or not + /// Forcibly saves a file or not + /// Updated file [Update("{fileId}/update")] public FileWrapper UpdateFileStream(Stream file, string fileId, string fileExtension, bool encrypted = false, bool forcesave = false) { @@ -523,17 +486,17 @@ namespace ASC.Api.Documents /// - /// Save file + /// Saves editing of a file with the ID specified in the request. /// - /// Editing save + /// Save editing /// File ID - /// - /// - /// - /// - /// + /// File extension + /// URI for downloading + /// File stream + /// Shared token + /// Forcibly saves a file or not /// Files - /// + /// Saved file [Update("file/{fileId}/saveediting")] public FileWrapper SaveEditing(String fileId, string fileExtension, string downloadUri, Stream stream, String doc, bool forcesave) { @@ -541,12 +504,12 @@ namespace ASC.Api.Documents } /// - /// Lock file when editing + /// Informs about opening a file with the ID specified in the request for editing locking it from deletion or movement (this method is called by the mobile editors). /// - /// Editing start + /// Start editing /// File ID - /// - /// + /// Shares a file with other users for editing or not + /// Shared token /// Files /// File key for Document Service [Create("file/{fileId}/startedit")] @@ -556,16 +519,16 @@ namespace ASC.Api.Documents } /// - /// Continue to lock file when editing + /// Tracks file changes when editing. /// - /// Editing track + /// Track editing /// File ID - /// - /// - /// - /// for unlock + /// Tab ID + /// Document key for tracking + /// Shared token + /// Finishes file editing or not /// Files - /// + /// File changes [Read("file/{fileId}/trackeditfile")] public KeyValuePair TrackEditFile(String fileId, Guid tabId, String docKeyForTrack, String doc, bool isFinish) { @@ -573,24 +536,22 @@ namespace ASC.Api.Documents } /// - /// Get initialization configuration for open editor + /// Returns the initialization configuration to open the editor. /// - /// Editing open + /// Open the editor /// File ID /// File version - /// + /// Shared token /// Files /// Configuration - [Read("file/{fileId}/openedit", false)] //NOTE: this method doesn't requires auth!!! + [Read("file/{fileId}/openedit", false)] // NOTE: This method doesn't require auth!!! public Configuration OpenEdit(String fileId, int version, String doc) { Configuration configuration; - DocumentServiceHelper.GetParams(fileId, version, doc, true, true, true, out configuration); + var file = DocumentServiceHelper.GetParams(fileId, version, doc, true, true, true, out configuration); configuration.Type = Configuration.EditorType.External; - configuration.EditorConfig.CallbackUrl = DocumentServiceTracker.GetCallbackUrl(configuration.Document.Info.File.ID.ToString()); - - if (configuration.Document.Info.File.RootFolderType == FolderType.Privacy + if (file.RootFolderType == FolderType.Privacy && PrivacyRoomSettings.Enabled) { var keyPair = EncryptionKeyPair.GetKeyPair(); @@ -604,7 +565,7 @@ namespace ASC.Api.Documents } } - if (!configuration.Document.Info.File.Encrypted && !configuration.Document.Info.File.ProviderEntry) EntryManager.MarkAsRecent(configuration.Document.Info.File); + if (!file.Encrypted && !file.ProviderEntry) EntryManager.MarkAsRecent(file); configuration.Token = DocumentServiceHelper.GetSignature(configuration); return configuration; @@ -612,34 +573,34 @@ namespace ASC.Api.Documents /// - /// Creates session to upload large files in multiple chunks. + /// Creates a session to upload large files in multiple chunks to the folder with the ID specified in the request. /// /// Chunked upload /// Uploads - /// Id of the folder in which file will be uploaded - /// Name of file which has to be uploaded - /// Length in bytes of file which has to be uploaded - /// Relative folder from folderId - /// + /// Folder ID + /// File name + /// File length in bytes + /// Folder which is relative to the selected folder + /// Encrypts a file or not /// /// 512 and greater or equal than 10 mb. Last chunk can have any size. - /// After initial request respond with status 200 OK you must obtain value of 'location' field from the response. Send all your chunks to that location. - /// Each chunk must be sent in strict order in which chunks appears in file. - /// After receiving each chunk if no errors occured server will respond with current information about upload session. - /// When number of uploaded bytes equal to the number of bytes you send in initial request server will respond with 201 Created and will send you info about uploaded file. + /// Each chunk can have different length but the length should be multiple of 512 and greater or equal to 10 mb. Last chunk can have any size. + /// After the initial response to the request with the 200 OK status, you must get the location field value from the response. Send all your chunks to this location. + /// Each chunk must be sent in the exact order the chunks appear in the file. + /// After receiving each chunk, the server will respond with the current information about the upload session if no errors occurred. + /// When the number of bytes uploaded is equal to the number of bytes you sent in the initial request, the server responds with the 201 Created status and sends you information about the uploaded file. /// ]]> /// /// /// - ///
  33. id: unique id of this upload session
  34. - ///
  35. created: UTC time when session was created
  36. - ///
  37. expired: UTC time when session will be expired if no chunks will be sent until that time
  38. - ///
  39. location: URL to which you must send your next chunk
  40. - ///
  41. bytes_uploaded: If exists contains number of bytes uploaded for specific upload id
  42. - ///
  43. bytes_total: Number of bytes which has to be uploaded
  44. + ///
  45. id: unique ID of this upload session
  46. + ///
  47. created: UTC time when the session was created
  48. + ///
  49. expired: UTC time when the session will expire if no chunks are sent before that time
  50. + ///
  51. location: URL where you should send your next chunk
  52. + ///
  53. bytes_uploaded: number of bytes uploaded for the specific upload ID
  54. + ///
  55. bytes_total: total number of bytes which will be uploaded
  56. /// /// ]]> ///
    @@ -648,9 +609,31 @@ namespace ASC.Api.Documents { var file = FileUploader.VerifyChunkedUpload(folderId, fileName, fileSize, FilesSettings.UpdateIfExist, relativePath); + return CreateUploadSession(file, encrypted); + } + + /// + /// Creates a session to edit the existing file with multiple chunks (needed for WebDAV). + /// + /// Create the editing session + /// Files + /// File ID + /// File size in bytes + /// Upload session + /// false + [Create("file/{fileId}/edit_session")] + public object CreateEditSession(object fileId, long fileSize) + { + var file = FileUploader.VerifyChunkedUploadForEditing(fileId, fileSize); + + return CreateUploadSession(file, false, true); + } + + private object CreateUploadSession(Files.Core.File file, bool encrypted, bool keepVersion = false) + { if (FilesLinkUtility.IsLocalFileUploader) { - var session = FileUploader.InitiateUpload(file.FolderID.ToString(), (file.ID ?? "").ToString(), file.Title, file.ContentLength, encrypted); + var session = FileUploader.InitiateUpload(file.FolderID.ToString(), (file.ID ?? "").ToString(), file.Title, file.ContentLength, encrypted, keepVersion); var response = ChunkedUploaderHandler.ToResponseObject(session, true); return new @@ -686,9 +669,9 @@ namespace ASC.Api.Documents } /// - /// Creates a text (.txt) file in My section with the title and contents sent in the request + /// Creates a text (.txt) file in the "My documents" section with the title and contents specified in the request. /// - /// Create txt in My + /// Create a txt file in my section /// Files /// File title /// File contents @@ -701,9 +684,9 @@ namespace ASC.Api.Documents } /// - /// Creates a text (.txt) file in Common Documents section with the title and contents sent in the request + /// Creates a text (.txt) file in the "Common" section with the title and contents specified in the request. /// - /// Create txt in Common + /// Create a txt file in the common section /// Files /// File title /// File contents @@ -716,9 +699,9 @@ namespace ASC.Api.Documents } /// - /// Creates a text (.txt) file in the selected folder with the title and contents sent in the request + /// Creates a text (.txt) file in the selected folder with the title and contents specified in the request. /// - /// Create txt + /// Create a txt file /// Files /// Folder ID /// File title @@ -753,9 +736,9 @@ namespace ASC.Api.Documents } /// - /// Creates an html (.html) file in the selected folder with the title and contents sent in the request + /// Creates an html (.html) file in the selected folder with the title and contents specified in the request. /// - /// Create html + /// Create an html file /// Files /// Folder ID /// File title @@ -770,9 +753,9 @@ namespace ASC.Api.Documents } /// - /// Creates an html (.html) file in My section with the title and contents sent in the request + /// Creates an html (.html) file in the "My documents" section with the title and contents specified in the request. /// - /// Create html in My + /// Create an html file in my section /// Files /// File title /// File contents @@ -786,9 +769,9 @@ namespace ASC.Api.Documents /// - /// Creates an html (.html) file in Common section with the title and contents sent in the request + /// Creates an html (.html) file in the common section with the title and contents specified in the request. /// - /// Create html in Common + /// Create an html file in the common section /// Files /// File title /// File contents @@ -802,14 +785,14 @@ namespace ASC.Api.Documents /// - /// Creates a new folder with the title sent in the request. The ID of a parent folder can be also specified. + /// Creates a new folder with the title specified in the request. The parent folder ID can be also specified. /// /// - /// Create folder + /// Create a folder /// /// Folders /// Parent folder ID - /// Title of new folder + /// Folder title /// New folder contents [Create("folder/{folderId}")] public FolderWrapper CreateFolder(string folderId, string title) @@ -819,41 +802,42 @@ namespace ASC.Api.Documents } /// - /// Creates a new file in the My section with the title sent in the request + /// Creates a new file in the "My documents" section with the title specified in the request. /// - /// Create file in My + /// Create a file in my section /// Files /// File title - /// In case the extension for the file title differs from DOCX/XLSX/PPTX and belongs to one of the known text, spreadsheet or presentation formats, it will be changed to DOCX/XLSX/PPTX accordingly. If the file extension is not set or is unknown, the DOCX extension will be added to the file title. - /// New file info + /// In case an extension for the file title differs from DOCX/XLSX/PPTX and belongs to one of the known text, spreadsheet or presentation formats, it will be changed to DOCX/XLSX/PPTX accordingly. If the file extension is not set or is unknown, the DOCX extension will be added to the file title. + /// New file information [Create("@my/file")] public FileWrapper CreateFile(string title) { - return CreateFile(Global.FolderMy.ToString(), title, null); + return CreateFile(Global.FolderMy.ToString(), title, null, false); } /// - /// Creates a new file in the specified folder with the title sent in the request + /// Creates a new file in the specified folder with the title specified in the request. /// - /// Create file + /// Create a file /// Files /// Folder ID /// File title - /// File ID for using as template - /// In case the extension for the file title differs from DOCX/XLSX/PPTX and belongs to one of the known text, spreadsheet or presentation formats, it will be changed to DOCX/XLSX/PPTX accordingly. If the file extension is not set or is unknown, the DOCX extension will be added to the file title. - /// New file info + /// Template file ID + /// The ability to create files of any extension + /// In case an extension for the file title differs from DOCX/XLSX/PPTX and belongs to one of the known text, spreadsheet or presentation formats, it will be changed to DOCX/XLSX/PPTX accordingly. If the file extension is not set or is unknown, the DOCX extension will be added to the file title. + /// New file information [Create("{folderId}/file")] - public FileWrapper CreateFile(string folderId, string title, string templateId) + public FileWrapper CreateFile(string folderId, string title, string templateId, bool enableExternalExt) { - var file = _fileStorageService.CreateNewFile(folderId, title, templateId); + var file = _fileStorageService.CreateNewFile(folderId, title, templateId, enableExternalExt); return new FileWrapper(file); } /// - /// Renames the selected folder to the new title specified in the request + /// Renames the selected folder with a new title specified in the request. /// /// - /// Rename folder + /// Rename a folder /// /// Folders /// Folder ID @@ -867,11 +851,12 @@ namespace ASC.Api.Documents } /// - /// Returns a detailed information about the folder with the ID specified in the request + /// Returns the detailed information about a folder with the ID specified in the request. /// - /// Folder information + /// Get the folder information + /// Folder ID /// Folders - /// Folder info + /// Folder information [Read("folder/{folderId}")] public FolderWrapper GetFolderInfo(string folderId) { @@ -881,12 +866,12 @@ namespace ASC.Api.Documents } /// - /// Returns parent folders + /// Returns a path to the folder with the ID specified in the request. /// - /// Folder path - /// + /// Get the folder path + /// Folder ID /// Folders - /// Parent folders + /// Folder path [Read("folder/{folderId}/path")] public IEnumerable GetFolderPath(string folderId) { @@ -894,11 +879,13 @@ namespace ASC.Api.Documents } /// - /// Returns a detailed information about the file with the ID specified in the request + /// Returns the detailed information about a file with the ID specified in the request. /// - /// File information + /// Get the file information + /// File ID + /// File version /// Files - /// File info + /// File information [Read("file/{fileId}")] public FileWrapper GetFileInfo(string fileId, int version = -1) { @@ -907,7 +894,7 @@ namespace ASC.Api.Documents } /// - /// Copies (and converts, if possible) an existing file to a new file. + /// Copies (and converts, if possible) an existing file to a new file. /// /// Copy file /// Files @@ -924,7 +911,7 @@ namespace ASC.Api.Documents if (ext == destExt) { - return CreateFile(destFolderId, destTitle, fileId); + return CreateFile(destFolderId, destTitle, fileId, true); } using (var fileStream = FileConverter.Exec(file, destExt)) @@ -934,14 +921,14 @@ namespace ASC.Api.Documents } /// - /// Updates the information of the selected file with the parameters specified in the request + /// Updates the information of the selected file with the parameters specified in the request. /// - /// Update file info + /// Update a file /// Files /// File ID - /// New title + /// New file title /// File last version number - /// File info + /// File information [Update("file/{fileId}")] public FileWrapper UpdateFile(String fileId, String title, int lastVersion) { @@ -955,13 +942,13 @@ namespace ASC.Api.Documents } /// - /// Deletes the file with the ID specified in the request + /// Deletes a file with the ID specified in the request. /// - /// Delete file + /// Delete a file /// Operations /// File ID - /// Delete after finished - /// Don't move to the Recycle Bin + /// Deletes after finished + /// Doesn't move a file to the recycle bin /// Operation result [Delete("file/{fileId}")] public IEnumerable DeleteFile(String fileId, bool deleteAfter, bool immediately) @@ -970,11 +957,11 @@ namespace ASC.Api.Documents } /// - /// Start conversion operation + /// Starts a conversion operation of a file with the ID specified in the request. /// - /// Convert start + /// Start file converting /// Operations - /// + /// File ID /// Operation result [Update("file/{fileId}/checkconversion")] public IEnumerable StartConversion(String fileId) @@ -983,12 +970,12 @@ namespace ASC.Api.Documents } /// - /// Check conversion status + /// Checks a status of converting a file with the ID specified in the request. /// - /// Convert status + /// Get converting status /// Operations - /// - /// + /// File ID + /// Starts a conversion operation or not /// Operation result [Read("file/{fileId}/checkconversion")] public IEnumerable CheckConversion(String fileId, bool start) @@ -1018,13 +1005,27 @@ namespace ASC.Api.Documents } /// - /// Deletes the folder with the ID specified in the request + /// Returns a link to download a file with the ID specified in the request. /// - /// Delete folder + /// Get file download link + /// Files + /// File ID + /// File download link + [Read("file/{fileId}/presigneduri")] + public string GetPresignedUri(String fileId) + { + var file = _fileStorageService.GetFile(fileId, -1); + return PathProvider.GetFileStreamUrl(file); + } + + /// + /// Deletes a folder with the ID specified in the request. + /// + /// Delete a folder /// Operations /// Folder ID - /// Delete after finished - /// Don't move to the Recycle Bin + /// Deletes after finished + /// Doesn't move a folder to the recycle bin /// Operation result [Delete("folder/{folderId}")] public IEnumerable DeleteFolder(String folderId, bool deleteAfter, bool immediately) @@ -1033,13 +1034,14 @@ namespace ASC.Api.Documents } /// - /// Checking for conflicts + /// Checks a batch of files and folders for conflicts when moving or copying them to the folder with the ID specified in the request. /// + /// Check files and folders for conflicts /// Operations /// Destination folder ID - /// Folder ID list - /// File ID list - /// Conflicts file ids + /// List of folder IDs + /// List of file IDs + /// Conflict file IDs [Read("fileops/move")] public IEnumerable MoveOrCopyBatchCheck(String destFolderId, IEnumerable folderIds, IEnumerable fileIds) { @@ -1055,15 +1057,15 @@ namespace ASC.Api.Documents } /// - /// Moves all the selected files and folders to the folder with the ID specified in the request + /// Moves all the selected files and folders to the folder with the ID specified in the request. /// - /// Move to folder + /// Move to a folder /// Operations /// Destination folder ID - /// Folder ID list - /// File ID list - /// Overwriting behavior: skip(0), overwrite(1) or duplicate(2) - /// Delete after finished + /// List of folder IDs + /// List of file IDs + /// Overwriting behavior: skip (0), overwrite (1) or duplicate (2) + /// Deletes after finished /// Operation result [Update("fileops/move")] public IEnumerable MoveBatchItems(String destFolderId, IEnumerable folderIds, IEnumerable fileIds, FileConflictResolveType conflictResolveType, bool deleteAfter) @@ -1077,15 +1079,15 @@ namespace ASC.Api.Documents } /// - /// Copies all the selected files and folders to the folder with the ID specified in the request + /// Copies all the selected files and folders to the folder with the ID specified in the request. /// - /// Copy to folder + /// Copy to a folder /// Operations /// Destination folder ID - /// Folder ID list - /// File ID list - /// Overwriting behavior: skip(0), overwrite(1) or duplicate(2) - /// Delete after finished + /// List of folder IDs + /// List of file IDs + /// Overwriting behavior: skip (0), overwrite (1) or duplicate (2) + /// Deletes after finished /// Operation result [Update("fileops/copy")] public IEnumerable CopyBatchItems(String destFolderId, IEnumerable folderIds, IEnumerable fileIds, FileConflictResolveType conflictResolveType, bool deleteAfter) @@ -1099,10 +1101,12 @@ namespace ASC.Api.Documents } /// - /// Marks all files and folders as read + /// Marks the files and folders with the IDs specified in the request as read. /// /// Mark as read /// Operations + /// List of folder IDs + /// List of file IDs /// Operation result [Update("fileops/markasread")] public IEnumerable MarkAsRead(IEnumerable folderIds, IEnumerable fileIds) @@ -1116,9 +1120,9 @@ namespace ASC.Api.Documents } /// - /// Finishes all the active Operations + /// Finishes all the active operations. /// - /// Finish all + /// Finish all operations /// Operations /// Operation result [Update("fileops/terminate")] @@ -1129,9 +1133,9 @@ namespace ASC.Api.Documents /// - /// Returns the list of all active Operations + /// Returns a list of all the active operations. /// - /// Operations list + /// Get operations /// Operations /// Operation result [Read("fileops")] @@ -1141,12 +1145,12 @@ namespace ASC.Api.Documents } /// - /// Start downlaod process of files and folders with ID + /// Starts the download process of files and folders with the IDs specified in the request. /// - /// Finish Operations - /// File ID list for download with convert to format - /// File ID list - /// Folder ID list + /// Bulk download + /// List of file IDs which will be converted + /// List of file IDs + /// List of folder IDs /// Operations /// Operation result [Update("fileops/bulkdownload")] @@ -1176,12 +1180,12 @@ namespace ASC.Api.Documents } /// - /// Deletes the files and folders with the IDs specified in the request + /// Deletes the files and folders with the IDs specified in the request. /// - /// Folder ID list - /// File ID list - /// Delete after finished - /// Don't move to the Recycle Bin + /// List of folder IDs + /// List of file IDs + /// Deletes after finished + /// Doesn't move the files and folders to the recycle bin /// Delete files and folders /// Operations /// Operation result @@ -1197,7 +1201,7 @@ namespace ASC.Api.Documents } /// - /// Deletes all files and folders from the recycle bin + /// Deletes all the files and folders from the recycle bin. /// /// Clear recycle bin /// Operations @@ -1209,9 +1213,9 @@ namespace ASC.Api.Documents } /// - /// Returns the detailed information about all the available file versions with the ID specified in the request + /// Returns the detailed information about all the available file versions with the ID specified in the request. /// - /// File versions + /// Get file versions /// Files /// File ID /// File information @@ -1223,13 +1227,14 @@ namespace ASC.Api.Documents } /// - /// Change version history + /// Changes version history of a file with the ID specified in the request. /// + /// Change history /// File ID - /// Version of history - /// Mark as version or revision + /// History version + /// Marks as version or revision /// Files - /// + /// File history [Update("file/{fileId}/history")] public IEnumerable ChangeHistory(string fileId, int version, bool continueVersion) { @@ -1238,9 +1243,9 @@ namespace ASC.Api.Documents } /// - /// Returns the detailed information about shared file with the ID specified in the request + /// Returns the detailed information about the shared file with the ID specified in the request. /// - /// File sharing + /// Get the shared file information /// Sharing /// File ID /// Shared file information @@ -1252,9 +1257,9 @@ namespace ASC.Api.Documents } /// - /// Returns the detailed information about shared folder with the ID specified in the request + /// Returns the detailed information about the shared folder with the ID specified in the request. /// - /// Folder sharing + /// Get the shared folder information /// Folder ID /// Sharing /// Shared folder information @@ -1266,16 +1271,16 @@ namespace ASC.Api.Documents } /// - /// Sets sharing settings for the file with the ID specified in the request + /// Sets the sharing settings to a file with the ID specified in the request. /// /// File ID - /// Collection of sharing rights - /// Should notify people - /// Sharing message to send when notifying - /// Share file + /// Collection of sharing parameters + /// Notifies users about the shared file + /// Message to send when notifying about the shared file + /// Share a file /// Sharing /// - /// Each of the FileShareParams must contain two parameters: 'ShareTo' - ID of the user with whom we want to share and 'Access' - access type which we want to grant to the user (Read, ReadWrite, etc) + /// Each of the "share" parameters must contain two values: 'ShareTo' - ID of the user with whom we want to share the file and 'Access' - access type which we want to grant to the user (Read, ReadWrite, etc). /// /// Shared file information [Update("file/{fileId}/share")] @@ -1296,15 +1301,15 @@ namespace ASC.Api.Documents } /// - /// Sets sharing settings for the folder with the ID specified in the request + /// Sets the sharing settings to a folder with the ID specified in the request. /// - /// Share folder + /// Share a folder /// Folder ID - /// Collection of sharing rights - /// Should notify people - /// Sharing message to send when notifying + /// Collection of sharing parameters + /// Notifies users about the shared file + /// Message to send when notifying about the shared file /// - /// Each of the FileShareParams must contain two parameters: 'ShareTo' - ID of the user with whom we want to share and 'Access' - access type which we want to grant to the user (Read, ReadWrite, etc) + /// Each of the "share" parameters must contain two values: 'ShareTo' - ID of the user with whom we want to share the folder and 'Access' - access type which we want to grant to the user (Read, ReadWrite, etc). /// /// Sharing /// Shared folder information @@ -1327,10 +1332,10 @@ namespace ASC.Api.Documents } /// - /// Removes sharing rights for the group with the ID specified in the request + /// Removes the sharing rights for the group with the ID specified in the request. /// - /// Folders ID - /// Files ID + /// List of folder IDs + /// List of file IDs /// Remove group sharing rights /// Sharing /// Shared file information @@ -1348,11 +1353,11 @@ namespace ASC.Api.Documents } /// - /// Returns the external link to the shared file with the ID specified in the request + /// Returns an external link to the shared file with the ID specified in the request. /// - /// Shared link + /// Get the shared link /// File ID - /// Access right + /// Sharing rights /// Sharing /// Shared file link [Update("{fileId}/sharedlink")] @@ -1386,12 +1391,67 @@ namespace ASC.Api.Documents } /// - /// Get a list of available providers + /// Returns file properties of the specified file. /// - /// Third-Party Integration - /// List of provider key - /// List of provider key: DropboxV2, Box, WebDav, Yandex, OneDrive, SharePoint, GoogleDrive - /// + /// Get file properties + /// File ID + /// Files + /// File properties + [Read("{fileId}/properties")] + public EntryProperties GetProperties(string fileId) + { + return _fileStorageService.GetFileProperties(fileId); + } + + /// + /// Saves file properties to the specified file. + /// + /// Save file properties to a file + /// File ID + /// File properties + /// Files + /// File properties + [Update("{fileId}/properties")] + public EntryProperties SetProperties(string fileId, EntryProperties fileProperties) + { + return _fileStorageService.SetFileProperties(fileId, fileProperties); + } + + /// + /// Saves file properties to the specified files. + /// + /// Save file properties to files + /// IDs of files + /// Creates a subfolder or not + /// File properties + /// Files + /// List of file properties + [Update("batch/properties")] + public List SetProperties(string[] filesId, bool createSubfolder, EntryProperties fileProperties) + { + var result = new List(); + + foreach (var fileId in filesId) + { + if (createSubfolder) + { + var file = _fileStorageService.GetFile(fileId, -1).NotFoundIfNull("File not found"); + fileProperties.FormFilling.CreateFolderTitle = Path.GetFileNameWithoutExtension(file.Title); + } + + result.Add(_fileStorageService.SetFileProperties(fileId, fileProperties)); + } + return result; + } + + /// + /// Returns a list of the available providers. + /// + /// Get providers + /// Third-party integration + /// List of provider keys + /// List of provider keys: DropboxV2, Box, WebDav, Yandex, OneDrive, SharePoint, GoogleDrive. + /// List of the available providers [Read("thirdparty/capabilities")] public List> Capabilities() { @@ -1443,20 +1503,20 @@ namespace ASC.Api.Documents } /// - /// Saves the third party file storage service account + /// Saves the storage service account of the third-party file. /// - /// Save third party account - /// Connection url for SharePoint + /// Save a third-party account + /// Connection URL for the share point /// Login /// Password /// Authentication token - /// - /// Title - /// Provider Key + /// Corporate account or not + /// Customer title + /// Provider key /// Provider ID - /// Third-Party Integration + /// Third-party integration /// Folder contents - /// List of provider key: DropboxV2, Box, WebDav, Yandex, OneDrive, SharePoint, GoogleDrive + /// List of provider keys: DropboxV2, Box, WebDav, Yandex, OneDrive, SharePoint, GoogleDrive. /// [Create("thirdparty")] public FolderWrapper SaveThirdParty( @@ -1484,10 +1544,10 @@ namespace ASC.Api.Documents } /// - /// Returns the list of all connected third party services + /// Returns a list of all the connected third-party services. /// - /// Third-Party Integration - /// Third party list + /// Third-party integration + /// Get third-party services /// Connected providers [Read("thirdparty")] public IEnumerable GetThirdPartyAccounts() @@ -1496,11 +1556,11 @@ namespace ASC.Api.Documents } /// - /// Returns the list of third party services connected in the Common section + /// Returns a list of the third-party services connected to the common section. /// - /// Third-Party Integration - /// Third party folder - /// Connected providers folder + /// Third-party integration + /// Get common third-party services + /// Common third-party folders [Read("thirdparty/common")] public IEnumerable GetCommonThirdPartyFolders() { @@ -1509,14 +1569,12 @@ namespace ASC.Api.Documents } /// - /// Removes the third party file storage service account with the ID specified in the request + /// Removes the storage service account of the third-party file with the ID specified in the request. /// - /// Provider ID. Provider id is part of folder id. - /// Example, folder id is "sbox-123", then provider id is "123" - /// - /// Remove third party account - /// Third-Party Integration - /// Folder id + /// Provider ID. It is a part of the folder ID. Example: folder ID is "sbox-123", then provider ID is "123". + /// Remove a third-party account + /// Third-party integration + /// Folder ID /// [Delete("thirdparty/{providerId:[0-9]+}")] public object DeleteThirdParty(int providerId) @@ -1526,9 +1584,11 @@ namespace ASC.Api.Documents } /// - /// Search files + /// Searches for files and folders by the query specified in the request. /// - /// Queary string + /// Search files and folders + /// Operations + /// Query string /// Files and folders [Read(@"@search/{query}")] public IEnumerable Search(string query) @@ -1541,13 +1601,13 @@ namespace ASC.Api.Documents } /// - /// Adding files to favorite list + /// Adds files and folders with the IDs specified in the request to the favorite list. /// - /// Favorite add + /// Add favorite files and folders /// Files - /// - /// File IDs - /// + /// List of folder IDs + /// List of file IDs + /// Bool value: true if the operation is successful [Create("favorites")] public bool AddFavorites(IEnumerable folderIds, IEnumerable fileIds) { @@ -1556,13 +1616,13 @@ namespace ASC.Api.Documents } /// - /// Removing files from favorite list + /// Removes files and folders with the IDs specified in the request from the favorite list. /// - /// Favorite delete + /// Delete favorite files and folders /// Files - /// - /// File IDs - /// + /// List of folder IDs + /// List of file IDs + /// Bool value: true if the operation is successful [Delete("favorites")] public bool DeleteFavorites(IEnumerable folderIds, IEnumerable fileIds) { @@ -1571,12 +1631,12 @@ namespace ASC.Api.Documents } /// - /// Adding files to template list + /// Adds files with the IDs specified in the request to the template list. /// - /// Template add + /// Add template files /// Files - /// File IDs - /// + /// List of file IDs + /// Bool value: true if the operation is successful [Create("templates")] public bool AddTemplates(IEnumerable fileIds) { @@ -1585,12 +1645,12 @@ namespace ASC.Api.Documents } /// - /// Removing files from template list + /// Removes files with the IDs specified in the request from the template list. /// - /// Template delete + /// Delete template files /// Files - /// File IDs - /// + /// List of file IDs + /// Bool value: true if the operation is successful [Delete("templates")] public bool DeleteTemplates(IEnumerable fileIds) { @@ -1600,11 +1660,12 @@ namespace ASC.Api.Documents /// - /// Store file in original formats when upload and convert + /// Stores files in the original formats when uploading and converting. /// - /// + /// Store original formats + /// Sets the parameter on or off /// Settings - /// + /// Bool value: true if the parameter is enabled [Update(@"storeoriginal")] public bool StoreOriginal(bool set) { @@ -1612,12 +1673,13 @@ namespace ASC.Api.Documents } /// - /// Do not show the confirmation dialog + /// Hides the confirmation dialog. /// - /// + /// Hide the confirmation dialog + /// Defines if it is for saving or not /// Settings /// false - /// + /// Bool value: true if the parameter is enabled [Update(@"hideconfirmconvert")] public bool HideConfirmConvert(bool save) { @@ -1625,11 +1687,12 @@ namespace ASC.Api.Documents } /// - /// Update the file version if the same name is exist + /// Updates a file version if a file with such a name already exists. /// - /// + /// Update a file version if exists + /// Sets the parameter on or off /// Settings - /// + /// Bool value: true if the parameter is enabled [Update(@"updateifexist")] public bool UpdateIfExist(bool set) { @@ -1637,11 +1700,12 @@ namespace ASC.Api.Documents } /// - /// Display recent folder + /// Displays the recent folder. /// - /// + /// Display recent folder + /// Sets the parameter on or off /// Settings - /// + /// Bool value: true if the parameter is enabled [Update(@"displayRecent")] public bool DisplayRecent(bool set) { @@ -1649,11 +1713,12 @@ namespace ASC.Api.Documents } /// - /// Display favorite folder + /// Displays the favorite folder. /// - /// + /// Display favorite folder + /// Sets the parameter on or off /// Settings - /// + /// Bool value: true if the parameter is enabled [Update(@"settings/favorites")] public bool DisplayFavorite(bool set) { @@ -1661,11 +1726,12 @@ namespace ASC.Api.Documents } /// - /// Display template folder + /// Displays the template folder. /// - /// + /// Display template folder + /// Sets the parameter on or off /// Settings - /// + /// Bool value: true if the parameter is enabled [Update(@"settings/templates")] public bool DisplayTemplates(bool set) { @@ -1673,11 +1739,51 @@ namespace ASC.Api.Documents } /// - /// + /// Updates the auto cleanup setting. /// - /// + /// Update the auto cleanup setting + /// Enables the auto cleanup or not + /// A time interval when the auto cleanup will be performed (one week, two weeks, one month, two months, three months) /// Settings - /// + /// The auto cleanup setting properties + [Update(@"settings/autocleanup")] + public AutoCleanUpData ChangeAutomaticallyCleanUp(bool set, DateToAutoCleanUp gap) + { + return _fileStorageService.ChangeAutomaticallyCleanUp(set, gap); + } + + /// + /// Returns the auto cleanup setting properties. + /// + /// Get the auto cleanup setting properties + /// Settings + /// The auto cleanup setting properties + [Read(@"settings/autocleanup")] + public AutoCleanUpData GetSettingsAutomaticallyCleanUp() + { + return _fileStorageService.GetSettingsAutomaticallyCleanUp(); + } + + /// + /// Changes the default access rights in sharing settings + /// + /// Change the default access rights + /// Default access rights + /// Settings + /// Default access rights + [Update(@"settings/dafaultaccessrights")] + public List ChangeDafaultAccessRights(List value) + { + return _fileStorageService.ChangeDafaultAccessRights(value); + } + + /// + /// Changes the archive format for downloading from zip to tar.gz. + /// + /// Change the archive format + /// Sets the parameter on or off + /// Settings + /// Archive [Update(@"settings/downloadtargz")] public ICompress ChangeDownloadZip(bool set) { @@ -1685,13 +1791,14 @@ namespace ASC.Api.Documents } /// - /// Checking document service location + /// Checks the document service location. /// - /// Document editing service Domain - /// Document command service Domain - /// Community Server Address + /// Check the document service URL + /// Document editing service domain + /// Document command service domain + /// Community Server address /// Settings - /// + /// Document service information [Update("docservice")] public IEnumerable CheckDocServiceUrl(string docServiceUrl, string docServiceUrlInternal, string docServiceUrlPortal) { @@ -1720,10 +1827,11 @@ namespace ASC.Api.Documents /// - /// Get the address of connected editors + /// Returns the address of the connected editors. /// + /// Get the document service URL /// Settings - /// + /// Specifies version or not /// Address [Read("docservice")] public object GetDocServiceUrl(bool version) @@ -1744,13 +1852,13 @@ namespace ASC.Api.Documents } /// - /// Create thumbnails for files with the IDs specified in the request + /// Creates thumbnails for the files with the IDs specified in the request. /// /// Create thumbnails /// Files - /// File IDs + /// List of file IDs /// false - /// + /// List of file IDs [Create("thumbnails")] public IEnumerable CreateThumbnails(IEnumerable fileIds) { @@ -1793,6 +1901,12 @@ namespace ASC.Api.Documents #region wordpress + /// + /// Returns the WordPress plugin information. + /// + /// Get the WordPress information + /// WordPress + /// WordPress information /// false [Read("wordpress-info")] public object GetWordpressInfo() @@ -1821,6 +1935,12 @@ namespace ASC.Api.Documents }; } + /// + /// Deletes the WordPress plugin information. + /// + /// Delete the WordPress information + /// WordPress + /// Object with the bool value: true if the operation is successful /// false [Read("wordpress-delete")] public object DeleteWordpressInfo() @@ -1840,6 +1960,13 @@ namespace ASC.Api.Documents }; } + /// + /// Saves the user WordPress information at login. + /// + /// Save the user WordPress information + /// Authorization code + /// WordPress + /// User WordPress information /// false [Create("wordpress-save")] public object WordpressSave(string code) @@ -1880,6 +2007,16 @@ namespace ASC.Api.Documents } } + /// + /// Creates a WordPress post with the parameters specified in the request. + /// + /// Create a WordPress post + /// Authorization code + /// Post title + /// Post content + /// Operation status + /// WordPress + /// WordPress post /// false [Create("wordpress")] public bool CreateWordpressPost(string code, string title, string content, int status) @@ -1909,6 +2046,14 @@ namespace ASC.Api.Documents #region easybib + /// + /// Returns the EasyBib citation list. + /// + /// Get the EasyBib citation list + /// Citation source: book (0), journal (1) or website (2) + /// Citation data + /// EasyBib + /// EasyBib citation list /// false [Read("easybib-citation-list")] public object GetEasybibCitationList(int source, string data) @@ -1932,6 +2077,12 @@ namespace ASC.Api.Documents } + /// + /// Returns the EasyBib styles. + /// + /// Get the EasyBib styles + /// EasyBib + /// List of EasyBib styles /// false [Read("easybib-styles")] public object GetEasybibStyles() @@ -1954,6 +2105,13 @@ namespace ASC.Api.Documents } } + /// + /// Returns the EasyBib citation book. + /// + /// Get the EasyBib citation + /// Citation data + /// EasyBib + /// EasyBib citation /// false [Create("easybib-citation")] public object EasyBibCitationBook(string citationData) @@ -1990,13 +2148,13 @@ namespace ASC.Api.Documents #endregion /// - /// Result of file conversation operation. + /// Result of the file conversion operation. /// [DataContract(Name = "operation_result", Namespace = "")] public class ConversationResult { /// - /// Operation Id. + /// Operation ID. /// [DataMember(Name = "id")] public string Id { get; set; } @@ -2026,7 +2184,7 @@ namespace ASC.Api.Documents public FileWrapper File { get; set; } /// - /// Error during conversation. + /// Error during conversion. /// [DataMember(Name = "error")] public string Error { get; set; } diff --git a/module/ASC.Api/ASC.Api.Documents/FileEntryWrapper.cs b/module/ASC.Api/ASC.Api.Documents/FileEntryWrapper.cs index 5c750598a..ffee0430d 100644 --- a/module/ASC.Api/ASC.Api.Documents/FileEntryWrapper.cs +++ b/module/ASC.Api/ASC.Api.Documents/FileEntryWrapper.cs @@ -98,6 +98,16 @@ namespace ASC.Api.Documents [DataMember(Order = 57, EmitDefaultValue = false)] public int ProviderId { get; set; } + /// + /// + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public bool DenyDownload { get; set; } + + /// + /// + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public bool DenySharing { get; set; } + /// /// @@ -117,6 +127,8 @@ namespace ASC.Api.Documents ProviderItem = entry.ProviderEntry; ProviderKey = entry.ProviderKey; ProviderId = entry.ProviderId; + DenyDownload = entry.DenyDownload; + DenySharing = entry.DenySharing; } /// diff --git a/module/ASC.Api/ASC.Api.Documents/FolderWrapper.cs b/module/ASC.Api/ASC.Api.Documents/FolderWrapper.cs index 6d5b616d6..fa01d8b5a 100644 --- a/module/ASC.Api/ASC.Api.Documents/FolderWrapper.cs +++ b/module/ASC.Api/ASC.Api.Documents/FolderWrapper.cs @@ -52,6 +52,11 @@ namespace ASC.Api.Documents [DataMember(EmitDefaultValue = false, IsRequired = false)] public bool IsShareable { get; set; } + /// + /// + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public bool IsFavorite { get; set; } + /// /// /// @@ -75,6 +80,7 @@ namespace ASC.Api.Documents FilesCount = folder.TotalFiles; FoldersCount = folder.TotalSubFolders; IsShareable = folder.Shareable; + IsFavorite = folder.IsFavorite; } private FolderWrapper() diff --git a/module/ASC.Api/ASC.Api.Feed/FeedApi.cs b/module/ASC.Api/ASC.Api.Feed/FeedApi.cs index 264e39c0b..e7ac90980 100644 --- a/module/ASC.Api/ASC.Api.Feed/FeedApi.cs +++ b/module/ASC.Api/ASC.Api.Feed/FeedApi.cs @@ -30,6 +30,9 @@ using ASC.Specific; namespace ASC.Api.Feed { + /// + /// Feed access. + /// public class FeedApi : IApiEntryPoint { private const string newFeedsCountCacheKey = "newfeedscount"; @@ -52,6 +55,12 @@ namespace ASC.Api.Feed this.context = context; } + /// + ///Opens feeds for reading. + /// + /// + ///Read feeds + /// [Update("/read")] public void Read() { @@ -59,6 +68,19 @@ namespace ASC.Api.Feed newFeedsCountCache.Remove(GetNewFeedsCountKey()); } + /// + ///Returns a list of feeds that are filtered by the parameters specified in the request. + /// + /// + ///Get feeds + /// + /// Product which feeds you want to read + /// Time from which the feeds should be displayed + /// Time until which the feeds should be displayed + /// Author whose feeds you want to read + /// Displays only fresh feeds + /// Time when the feeds were read + ///List of filtered feeds [Read("/filter")] public object GetFeed( string product, @@ -124,6 +146,13 @@ namespace ASC.Api.Feed return new { feeds, readedDate }; } + /// + ///Returns a number of fresh feeds. + /// + /// + ///Count fresh feeds + /// + ///Number of fresh feeds [Read("/newfeedscount")] public object GetFreshNewsCount() { diff --git a/module/ASC.Api/ASC.Api.Feed/FeedWrapper.cs b/module/ASC.Api/ASC.Api.Feed/FeedWrapper.cs index 2335054e6..245c533cb 100644 --- a/module/ASC.Api/ASC.Api.Feed/FeedWrapper.cs +++ b/module/ASC.Api/ASC.Api.Feed/FeedWrapper.cs @@ -32,6 +32,7 @@ namespace ASC.Api.Feed GroupId = item.GroupId; IsToday = item.IsToday; IsYesterday = item.IsYesterday; + IsTomorrow = item.IsTomorrow; CreatedDate = (ApiDateTime)item.CreatedDate; ModifiedDate = (ApiDateTime)item.ModifiedDate; AggregatedDate = (ApiDateTime)item.AggregatedDate; @@ -45,6 +46,8 @@ namespace ASC.Api.Feed public bool IsYesterday { get; private set; } + public bool IsTomorrow { get; private set; } + public ApiDateTime CreatedDate { get; private set; } public ApiDateTime ModifiedDate { get; private set; } diff --git a/module/ASC.Api/ASC.Api.Mail/MailApi.Accounts.cs b/module/ASC.Api/ASC.Api.Mail/MailApi.Accounts.cs index 32d0b9610..c7323fe03 100644 --- a/module/ASC.Api/ASC.Api.Mail/MailApi.Accounts.cs +++ b/module/ASC.Api/ASC.Api.Mail/MailApi.Accounts.cs @@ -42,11 +42,11 @@ namespace ASC.Api.Mail public partial class MailApi { /// - /// Returns lists of all mailboxes, aliases and groups for user. + /// Returns a list of all the user mailboxes, aliases and groups. /// - /// User id - /// Mailboxes, aliases and groups list - /// Get mailboxes, aliases and groups list + /// User name + /// List of user mailboxes, aliases and groups + /// Get user accounts /// Accounts [Read(@"accounts")] public IEnumerable GetAccounts(string username = "") @@ -56,13 +56,13 @@ namespace ASC.Api.Mail } /// - /// Returns the information about the account. + /// Returns the account information by the email address specified in the request. /// /// Account email address - /// Account with specified email - /// Exception happens when in parameters is invalid. Text description contains parameter name and text description. + /// Account information + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. /// Exception happens when mailbox wasn't found by email. - /// Get account by email + /// Get an account by email /// Accounts [Read(@"accounts/single")] public MailBoxData GetAccount(string email) @@ -104,14 +104,14 @@ namespace ASC.Api.Mail } /// - /// Creates an account based on email and password. + /// Creates an account based on the email address and password specified in the request. /// - /// Account email in string format like: name@domain - /// Password as plain text. - /// Exception happens when in parameters is invalid. Text description contains parameter name and text description. + /// Account email address in the name@domain format + /// Email password + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. /// Exception contains text description of happened error. /// Created account - /// Create new account by email and password + /// Create an account by email and password /// Accounts [Create(@"accounts/simple")] public MailAccountData CreateAccountSimple(string email, string password) @@ -162,28 +162,28 @@ namespace ASC.Api.Mail } /// - /// Creates account using full information about mail servers. + /// Creates an account using full information about mail servers specified in the request. /// /// Account name in Teamlab - /// Account email in string format like: name@domain. - /// Login for imap or pop server. - /// Password for imap or pop server - /// Port for imap or pop server - /// Imap or pop server address or IP. - /// Login for smtp server - /// Password for smtp server - /// Smtp server port - /// Smtp server adress or IP. - /// Flag is smtp server authentication needed. Value: true or false. - /// Flag is imap server using for incoming mails. Value: true or false. - /// Flag is all mails needed for download. Value: true or false. If vslue true, it will be downloaded messages from last 30 days only. - /// Specify encription type for imap or pop server. 0- None, 1 - SSL, 2- StartTLS - /// Specify encription type for smtp server. 0- None, 1 - SSL, 2- StartTLS - /// Specify authentication type for imap or pop server. 1 - Login, 4 - CremdMd5, 5 - OAuth2 - /// Specify authentication type for imap or pop server. 0- None, 1 - Login, 4 - CremdMd5, 5 - OAuth2 + /// Account email address in the name@domain format + /// IMAP or POP server login + /// IMAP or POP server password + /// IMAP or POP server port + /// IMAP or POP server address or IP + /// SMTP server login + /// SMTP server password + /// SMTP server port + /// SMTP server address or IP + /// Specifies if the authentication is needed for the SMTP server or not + /// Specifies if the IMAP server is used for incoming mails or not + /// Specifies if all the mails should be downloaded from the account (false) or not (true). If true, then messages from last 30 days only will be downloaded + /// Encryption type for the IMAP or POP server: 0 - None, 1 - SSL, 2 - StartTLS + /// Encryption type for the SMTP server: 0 - None, 1 - SSL, 2 - StartTLS + /// Authentication type for the IMAP or POP server: 1 - Login, 4 - CremdMd5, 5 - OAuth2 + /// Authentication type for the SMTP server: 0 - None, 1 - Login, 4 - CremdMd5, 5 - OAuth2 /// Created account /// Exception contains text description of happened error. - /// Create account with custom mail servers. + /// Create an account by custom mail servers /// Accounts [Create(@"accounts")] public MailAccountData CreateAccount(string name, @@ -268,13 +268,13 @@ namespace ASC.Api.Mail } /// - /// Creates Mail account with OAuth authentication. Only Google OAuth supported. + /// Creates a mail account with OAuth authentication (only Google OAuth is supported). /// - /// Oauth code - /// Type of OAuth service. 0- Unknown, 1 - Google. - /// Exception contains text description of happened error. + /// OAuth code + /// OAuth service type: 0 - Unknown, 1 - Google + /// Exception contains text description of an error occurred. /// Created account - /// Create OAuth account + /// Create the OAuth account /// Accounts [Create(@"accounts/oauth")] public MailAccountData CreateAccountOAuth(string code, byte type) @@ -295,14 +295,14 @@ namespace ASC.Api.Mail } /// - /// Update Mail account with OAuth authentication. Only Google OAuth supported. + /// Updates a mail account with OAuth authentication (only Google OAuth is supported). /// - /// Oauth code - /// Type of OAuth service. 0- Unknown, 1 - Google. - /// Mailbox ID to update - /// Exception contains text description of happened error. + /// New OAuth code + /// New OAuth service type: 0 - Unknown, 1 - Google + /// Mailbox ID + /// Exception contains text description of an error occurred. /// Updated OAuth account - /// Update OAuth account + /// Update the OAuth account /// Accounts [Update(@"accounts/oauth")] public MailAccountData UpdateAccountOAuth(string code, byte type, int mailboxId) @@ -328,27 +328,27 @@ namespace ASC.Api.Mail } /// - /// Updates the existing account. + /// Updates an account with the name specified in the request. /// /// Account name in Teamlab - /// Account email in string format like: name@domain. - /// Login for imap or pop server. - /// Password for imap or pop server - /// Port for imap or pop server - /// Imap or pop server address or IP. - /// Login for smtp server - /// Password for smtp server - /// Smtp server port - /// Smtp server adress or IP. - /// Flag is smtp server authentication needed. Value: true or false. - /// Flag is all mails needed for download. Value: true or false. If vslue true, it will be downloaded messages from last 30 days only. - /// Specify encription type for imap or pop server. 0- None, 1 - SSL, 2- StartTLS - /// Specify encription type for smtp server. 0- None, 1 - SSL, 2- StartTLS - /// Specify authentication type for imap or pop server. 1 - Login, 4 - CremdMd5, 5 - OAuth2 - /// Specify authentication type for imap or pop server. 0- None, 1 - Login, 4 - CremdMd5, 5 - OAuth2 + /// New account email in the name@domain format + /// New IMAP or POP server login + /// New IMAP or POP server password + /// New IMAP or POP server port + /// New IMAP or POP server address or IP + /// New SMTP server login + /// New SMTP server password + /// New SMTP server port + /// New SMTP server address or IP + /// Specifies if the authentication is needed for the SMTP server or not + /// Specifies if all the mails should be downloaded from the account (false) or not (true). If true, then messages from last 30 days only will be downloaded + /// New encryption type for the IMAP or POP server: 0 - None, 1 - SSL, 2 - StartTLS + /// New encryption type for the SMTP server: 0 - None, 1 - SSL, 2 - StartTLS + /// New authentication type for the IMAP or POP server: 1 - Login, 4 - CremdMd5, 5 - OAuth2 + /// New authentication type for the SMTP server: 0 - None, 1 - Login, 4 - CremdMd5, 5 - OAuth2 /// Updated account - /// Exception contains text description of happened error. - /// Update account + /// Exception contains text description of an error occurred. + /// Update an account /// Accounts [Update(@"accounts")] public MailAccountData UpdateAccount(string name, @@ -432,13 +432,13 @@ namespace ASC.Api.Mail } /// - /// Deletes account by email. + /// Deletes an account by email address specified in the request. /// - /// Email the account to delete - /// MailOperationResult object + /// Account email address + /// Operation status /// Exception happens when some parameters are invalid. Text description contains parameter name and text description. /// Exception happens when mailbox wasn't found. - /// Delete account + /// Delete an account /// Accounts [Delete(@"accounts")] public MailOperationStatus DeleteAccount(string email) @@ -460,14 +460,14 @@ namespace ASC.Api.Mail } /// - /// Sets the state for the account specified in the request + /// Sets a state of an account with email address specified in the request. /// - /// Email of the account - /// Account activity state. Value: true or false. True - enabled, False - disabled. - /// Account mailbox id - /// Exception happens when in parameters is invalid. Text description contains parameter name and text description. + /// Account email address + /// Account activity state: true - enabled, false - disabled + /// Account mailbox ID + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. /// Exception happens when update operation failed. - /// Set account state + /// Set an account state /// Accounts [Update(@"accounts/state")] public int SetAccountEnable(string email, bool state) @@ -509,14 +509,14 @@ namespace ASC.Api.Mail } /// - /// Sets the default account specified in the request + /// Sets the default account with email address specified in the request. /// - /// Email of the account + /// Account email address /// Set or reset account as default /// Account email address - /// Exception happens when in parameters is invalid. Text description contains parameter name and text description. + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. /// Exception happens when update operation failed. - /// Set default account + /// Set the default account /// Accounts [Update(@"accounts/default")] public string SetDefaultAccount(string email, bool isDefault) @@ -555,17 +555,17 @@ namespace ASC.Api.Mail } /// - /// Gets the default settings for the account based on the email domain. + /// Returns the default settings for an account based on its email domain. /// /// Account email address - /// This string parameter specifies action for default settings. Values: - /// "get_imap_pop_settings" - get imap or pop settings, imap settings are prior. - /// "get_imap_server" | "get_imap_server_full" - get imap settings - /// "get_pop_server" | "get_pop_server_full" - get pop settings - /// By default returns default imap settings. + /// Action for default settings: + /// "get_imap_pop_settings" - get IMAP or POP settings (IMAP settings are prior), + /// "get_imap_server" | "get_imap_server_full" - get IMAP settings, + /// "get_pop_server" | "get_pop_server_full" - get POP settings. + /// By default returns default IMAP settings /// /// Account with default settings - /// Get default account settings + /// Get the default account settings /// Accounts [Read(@"accounts/setups")] public MailBoxData GetAccountDefaults(string email, string action) @@ -593,14 +593,14 @@ namespace ASC.Api.Mail } /// - /// Sets the state for the account specified in the request + /// Sets the account email in a folder with the ID specified in the request. /// - /// Id of the account - /// Document's folder Id + /// Mailbox ID + /// Document folder ID /// Account email address - /// Exception happens when in parameters is invalid. Text description contains parameter name and text description. + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. /// Exception happens when update operation failed. - /// Set account state + /// Set the account email in a folder /// Accounts [Update(@"accounts/emailinfolder")] public void SetAccountEMailInFolder(int mailbox_id, string email_in_folder) @@ -612,9 +612,10 @@ namespace ASC.Api.Mail } /// - /// UpdateUserActivity + /// Updates the user activity state. /// - /// + /// Update the user activity state + /// Specifies if the user is online or not /// Accounts [Update(@"accounts/updateuseractivity")] public void UpdateUserActivity(bool userOnline) diff --git a/module/ASC.Api/ASC.Api.Mail/MailApi.Alerts.cs b/module/ASC.Api/ASC.Api.Mail/MailApi.Alerts.cs index 9563214b6..24b7c9bd6 100644 --- a/module/ASC.Api/ASC.Api.Mail/MailApi.Alerts.cs +++ b/module/ASC.Api/ASC.Api.Mail/MailApi.Alerts.cs @@ -26,10 +26,10 @@ namespace ASC.Api.Mail public partial class MailApi { /// - /// Returns the list of alerts for the authenticated user + /// Returns a list of all the alerts for the authenticated user. /// - /// Alerts list - /// Get alerts list + /// List of alerts + /// Get alerts /// Alerts [Read(@"alert")] public IList GetAlerts() @@ -38,11 +38,11 @@ namespace ASC.Api.Mail } /// - /// Deletes the alert with the ID specified in the request + /// Deletes an alert with the ID specified in the request. /// /// Alert ID - /// Deleted alert id. Same as request parameter. - /// Delete alert by ID + /// Deleted alert ID. Same as the request parameter + /// Delete an alert by ID /// Alerts [Delete(@"alert/{id}")] public long DeleteAlert(long id) diff --git a/module/ASC.Api/ASC.Api.Mail/MailApi.Attachments.cs b/module/ASC.Api/ASC.Api.Mail/MailApi.Attachments.cs index e53fc3947..fecdace1f 100644 --- a/module/ASC.Api/ASC.Api.Mail/MailApi.Attachments.cs +++ b/module/ASC.Api/ASC.Api.Mail/MailApi.Attachments.cs @@ -33,10 +33,11 @@ namespace ASC.Api.Mail public partial class MailApi { /// - /// Export all message's attachments to MyDocuments + /// Exports all the message attachments to the folder with documents. /// - /// Id of any message - /// Id of Documents folder (if empty then @My) + /// Export message attachments + /// Message ID + /// Folder ID (if empty, the "My documents" folder is used) /// Count of exported attachments /// Messages [Update(@"messages/attachments/export")] @@ -56,11 +57,12 @@ namespace ASC.Api.Mail } /// - /// Export attachment to MyDocuments + /// Exports an attachment with the ID specified in the request to the folder with documents. /// - /// Id of any attachment from the message - /// Id of Documents folder (if empty then @My) - /// Id document in My Documents + /// Export an attachment + /// Attachment ID + /// Folder ID (if empty, the "My documents" folder is used) + /// Document ID in the folder with documents /// Messages [Update(@"messages/attachment/export")] public object ExportAttachmentToDocuments(int id_attachment, string id_folder = null) @@ -79,13 +81,14 @@ namespace ASC.Api.Mail } /// - /// Add attachment to draft + /// Adds an attachment to the draft with the ID specified in the request. /// - /// Id of any message + /// Add an attachment + /// Message ID /// File name /// File stream /// File content type - /// MailAttachment + /// Mail attachment /// Messages [Create(@"messages/attachment/add")] public MailAttachmentData AddAttachment(int id_message, string name, Stream file, string content_type) @@ -97,11 +100,12 @@ namespace ASC.Api.Mail } /// - /// Add attachment to draft + /// Adds an iCal body to the draft with the ID specified in the request. /// - /// Id of any message - /// File name - /// MailAttachment + /// Add a calendar + /// Message ID + /// iCal body + /// Mail attachment /// Messages [Create(@"messages/calendarbody/add")] public MailAttachmentData AddCalendarBody(int id_message, string ical_body) @@ -132,13 +136,14 @@ namespace ASC.Api.Mail } /// - /// Download all attachments from message + /// Downloads all the attachments from the message with the ID specified in the request. /// /// - /// Download all attachments from message + /// Download attachments /// - /// Id of message - /// Attachment Archive + /// Messages + /// Message ID + /// Attachment archive [Update(@"messages/attachment/downloadall/{messageId}")] public MailOperationStatus DownloadAllAttachments(int messageId) { diff --git a/module/ASC.Api/ASC.Api.Mail/MailApi.Autoreply.cs b/module/ASC.Api/ASC.Api.Mail/MailApi.Autoreply.cs index 648e763e2..8aca577d8 100644 --- a/module/ASC.Api/ASC.Api.Mail/MailApi.Autoreply.cs +++ b/module/ASC.Api/ASC.Api.Mail/MailApi.Autoreply.cs @@ -26,16 +26,19 @@ namespace ASC.Api.Mail public partial class MailApi { /// - /// This method needed for update or create autoreply. + /// Updates or creates an autoreply with the parameters specified in the request. /// - /// Id of updated mailbox. - /// New autoreply status. - /// If true then send autoreply only for contacts. - /// If true then field To is active. - /// Start date of autoreply sending. - /// End date of autoreply sending. - /// New autoreply subject. - /// New autoreply value. + /// Update an autoreply + /// Autoreply + /// Mailbox ID + /// New autoreply status + /// If true, then send autoreply only to contacts + /// If true, then the "toDate" field is active + /// New start date of autoreply sending + /// New end date of autoreply sending + /// New autoreply subject + /// New autoreply value in the HTML format + /// Updated autoreply information [Create(@"autoreply/update/{mailboxId:[0-9]+}")] public MailAutoreplyData UpdateAutoreply(int mailboxId, bool turnOn, bool onlyContacts, bool turnOnToDate, DateTime fromDate, DateTime toDate, string subject, string html) diff --git a/module/ASC.Api/ASC.Api.Mail/MailApi.Contacts.cs b/module/ASC.Api/ASC.Api.Mail/MailApi.Contacts.cs index ce0e241fb..f4c43db34 100644 --- a/module/ASC.Api/ASC.Api.Mail/MailApi.Contacts.cs +++ b/module/ASC.Api/ASC.Api.Mail/MailApi.Contacts.cs @@ -35,13 +35,13 @@ namespace ASC.Api.Mail public partial class MailApi { /// - /// Returns the list of the contacts for auto complete feature. + /// Searches for contacts by their emails. /// - /// string part of contact name, lastname or email. - /// Strings list. Strings format: "Name Lastname" email - /// Get contact list for auto complete + /// String part of contact name, lastname or email + /// List of strings in the following format: "Name Lastname" email + /// Search contacts by email /// Contacts - /// Exception happens when in parameters is invalid. Text description contains parameter name and text description. + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. [Read(@"emails/search")] public IEnumerable SearchEmails(string term) { @@ -57,15 +57,15 @@ namespace ASC.Api.Mail } /// - /// Returns lists of mail contacts. + /// Returns a list of filtered mail contacts by the search query specified in the request. /// - /// Text to search in contacts name or emails. - /// Type of contacts + /// Text to search in contact names or emails + /// Contact type /// Count of contacts on page /// Page number - /// Sort order by name. String parameter: "ascending" - ascended, "descending" - descended. + /// Sort order by name. String parameter: "ascending" - ascended, "descending" - descended /// List of filtered contacts - /// Gets filtered contacts + /// Get contacts by search query /// Contacts [Read(@"contacts")] public IEnumerable GetContacts(string search, int? contactType, int? pageSize, int fromIndex, @@ -96,13 +96,13 @@ namespace ASC.Api.Mail } /// - /// Returns lists of mail contacts with contact information + /// Returns a list of mail contacts with the contact information specified in the request. /// - /// infoType - /// data - /// isPrimary + /// Information type + /// Contact data + /// Primary contact or not /// List of filtered contacts - /// Gets filtered contacts + /// Get contacts by contact information /// Contacts [Read(@"contacts/bycontactinfo")] public IEnumerable GetContactsByContactInfo(ContactInfoType infoType, String data, bool? isPrimary) @@ -115,14 +115,14 @@ namespace ASC.Api.Mail } /// - /// Create mail contact + /// Creates a mail contact with the parameters specified in the request. /// - /// Contact's name - /// Description of contact + /// Contact name + /// Contact description /// List of emails /// List of phone numbers - /// Information about created contact - /// Create mail contact + /// Information about created contact + /// Create a mail contact /// Contacts [Create(@"contact/add")] public MailContactData CreateContact(string name, string description, List emails, List phoneNumbers) @@ -139,11 +139,11 @@ namespace ASC.Api.Mail } /// - /// Removes selected mail contacts + /// Removes the mail contacts with the IDs specified in the request. /// - /// List of mail contact ids - /// List of removed mail contact ids - /// Remove mail contact + /// List of mail contact IDs + /// List of removed mail contact IDs + /// Remove mail contacts /// Contacts [Update(@"contacts/remove")] public IEnumerable RemoveContacts(List ids) @@ -157,15 +157,15 @@ namespace ASC.Api.Mail } /// - /// Updates the existing mail contact + /// Updates a mail contact with the ID specified in the request. /// - /// id of mail contact - /// Contact's name - /// Description of contact - /// List of emails - /// List of phone numbers - /// Information about updated contact - /// Update mail contact + /// Mail contact ID + /// New contact name + /// New contact description + /// New list of emails + /// New list of phone numbers + /// Information about updated contact + /// Update a mail contact /// Contacts [Update(@"contact/update")] public MailContactData UpdateContact(int id, string name, string description, List emails, List phoneNumbers) @@ -184,13 +184,13 @@ namespace ASC.Api.Mail } /// - /// Returns list of crm entities linked with chain. Entity: contact, case or opportunity. + /// Returns a list of the CRM entities (contact, case or opportunity) linked with a chain. /// - /// Id of message included in the chain. It may be id any of messages included in the chain. - /// List of structures: {entity_id, entity_type, avatar_link, title} - /// Get crm linked entities + /// Message ID. It may be ID of any message included in the chain + /// List of entity information: {entity_id, entity_type, avatar_link, title} + /// Get the linked CRM entities /// Contacts - ///Exception happens when in parameters is invalid. Text description contains parameter name and text description. + ///Exception happens when the parameters are invalid. Text description contains parameter name and text description. [Read(@"crm/linked/entities")] public IEnumerable GetLinkedCrmEntitiesInfo(int message_id) { diff --git a/module/ASC.Api/ASC.Api.Mail/MailApi.Conversations.cs b/module/ASC.Api/ASC.Api.Mail/MailApi.Conversations.cs index 44053b4db..1f83e1e66 100644 --- a/module/ASC.Api/ASC.Api.Mail/MailApi.Conversations.cs +++ b/module/ASC.Api/ASC.Api.Mail/MailApi.Conversations.cs @@ -25,6 +25,7 @@ using System.Web; using ASC.Api.Attributes; using ASC.Mail; +using ASC.Mail.Core.Entities; using ASC.Mail.Data.Contracts; using ASC.Mail.Enums; using ASC.Specific; @@ -35,28 +36,28 @@ namespace ASC.Api.Mail public partial class MailApi { /// - /// Returns filtered conversations, if were changes since last check date + /// Returns the conversations with the parameters specified in the request if there were changes since last check date. /// - /// Folder ID - integer. 1 - inbox, 2 - sent, 3 - drafts, 4 - trash, 5 - spam. - /// Message unread status. bool flag. Search in unread(true), read(false) or all(null) messages. - /// Message attachments status. bool flag. Search messages with attachments(true), without attachments(false) or all(null) messages. - /// Begin search period date + /// Folder type: 1 - inbox, 2 - sent, 3 - drafts, 4 - trash, 5 - spam + /// Conversation status: unread (true), read (false) or all (null) messages + /// Defines if a conversation has attachments or not: with attachments (true), without attachments (false) or all (null) messages + /// Start search period date /// End search period date - /// Message has importance flag. bool flag. - /// Address to find 'From' field - /// Address to find 'To' field - /// Recipient mailbox id. - /// Messages tags. Id of tags linked with target messages. - /// Text to search in messages body and subject. - /// Count of messages on page - /// Sort order by date. String parameter: "ascending" - ascended, "descending" - descended. - /// Date from wich conversations search performed - /// Message from wich conversations search performed - /// Message has calendar flag. bool flag. - /// id of user's folder - /// - /// List of filtered chains - /// Gets filtered conversations + /// Important conversation or not + /// Mail address from which a letter came + /// Mail address to which a letter came + /// Recipient mailbox ID + /// IDs of tags linked to the target conversation + /// Text to search in conversation body and subject + /// Count of conversations on page + /// Sort order by date: "ascending" - ascended, "descending" - descended + /// Date from which conversation search performed + /// Message from which conversation search performed + /// Conversation has a calendar or not + /// User folder ID + /// Sorting direction of conversation list (previous or next) + /// List of filtered conversations + /// Get filtered conversations /// Conversations [Read(@"conversations")] public IEnumerable GetFilteredConversations(int? folder, @@ -82,6 +83,8 @@ namespace ASC.Api.Mail ? FolderType.UserFolder : folder.HasValue ? (FolderType)folder.Value : FolderType.Inbox; + SendUserAlive(folder ?? -1, tags); + var filter = new MailSearchFilterData { PrimaryFolder = primaryFolder, @@ -122,15 +125,16 @@ namespace ASC.Api.Mail } /// - /// Get list of messages linked into one chain (conversation) + /// Returns a list of messages linked in one chain (conversation). /// - /// ID of any message in the chain - /// Load content of all messages - /// Mark conversation as read - /// Flag specifies is needed to prepare html for FCKeditor - /// List messages linked in one chain + /// Get a conversation + /// ID of any message from the chain + /// Loads content of all messages + /// Marks a conversation as read + /// Specifies that HTML for the FCK editor needs to be prepared + /// List of messages linked in one chain /// Conversations - /// Exception happens when in parameters is invalid. Text description contains parameter name and text description. + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. [Read(@"conversation/{id:[0-9]+}")] public IEnumerable GetConversation(int id, bool? loadAll, bool? markRead, bool? needSanitize) { @@ -166,26 +170,27 @@ namespace ASC.Api.Mail } /// - /// Get previous or next conversation id. + /// Returns the previous or next conversation ID filtered with the parameters specified in the request. /// - /// Head message id of current conversation. - /// String parameter for determine prev or next conversation needed. "prev" for previous, "next" for next. - /// Folder ID - integer. 1 - inbox, 2 - sent, 5 - spam. - /// Message unread status. bool flag. Search in unread(true), read(false) or all(null) messages. - /// Message attachments status. bool flag. Search messages with attachments(true), without attachments(false) or all(null) messages. - /// Begin search period date + /// Get the previous or next conversation ID + /// Head message ID of the current conversation + /// Defines if the previous or next conversation is needed: "prev" for previous, "next" for next + /// Folder type: 1 - inbox, 2 - sent, 5 - spam + /// Message status: unread (true), read (false) or all (null) messages + /// Defines if a message has attachments or not: with attachments (true), without attachments (false) or all (null) messages + /// Start search period date /// End search period date - /// Message has importance flag. bool flag. - /// Address to find 'From' field - /// Address to find 'To' field - /// Recipient mailbox id. - /// Messages tags. Id of tags linked with target messages. - /// Text to search in messages body and subject. - /// Count on messages on page - /// Sort order by date. String parameter: "ascending" - ascended, "descending" - descended. - /// Message has with_calendar flag. bool flag. - /// id of user's folder - /// Head message id of previous or next conversation. + /// Important message or not + /// Mail address from which a letter came + /// Mail address to which a letter came + /// Recipient mailbox ID + /// IDs of tags linked to the target message + /// Text to search in message body and subject + /// Count of messages on page + /// Sort order by date: "ascending" - ascended, "descending" - descended + /// Message has a calendar or not + /// User folder ID + /// Head message ID of the previous or next conversation /// Conversations [Read(@"conversation/{id:[0-9]+}/{direction:(next|prev)}")] public long GetPrevNextConversationId(int id, @@ -236,13 +241,13 @@ namespace ASC.Api.Mail } /// - /// Moves conversation specified in ids to the folder. + /// Moves conversations with the IDs specified in the request to the selected folder. /// - /// List of mesasges ids from conversations. - /// Folder ID - integer. 1 - inbox, 2 - sent, 3 - drafts, 4 - trash, 5 - spam. - /// User Folder Id - /// List of mesasges ids from conversations. - /// Move conversations to folder + /// List of message IDs from conversations + /// Folder type: 1 - inbox, 2 - sent, 3 - drafts, 4 - trash, 5 - spam + /// User folder ID + /// List of message IDs from conversations + /// Move conversations to the folder /// Conversations [Update(@"conversations/move")] public IEnumerable MoveConversations(List ids, int folder, uint? userFolderId = null) @@ -257,9 +262,12 @@ namespace ASC.Api.Mail MailEngineFactory.ChainEngine.SetConversationsFolder(ids, toFolder, userFolderId); + SendUserActivity(ids, MailUserAction.MoveTo, folder); + if (toFolder != FolderType.Spam) return ids; + //TODO: Try to move message (IMAP only) to spam folder on original server (need new separated operation) var scheme = HttpContext.Current == null ? Uri.UriSchemeHttp : HttpContext.Current.Request.GetUrlRewriter().Scheme; @@ -269,12 +277,12 @@ namespace ASC.Api.Mail } /// - /// Restores all the conversations previously moved to specific folders to their original folders. + /// Restores all the conversations previously moved to the specific folders to their original folders. /// - /// List of conversation ids for restore. - /// send messages tp spam training - /// List of restored conversations ids - /// Restore conversations to original folders + /// List of conversation IDs + /// Sends messages to the spam training or not + /// List of restored conversation IDs + /// Restore conversations /// Conversations [Update(@"conversations/restore")] public IEnumerable RestoreConversations(List ids, bool learnSpamTrainer = false) @@ -294,10 +302,10 @@ namespace ASC.Api.Mail } /// - /// Removes conversations from folders + /// Removes conversations with the IDs specified in the request from the folders. /// - /// List of conversation ids for remove. - /// List of removed conversation ids + /// List of conversation IDs + /// List of removed conversation IDs /// Remove conversations /// Conversations [Update(@"conversations/remove")] @@ -306,17 +314,19 @@ namespace ASC.Api.Mail if (!ids.Any()) throw new ArgumentException(@"Empty ids collection", "ids"); + SendUserActivity(ids, MailUserAction.SetAsDeleted); + MailEngineFactory.ChainEngine.DeleteConversations(TenantId, Username, ids); return ids; } /// - /// Sets the status for the conversations specified by ids. + /// Sets a status to the conversations with the IDs specified in the request. /// - /// List of conversation ids for status changing. - /// String parameter specifies status for changing. Values: "read", "unread", "important" and "normal" - /// List of status changed conversations. - /// Set conversations status + /// List of conversation IDs + /// New status ("read", "unread", "important" or "normal") + /// List of conversations with changed status + /// Set a conversation status /// Conversations [Update(@"conversations/mark")] public IEnumerable MarkConversations(List ids, string status) @@ -324,36 +334,45 @@ namespace ASC.Api.Mail if (!ids.Any()) throw new ArgumentException(@"Empty ids collection", "ids"); + MailUserAction mailUserAction = MailUserAction.Nothing; + switch (status) { case "read": MailEngineFactory.MessageEngine.SetUnread(ids, false, true); + mailUserAction = MailUserAction.SetAsRead; break; case "unread": MailEngineFactory.MessageEngine.SetUnread(ids, true, true); + mailUserAction = MailUserAction.SetAsUnread; break; case "important": MailEngineFactory.ChainEngine.SetConversationsImportanceFlags(TenantId, Username, true, ids); + mailUserAction = MailUserAction.SetAsImportant; break; case "normal": MailEngineFactory.ChainEngine.SetConversationsImportanceFlags(TenantId, Username, false, ids); + mailUserAction = MailUserAction.SetAsNotImpotant; break; } + + SendUserActivity(ids, mailUserAction); + return ids; } /// - /// Add the specified tag to conversations. + /// Adds a tag specified in the request to the conversations. /// - /// Tag id for adding. - /// List of conversation ids for tag adding. - /// Added tag_id - /// Add tag to conversations + /// Tag ID + /// List of conversation IDs + /// Tag ID + /// Add a tag to the conversations /// Conversations - ///Exception happens when in parameters is invalid. Text description contains parameter name and text description. + ///Exception happens when the parameters are invalid. Text description contains parameter name and text description. [Update(@"conversations/tag/{tag_id}/set")] public int SetConversationsTag(int tag_id, List messages) { @@ -366,12 +385,12 @@ namespace ASC.Api.Mail } /// - /// Removes the specified tag from conversations. + /// Removes a tag specified in the request from the conversations. /// - /// Tag id to removing. - /// List of conversation ids for tag removing. - /// Removed tag_id - /// Remove tag from conversations + /// Tag ID + /// List of conversation IDs + /// Tag ID + /// Remove a tag from the conversations /// Conversations ///Exception happens when in parameters is invalid. Text description contains parameter name and text description. [Update(@"conversations/tag/{tag_id}/unset")] @@ -386,15 +405,15 @@ namespace ASC.Api.Mail } /// - /// Marks conversation as CRM linked. All new mail will be added to CRM history. + /// Links a conversation to CRM. All the new mails will be added to the CRM history. /// - /// Id of any messages from the chain - /// List of CrmContactEntity. List item format: {entity_id: 0, entity_type: 0}. - /// Entity types: 1 - Contact, 2 - Case, 3 - Opportunity. + /// Link a conversation to CRM + /// ID of any message from the chain + /// List of CRM contact entities in the following format: {entity_id: 0, entity_type: 0}. + /// Entity types: 1 - Contact, 2 - Case, 3 - Opportunity /// - /// none /// Conversations - /// Exception happens when in parameters is invalid. Text description contains parameter name and text description. + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. [Update(@"conversations/crm/link")] public void LinkConversationToCrm(int id_message, IEnumerable crm_contact_ids) { @@ -411,15 +430,15 @@ namespace ASC.Api.Mail } /// - /// Marks conversation as CRM linked. All new mail will be added to CRM history. + /// Marks a conversation as CRM linked. All the new mails will be added to the CRM history. /// - /// Id of any messages from the chain - /// List of CrmContactEntity. List item format: {entity_id: 0, entity_type: 0}. - /// Entity types: 1 - Contact, 2 - Case, 3 - Opportunity. + /// Mark a conversation as CRM linked + /// ID of any messages from the chain + /// List of CRM contact entities in the following format: {entity_id: 0, entity_type: 0}. + /// Entity types: 1 - Contact, 2 - Case, 3 - Opportunity /// - /// none /// Conversations - /// Exception happens when in parameters is invalid. Text description contains parameter name and text description. + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. [Update(@"conversations/crm/mark")] public void MarkConversationAsCrmLinked(int id_message, IEnumerable crm_contact_ids) { @@ -432,15 +451,15 @@ namespace ASC.Api.Mail } /// - /// Method tears conversation link with crm. + /// Unmarks a conversation as CRM linked. /// - /// Id of any messages from the chain - /// List of CrmContactEntity. List item format: {entity_id: 0, entity_type: 0}. - /// Entity types: 1 - Contact, 2 - Case, 3 - Opportunity. + /// Unmark a conversation as CRM linked + /// ID of any message from the chain + /// List of CRM contact entities in the following format: {entity_id: 0, entity_type: 0}. + /// Entity types: 1 - Contact, 2 - Case, 3 - Opportunity /// - /// none /// Conversations - /// Exception happens when in parameters is invalid. Text description contains parameter name and text description. + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. [Update(@"conversations/crm/unmark")] public void UnmarkConversationAsCrmLinked(int id_message, IEnumerable crm_contact_ids) { @@ -453,12 +472,13 @@ namespace ASC.Api.Mail } /// - /// Method checks is chain crm linked by message_id. + /// Checks if a conversation is CRM linked or not by message ID. /// - /// Id of any messages from the chain - /// MailCrmStatus + /// Check a conversation CRM status + /// ID of any message from the chain + /// Conversation CRM status /// Conversations - /// Exception happens when in parameters is invalid. Text description contains parameter name and text description. + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. [Read(@"conversations/link/crm/status")] public MailCrmStatus IsConversationLinkedWithCrm(int message_id) { diff --git a/module/ASC.Api/ASC.Api.Mail/MailApi.Filters.cs b/module/ASC.Api/ASC.Api.Mail/MailApi.Filters.cs index 7b8a8f790..89041b041 100644 --- a/module/ASC.Api/ASC.Api.Mail/MailApi.Filters.cs +++ b/module/ASC.Api/ASC.Api.Mail/MailApi.Filters.cs @@ -29,10 +29,10 @@ namespace ASC.Api.Mail public partial class MailApi { /// - /// Returns list of the tags used in Mail + /// Returns a list of filters used in all the mails. /// - /// Filters list. Filters represented as JSON. - /// Get filters list + /// List of filters which is represented as JSON + /// Get filters /// Filters [Read(@"filters")] public IEnumerable GetFilters() @@ -42,13 +42,13 @@ namespace ASC.Api.Mail } /// - /// Creates a new filter + /// Creates a new filter with the parameters specified in the request. /// - /// + /// Filter parameters: ID, name, position, enabled, conditions, actions, options /// Filter - /// Create filter + /// Create a filter /// Filters - /// Exception happens when in parameters is invalid. Text description contains parameter name and text description. + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. [Create(@"filters")] public MailSieveFilterData CreateFilter(MailSieveFilterData filter) { @@ -58,13 +58,13 @@ namespace ASC.Api.Mail } /// - /// Updates the selected filter + /// Updates the selected filter with the parameters specified in the request. /// - /// + /// New filter parameters: ID, name, position, enabled, conditions, actions, options /// Updated filter - /// Update filter + /// Update a filter /// Filters - /// Exception happens when in parameters is invalid. Text description contains parameter name and text description. + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. [Update(@"filters")] public MailSieveFilterData UpdateFilter(MailSieveFilterData filter) { @@ -74,13 +74,13 @@ namespace ASC.Api.Mail } /// - /// Deletes the selected filter + /// Deletes a filter with the ID specified in the request. /// - /// Filter id - /// Deleted Filter id - /// Delete filter + /// Filter ID + /// Filter ID + /// Delete a filter /// Filters - /// Exception happens when in parameters is invalid. Text description contains parameter name and text description. + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. [Delete(@"filters/{id}")] public int DeleteFilter(int id) { @@ -89,15 +89,15 @@ namespace ASC.Api.Mail } /// - /// Check filter result + /// Checks the results of a filter specified in the request. /// - /// + /// Filter parameters: ID, name, position, enabled, conditions, actions, options /// Page number - /// Number of messages on page - /// List messages - /// Check filter + /// Number of messages on the page + /// List of messages + /// Check filter results /// Filters - /// Exception happens when in parameters is invalid. Text description contains parameter name and text description. + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. [Read(@"filters/check")] public List CheckFilter(MailSieveFilterData filter, int? page, int? pageSize) { @@ -116,13 +116,13 @@ namespace ASC.Api.Mail } /// - /// Apply filter to existing messages + /// Applies a filter to the existing messages. /// - /// Filter id - /// MailOperationResult object - /// Check filter + /// Filter ID + /// Mail operation status + /// Apply a filter /// Filters - /// Exception happens when in parameters is invalid. Text description contains parameter name and text description. + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. [Read(@"filters/{id}/apply")] public MailOperationStatus ApplyFilter(int id) { diff --git a/module/ASC.Api/ASC.Api.Mail/MailApi.Folders.cs b/module/ASC.Api/ASC.Api.Mail/MailApi.Folders.cs index 8f581e0e2..b327b8b96 100644 --- a/module/ASC.Api/ASC.Api.Mail/MailApi.Folders.cs +++ b/module/ASC.Api/ASC.Api.Mail/MailApi.Folders.cs @@ -34,10 +34,10 @@ namespace ASC.Api.Mail public partial class MailApi { /// - /// Returns the list of default folders + /// Returns a list of default folders. /// - /// Folders list - /// Get folders + /// List of default folders + /// Get the default folders /// Folders [Read(@"folders")] public IEnumerable GetFolders() @@ -45,6 +45,8 @@ namespace ASC.Api.Mail if (!Defines.IsSignalRAvailable) MailEngineFactory.AccountEngine.SetAccountsActivity(); + SendUserActivity(new List()); + return MailEngineFactory.FolderEngine.GetFolders() .Where(f => f.id != FolderType.Sending) .ToList() @@ -52,11 +54,12 @@ namespace ASC.Api.Mail } /// - /// Removes all the messages from the folder. Trash or Spam. + /// Removes all the messages from the trash or spam folder. /// - /// Selected folder id. Trash - 4, Spam 5. - /// Remove all messages from folder + /// Folder ID: 4 - Trash, 5 - Spam + /// Remove folder messages /// Folders + /// Folder ID [Delete(@"folders/{folderid:[0-9]+}/messages")] public int RemoveFolderMessages(int folderid) { @@ -72,10 +75,10 @@ namespace ASC.Api.Mail /// - /// Recalculate folders counters + /// Recalculates folder counters. /// - /// MailOperationResult object - /// Get folders + /// Operation status + /// Recalculate folders /// Folders /// false [Read(@"folders/recalculate")] @@ -85,12 +88,12 @@ namespace ASC.Api.Mail } /// - /// Returns the list of user folders + /// Returns a list of user folders with the IDs specified in the request. /// - /// List of folder's id - /// Selected parent folder id (root level equals 0) - /// Folders list - /// Get folders + /// List of folder IDs + /// Parent folder ID (root level equals to 0) + /// List of folders + /// Get the user folders /// Folders [Read(@"userfolders")] public IEnumerable GetUserFolders(List ids, uint? parentId) @@ -100,14 +103,14 @@ namespace ASC.Api.Mail } /// - /// Create user folder + /// Creates a user folder with the name specified in the request. /// /// Folder name - /// Parent folder id (default = 0) - /// Folders list - /// Create folder + /// Parent folder ID (default = 0) + /// List of folders + /// Create a folder /// Folders - /// Exception happens when in parameters is invalid. Text description contains parameter name and text description. + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. [Create(@"userfolders")] public MailUserFolderData CreateUserFolder(string name, uint parentId = 0) { @@ -135,15 +138,15 @@ namespace ASC.Api.Mail } /// - /// Update user folder + /// Updates a user folder with the parameters specified in the request. /// - /// Folder id - /// new Folder name - /// new Parent folder id (default = 0) - /// Folders list - /// Update folder + /// Folder ID + /// New folder name + /// New parent folder ID (default = 0) + /// List of folders + /// Update a folder /// Folders - /// Exception happens when in parameters is invalid. Text description contains parameter name and text description. + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. [Update(@"userfolders/{id}")] public MailUserFolderData UpdateUserFolder(uint id, string name, uint? parentId = null) { @@ -171,13 +174,13 @@ namespace ASC.Api.Mail } /// - /// Delete user folder + /// Deletes a user folder with the ID specified in the request. /// - /// Folder id - /// Delete folder + /// Folder ID + /// Delete a folder /// Folders - /// Exception happens when in parameters is invalid. Text description contains parameter name and text description. - /// MailOperationResult object + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. + /// Operation status [Delete(@"userfolders/{id}")] public MailOperationStatus DeleteUserFolder(uint id) { @@ -195,11 +198,11 @@ namespace ASC.Api.Mail } /// - /// Returns the user folders by mail id + /// Returns a user folder by the mail ID specified in the request. /// - /// List of folder's id - /// User Folder - /// Get folder by mail id + /// Mail ID + /// User folder + /// Get a folder by mail ID /// Folders [Read(@"userfolders/bymail")] public MailUserFolderData GetUserFolderByMailId(uint mailId) diff --git a/module/ASC.Api/ASC.Api.Mail/MailApi.HelpCenter.cs b/module/ASC.Api/ASC.Api.Mail/MailApi.HelpCenter.cs index 3157f8d7c..20cc21dc5 100644 --- a/module/ASC.Api/ASC.Api.Mail/MailApi.HelpCenter.cs +++ b/module/ASC.Api/ASC.Api.Mail/MailApi.HelpCenter.cs @@ -25,11 +25,11 @@ namespace ASC.Api.Mail public partial class MailApi { /// - /// Returns the string with html of help center page + /// Returns the help center page HTML. /// - /// String with html of help center page - /// Get html of help center page - /// HelpCenter + /// String with the help center page HTML + /// Get help center page HTML + /// Help center [Read(@"helpcenter")] public string GetHelpCenterHtml() { diff --git a/module/ASC.Api/ASC.Api.Mail/MailApi.Images.cs b/module/ASC.Api/ASC.Api.Mail/MailApi.Images.cs index 6ca750312..dfd629d15 100644 --- a/module/ASC.Api/ASC.Api.Mail/MailApi.Images.cs +++ b/module/ASC.Api/ASC.Api.Mail/MailApi.Images.cs @@ -25,10 +25,10 @@ namespace ASC.Api.Mail public partial class MailApi { /// - /// Returns list of all trusted addresses for image displaying. + /// Returns a list of all the trusted addresses for image displaying. /// - /// Addresses list. Email adresses represented as string name@domain. - /// Get trusted addresses + /// List of addresses. Email adresses are represented as strings in the name@domain format + /// Get trusted image addresses /// Images [Read(@"display_images/addresses")] public IEnumerable GetDisplayImagesAddresses() @@ -37,13 +37,13 @@ namespace ASC.Api.Mail } /// - /// Add the address to trusted addresses. + /// Adds the image address specified in the request to the list of trusted image addresses. /// - /// Address for adding. - /// Added address - /// Add trusted address - /// Exception happens when in parameters is invalid. Text description contains parameter name and text description. - /// Images + /// Image address + /// Added image address + /// Add the trusted image address + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. + /// Images [Create(@"display_images/address")] public string AddDisplayImagesAddress(string address) { @@ -53,12 +53,12 @@ namespace ASC.Api.Mail } /// - /// Remove the address from trusted addresses. + /// Removes the image address specified in the request from the list of trusted image addresses. /// - /// Address for removing - /// Removed address - /// Remove from trusted addresses - /// Exception happens when in parameters is invalid. Text description contains parameter name and text description. + /// Image address + /// Removed image address + /// Remove the trusted image address + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. /// Images [Delete(@"display_images/address")] public string RemovevDisplayImagesAddress(string address) diff --git a/module/ASC.Api/ASC.Api.Mail/MailApi.MailService.cs b/module/ASC.Api/ASC.Api.Mail/MailApi.MailService.cs index 9c0ddf5b3..59f7a0019 100644 --- a/module/ASC.Api/ASC.Api.Mail/MailApi.MailService.cs +++ b/module/ASC.Api/ASC.Api.Mail/MailApi.MailService.cs @@ -30,6 +30,12 @@ namespace ASC.Api.Mail { public partial class MailApi { + /// + /// Returns the mail server information. + /// + /// Mail server information + /// Get the mail server information + /// Mail service /// false [Read("mailservice/get")] public object GetMailServerInfo() @@ -37,6 +43,17 @@ namespace ASC.Api.Mail return MailServiceHelper.GetMailServerInfo(); } + /// + /// Connects the mail server to the portal. + /// + /// Mail server IP + /// Mail server SQL IP + /// Mail server database + /// User + /// Password + /// Mail server information + /// Connect the mail server + /// Mail service /// false [Create("mailservice/connect")] public object ConnectMailServer(string ip, string sqlip, string database, string user, string password) @@ -95,6 +112,19 @@ namespace ASC.Api.Mail } } + /// + /// Saves the mail server information specified in the request. + /// + /// Mail server IP + /// Mail server SQL IP + /// Mail server database + /// User + /// Password + /// Token + /// Mail server host + /// Operation result + /// Save the mail server information + /// Mail service /// false [Create("mailservice/save")] public object SaveMailServerInfo(string ip, string sqlip, string database, string user, string password, string token, string host) @@ -144,6 +174,16 @@ namespace ASC.Api.Mail } } + /// + /// Connects the mail server and saves its information. + /// + /// Mail server host + /// Mail server database + /// User + /// Password + /// Operation result + /// Connect the mail server and save its information + /// Mail service /// false [Create("mailservice/connectandsave")] public object ConnectAndSaveMailServerInfo(string host, string database, string user, string password) @@ -199,6 +239,17 @@ namespace ASC.Api.Mail } } + /// + /// Connects the mail server and saves its partitional information. + /// + /// Mail server host + /// MySQL host + /// MySQL database + /// MySQL user + /// MySQL password + /// Operation result + /// Connect the mail server and save its partitional information + /// Mail service /// false [Create("mailservice/connectandsavepartitional")] public object ConnectAndSavePartitionalMailServerInfo(string mailHost, string mysqlHost, string mysqlDatabase, string mysqlUser, string mysqlPassword) diff --git a/module/ASC.Api/ASC.Api.Mail/MailApi.Messages.cs b/module/ASC.Api/ASC.Api.Mail/MailApi.Messages.cs index 93d97547c..9bcaa7bb6 100644 --- a/module/ASC.Api/ASC.Api.Mail/MailApi.Messages.cs +++ b/module/ASC.Api/ASC.Api.Mail/MailApi.Messages.cs @@ -27,6 +27,7 @@ using ASC.Api.Attributes; using ASC.Api.Exceptions; using ASC.Mail; using ASC.Mail.Core.Engine; +using ASC.Mail.Core.Entities; using ASC.Mail.Data.Contracts; using ASC.Mail.Enums; using ASC.Mail.Exceptions; @@ -42,25 +43,25 @@ namespace ASC.Api.Mail public partial class MailApi { /// - /// Returns the filtered messages in case there were changes since last check date + /// Returns the messages with the parameters specified in the request if there were changes since last check date. /// /// Folder ID - /// Message unread status - /// Message with attachments status - /// Period start date - /// Period end date - /// Message with importance flag - /// Address to find 'From' field - /// Address to find 'To' field - /// Recipient mailbox id - /// Message tags - /// Text to search in messages + /// Message status: unread (true), read (false) or all (null) messages + /// Defines if a message has attachments or not: with attachments (true), without attachments (false) or all (null) messages + /// Start search period date + /// End search period date + /// Important message or not + /// Mail address from which a letter came + /// Mail address to which a letter came + /// Recipient mailbox ID + /// IDs of tags linked to the target message + /// Text to search in message body and subject /// Page number - /// Message has calendar flag. bool flag. - /// Number of messages on page - /// id of user's folder - /// Sort order - /// Messages list + /// Message has a calendar or not + /// Count of messages on page + /// User folder ID + /// Sort order by date: "ascending" - ascended, "descending" - descended + /// List of filtered messages /// Get filtered messages /// Messages [Read(@"messages")] @@ -85,6 +86,8 @@ namespace ASC.Api.Mail ? FolderType.UserFolder : folder.HasValue ? (FolderType)folder.Value : FolderType.Inbox; + SendUserAlive(folder ?? -1, tags); + var filter = new MailSearchFilterData { PrimaryFolder = primaryFolder, @@ -116,17 +119,17 @@ namespace ASC.Api.Mail } /// - /// Returns the detailed information about message with the ID specified in the request + /// Returns the detailed information about a message with the ID specified in the request. /// /// Message ID - /// Unblock suspicious content or not - /// Flag specifies is needed to prepare html for FCKeditor - /// Mark message as read - /// MailMessageItem - /// Get message + /// Unblocks suspicious content or not + /// Specifies if HTML for the FCK editor needs to be prepared or not + /// Marks a message as read or not + /// Message information + /// Get a message /// Messages - /// Exception happens when in parameters is invalid. Text description contains parameter name and text description. - /// Exception happens when message with specified id wasn't found. + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. + /// Exception happens when message with the specified ID wasn't found. [Read(@"messages/{id:[0-9]+}")] public MailMessage GetMessage(int id, bool? loadImages, bool? needSanitize, bool? markRead) { @@ -159,8 +162,12 @@ namespace ASC.Api.Mail if (item.WasNew && markRead.HasValue && markRead.Value) { - MailEngineFactory.MessageEngine.SetUnread(new List { item.Id }, false); + var ids = new List { item.Id }; + + MailEngineFactory.MessageEngine.SetUnread(ids, false); item.IsNew = false; + + SendUserActivity(ids, MailUserAction.SetAsRead); } if (needSanitizeHtml) @@ -186,11 +193,10 @@ namespace ASC.Api.Mail } /// - /// Reassigns drafts/templates to selected email. + /// Reassigns drafts/templates to the selected email. /// - /// Folder id + /// Folder ID /// Email to which messages will be reassigned - /// none /// Reassign drafts/templates /// Messages [Update(@"messages/reassign")] @@ -240,27 +246,27 @@ namespace ASC.Api.Mail } /// - /// Get previous or next message id. U + /// Returns the previous or next message ID filtered with the parameters specified in the request.. /// - /// Head message id of current conversation. - /// String parameter for determine prev or next conversation needed. "prev" for previous, "next" for next. - /// Folder ID - integer. 1 - inbox, 2 - sent, 5 - spam. - /// Message unread status. bool flag. Search in unread(true), read(false) or all(null) messages. - /// Message attachments status. bool flag. Search messages with attachments(true), without attachments(false) or all(null) messages. - /// Begin search period date + /// Head message ID of current conversation + /// Defines if the previous or next conversation is needed: "prev" for previous, "next" for next + /// Folder type: 1 - inbox, 2 - sent, 5 - spam + /// Message status: unread (true), read (false) or all (null) messages + /// Defines if a conversation has attachments or not: with attachments (true), without attachments (false) or all (null) messages + /// Start search period date /// End search period date - /// Message has importance flag. bool flag. - /// Address to find 'From' field - /// Address to find 'To' field - /// Recipient mailbox id. - /// Messages tags. Id of tags linked with target messages. - /// Text to search in messages body and subject. - /// Count on messages on page - /// Sort order by date. String parameter: "ascending" - ascended, "descending" - descended. - /// Message has with_calendar flag. bool flag. - /// id of user's folder - /// Previous or next message id - /// Get previous or next message id + /// Important message or not + /// Mail address from which a letter came + /// Mail address to which a letter came + /// Recipient mailbox ID + /// IDs of tags linked to the target message + /// Text to search in message body and subject + /// Count of messages on page + /// Sort order by date: "ascending" - ascended, "descending" - descended + /// Message has a calendar or not + /// User folder ID + /// Previous or next message ID + /// Get the previous or next message ID /// Messages [Read(@"messages/{id:[0-9]+}/{direction:(next|prev)}")] public long GetPrevNextMessageId(int id, @@ -314,14 +320,14 @@ namespace ASC.Api.Mail } /// - /// Deletes the selected attachment from the message with the ID specified in the request + /// Deletes the selected attachment from the message with the ID specified in the request. /// - /// The message id which attachment will be removed. - /// Specifies attachment id for deleting. - /// The message id which removed an attachment - /// Delete attachment from message + /// Message ID + /// Attachment ID + /// The message ID which attachment was removed + /// Delete an attachment from the message /// Messages - /// Exception happens when in parameters is invalid. Text description contains parameter name and text description. + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. [Delete(@"messages/{messageid:[0-9]+}/attachments/{attachmentid:[0-9]+}")] public int DeleteMessageAttachment(int messageid, int attachmentid) { @@ -338,12 +344,12 @@ namespace ASC.Api.Mail } /// - /// Sets the status for messages specified by ids. + /// Sets a status to the messages with the IDs specified in the request. /// - /// List of messages ids for status changing. - /// String parameter specifies status for changing. Values: "read", "unread", "important" and "normal" + /// List of message IDs + /// Message status: "read", "unread", "important" and "normal" /// List of messages with changed status - /// Set message status + /// Set a message status /// Messages [Update(@"messages/mark")] public IEnumerable MarkMessages(List ids, string status) @@ -351,33 +357,47 @@ namespace ASC.Api.Mail if (!ids.Any()) throw new ArgumentException(@"Empty ids collection", "ids"); + MailUserAction mailUserAction = MailUserAction.Nothing; + switch (status) { case "read": MailEngineFactory.MessageEngine.SetUnread(ids, false); + mailUserAction = MailUserAction.SetAsRead; break; case "unread": MailEngineFactory.MessageEngine.SetUnread(ids, true); + mailUserAction = MailUserAction.SetAsUnread; break; case "important": MailEngineFactory.MessageEngine.SetImportant(ids, true); + mailUserAction = MailUserAction.SetAsImportant; break; case "normal": MailEngineFactory.MessageEngine.SetImportant(ids, false); + mailUserAction = MailUserAction.SetAsNotImpotant; + break; + + case "receiptProcessed": + MailEngineFactory.MessageEngine.ReceiptStatus(ids, false); + mailUserAction = MailUserAction.ReceiptStatusChanged; break; } + + SendUserActivity(ids, mailUserAction); + return ids; } /// - /// Restores the messages to their original folders + /// Restores the messages with the IDs specified in the request to their original folders. /// - /// List of conversation ids for restore. - /// List of restored messages ids - /// Restore messages to original folders + /// List of message IDs + /// List of restored message IDs + /// Restore messages /// Messages [Update(@"messages/restore")] public IEnumerable RestoreMessages(List ids) @@ -393,13 +413,13 @@ namespace ASC.Api.Mail } /// - /// Moves the messages to the specified folder + /// Moves the messages to a folder with the ID specified in the request. /// - /// List of mesasges ids. - /// Folder ID - integer. 1 - inbox, 2 - sent, 3 - drafts, 4 - trash, 5 - spam. - /// User Folder Id - /// List of moved messages ids. - /// Move message to folder + /// List of message IDs + /// Folder type: 1 - inbox, 2 - sent, 3 - drafts, 4 - trash, 5 - spam + /// User folder ID + /// List of moved message IDs + /// Move messages to the folder /// Messages [Update(@"messages/move")] public IEnumerable MoveMessages(List ids, int folder, uint? userFolderId = null) @@ -414,6 +434,8 @@ namespace ASC.Api.Mail MailEngineFactory.MessageEngine.SetFolder(ids, toFolder, userFolderId); + SendUserActivity(ids, MailUserAction.MoveTo, folder); + if (toFolder == FolderType.Spam || toFolder == FolderType.Sent || toFolder == FolderType.Inbox) MailEngineFactory.OperationEngine.ApplyFilters(ids); @@ -421,26 +443,26 @@ namespace ASC.Api.Mail } /// - /// Sends the message with the ID specified in the request + /// Sends a message with the ID specified in the request. /// - /// Message id which will be saved or 0. - /// From email. Format: Name<name@domain> - /// List of "to" emails. Format: Name<name@domain> - /// List of "cc" emails. Format: Name<name@domain> - /// List of "bcc" emails. Format: Name<name@domain> - /// Message id to which the reply answer - /// Importanse flag. Values: true - important, false - not important. + /// Message ID which will be sent or 0 + /// Mail address from which a letter will be sent. + /// List of mail addresses to which a letter will be sent. + /// List of "cc" mail addresses. + /// List of "bcc" mail addresses. + /// Message ID to which this message replies + /// Important message or not: true - important, false - not important /// Message subject - /// List of tags id added to message - /// Message body as html string. - /// List of attachments represented as MailAttachment object - /// Share mode for attached file links - /// Calendar event ical-format for sending - /// Indicate that message is autoreply or not - /// Add request Return-Receipt-To header - /// Add request Disposition-Notification-To header - /// message id - /// Send message + /// List of tag IDs added to the message + /// Message body as HTML string + /// List of message attachments + /// Share mode for the links of attached files + /// Calendar event in the iCal format for sending + /// Specifies that this message is autoreply or not + /// Adds a request with the Return-Receipt-To header + /// Adds a request with the Disposition-Notification-To header + /// Message ID + /// Send a message /// Messages [Update(@"messages/send")] public long SendMessage(int id, @@ -514,6 +536,56 @@ namespace ASC.Api.Mail } } + [Update(@"messages/simpleSend")] + public bool SimpleSend( + string from, + List to, + string subject, + string body, + bool isReceipt) + { + Thread.CurrentThread.CurrentCulture = CurrentCulture; + Thread.CurrentThread.CurrentUICulture = CurrentCulture; + + var daemonLabels = + new DraftEngine.DeliveryFailureMessageTranslates( + Defines.MailDaemonEmail, + MailApiResource.DeliveryFailureSubject, + MailApiResource.DeliveryFailureAutomaticMessage, + MailApiResource.DeliveryFailureMessageIdentificator, + MailApiResource.DeliveryFailureRecipients, + MailApiResource.DeliveryFailureRecommendations, + MailApiResource.DeliveryFailureBtn, + MailApiResource.DeliveryFailureFAQInformation, + MailApiResource.DeliveryFailureReason); + + return MailEngineFactory.DraftEngine.SimpleSend( + from, + to, + subject, + body, + isReceipt, + daemonLabels); + } + + /// + /// Saves a message with the ID specified in the request. + /// + /// Message ID which will be saved or 0 + /// Mail address from which a letter will be sent. + /// List of mail addresses to which the letter will be sent. + /// List of "cc" mail addresses. + /// List of "bcc" mail addresses. + /// Message ID to which this message replies + /// Important message or not: true - important, false - not important + /// Message subject + /// List of tag IDs added to the message + /// Message body as HTML string + /// List of message attachments + /// Calendar event in the iCal format for sending + /// Saved message ID + /// Save a message + /// Messages /// false [Obsolete] [Update(@"messages/save")] @@ -545,22 +617,22 @@ namespace ASC.Api.Mail } /// - /// Saves the message with the ID specified in the request + /// Saves a message with the ID specified in the request as a draft. /// - /// Message id which will be saved or 0. - /// From email. Format: Name<name@domain> - /// List of "to" emails. Format: Name<name@domain> - /// List of "cc" emails. Format: Name<name@domain> - /// List of "bcc" emails. Format: Name<name@domain> - /// Message id to which the reply answer - /// Importanse flag. Values: true - important, false - not important. + /// Message ID which will be saved or 0 + /// Mail address from which a letter will be sent. + /// List of mail addresses to which a letter will be sent. + /// List of "cc" mail addresses. + /// List of "bcc" mail addresses. + /// Message ID to which this message replies + /// Important message or not: true - important, false - not important /// Message subject - /// List of tags id added to message - /// Message body as html string. - /// List of attachments represented as MailAttachment object - /// Calendar event ical-format for sending - /// Saved message id - /// SaveToDraft message + /// List of tag IDs added to the message + /// Message body as HTML string + /// List of message attachments + /// Calendar event in the iCal format for sending + /// Saved message ID + /// Save a message as a draft /// Messages [Update(@"drafts/save")] public MailMessage SaveMessage(int id, @@ -618,22 +690,22 @@ namespace ASC.Api.Mail } /// - /// Saves the template with the ID specified in the request + /// Saves a template with the ID specified in the request. /// - /// Template id which will be saved. - /// From email. Format: Name<name@domain> - /// List of "to" emails. Format: Name<name@domain> - /// List of "cc" emails. Format: Name<name@domain> - /// List of "bcc" emails. Format: Name<name@domain> - /// Template id to which the reply answer - /// Importanse flag. Values: true - important, false - not important. - /// Template subject - /// List of tags id added to message - /// Template body as html string. - /// List of attachments represented as MailAttachment object - /// Calendar event ical-format for sending - /// Saved template id - /// SaveToTemplate message + /// Template ID which will be saved + /// Mail address from which a letter will be sent. + /// List of mail addresses to which a letter will be sent. + /// List of "cc" mail addresses. + /// List of "bcc" mail addresses. + /// Message ID to which this message replies + /// Important message or not: true - important, false - not important + /// Message subject + /// List of tag IDs added to the message + /// Message body as HTML string + /// List of message attachments + /// Calendar event in the iCal format for sending + /// Saved template ID + /// Save a message as a template /// Templates [Update(@"templates/save")] public MailMessage SaveTemplate(int id, string from, List to, List cc, List bcc, string mimeReplyToId, bool importance, string subject, @@ -678,10 +750,10 @@ namespace ASC.Api.Mail } /// - /// Removes the selected messages + /// Removes messages with the IDs specified in the request. /// - /// List of messages ids for remove. - /// List of removed messages ids + /// List of message IDs + /// List of removed message IDs /// Remove messages /// Messages [Update(@"messages/remove")] @@ -692,14 +764,16 @@ namespace ASC.Api.Mail MailEngineFactory.MessageEngine.SetRemoved(ids); + SendUserActivity(ids, MailUserAction.SetAsDeleted); + return ids; } /// - /// Returns the message template. Message teplate - empty message JSON. + /// Returns a message template - empty message in the JSON format. /// - /// Empty MailMessageItem - /// Get message template + /// Empty message in the JSON format + /// Get a message template /// Messages [Read(@"messages/template")] public MailMessage GetMessageTemplate() @@ -708,16 +782,16 @@ namespace ASC.Api.Mail } /// - /// Attaches Teamlab document to the specified message + /// Attaches the Teamlab document to the message with the ID specified in the request. /// - /// Message id for adding attachment - /// Teamlab document id. + /// Message ID + /// Teamlab document ID /// Teamlab document version - /// Need save to temp for templates - /// Attached document as MailAttachment object - /// Attach Teamlab document + /// Specifies if this message needs to be saved as a template or not + /// Attached document + /// Attach the Teamlab document /// Messages - /// Exception happens when in parameters is invalid. Text description contains parameter name and text description. + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. [Create(@"messages/{id:[0-9]+}/document")] public MailAttachmentData AttachDocument(int id, string fileId, string version, bool needSaveToTemp) { @@ -767,13 +841,13 @@ namespace ASC.Api.Mail } /// - /// Export mail to CRM relations history for some entities + /// Exports a mail to the CRM relation history for some entities. /// - /// Id of any messages from the chain - /// List of CrmContactEntity. List item format: {entity_id: 0, entity_type: 0}. - /// Entity types: 1 - Contact, 2 - Case, 3 - Opportunity. + /// ID of any message from the chain + /// List of CRM contact entity IDs in the following format: {entity_id: 0, entity_type: 0}. + /// Entity types: 1 - Contact, 2 - Case, 3 - Opportunity /// - /// none + /// Export a message to CRM /// Messages [Update(@"messages/crm/export")] public void ExportMessageToCrm(int id_message, IEnumerable crm_contact_ids) diff --git a/module/ASC.Api/ASC.Api.Mail/MailApi.Settings.cs b/module/ASC.Api/ASC.Api.Mail/MailApi.Settings.cs index 437688d41..ff3bf1dd0 100644 --- a/module/ASC.Api/ASC.Api.Mail/MailApi.Settings.cs +++ b/module/ASC.Api/ASC.Api.Mail/MailApi.Settings.cs @@ -23,9 +23,9 @@ namespace ASC.Api.Mail public partial class MailApi { /// - /// Returns Common Settings + /// Returns the common settings. /// - /// MailCommonSettings object + /// Common settings /// Get common settings /// Settings [Read(@"settings")] @@ -36,10 +36,10 @@ namespace ASC.Api.Mail } /// - /// Returns EnableConversations flag + /// Returns the "Enable conversations" flag. /// - /// boolean - /// Get EnableConversations flag + /// Boolean value: True - the flag is enabled, False - the flag is disabled + /// Get the "Enable conversations" flag /// Settings [Read(@"settings/conversationsEnabled")] public bool GetEnableConversationFlag() @@ -49,10 +49,10 @@ namespace ASC.Api.Mail } /// - /// Set EnableConversations flag + /// Sets the "Enable Conversations" flag. /// - /// True or False value - /// Set EnableConversations flag + /// Specifies if the "Enable Conversations" flag is enabled or not + /// Set the "Enable Conversations" flag /// Settings [Update(@"settings/conversationsEnabled")] public void SetEnableConversationFlag(bool enabled) @@ -61,10 +61,10 @@ namespace ASC.Api.Mail } /// - /// Returns AlwaysDisplayImages flag + /// Returns the "Always display images" flag. /// - /// boolean - /// Get AlwaysDisplayImages flag + /// Boolean value: True - the flag is enabled, False - the flag is disabled + /// Get the "Always display images" flag /// Settings [Read(@"settings/alwaysDisplayImages")] public bool GetAlwaysDisplayImagesFlag() @@ -74,10 +74,10 @@ namespace ASC.Api.Mail } /// - /// Set AlwaysDisplayImages flag + /// Sets the "Always display images" flag. /// - /// True or False value - /// Set AlwaysDisplayImages flag + /// Specifies if the "Always display images" flag is enabled or not + /// Set the "Always display images" flag /// Settings [Update(@"settings/alwaysDisplayImages")] public void SetAlwaysDisplayImagesFlag(bool enabled) @@ -86,15 +86,15 @@ namespace ASC.Api.Mail } /// - /// Returns CacheUnreadMessages flag + /// Returns the "Cache unread messages" flag. /// - /// boolean - /// Get CacheUnreadMessages flag + /// Boolean value: True - the flag is enabled, False - the flag is disabled + /// Get the "Cache unread messages" flag /// Settings [Read(@"settings/cacheMessagesEnabled")] public bool GetCacheUnreadMessagesFlag() { - //TODO: Change cache algoritnm and restore it back + //TODO: Change cache algorithm and restore it back. /*var value = MailCommonSettings.CacheUnreadMessages; return value;*/ @@ -103,10 +103,10 @@ namespace ASC.Api.Mail } /// - /// Set CacheUnreadMessages flag + /// Sets the "Cache unread messages" flag. /// - /// True or False value - /// Set CacheUnreadMessages flag + /// Specifies if the "Cache unread messages" flag is enabled or not + /// Set the "Cache unread messages" flag /// Settings [Update(@"settings/cacheMessagesEnabled")] public void SetCacheUnreadMessagesFlag(bool enabled) @@ -115,10 +115,10 @@ namespace ASC.Api.Mail } /// - /// Returns GoNextAfterMove flag + /// Returns the "Go next after move" flag. /// - /// boolean - /// Get GoNextAfterMove flag + /// Boolean value: True - the flag is enabled, False - the flag is disabled + /// Get the "Go next after move" flag /// Settings [Read(@"settings/goNextAfterMoveEnabled")] public bool GetEnableGoNextAfterMoveFlag() @@ -128,10 +128,10 @@ namespace ASC.Api.Mail } /// - /// Set GoNextAfterMove flag + /// Sets the "Go next after move" flag. /// - /// True or False value - /// Set GoNextAfterMove flag + /// Specifies if the "Go next after move" flag is enabled or not + /// Set the "Go next after move" flag /// Settings [Update(@"settings/goNextAfterMoveEnabled")] public void SetEnableGoNextAfterMoveFlag(bool enabled) @@ -140,10 +140,10 @@ namespace ASC.Api.Mail } /// - /// Returns ReplaceMessageBody flag + /// Returns the "Replace message body" flag. /// - /// boolean - /// Get ReplaceMessageBody flag + /// Boolean value: True - the flag is enabled, False - the flag is disabled + /// Get the "Replace message body" flag /// Settings [Read(@"settings/replaceMessageBody")] public bool GetEnableReplaceMessageBodyFlag() @@ -153,10 +153,10 @@ namespace ASC.Api.Mail } /// - /// Set ReplaceMessageBody flag + /// Sets the "Replace message body" flag. /// - /// True or False value - /// Set ReplaceMessageBody flag + /// Specifies if the "Replace message body" flag is enabled or not + /// Set the "Replace message body" flag /// Settings [Update(@"settings/replaceMessageBody")] public void SetEnableReplaceMessageBodyFlag(bool enabled) diff --git a/module/ASC.Api/ASC.Api.Mail/MailApi.Signature.cs b/module/ASC.Api/ASC.Api.Mail/MailApi.Signature.cs index 6a2e3a12f..6996a299a 100644 --- a/module/ASC.Api/ASC.Api.Mail/MailApi.Signature.cs +++ b/module/ASC.Api/ASC.Api.Mail/MailApi.Signature.cs @@ -28,9 +28,11 @@ namespace ASC.Api.Mail public partial class MailApi { /// - /// This method needed for getting mailbox signature. + /// Returns a signature of a mailbox with the ID specified in the request. /// - /// + /// Get a signature + /// Signature + /// Mailbox ID /// Signature object [Read(@"signature/{mailbox_id:[0-9]+}")] public MailSignatureData GetSignature(int mailbox_id) @@ -46,11 +48,14 @@ namespace ASC.Api.Mail } /// - /// This method needed for update or create signature. + /// Updates or creates a signature of a mailbox with the ID specified in the request. /// - /// Id of updated mailbox. - /// New signature value. - /// New signature status. + /// Update a signature + /// Signature + /// Mailbox ID + /// New signature value + /// New signature status (active or not) + /// Updated signature object [Create(@"signature/update/{mailbox_id:[0-9]+}")] public MailSignatureData UpdateSignature(int mailbox_id, string html, bool is_active) { diff --git a/module/ASC.Api/ASC.Api.Mail/MailApi.Tags.cs b/module/ASC.Api/ASC.Api.Mail/MailApi.Tags.cs index 2abd440d4..7c7dbda5c 100644 --- a/module/ASC.Api/ASC.Api.Mail/MailApi.Tags.cs +++ b/module/ASC.Api/ASC.Api.Mail/MailApi.Tags.cs @@ -30,10 +30,10 @@ namespace ASC.Api.Mail public partial class MailApi { /// - /// Returns list of the tags used in Mail + /// Returns a list of all the tags used in the Mail module. /// - /// Tags list. Tags represented as JSON. - /// Get tags list + /// List of tags represented as JSON + /// Get tags /// Tags [Read(@"tags")] public IEnumerable GetTags() @@ -42,15 +42,15 @@ namespace ASC.Api.Mail } /// - /// Creates a new tag + /// Creates a new tag with the parameters specified in the request. /// - /// Tag name represented as string - /// Style identificator. With postfix will be added to tag css style whe it will represent. Specifies color of tag. - /// Specifies list of addresses tag associated with. - /// MailTag - /// Create tag + /// Tag name + /// Style identificator: a postfix which represents the css style (tag color) + /// List of addresses which tag is associated with + /// Mail tag + /// Create a tag /// Tags - /// Exception happens when in parameters is invalid. Text description contains parameter name and text description. + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. [Create(@"tags")] public MailTagData CreateTag(string name, string style, IEnumerable addresses) { @@ -69,16 +69,16 @@ namespace ASC.Api.Mail } /// - /// Updates the selected tag + /// Updates a tag with the ID specified in the request. /// - /// - /// Tag name represented as string - /// Style identificator. With postfix will be added to tag css style whe it will represent. Specifies color of tag. - /// Specifies list of addresses tag associated with. - /// Updated MailTag - /// Update tag + /// Tag ID + /// New tag name + /// New style identificator: a postfix which represents the css style (tag color) + /// New list of addresses which tag is associated with + /// Updated mail tag + /// Update a tag /// Tags - /// Exception happens when in parameters is invalid. Text description contains parameter name and text description. + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. [Update(@"tags/{id}")] public MailTagData UpdateTag(int id, string name, string style, IEnumerable addresses) { @@ -109,13 +109,13 @@ namespace ASC.Api.Mail } /// - /// Deletes the selected tag from TLMail + /// Deletes a tag with the ID specified in the request from TLMail. /// - /// Tag for deleting id - /// Deleted MailTag - /// Delete tag + /// Tag ID + /// Deleted mail tag + /// Delete a tag /// Tags - /// Exception happens when in parameters is invalid. Text description contains parameter name and text description. + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. [Delete(@"tags/{id}")] public int DeleteTag(int id) { @@ -129,14 +129,14 @@ namespace ASC.Api.Mail } /// - /// Adds the selected tag to the messages + /// Adds a tag with the ID specified in the request to the messages. /// - /// Tag for setting id - /// Messages id for setting. - /// Setted MailTag - /// Set tag to messages + /// Tag ID + /// List of message IDs + /// Added mail tag + /// Set a tag to the messages /// Tags - /// Exception happens when in parameters is invalid. Text description contains parameter name and text description. + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. [Update(@"tags/{id}/set")] public int SetTag(int id, List messages) { @@ -149,14 +149,14 @@ namespace ASC.Api.Mail } /// - /// Removes the specified tag from messages + /// Removes a tag with the ID specified in the request from the messages. /// - /// Tag for removing id - /// Messages id for removing. + /// Tag ID + /// List of message IDs /// Removed mail tag - /// Remove tag from messages + /// Remove a tag from the messages /// Tags - /// Exception happens when parameters are invalid. Text description contains parameter name and text description. + /// Exception happens when the parameters are invalid. Text description contains parameter name and text description. [Update(@"tags/{id}/unset")] public int UnsetTag(int id, List messages) { diff --git a/module/ASC.Api/ASC.Api.Mail/MailApi.Tests.cs b/module/ASC.Api/ASC.Api.Mail/MailApi.Tests.cs index 5700b42f1..f13007a41 100644 --- a/module/ASC.Api/ASC.Api.Mail/MailApi.Tests.cs +++ b/module/ASC.Api/ASC.Api.Mail/MailApi.Tests.cs @@ -26,18 +26,19 @@ namespace ASC.Api.Mail public partial class MailApi { /// - /// Create sample message [Tests] + /// Creates a sample message with the parameters specified in the request. [Tests] /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Id message + /// Create a sample message + /// Folder ID + /// Mailbox ID + /// List of mail addresses to which the letter came. ]]> + /// List of "cc" mail addresses. ]]> + /// List of "bcc" mail addresses. ]]> + /// Important message or not: true - important, false - not important + /// Message status: unread (true), read (false) or all (null) messages + /// Message subject + /// Message body as HTML string + /// Message ID /// Tests /// false [Create(@"messages/sample/create")] @@ -59,13 +60,14 @@ namespace ASC.Api.Mail } /// - /// Append attachment to sample message [Tests] + /// Appends an attachment to the sample message with the ID specified in the request. [Tests] /// - /// Id of any message + /// Append attachments to a sample message + /// Message ID /// File name /// File stream /// File content type - /// Id message + /// Message ID /// Tests /// false [Create(@"messages/sample/attachments/append")] @@ -79,14 +81,15 @@ namespace ASC.Api.Mail } /// - /// Load sample message from EML [Tests] + /// Loads a sample message with the parameters specified in the request from EML. [Tests] /// - /// - /// - /// - /// - /// - /// Id message + /// Load a sample message + /// Folder ID + /// User folder ID + /// Mailbox ID + /// Message status: unread (true), read (false) or all (null) messages + /// EML stream + /// Message ID /// Tests /// false [Create(@"messages/sample/eml/load")] diff --git a/module/ASC.Api/ASC.Api.Mail/MailApi.cs b/module/ASC.Api/ASC.Api.Mail/MailApi.cs index a0028f2f2..43e2c58b9 100644 --- a/module/ASC.Api/ASC.Api.Mail/MailApi.cs +++ b/module/ASC.Api/ASC.Api.Mail/MailApi.cs @@ -19,15 +19,19 @@ using System; using System.Collections.Generic; using System.Configuration; using System.Globalization; +using System.Linq; using ASC.Api.Attributes; using ASC.Api.Impl; using ASC.Api.Interfaces; +using ASC.Common.Caching; using ASC.Common.Logging; using ASC.Common.Threading; using ASC.Core; using ASC.Mail.Core; +using ASC.Mail.Core.Dao.Expressions.Mailbox; using ASC.Mail.Core.Engine.Operations.Base; +using ASC.Mail.Core.Entities; using ASC.Web.Mail.Resources; namespace ASC.Api.Mail @@ -42,6 +46,10 @@ namespace ASC.Api.Mail public const int DEFAULT_PAGE_SIZE = 25; + public const string RedisClientPrefix = "ASC.MailAction:"; + public const string RedisClientQueuesKey = RedisClientPrefix + "Queues"; + + /// /// Api name entry /// @@ -52,12 +60,59 @@ namespace ASC.Api.Mail private EngineFactory MailEngineFactory { - get { return _engineFactory ?? (_engineFactory = new EngineFactory(TenantId, Username)); } + get { return _engineFactory ?? (_engineFactory = new EngineFactory(TenantId, Username, Logger)); } + } + + private void SendUserAlive(int folder, IEnumerable tags) + { + if (!IsSendUserActivity) return; + + if (!(AscCache.Default is RedisCache cache)) return; + + CachedTenantUserMailBox cashedTenantUserMailBox = new CachedTenantUserMailBox() + { + Tenant = TenantId, + UserName = Username, + Tags = tags, + Folder = folder + }; + + cache.Publish(cashedTenantUserMailBox, CacheNotifyAction.Insert); + } + + private void SendUserActivity(List ids, MailUserAction action = MailUserAction.StartImapClient, int destinationFolder = -1) + { + if (!IsSendUserActivity) return; + + if (!(AscCache.Default is RedisCache cache)) return; + + var exp = new UserMailboxesExp(TenantId, Username, onlyTeamlab: true); + + var mailboxes = MailEngineFactory.MailboxEngine.GetMailboxDataList(exp); + + var mailboxesOnlyoffice = mailboxes.ToList(); + + if (!mailboxesOnlyoffice.Any()) return; + + string key = RedisClientPrefix + Username; + + CashedMailUserAction cashedMailUserAction = new CashedMailUserAction() + { + Tenant = TenantId, + UserName = Username, + Uds = ids, + Action = action, + Destination = destinationFolder + }; + + cache.PushMailAction(key, cashedMailUserAction); + + SendUserAlive(-1, null); } private ILog Logger { - get { return _log ?? (_log = LogManager.GetLogger("ASC.Api")); } + get { return _log ?? (_log = LogManager.GetLogger("ASC.Api.Mail")); } } private static int TenantId @@ -100,6 +155,18 @@ namespace ASC.Api.Mail } } + private static bool IsSendUserActivity + { + get + { + if (ConfigurationManagerExtension.AppSettings["mail.send-user-activity"] == null) + return false; + + return Convert.ToBoolean(ConfigurationManagerExtension.AppSettings["mail.send-user-activity"]); ; + } + } + + /// /// Timeout in milliseconds /// @@ -126,12 +193,13 @@ namespace ASC.Api.Mail } /// - /// Returns all Mail runnung operations (only complex) + /// Returns all the running complex mail operations. /// /// - /// Get all Mail running complex operations + /// Get running complex mail operations /// - /// list of MailOperationResult + /// Operations + /// List of running mail operations [Read("operations")] public List GetMailOperations() { @@ -140,13 +208,14 @@ namespace ASC.Api.Mail } /// - /// Returns Mail complex operation status + /// Returns a status of complex mail operation with the ID specified in the request. /// /// - /// Get Mail complex operation status + /// Get a status of complex mail operation /// - /// Id of operation - /// MailOperationResult + /// Operations + /// Operation ID + /// Status of complex mail operation [Read("operations/{operationId}")] public MailOperationStatus GetMailOperation(string operationId) { @@ -154,10 +223,12 @@ namespace ASC.Api.Mail } /// - /// Method for translation mail operation statuses + /// Translates a mail operation status. /// - /// instance of DistributedTask - /// translated status text + /// Translate a mail operation status + /// Operations + /// Instance of distributed task + /// Translated status private static string TranslateMailOperationStatus(DistributedTask op) { var type = op.GetProperty(MailOperation.OPERATION_TYPE); diff --git a/module/ASC.Api/ASC.Api.MailServer/MailServerApi.MailGroup.cs b/module/ASC.Api/ASC.Api.MailServer/MailServerApi.MailGroup.cs index 8295e7900..c94599a84 100644 --- a/module/ASC.Api/ASC.Api.MailServer/MailServerApi.MailGroup.cs +++ b/module/ASC.Api/ASC.Api.MailServer/MailServerApi.MailGroup.cs @@ -30,14 +30,14 @@ namespace ASC.Api.MailServer public partial class MailServerApi { /// - /// Create group address + /// Creates a mail group with the parameters specified in the request. /// - /// - /// - /// - /// MailGroupData associated with tenant - /// Create mail group address - /// MailGroup + /// Sender name + /// Domain ID + /// List of address IDs + /// Mail group data associated with the tenant + /// Create a mail group + /// Mail groups [Create(@"groupaddress/add")] public ServerDomainGroupData CreateMailGroup(string name, int domain_id, List address_ids) { @@ -47,13 +47,13 @@ namespace ASC.Api.MailServer } /// - /// Add addresses to group + /// Adds an address with the ID specified in the request to the mail group. /// - /// id of group address - /// - /// MailGroupData associated with tenant - /// Add group's addresses - /// MailGroup + /// Mail group ID + /// Address ID + /// Mail group data associated with the tenant + /// Add an address to the mail group + /// Mail groups [Update(@"groupaddress/address/add")] public ServerDomainGroupData AddMailGroupAddress(int mailgroup_id, int address_id) { @@ -63,13 +63,13 @@ namespace ASC.Api.MailServer } /// - /// Remove address from group + /// Remove an address with the ID specified in the request from the mail group. /// - /// id of group address - /// - /// id of group address - /// Remove group's address - /// MailGroup + /// Mail group ID + /// Address ID + /// Mail group ID + /// Remove an address from the mail group + /// Mail groups [Delete(@"groupaddress/addresses/remove")] public int RemoveMailGroupAddress(int mailgroup_id, int address_id) { @@ -79,11 +79,11 @@ namespace ASC.Api.MailServer } /// - /// Returns list of group addresses associated with tenant + /// Returns a list of mail groups associated with the tenant. /// - /// List of MailGroupData for current tenant - /// Get mail group list - /// MailGroup + /// List of mail group data for the current tenant + /// Get mail groups + /// Mail groups [Read(@"groupaddress/get")] public List GetMailGroups() { @@ -93,12 +93,12 @@ namespace ASC.Api.MailServer } /// - /// Deletes the selected group address + /// Deletes a mail group with the ID specified in the request. /// - /// id of group address - /// id of group address - /// Remove group address from mail server - /// MailGroup + /// Mail group ID + /// Mail group ID + /// Remove a mail group + /// Mail groups [Delete(@"groupaddress/remove/{id}")] public int RemoveMailGroup(int id) { diff --git a/module/ASC.Api/ASC.Api.MailServer/MailServerApi.Mailbox.cs b/module/ASC.Api/ASC.Api.MailServer/MailServerApi.Mailbox.cs index 38e6e9a4e..a3a73c33e 100644 --- a/module/ASC.Api/ASC.Api.MailServer/MailServerApi.Mailbox.cs +++ b/module/ASC.Api/ASC.Api.MailServer/MailServerApi.Mailbox.cs @@ -38,16 +38,16 @@ namespace ASC.Api.MailServer public partial class MailServerApi { /// - /// Create mailbox + /// Creates a mailbox with the parameters specified in the request. /// - /// - /// - /// - /// - /// Send message to creating mailbox's address - /// Send message to email from user profile - /// MailboxData associated with tenant - /// Create mailbox + /// Mailbox name + /// Mailbox local part + /// Mailbox domain ID + /// User ID + /// Specifies if the notifications will be sent to the email address from which this mailbox was created or not + /// Specifies if the notifications will be sent to the email address from the current user's profile or not + /// Mailbox data associated with the tenant + /// Create a mailbox /// Mailboxes [Create(@"mailboxes/add")] public ServerMailboxData CreateMailbox(string name, string local_part, int domain_id, string user_id, @@ -63,11 +63,11 @@ namespace ASC.Api.MailServer } /// - /// Create my mailbox + /// Create my common domain mailbox with the name specified in the request. /// - /// - /// MailboxData associated with tenant - /// Create mailbox + /// Sender name + /// Mailbox data associated with the tenant + /// Create my mailbox /// Mailboxes [Create(@"mailboxes/addmy")] public ServerMailboxData CreateMyMailbox(string name) @@ -78,10 +78,10 @@ namespace ASC.Api.MailServer } /// - /// Returns list of the mailboxes associated with tenant + /// Returns a list of all the mailboxes associated with the tenant. /// - /// List of MailboxData for current tenant - /// Get mailboxes list + /// List of mailbox data for the current tenant + /// Get mailboxes /// Mailboxes [Read(@"mailboxes/get")] public List GetMailboxes() @@ -92,13 +92,13 @@ namespace ASC.Api.MailServer } /// - /// Deletes the selected mailbox + /// Deletes a mailbox with the ID specified in the request. /// - /// id of mailbox - /// MailOperationResult object + /// Mailbox ID + /// Operation status /// Exception happens when some parameters are invalid. Text description contains parameter name and text description. /// Exception happens when mailbox wasn't found. - /// Remove mailbox from mail server + /// Remove a mailbox from the mail server /// Mailboxes [Delete(@"mailboxes/remove/{id}")] public MailOperationStatus RemoveMailbox(int id) @@ -109,12 +109,12 @@ namespace ASC.Api.MailServer } /// - /// Update mailbox + /// Updates a mailbox with the ID specified in the request. /// - /// id of mailbox - /// sender name - /// Updated MailboxData - /// Update mailbox + /// Mailbox ID + /// New sender name + /// Updated mailbox data + /// Update a mailbox /// Mailboxes [Update(@"mailboxes/update")] public ServerMailboxData UpdateMailbox(int mailbox_id, string name) @@ -125,13 +125,13 @@ namespace ASC.Api.MailServer } /// - /// Add alias to mailbox + /// Adds an alias to the mailbox with the ID specified in the request. /// - /// id of mailbox - /// name of alias - /// MailboxData associated with tenant - /// Add mailbox's aliases - /// AddressData + /// Mailbox ID + /// Mailbox alias + /// Mailbox data associated with the tenant + /// Add a mailbox alias + /// Address data [Update(@"mailboxes/alias/add")] public ServerDomainAddressData AddMailboxAlias(int mailbox_id, string alias_name) { @@ -141,12 +141,12 @@ namespace ASC.Api.MailServer } /// - /// Remove alias from mailbox + /// Removes an alias from the mailbox with the ID specified in the request. /// - /// id of mailbox - /// - /// id of mailbox - /// Remove mailbox's aliases + /// Mailbox ID + /// Mailbox address ID + /// Mailbox ID + /// Remove a mailbox alias /// Mailboxes [Update(@"mailboxes/alias/remove")] public int RemoveMailboxAlias(int mailbox_id, int address_id) @@ -157,11 +157,11 @@ namespace ASC.Api.MailServer } /// - /// Change mailbox password + /// Changes a password of the mailbox with the ID specified in the request. /// - /// - /// - /// Change mailbox password + /// Mailbox ID + /// New password + /// Change a mailbox password /// Mailboxes [Update(@"mailboxes/changepwd")] public void ChangeMailboxPassword(int mailbox_id, string password) @@ -173,12 +173,12 @@ namespace ASC.Api.MailServer } /// - /// Check existence of mailbox address + /// Checks if the mailbox address is already registered or not. /// - /// - /// - /// Is server mailbox address exists - /// True - address exists, False - not exists + /// Mailbox local part + /// Mailbox domain ID + /// Check the mailbox address existence + /// Boolean value: True - address exists, False - address does not exist /// Mailboxes [Read(@"mailboxes/alias/exists")] public bool IsAddressAlreadyRegistered(string local_part, int domain_id) @@ -188,12 +188,12 @@ namespace ASC.Api.MailServer } /// - /// Validate mailbox address + /// Checks if the mailbox address is valid or not. /// - /// - /// - /// Is server mailbox address valid - /// True - address valid, False - not valid + /// Mailbox local part + /// Mailbox domain ID + /// Validate the mailbox address + /// Boolean value: True - address is valid, False - address is not valid /// Mailboxes [Read(@"mailboxes/alias/valid")] public bool IsAddressValid(string local_part, int domain_id) diff --git a/module/ASC.Api/ASC.Api.MailServer/MailServerApi.Notification.cs b/module/ASC.Api/ASC.Api.MailServer/MailServerApi.Notification.cs index e43623926..f18cbb2f5 100644 --- a/module/ASC.Api/ASC.Api.MailServer/MailServerApi.Notification.cs +++ b/module/ASC.Api/ASC.Api.MailServer/MailServerApi.Notification.cs @@ -29,13 +29,13 @@ namespace ASC.Api.MailServer public partial class MailServerApi { /// - /// Create address for tenant notifications + /// Creates an address for the tenant notifications with the parameters specified in the request. /// - /// - /// - /// - /// NotificationAddressData associated with tenant - /// Create notification address + /// Address name + /// Address password + /// Domain ID + /// Notification address data associated with the tenant + /// Create the notification address /// Notifications [Create(@"notification/address/add")] public ServerNotificationAddressData CreateNotificationAddress(string name, string password, int domain_id) @@ -46,9 +46,10 @@ namespace ASC.Api.MailServer } /// - /// Deletes address for notification + /// Deletes an address for the tenant notification specified in the request. /// - /// Remove mailbox from mail server + /// Address name + /// Remove the notification address /// Notifications [Delete(@"notification/address/remove")] public void RemoveNotificationAddress(string address) diff --git a/module/ASC.Api/ASC.Api.MailServer/MailServerApi.Server.cs b/module/ASC.Api/ASC.Api.MailServer/MailServerApi.Server.cs index 3d8d5a01f..e5237ec07 100644 --- a/module/ASC.Api/ASC.Api.MailServer/MailServerApi.Server.cs +++ b/module/ASC.Api/ASC.Api.MailServer/MailServerApi.Server.cs @@ -29,10 +29,10 @@ namespace ASC.Api.MailServer public partial class MailServerApi { /// - /// Returns ServerData for mail server associated with tenant + /// Returns the mail server associated with the current tenant. /// - /// ServerData for current tenant. - /// Get mail server + /// Mail server data for the current tenant + /// Get the mail server /// Servers [Read(@"server")] public ServerData GetMailServer() @@ -42,10 +42,10 @@ namespace ASC.Api.MailServer } /// - /// Returns ServerData for mail server associated with tenant + /// Returns full information on the mail server associated with the current tenant. /// - /// ServerData for current tenant. - /// Get mail server + /// Full mail server information for the current tenant + /// Get the mail server information /// Servers [Read(@"serverinfo/get")] public ServerFullData GetMailServerFullInfo() @@ -68,11 +68,11 @@ namespace ASC.Api.MailServer } /// - /// Get or generate free to any domain DNS records + /// Returns or generates free DNS records. /// - /// DNS records for current tenant and user. - /// Get free DNS records - /// DnsRecords + /// DNS records for the current tenant and user + /// Get or create free DNS records + /// DNS records [Read(@"freedns/get")] public ServerDomainDnsData GetUnusedDnsRecords() { diff --git a/module/ASC.Api/ASC.Api.MailServer/MailServerApi.WebDomain.cs b/module/ASC.Api/ASC.Api.MailServer/MailServerApi.WebDomain.cs index 42bd8ac29..199c9a35c 100644 --- a/module/ASC.Api/ASC.Api.MailServer/MailServerApi.WebDomain.cs +++ b/module/ASC.Api/ASC.Api.MailServer/MailServerApi.WebDomain.cs @@ -33,10 +33,10 @@ namespace ASC.Api.MailServer public partial class MailServerApi { /// - /// Returns list of the web domains associated with tenant + /// Returns a list of all the web domains associated with the current tenant. /// - /// List of WebDomainData for current tenant - /// Get tenant web domain list + /// List of web domains for the current tenant + /// Get web domains /// Domains [Read(@"domains/get")] public List GetDomains() @@ -54,9 +54,9 @@ namespace ASC.Api.MailServer } /// - /// Returns the common web domain + /// Returns the common web domain. /// - /// WebDomainData for common web domain + /// Common web domain /// Get common web domain /// Domains [Read(@"domains/common")] @@ -67,12 +67,12 @@ namespace ASC.Api.MailServer } /// - /// Associate a web domain with tenant + /// Adds a web domain to the current tenant. /// - /// web domain name - /// - /// WebDomainData associated with tenant - /// Add domain to mail server + /// Web domain name + /// DNS ID + /// Web domain data associated with the tenant + /// Add a domain to the mail server /// Domains [Create(@"domains/add")] public ServerDomainData AddDomain(string name, int id_dns) @@ -83,11 +83,11 @@ namespace ASC.Api.MailServer } /// - /// Deletes the selected web domain + /// Deletes a web domain with the ID specified in the request from the mail server. /// - /// id of web domain - /// MailOperationResult object - /// Remove domain from mail server + /// Web domain ID + /// Operation status + /// Remove a domain from the mail server /// Domains [Delete(@"domains/remove/{id}")] public MailOperationStatus RemoveDomain(int id) @@ -98,12 +98,12 @@ namespace ASC.Api.MailServer } /// - /// Returns dns records associated with domain + /// Returns DNS records related to the domain with the ID specified in the request. /// - /// id of domain - /// Dns records associated with domain - /// Returns dns records - /// DnsRecords + /// Domain ID + /// DNS records associated with the domain + /// Get DNS records by domain ID + /// DNS records [Read(@"domains/dns/get")] public ServerDomainDnsData GetDnsRecords(int id) { @@ -113,11 +113,11 @@ namespace ASC.Api.MailServer } /// - /// Check web domain name existance + /// Checks if the web domain name specified in the request already exists or not. /// - /// web domain name - /// True if domain name already exists. - /// Is domain name exists. + /// Web domain name + /// Boolean value: True - domain name exists, False - domain name does not exist + /// Check the domain name existence /// Domains [Read(@"domains/exists")] public bool IsDomainExists(string name) @@ -128,11 +128,11 @@ namespace ASC.Api.MailServer } /// - /// Check web domain name ownership over txt record in dns + /// Checks if the web domain specified in the request belongs to the current user or not. /// - /// web domain name - /// True if user is owner of this domain. - /// Check domain ownership. + /// Web domain name + /// Boolean value: True - current user is the domain owner, False - current user is not the domain owner + /// Check the domain ownership /// Domains [Read(@"domains/ownership/check")] public bool CheckDomainOwnership(string name) diff --git a/module/ASC.Api/ASC.Api.Migration/ASC.Api.Migration.csproj b/module/ASC.Api/ASC.Api.Migration/ASC.Api.Migration.csproj new file mode 100644 index 000000000..9e3167259 --- /dev/null +++ b/module/ASC.Api/ASC.Api.Migration/ASC.Api.Migration.csproj @@ -0,0 +1,75 @@ + + + + + Debug + AnyCPU + {2DA19D11-DC5A-48BB-B0CC-D070A5A2C363} + Library + Properties + ASC.Api.Migration + ASC.Api.Migration + v4.8 + 512 + true + ..\..\..\web\studio\ASC.Web.Studio\bin\ + ..\..\..\web\studio\ASC.Web.Studio\bin\ASC.Api.Migration.xml + + + true + full + false + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + {76de7717-3d4b-4a5b-b740-15b8913df0cb} + ASC.Common + + + {A51D0454-4AFA-46DE-89D4-B03D37E1816C} + ASC.Core.Common + + + {e7be6ce8-f6b0-4b9b-831b-ba0c85c8d130} + ASC.Web.Studio + + + {72d10cca-ccf1-4e2f-a17d-443917b154cb} + ASC.Migration + + + {49f07fff-98a5-47d2-a9e9-a46b98c41245} + ASC.Api.Core + + + + \ No newline at end of file diff --git a/module/ASC.Api/ASC.Api.Migration/MigrationApi.cs b/module/ASC.Api/ASC.Api.Migration/MigrationApi.cs new file mode 100644 index 000000000..728044bd7 --- /dev/null +++ b/module/ASC.Api/ASC.Api.Migration/MigrationApi.cs @@ -0,0 +1,240 @@ + +using System; +using System.IO; +using System.Reflection; +using System.Runtime.Caching; +using System.Threading; +using System.Threading.Tasks; + +using ASC.Api.Attributes; +using ASC.Api.Exceptions; +using ASC.Api.Impl; +using ASC.Core; +using ASC.Core.Users; +using ASC.Migration.Core; +using ASC.Migration.Core.Models.Api; +using ASC.Web.Studio.Core.Notify; + + +namespace ASC.Api.Migration +{ + /// + /// + /// + public class MigrationApi : Interfaces.IApiEntryPoint + { + /// + /// + /// + public string Name => "migration"; + + private readonly ApiContext _context; + + /// + /// + /// + public MigrationApi(ApiContext context) + { + _context = context; + } + + /// + /// + /// + /// + [Read("backuptmp")] + public string GetTmpFolder() + { + if (!CoreContext.Configuration.Standalone || !CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID).IsAdmin()) throw new System.Security.SecurityException(); + + var tempFolder = Path.Combine(TempPath.GetTempPath(), "migration", DateTime.Now.ToString("dd.MM.yyyy_HH_mm")); + + if (!Directory.Exists(tempFolder)) + { + Directory.CreateDirectory(tempFolder); + } + return tempFolder; + } + + /// + /// + /// + /// + [Read("list")] + public string[] List() + { + if (!CoreContext.Configuration.Standalone || !CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID).IsAdmin()) throw new System.Security.SecurityException(); + return MigrationCore.GetAvailableMigrations(); + } + + /// + /// + /// + /// + /// + [Create("init/{migratorName}")] + public void UploadAndInit(string migratorName, string path) + { + if (!CoreContext.Configuration.Standalone || !CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID).IsAdmin()) throw new System.Security.SecurityException(); + if (GetOngoingMigration() != null) + { + throw new Exception("Migration is already in progress"); + } + + var migratorMeta = MigrationCore.GetMigrator(migratorName); + if (migratorMeta == null) + { + throw new ItemNotFoundException("No such migration provider"); + } + var cts = new CancellationTokenSource(); + var migrator = (IMigration)Activator.CreateInstance(migratorMeta.MigratorType); + try + { + migrator.Init(path, cts.Token); + } + catch (Exception ex) + { + throw new Exception($"Error while initializing {migratorMeta.MigratorInfo.Name} migrator", ex); + } + + var ongoingMigration = new OngoingMigration { Migration = migrator, CancelTokenSource = cts }; + StoreOngoingMigration(ongoingMigration); + + ongoingMigration.ParseTask = Task.Run(migrator.Parse); + } + + /// + /// + /// + /// + [Read("status")] + public object Status() + { + if (!CoreContext.Configuration.Standalone || !CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID).IsAdmin()) throw new System.Security.SecurityException(); + var ongoingMigration = GetOngoingMigration(); + if (ongoingMigration == null) + { + return null; + } + + if(ongoingMigration.CancelTokenSource.IsCancellationRequested == true) + { + var migratorName = ongoingMigration.Migration.GetType().GetCustomAttribute().Name; + return migratorName; + } + + var result = new MigrationStatus() + { + ParseResult = ongoingMigration.ParseTask.IsCompleted ? ongoingMigration.ParseTask.Result : null, + MigrationEnded = ongoingMigration.MigrationEnded, + Progress = ongoingMigration.Migration.GetProgress(), + ProgressStatus = ongoingMigration.Migration.GetProgressStatus() + }; + + return result; + } + + /// + /// + /// + [Create("cancel")] + public void Cancel() + { + if (!CoreContext.Configuration.Standalone || !CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID).IsAdmin()) throw new System.Security.SecurityException(); + var ongoingMigration = GetOngoingMigration(); + if (ongoingMigration == null) + { + throw new Exception("No migration is in progress"); + } + ongoingMigration.CancelTokenSource.Cancel(); + } + + /// + /// + /// + /// + [Create("migrate")] + public void Migrate(MigrationApiInfo info) + { + if (!CoreContext.Configuration.Standalone || !CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID).IsAdmin()) throw new System.Security.SecurityException(); + var ongoingMigration = GetOngoingMigration(); + if (ongoingMigration == null) + { + throw new Exception("No migration is in progress"); + } + else if (!ongoingMigration.ParseTask.IsCompleted) + { + throw new Exception("Parsing is still in progress"); + } + + ongoingMigration.MigrationTask = ongoingMigration.Migration.Migrate(info); + } + + /// + /// + /// + /// + [Read("logs")] + public MigrationLogApiContentResponce Logs() + { + if (!CoreContext.Configuration.Standalone || !CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID).IsAdmin()) throw new System.Security.SecurityException(); + var ongoingMigration = GetOngoingMigration(); + if (ongoingMigration == null) + { + throw new Exception("No migration is in progress"); + } + return new MigrationLogApiContentResponce(ongoingMigration.Migration.GetLogs(), "migration.log"); + } + + /// + /// + /// + /// + [Create("finish")] + public void Finish(bool isSendWelcomeEmail) + { + if (!CoreContext.Configuration.Standalone || !CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID).IsAdmin()) throw new System.Security.SecurityException(); + if(isSendWelcomeEmail) + { + var ongoingMigration = GetOngoingMigration(); + if (ongoingMigration == null) + { + throw new Exception("No migration is in progress"); + } + var guidUsers = ongoingMigration.Migration.GetGuidImportedUsers(); + foreach (var gu in guidUsers) + { + var u = CoreContext.UserManager.GetUsers(gu); + StudioNotifyService.Instance.UserInfoActivation(u); + } + } + ClearCache(); + } + + private const string MigrationCacheKey = "ASC.Migration.Ongoing"; + // ToDo: Use ASCCache + private void StoreOngoingMigration(OngoingMigration migration) + { + MemoryCache.Default.Set(MigrationCacheKey, migration, new CacheItemPolicy() { RemovedCallback = ClearMigration, SlidingExpiration = TimeSpan.FromDays(1) }); + } + + private OngoingMigration GetOngoingMigration() + { + return (OngoingMigration)MemoryCache.Default.Get(MigrationCacheKey); + } + + private void ClearCache() + { + MemoryCache.Default.Remove(MigrationCacheKey); + } + + private void ClearMigration(CacheEntryRemovedArguments arguments) + { + if (typeof(OngoingMigration).IsAssignableFrom(arguments.CacheItem.Value.GetType())) + { + var ongoingMigration = (OngoingMigration)arguments.CacheItem.Value; + ongoingMigration.Migration.Dispose(); + } + } + } +} diff --git a/module/ASC.Api/ASC.Api.Migration/MigrationStatus.cs b/module/ASC.Api/ASC.Api.Migration/MigrationStatus.cs new file mode 100644 index 000000000..0722ad6de --- /dev/null +++ b/module/ASC.Api/ASC.Api.Migration/MigrationStatus.cs @@ -0,0 +1,30 @@ +using ASC.Migration.Core.Models.Api; + +namespace ASC.Api.Migration +{ + /// + /// + /// + public class MigrationStatus + { + /// + /// + /// + public double Progress { get; set; } + + /// + /// + /// + public string ProgressStatus { get; set; } + + /// + /// + /// + public MigrationApiInfo ParseResult { get; set; } + + /// + /// + /// + public bool MigrationEnded { get; set; } + } +} diff --git a/module/ASC.Api/ASC.Api.Migration/OngoingMigration.cs b/module/ASC.Api/ASC.Api.Migration/OngoingMigration.cs new file mode 100644 index 000000000..e80a236e3 --- /dev/null +++ b/module/ASC.Api/ASC.Api.Migration/OngoingMigration.cs @@ -0,0 +1,41 @@ +using System.Threading; +using System.Threading.Tasks; + +using ASC.Migration.Core; +using ASC.Migration.Core.Models.Api; + +namespace ASC.Api.Migration +{ + /// + /// + /// + public class OngoingMigration + { + /// + /// + /// + public CancellationTokenSource CancelTokenSource { get; set; } + + /// + /// + /// + public IMigration Migration { get; set; } + + /// + /// + /// + public Task ParseTask { get; set; } + + /// + /// + /// + public Task MigrationTask { get; set; } + + /// + /// + /// + public bool MigrationEnded => + ParseTask != null && ParseTask.IsCompleted + && MigrationTask != null && MigrationTask.IsCompleted; + } +} diff --git a/module/ASC.Api/ASC.Api.Migration/Properties/AssemblyInfo.cs b/module/ASC.Api/ASC.Api.Migration/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..c5a3650bd --- /dev/null +++ b/module/ASC.Api/ASC.Api.Migration/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ASC.Api.Migration")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Ascensio System SIA")] +[assembly: AssemblyProduct("ASC.Api.Migration")] +[assembly: AssemblyCopyright("(c) Ascensio System SIA. All rights reserved")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("2da19d11-dc5a-48bb-b0cc-d070a5a2c363")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/module/ASC.Api/ASC.Api.Portal/PortalApi.cs b/module/ASC.Api/ASC.Api.Portal/PortalApi.cs index f77afebc6..7abda8785 100644 --- a/module/ASC.Api/ASC.Api.Portal/PortalApi.cs +++ b/module/ASC.Api/ASC.Api.Portal/PortalApi.cs @@ -63,12 +63,14 @@ using UrlShortener = ASC.Web.Core.Utility.UrlShortener; namespace ASC.Api.Portal { /// - /// Portal info access + /// Portal information access. /// public class PortalApi : IApiEntryPoint { private readonly IMobileAppInstallRegistrator mobileAppRegistrator; private readonly BackupAjaxHandler backupHandler = new BackupAjaxHandler(); + private ILog Log = LogManager.GetLogger("ASC"); + private ILog LogWeb = LogManager.GetLogger("ASC.Web"); /// @@ -90,10 +92,10 @@ namespace ASC.Api.Portal } /// - ///Returns the current portal + ///Returns the current portal. /// /// - ///Current portal + ///Get the current portal /// ///Portal [Read("")] @@ -103,10 +105,10 @@ namespace ASC.Api.Portal } /// - ///Returns the user with specified userID from the current portal + ///Returns a user with the ID specified in the request from the current portal. /// /// - ///User with specified userID + ///Get a user by ID /// /// Users ///User @@ -118,17 +120,17 @@ namespace ASC.Api.Portal /// - /// Returns invitational link to the portal + /// Returns an invitation link for joining the portal. /// /// - /// Returns invitational link to the portal + /// Get an invitation link /// /// - /// User or Visitor + /// Employee type (User or Visitor) /// ///Users /// - /// Invite link + /// Invitation link /// [Read("users/invite/{employeeType}")] public string GeInviteLink(EmployeeType employeeType) @@ -142,10 +144,12 @@ namespace ASC.Api.Portal } /// - /// Returns shorten link + /// Returns a shortened invitation link for joining the portal. /// - /// Link for shortening - ///link + /// Get a shortened invitation link + /// Invitation link + /// Users + ///Shortened invitation link ///false [Update("getshortenlink")] public String GetShortenLink(string link) @@ -156,17 +160,17 @@ namespace ASC.Api.Portal } catch (Exception ex) { - LogManager.GetLogger("ASC.Web").Error("getshortenlink", ex); + LogWeb.Error("getshortenlink", ex); return link; } } /// - ///Returns the used space of the current portal + ///Returns the used space of the current portal. /// /// - ///Used space of the current portal + ///Get the used portal space /// /// Quota ///Used space @@ -180,19 +184,28 @@ namespace ASC.Api.Portal } /// - ///Returns the users count of the current portal + ///Returns a number of portal users. /// /// - ///Users count of the current portal + ///Get a number of portal users /// /// Users - ///Users count + ///User count [Read("userscount")] public long GetUsersCount() { return CoreContext.Configuration.Personal ? 1 : CoreContext.UserManager.GetUserNames(EmployeeStatus.Active).Count(); } + /// + ///Uploads a portal license specified in the request. + /// + /// + ///Upload a license + /// + ///License attachments + /// Quota + ///License ///false [Create("uploadlicense")] public FileUploadResult UploadLicense(IEnumerable attachments) @@ -219,28 +232,36 @@ namespace ASC.Api.Portal } catch (LicenseExpiredException ex) { - LogManager.GetLogger("ASC").Error("License upload", ex); + Log.Error("License upload", ex); result.Message = Resource.LicenseErrorExpired; } catch (LicenseQuotaException ex) { - LogManager.GetLogger("ASC").Error("License upload", ex); + Log.Error("License upload", ex); result.Message = Resource.LicenseErrorQuota; } catch (LicensePortalException ex) { - LogManager.GetLogger("ASC").Error("License upload", ex); + Log.Error("License upload", ex); result.Message = Resource.LicenseErrorPortal; } catch (Exception ex) { - LogManager.GetLogger("ASC").Error("License upload", ex); + Log.Error("License upload", ex); result.Message = Resource.LicenseError; } return result; } + /// + ///Activates a license for the portal. + /// + /// + ///Activate a license + /// + /// Quota + ///License ///false [Create("activatelicense")] public FileUploadResult ActivateLicense() @@ -258,22 +279,22 @@ namespace ASC.Api.Portal } catch (BillingNotFoundException ex) { - LogManager.GetLogger("ASC").Error("License activate", ex); + Log.Error("License activate", ex); result.Message = UserControlsCommonResource.LicenseKeyNotFound; } catch (BillingNotConfiguredException ex) { - LogManager.GetLogger("ASC").Error("License activate", ex); + Log.Error("License activate", ex); result.Message = UserControlsCommonResource.LicenseKeyNotCorrect; } catch (BillingException ex) { - LogManager.GetLogger("ASC").Error("License activate", ex); + Log.Error("License activate", ex); result.Message = UserControlsCommonResource.LicenseException; } catch (Exception ex) { - LogManager.GetLogger("ASC").Error("License activate", ex); + Log.Error("License activate", ex); result.Message = ex.Message; } @@ -281,6 +302,14 @@ namespace ASC.Api.Portal } + /// + ///Activates a trial license for the portal. + /// + /// + ///Activate a trial license + /// + /// Quota + ///Trial license ///false [Create("activatetrial")] public bool ActivateTrial() @@ -322,6 +351,14 @@ namespace ASC.Api.Portal return true; } + /// + ///Returns an extra tenant license for the portal. + /// + /// + ///Get an extra tenant license + /// + /// Quota + ///Extra tenant license information ///false [Read("tenantextra")] public object GetTenantExtra() @@ -345,10 +382,10 @@ namespace ASC.Api.Portal } /// - ///Returns the current tariff of the current portal + ///Returns the current portal tariff. /// /// - ///Tariff of the current portal + ///Get a portal tariff /// /// Quota ///Tariff @@ -359,10 +396,10 @@ namespace ASC.Api.Portal } /// - ///Returns the current quota of the current portal + ///Returns the current portal quota. /// /// - ///Quota of the current portal + ///Get a portal quota /// /// Quota ///Quota @@ -373,10 +410,10 @@ namespace ASC.Api.Portal } /// - ///Returns the recommended quota of the current portal + ///Returns the recommended quota for the current portal. /// /// - ///Quota of the current portal + ///Get the recommended quota /// /// Quota ///Quota @@ -394,12 +431,13 @@ namespace ASC.Api.Portal } /// - ///Returns path + ///Returns the full absolute path to the current portal. /// /// - ///path + ///Get a path to the portal /// - ///path + ///Portal virtual path + ///Portal path ///false [Read("path")] public string GetFullAbsolutePath(string virtualPath) @@ -407,6 +445,14 @@ namespace ASC.Api.Portal return CommonLinkUtility.GetFullAbsolutePath(virtualPath); } + /// + ///Returns a number of unread messages from the portal. + /// + /// + ///Get a number of unread messages + /// + ///Talk + ///Number of unread messages ///false [Read("talk/unreadmessages")] public int GetMessageCount() @@ -421,6 +467,15 @@ namespace ASC.Api.Portal return 0; } + /// + ///Removes the XMPP connection specified in the request from the inner channel. + /// + /// + ///Talk + ///Removes the XMPP connection + /// + ///Connection ID + ///XMPP connection ID ///false [Delete("talk/connection")] public int RemoveXmppConnection(string connectionId) @@ -435,6 +490,16 @@ namespace ASC.Api.Portal return 0; } + /// + ///Adds the XMPP connection to the inner channel. + /// + /// + ///Add the XMPP connection + /// + ///Talk + ///Connection ID + ///Service state + ///Updated inner channel ///false [Create("talk/connection")] public byte AddXmppConnection(string connectionId, byte state) @@ -449,6 +514,15 @@ namespace ASC.Api.Portal return 0; } + /// + ///Returns the service state for the user with the name specified in the request. + /// + /// + ///Get a service state + /// + ///Talk + ///User name + ///State ///false [Read("talk/state")] public int GetState(string userName) @@ -463,6 +537,15 @@ namespace ASC.Api.Portal return 0; } + /// + ///Sends a service state specified in the request. + /// + /// + ///Send a service state + /// + ///Talk + ///Service state + ///State ///false [Create("talk/state")] public byte SendState(byte state) @@ -477,6 +560,16 @@ namespace ASC.Api.Portal return 4; } + /// + ///Sends a message to the user specified in the request. + /// + /// + ///Send a message + /// + ///Talk + ///User to whom a message will be sent + ///Message text + ///Message subject ///false [Create("talk/message")] public void SendMessage(string to, string text, string subject) @@ -491,6 +584,14 @@ namespace ASC.Api.Portal } } + /// + ///Returns a dictionary of all the service states. + /// + /// + ///Get service states + /// + ///Talk + ///Dictionary of all the service states ///false [Read("talk/states")] public Dictionary GetAllStates() @@ -506,6 +607,16 @@ namespace ASC.Api.Portal return new Dictionary(); } + /// + ///Returns all the recent messages. + /// + /// + ///Get recent messages + /// + ///Talk + ///Callee user name + ///ID + ///Recent messages ///false [Read("talk/recentMessages")] public MessageClass[] GetRecentMessages(string calleeUserName, int id) @@ -538,6 +649,14 @@ namespace ASC.Api.Portal return new MessageClass[0]; } + /// + ///Pings when a message is received. + /// + /// + ///Ping + /// + ///Talk + ///Service state ///false [Create("talk/ping")] public void Ping(byte state) @@ -551,6 +670,14 @@ namespace ASC.Api.Portal } } + /// + ///Registers the mobile app installation. + /// + /// + ///Register the mobile app installation + /// + ///Mobile + ///Mobile app type ///false [Create("mobile/registration")] public void RegisterMobileAppInstall(MobileAppType type) @@ -561,10 +688,11 @@ namespace ASC.Api.Portal /// - /// Returns the backup schedule of the current portal + /// Returns the backup schedule of the current portal. /// + /// Get the backup schedule /// Backup - /// Backup Schedule + /// Backup schedule [Read("getbackupschedule")] public BackupAjaxHandler.Schedule GetBackupSchedule() { @@ -577,13 +705,14 @@ namespace ASC.Api.Portal } /// - /// Create the backup schedule of the current portal + /// Creates the backup schedule of the current portal with the parameters specified in the request. /// + /// Create the backup schedule /// Storage type /// Storage parameters - /// Max of the backup's stored copies + /// Maximum number of backup stored copies /// Cron parameters - /// Include mail in the backup + /// Specifies if the mails will be included into the backup or not /// Backup [Create("createbackupschedule")] public void CreateBackupSchedule(BackupStorageType storageType, IEnumerable> storageParams, int backupsStored, BackupAjaxHandler.CronParams cronParams, bool backupMail) @@ -604,8 +733,9 @@ namespace ASC.Api.Portal } /// - /// Delete the backup schedule of the current portal + /// Deletes the backup schedule of the current portal. /// + /// Delete the backup schedule /// Backup [Delete("deletebackupschedule")] public void DeleteBackupSchedule() @@ -626,13 +756,14 @@ namespace ASC.Api.Portal } /// - /// Start a backup of the current portal + /// Starts the backup of the current portal with the parameters specified in the request. /// - /// Storage Type - /// Storage Params - /// Include mail in the backup + /// Start the backup + /// Storage type + /// Storage parameters + /// Specifies if the mails will be included into the backup or not /// Backup - /// Backup Progress + /// Backup progress [Create("startbackup")] public BackupProgress StartBackup(BackupStorageType storageType, IEnumerable> storageParams, bool backupMail) { @@ -645,10 +776,11 @@ namespace ASC.Api.Portal } /// - /// Returns the progress of the started backup + /// Returns the progress of the started backup. /// + /// Get the backup progress /// Backup - /// Backup Progress + /// Backup progress [Read("getbackupprogress")] public BackupProgress GetBackupProgress() { @@ -661,10 +793,11 @@ namespace ASC.Api.Portal } /// - /// Returns the backup history of the started backup + /// Returns the history of the started backup. /// + /// Get the backup history /// Backup - /// Backup History + /// Backup history [Read("getbackuphistory")] public List GetBackupHistory() { @@ -677,8 +810,10 @@ namespace ASC.Api.Portal } /// - /// Delete the backup with the specified id + /// Deletes the backup with the ID specified in the request. /// + /// Delete the backup + /// Backup ID /// Backup [Delete("deletebackup/{id}")] public void DeleteBackup(Guid id) @@ -692,10 +827,11 @@ namespace ASC.Api.Portal } /// - /// Delete all backups of the current portal + /// Deletes the backup history of the current portal. /// + /// Delete the backup history /// Backup - /// Backup History + /// Backup history [Delete("deletebackuphistory")] public void DeleteBackupHistory() { @@ -708,14 +844,15 @@ namespace ASC.Api.Portal } /// - /// Start a data restore of the current portal + /// Starts the data restoring process of the current portal with the parameters specified in the request. /// - /// Backup Id - /// Storage Type - /// Storage Params - /// Notify about backup to users + /// Start the restoring process + /// Backup ID + /// Storage type + /// Storage parameters + /// Notifies users about backup or not /// Backup - /// Restore Progress + /// Restoring progress [Create("startrestore")] public BackupProgress StartBackupRestore(string backupId, BackupStorageType storageType, IEnumerable> storageParams, bool notify) { @@ -728,10 +865,11 @@ namespace ASC.Api.Portal } /// - /// Returns the progress of the started restore + /// Returns the progress of the started restoring process. /// + /// Get the restoring progress /// Backup - /// Restore Progress + /// Restoring progress [Read("getrestoreprogress", true, false)] //NOTE: this method doesn't check payment!!! public BackupProgress GetRestoreProgress() { @@ -743,6 +881,12 @@ namespace ASC.Api.Portal return backupHandler.GetRestoreProgress(); } + /// + /// Returns the path to the backup temporary directory. + /// + /// Get the backup temporary path + /// Backup + /// Backup temporary path ///false [Read("backuptmp")] public string GetTempPath(string alias) @@ -755,7 +899,49 @@ namespace ASC.Api.Portal return backupHandler.GetTmpFolder(); } + /// + /// Deletes the current portal immediately + /// + /// Delete the current portal + /// + ///false + [Delete("deleteportalimmediately")] + public void DeletePortalImmediately() + { + var tenant = CoreContext.TenantManager.GetCurrentTenant(); + if (SecurityContext.CurrentAccount.ID != tenant.OwnerId) + { + throw new Exception(Resource.ErrorAccessDenied); + } + + CoreContext.TenantManager.RemoveTenant(tenant.TenantId); + + if (!string.IsNullOrEmpty(ApiSystemHelper.ApiCacheUrl)) + { + ApiSystemHelper.RemoveTenantFromCache(tenant.TenantAlias); + } + + try + { + if (!SecurityContext.IsAuthenticated) + { + SecurityContext.CurrentAccount = ASC.Core.Configuration.Constants.CoreSystem; + } + MessageService.Send(HttpContext.Current.Request, MessageAction.PortalDeleted); + } + finally + { + SecurityContext.Logout(); + } + } + + /// + /// Updates a portal name with a new one specified in the request. + /// + /// Update a portal name + /// New portal name + /// Message about renaming a portal ///false [Update("portalrename")] public object UpdatePortalName(string alias) @@ -839,8 +1025,14 @@ namespace ASC.Api.Portal #endregion + /// + /// Sends congratulations to the user after registering the portal. + /// + /// Send congratulations + /// User ID + /// Email key ///false - [Create("sendcongratulations", false)] //NOTE: this method doesn't requires auth!!! + [Create("sendcongratulations", false)] //NOTE: this method doesn't require auth!!! public void SendCongratulations(Guid userid, string key) { var authInterval = TimeSpan.FromHours(1); @@ -872,6 +1064,14 @@ namespace ASC.Api.Portal } + /// + /// Removes a comment with the ID specified in the request. + /// + /// Remove a comment + /// Comments + /// Comment ID + /// Domain name + /// Operation status ///false [Update("fcke/comment/removecomplete")] public object RemoveCommentComplete(string commentid, string domain) @@ -887,6 +1087,15 @@ namespace ASC.Api.Portal } } + /// + /// Cancels editing a comment with the ID specified in the request. + /// + /// Cancel comment editing + /// Comments + /// Comment ID + /// Domain name + /// Specifies if a comment was edited or not + /// Operation status ///false [Update("fcke/comment/cancelcomplete")] public object CancelCommentComplete(string commentid, string domain, bool isedit) @@ -906,6 +1115,16 @@ namespace ASC.Api.Portal } } + /// + /// Edits a comment with the ID specified in the request. + /// + /// Edit a comment + /// Comments + /// Comment ID + /// Domain name + /// New comment in the HTML format + /// Specifies if a comment was edited or not + /// Operation status ///false [Update("fcke/comment/editcomplete")] public object EditCommentComplete(string commentid, string domain, string html, bool isedit) @@ -922,6 +1141,15 @@ namespace ASC.Api.Portal } } + /// + /// Returns the promotion bar. + /// + /// Get the promotion bar + /// Promotions + /// Domain name + /// Page + /// Specifies if the bar will be displayed in the desktop app or not + /// Promotion bar ///false [Read("bar/promotions")] public string GetBarPromotions(string domain, string page, bool desktop) @@ -972,11 +1200,17 @@ namespace ASC.Api.Portal } catch (Exception ex) { - LogManager.GetLogger("ASC.Web").Error("GetBarTips", ex); + LogWeb.Error("GetBarTips", ex); return null; } } + /// + /// Marks the promotion bar as read. + /// + /// Mark the promotion bar as read + /// Promotions + /// ID ///false [Create("bar/promotions/mark/{id}")] public void MarkBarPromotion(string id) @@ -996,10 +1230,20 @@ namespace ASC.Api.Portal } catch (Exception ex) { - LogManager.GetLogger("ASC.Web").Error("MarkBarPromotion", ex); + LogWeb.Error("MarkBarPromotion", ex); } } + /// + /// Returns the promotion bar tips. + /// + /// Get the promotion bar tips + /// Promotions + /// Domain name + /// Page + /// Product administator + /// Specifies if the bar will be displayed in the desktop app or not + /// Promotion bar tips ///false [Read("bar/tips")] public string GetBarTips(string domain, string page, bool productAdmin, bool desktop) @@ -1041,11 +1285,17 @@ namespace ASC.Api.Portal } catch (Exception ex) { - LogManager.GetLogger("ASC.Web").Error("GetBarTips", ex); + LogWeb.Error("GetBarTips", ex); return null; } } + /// + /// Marks the promotion bar tips as read. + /// + /// Mark the promotion bar tips as read + /// Promotions + /// ID ///false [Create("bar/tips/mark/{id}")] public void MarkBarTip(string id) @@ -1067,10 +1317,15 @@ namespace ASC.Api.Portal } catch (Exception ex) { - LogManager.GetLogger("ASC.Web").Error("MarkBarTip", ex); + LogWeb.Error("MarkBarTip", ex); } } + /// + /// Deletes the promotion bar tips. + /// + /// Delete the promotion bar tips + /// Promotions ///false [Delete("bar/tips")] public void DeleteBarTips() @@ -1091,10 +1346,16 @@ namespace ASC.Api.Portal } catch (Exception ex) { - LogManager.GetLogger("ASC.Web").Error("DeleteBarTips", ex); + LogWeb.Error("DeleteBarTips", ex); } } + /// + /// Returns the search settings. + /// + /// Get the search settings + /// Search + /// Search settings [Read("search")] public IEnumerable GetSearchSettings() { @@ -1108,6 +1369,12 @@ namespace ASC.Api.Portal }); } + /// + /// Checks if the searching process is available or not. + /// + /// Check the search availability + /// Search + /// Search information [Read("search/state")] public object CheckSearchAvailable() { @@ -1116,6 +1383,13 @@ namespace ASC.Api.Portal return FactoryIndexer.GetState(); } + /// + /// Reindexes a page during the search process. + /// + /// Reindex a page + /// Search + /// Index name + /// Search information [Create("search/reindex")] public object Reindex(string name) { @@ -1125,6 +1399,12 @@ namespace ASC.Api.Portal return CheckSearchAvailable(); } + /// + /// Sets the search settings specified in the request. + /// + /// Set the search settings + /// Search + /// Search settings [Create("search")] public void SetSearchSettings(List items) { @@ -1134,14 +1414,15 @@ namespace ASC.Api.Portal } /// - /// Get random password + /// Returns a random password. /// - /// Get random password + /// Get a random password + /// Random password ///false [Read(@"randompwd")] public string GetRandomPassword() { - var Noise = "1234567890mnbasdflkjqwerpoiqweyuvcxnzhdkqpsdk_-()="; + var Noise = "1234567890mnbasdflkjqwerpoiqweyuvcxnzhdkqpsdk_#()$"; var ps = PasswordSettings.Load(); @@ -1175,9 +1456,11 @@ namespace ASC.Api.Portal } /// - /// Get information by IP address + /// Returns the information about the IP address specified in the request. /// - /// Get information by IP address + /// Get the IP information + /// IP address + /// IP information ///false [Read("ip/{ipAddress}")] public object GetIPInformation(string ipAddress) @@ -1187,6 +1470,10 @@ namespace ASC.Api.Portal } + /// + /// Marks a gift message as read. + /// + /// Mark a gift message as read ///false [Create("gift/mark")] public void MarkGiftAsReaded() @@ -1199,7 +1486,7 @@ namespace ASC.Api.Portal } catch (Exception ex) { - LogManager.GetLogger("ASC.Web").Error("MarkGiftAsReaded", ex); + LogWeb.Error("MarkGiftAsReaded", ex); } } } diff --git a/module/ASC.Api/ASC.Api.Projects/ASC.Api.Projects.csproj b/module/ASC.Api/ASC.Api.Projects/ASC.Api.Projects.csproj index 13ae1a095..806cc7d7a 100644 --- a/module/ASC.Api/ASC.Api.Projects/ASC.Api.Projects.csproj +++ b/module/ASC.Api/ASC.Api.Projects/ASC.Api.Projects.csproj @@ -134,10 +134,10 @@ - 5.1.2 + 6.2.0 - 12.0.3 + 13.0.1 diff --git a/module/ASC.Api/ASC.Api.Projects/ProjectApi.Comments.cs b/module/ASC.Api/ASC.Api.Projects/ProjectApi.Comments.cs index ec1b714ce..ec9d60864 100644 --- a/module/ASC.Api/ASC.Api.Projects/ProjectApi.Comments.cs +++ b/module/ASC.Api/ASC.Api.Projects/ProjectApi.Comments.cs @@ -40,10 +40,10 @@ namespace ASC.Api.Projects #region comments /// - ///Returns the information about the comment with the ID specified in the request + ///Returns the information about a comment with the ID specified in the request. /// /// - ///Get comment + ///Get a comment /// ///Comments ///Comment ID @@ -59,15 +59,15 @@ namespace ASC.Api.Projects } ///// - /////Updates the seleted comment using the comment text specified in the request + /////Updates the seleted comment using the comment text specified in the request. ///// ///// - /////Update comment + /////Update a comment ///// /////Comments - /////comment ID - /////comment text - /////Comment + /////Comment ID + /////Comment text + /////Updated comment ///// ///// ///// - ///Get preview + ///Get a preview of a project comment with the ID specified in the request. /// /// - ///Get preview + ///Get a comment preview /// ///Comments - ///html to create preview - ///guid of editing comment or empty string if comment is new + ///Comment text in the HTML format + ///Comment ID or empty string if a comment is new + ///Comment information [Create(@"comment/preview")] public CommentInfo GetProjectCommentPreview(string htmltext, string commentid) { @@ -145,12 +146,12 @@ namespace ASC.Api.Projects } /// - ///Remove comment with the id specified in the request + ///Removes a comment with the ID specified in the request. /// - ///Remove comment + ///Remove a comment ///
    Comments
    ///Comment ID - ///Comment id + ///Comment ID ///Comments [Delete("comment/{commentid}")] public string RemoveProjectComment(string commentid) @@ -172,14 +173,15 @@ namespace ASC.Api.Projects } /// - /// + /// Adds a project comment with the parameters specified in the request. The parent comment ID can also be selected. /// - /// - /// - /// - /// + /// Add a project comment + /// Parent comment ID + /// Entity ID + /// Comment text + /// Comment type (message or task) /// Comments - /// + /// Comment information [Create("comment")] public CommentInfo AddProjectComment(string parentcommentid, int entityid, string content, string type) { @@ -206,12 +208,13 @@ namespace ASC.Api.Projects } /// - /// + /// Updates the seleted comment using the comment text specified in the request. /// - /// - /// + /// Update a comment + /// Comment ID + /// New comment text ///Comments - /// + /// Updated comment [Update("comment/{commentid}")] public string UpdateComment(string commentid, string content) { diff --git a/module/ASC.Api/ASC.Api.Projects/ProjectApi.Messages.cs b/module/ASC.Api/ASC.Api.Projects/ProjectApi.Messages.cs index c5949f953..a974cf9ea 100644 --- a/module/ASC.Api/ASC.Api.Projects/ProjectApi.Messages.cs +++ b/module/ASC.Api/ASC.Api.Projects/ProjectApi.Messages.cs @@ -36,22 +36,22 @@ namespace ASC.Api.Projects public partial class ProjectApi { /// - ///Returns the list with the detailed information about all the message matching the filter parameters specified in the request + ///Returns a list with the detailed information about all the messages matching the filter parameters specified in the request. /// /// - /// Get message by filter + /// Get messages by filter /// ///Discussions /// Project ID - ///Project Tag + ///Project tag ///Departament GUID ///Participant GUID ///Minimum value of message creation date ///Maximum value of message creation date ///Last message ID - ///Messages in my projects - ///Followed messages - /// + ///Messages only from my projects or not + ///Messages only from followed discussions or not + ///Message status ///List of messages /// [Read(@"message/filter")] @@ -81,10 +81,10 @@ namespace ASC.Api.Projects } /// - ///Returns the list of all the messages in the discussions within the project with the ID specified in the request + ///Returns a list of all the messages in the discussions within a project with the ID specified in the request. /// /// - ///Messages + ///Get messages /// ///Discussions ///Project ID @@ -101,18 +101,18 @@ namespace ASC.Api.Projects } /// - ///Adds a message to the selected discussion within the project with the ID specified in the request + ///Adds a message to the selected discussion within a project with the ID specified in the request. /// /// - ///Add message + ///Add a message /// ///Discussions ///Project ID ///Discussion title ///Message text - ///IDs (GUIDs) of users separated with ',' - ///Notify participants - /// + ///User IDs (GUIDs) separated with ',' + ///Notifies participants about a message or not + ///Message /// /// [Create(@"{projectid:[0-9]+}/message")] @@ -139,19 +139,19 @@ namespace ASC.Api.Projects } /// - ///Updates the selected message in the discussion within the project with the ID specified in the request + ///Updates the selected message in the discussion within a project with the ID specified in the request. /// /// - ///Update message + ///Update a message /// ///Discussions ///Message ID ///Project ID ///Discussion title - ///Message text - ///IDs (GUIDs) of users separated with ',' - ///Notify participants - /// + ///New message text + ///New user IDs (GUIDs) separated with ',' + ///Notifies participants about a message or not + ///Updated message /// /// [Update(@"message/{messageid:[0-9]+}")] @@ -174,15 +174,15 @@ namespace ASC.Api.Projects } /// - ///Updates the selected message status + ///Updates the selected message status. /// /// - ///Update message status + ///Update a message status /// ///Discussions ///Message ID - ///Project ID - /// + ///New message status + ///Updated message /// /// [Update(@"message/{messageid:[0-9]+}/status")] @@ -200,14 +200,14 @@ namespace ASC.Api.Projects } /// - ///Deletes the message with the ID specified in the request from a project discussion + ///Deletes a message with the ID specified in the request from a project discussion. /// /// - ///Delete message + ///Delete a message /// ///Discussions ///Message ID - /// + ///Message /// [Delete(@"message/{messageid:[0-9]+}")] public MessageWrapper DeleteProjectMessage(int messageid) @@ -231,10 +231,10 @@ namespace ASC.Api.Projects } /// - ///Returns the detailed information about the message with the ID specified in the request + ///Returns the detailed information about a message with the ID specified in the request. /// /// - ///Message + ///Get a message /// ///Discussions ///Message ID @@ -252,14 +252,14 @@ namespace ASC.Api.Projects } /// - ///Returns the detailed information about files attached to the message with the ID specified in the request + ///Returns the detailed information about files attached to the message with the ID specified in the request. /// /// - ///Message files + ///Get message files /// ///Files ///Message ID - /// List of message files + ///List of message files /// [Read(@"message/{messageid:[0-9]+}/files")] public IEnumerable GetMessageFiles(int messageid) @@ -273,14 +273,14 @@ namespace ASC.Api.Projects } /// - ///Uploads the file specified in the request to the selected message + ///Uploads files specified in the request to the selected message. /// /// - ///Upload file to message + ///Upload files to the message /// ///Files ///Message ID - ///File ID + ///File IDs ///Message /// [Create(@"message/{messageid:[0-9]+}/files")] @@ -307,10 +307,10 @@ namespace ASC.Api.Projects } /// - ///Detaches the selected file from the message with the ID specified in the request + ///Detaches the selected file from a message with the ID specified in the request. /// /// - ///Detach file from message + ///Detach a file from a message /// ///Files ///Message ID @@ -334,10 +334,10 @@ namespace ASC.Api.Projects } /// - ///Detaches the selected file from the message with the ID specified in the request + ///Detaches the selected files from a message with the ID specified in the request. /// /// - ///Detach file from message + ///Detach files from a message /// ///Files ///Message ID @@ -369,13 +369,13 @@ namespace ASC.Api.Projects } /// - ///Returns the list of latest messages in the discussions within the project with the ID specified in the request + ///Returns a list of the recent messages in the discussions within a project with the ID specified in the request. /// /// - ///Latest messages + ///Get recent messages /// ///Discussions - ///List of messages + ///List of recent messages /// [Read(@"message")] public IEnumerable GetProjectRecentMessages() @@ -384,14 +384,14 @@ namespace ASC.Api.Projects } /// - ///Returns the list of comments to the messages in the discussions within the project with the ID specified in the request + ///Returns a list of comments for the discussion message within a project with the ID specified in the request. /// /// - ///Message comments + ///Get message comments /// ///Comments ///Message ID - ///Comments for message + ///Message comments /// [Read(@"message/{messageid:[0-9]+}/comment")] public IEnumerable GetProjectMessagesComments(int messageid) @@ -403,16 +403,16 @@ namespace ASC.Api.Projects } /// - ///Adds a comment to the selected message in a discussion within the project with the content specified in the request. The parent comment ID can also be selected. + ///Adds a comment to the selected message with the text specified in the request. The parent comment ID can be also selected. /// /// - ///Add message comment + ///Add a message comment /// ///Comments ///Message ID - ///Comment content - ///Parrent comment ID - /// + ///Comment text + ///Parent comment ID + ///Comment /// [Create(@"message/{messageid:[0-9]+}/comment")] public CommentWrapper AddProjectMessagesComment(int messageid, string content, Guid parentId) @@ -443,10 +443,10 @@ namespace ASC.Api.Projects } /// - ///Subscribe to notifications about the actions performed with the task with the ID specified in the request + ///Subscribes to the notifications about the actions performed in the discussion with the selected message. /// /// - ///Subscribe to message action + ///Subscribe to discussion /// ///Discussions ///Discussion @@ -467,13 +467,14 @@ namespace ASC.Api.Projects } /// - ///Checks subscription to notifications about the actions performed with the discussion with the ID specified in the request + ///Checks subscription to the notifications about the actions performed in the discussion with the selected message. /// /// - ///Check subscription to discussion action + ///Check subscription to discussion /// ///Discussions ///Message ID + ///Boolean value: True - subscibed, False - unsubscribed /// [Read(@"message/{messageid:[0-9]+}/subscribe")] public bool IsSubscribedToMessage(int messageid) @@ -488,13 +489,14 @@ namespace ASC.Api.Projects } /// - ///Get subscribers + ///Returns a list of all the subscribers of the discussion with the selected message. /// /// ///Get subscribers /// ///Discussions ///Message ID + ///List of subscibers /// [Read(@"message/{messageid:[0-9]+}/subscribes")] public IEnumerable GetProjectMessageSubscribers(int messageid) @@ -510,17 +512,18 @@ namespace ASC.Api.Projects } /// - ///Get preview + ///Returns a preview of the discussion message. /// /// - ///Get preview + ///Get a message preview /// ///Discussions - ///html to create preview + ///Message text in the HTML format + ///Message preview [Create(@"message/discussion/preview")] public string GetPreview(string htmltext) { - return HtmlUtility.GetFull(htmltext); + return HtmlUtility.GetFull(htmltext, false); } } } diff --git a/module/ASC.Api/ASC.Api.Projects/ProjectApi.Milestones.cs b/module/ASC.Api/ASC.Api.Projects/ProjectApi.Milestones.cs index 24747ee7b..5c43d4d3e 100644 --- a/module/ASC.Api/ASC.Api.Projects/ProjectApi.Milestones.cs +++ b/module/ASC.Api/ASC.Api.Projects/ProjectApi.Milestones.cs @@ -35,10 +35,10 @@ namespace ASC.Api.Projects #region milestone /// - ///Returns the list of all upcoming milestones within all portal projects + ///Returns a list of all the upcoming milestones within all the portal projects. /// /// - ///Upcoming milestones + ///Get milestones /// ///Milestones ///List of milestones @@ -49,21 +49,21 @@ namespace ASC.Api.Projects } /// - ///Returns the list of all milestones matching the filter with the parameters specified in the request + ///Returns a list of all the milestones matching the parameters specified in the request. /// /// - ///Milestones by filter + ///Get milestones by filter /// ///Milestones ///Project ID ///Project tag - ///Milstone status/ Can be open or closed + ///Milestone status (Open or Closed) ///Minimum value of task deadline ///Maximum value of task deadline - ///Responsible for the task in milestone GUID + ///Task responsible GUID ///Last milestone ID - ///Miletone in my Projects - ///Responsible for the milestone GUID + ///Returns milestones only from my projects + ///Milestone responsible GUID ///List of milestones /// [Read(@"milestone/filter")] @@ -104,10 +104,10 @@ namespace ASC.Api.Projects } /// - ///Returns the list of all overdue milestones in the portal projects + ///Returns a list of all the overdue milestones in the portal projects. /// /// - ///Overdue milestones + ///Get overdue milestones /// ///Milestones ///List of milestones @@ -118,10 +118,10 @@ namespace ASC.Api.Projects } /// - ///Returns the list of all milestones due on the date specified in the request + ///Returns a list of all the milestones with the deadline specified in the request. /// /// - ///Milestones by full date + ///Get milestones by deadline /// ///Milestones ///Deadline year @@ -136,10 +136,10 @@ namespace ASC.Api.Projects } /// - ///Returns the list of all milestones due in the month specified in the request + ///Returns a list of all the milestones with the deadline month specified in the request. /// /// - ///Milestones by month + ///Get milestones by deadline month /// ///Milestones ///Deadline year @@ -153,10 +153,10 @@ namespace ASC.Api.Projects } /// - ///Returns the list with the detailed information about the milestone with the ID specified in the request + ///Returns a list with the detailed information about a milestone with the ID specified in the request. /// /// - ///Get milestone + ///Get a milestone /// ///Milestones ///Milestone ID @@ -171,14 +171,14 @@ namespace ASC.Api.Projects } /// - ///Returns the list of all tasks within the milestone with the ID specified in the request + ///Returns a list of all the tasks within a milestone with the ID specified in the request. /// /// ///Get milestone tasks /// ///Milestones - ///Milestone ID - ///Tasks list + ///Milestone ID + ///List of tasks /// [Read(@"milestone/{id:[0-9]+}/task")] public IEnumerable GetMilestoneTasks(int id) @@ -188,22 +188,22 @@ namespace ASC.Api.Projects } /// - ///Updates the selected milestone changing the milestone parameters (title, deadline, status, etc.) specified in the request + ///Updates the selected milestone changing the milestone parameters (title, deadline, status, etc.) specified in the request. /// /// - ///Update milestone + ///Update a milestone /// ///Milestones ///Milestone ID - ///Title - ///Deadline - ///Is key or not - ///Status - ///Remind me 48 hours before the due date - ///Milestone description - ///Project ID - ///Milestone responsible - ///Notify responsible + ///New milestone title + ///New milestone deadline + ///Specifies if this is a key milestone or not + ///New milestone status + ///Reminds me 48 hours before the milestone due date + ///New milestone description + ///New project ID + ///New milestone responsible + ///Notifies responsible about the milestone actions or not ///Updated milestone /// /// @@ -251,14 +251,14 @@ namespace ASC.Api.Projects } /// - ///Updates the status of the milestone with the ID specified in the request + ///Updates a status of a milestone with the ID specified in the request. /// /// - ///Update milestone status + ///Update a milestone status /// ///Milestones ///Milestone ID - ///Status + ///New milestone status (Open or Closed) ///Updated milestone /// /// @@ -285,10 +285,10 @@ namespace ASC.Api.Projects } /// - ///Deletes the milestone with the ID specified in the request + ///Deletes a milestone with the ID specified in the request. /// /// - ///Delete milestone + ///Delete a milestone /// ///Milestones ///Milestone ID @@ -308,13 +308,13 @@ namespace ASC.Api.Projects } /// - ///Deletes milestones with the IDs specified in the request + ///Deletes the milestones with the IDs specified in the request. /// /// ///Delete milestones /// ///Milestones - ///Milestones ID + ///Milestone IDs ///Deleted milestones /// [Delete(@"milestone")] diff --git a/module/ASC.Api/ASC.Api.Projects/ProjectApi.Projects.cs b/module/ASC.Api/ASC.Api.Projects/ProjectApi.Projects.cs index ee44516d3..169479d41 100644 --- a/module/ASC.Api/ASC.Api.Projects/ProjectApi.Projects.cs +++ b/module/ASC.Api/ASC.Api.Projects/ProjectApi.Projects.cs @@ -53,10 +53,10 @@ namespace ASC.Api.Projects #region Read /// - ///Returns the list of all the portal projects with base information about them + ///Returns a list of all the portal projects with the base information about them. /// /// - ///Projects + ///Get projects /// ///Projects ///List of projects @@ -67,10 +67,10 @@ namespace ASC.Api.Projects } /// - ///Returns the list of all projects in which the current user participates + ///Returns a list of all the projects in which the current user participates. /// /// - ///Participated projects + ///Get my projects /// ///Projects ///List of projects @@ -85,10 +85,10 @@ namespace ASC.Api.Projects } /// - ///Returns the list of all projects which the current user follows + ///Returns a list of all the projects which the current user follows. /// /// - ///Followed projects + ///Get my followed projects /// ///Projects ///List of projects @@ -103,13 +103,13 @@ namespace ASC.Api.Projects } /// - ///Returns the list of all projects with the status specified in the request + ///Returns a list of all the projects with a status specified in the request. /// /// - ///Project by status + ///Get projects by status /// ///Projects - ///"open"|"paused"|"closed" + ///Project status: "open"|"paused"|"closed" ///List of projects [Read("{status:(open|paused|closed)}")] public IEnumerable GetProjects(ProjectStatus status) @@ -118,10 +118,10 @@ namespace ASC.Api.Projects } /// - ///Returns the detailed information about the project with ID specified in the request + ///Returns the detailed information about a project with ID specified in the request. /// /// - ///Project by ID + ///Get a project by ID /// ///Projects ///Project ID @@ -136,19 +136,19 @@ namespace ASC.Api.Projects } /// - ///Returns the list of all the portal projects filtered using project title, status or participant ID and 'Followed' status specified in the request + ///Returns a list of all the portal projects filtered by project title, status, participant ID and other parameters specified in the request. /// /// - ///Projects + ///Get filtered projects /// ///Projects ///Project tag - ///Project status - ///Participant GUID + ///Project status: "open"|"paused"|"closed" + ///Project participant GUID ///Project manager GUID - /// - ///My followed project - ///Projects list + ///Project department + ///Specifies if the current user follows this project or not + ///List of projects [Read(@"filter")] public IEnumerable GetProjectsByFilter(int tag, ProjectStatus? status, Guid participant, Guid manager, Guid departament, bool follow) @@ -176,10 +176,10 @@ namespace ASC.Api.Projects } /// - ///Returns the search results for the project containing the words/phrases matching the query specified in the request + ///Returns the search results for a project containing the words/phrases matching the query specified in the request. /// /// - ///Search project + ///Search in a project /// ///Projects ///Project ID @@ -194,10 +194,10 @@ namespace ASC.Api.Projects } /// - ///Returns the list of all projects matching the query specified in the request + ///Returns a list of all the projects matching the query specified in the request. /// /// - ///Search all projects + ///Search projects /// ///Projects ///Search query @@ -214,22 +214,22 @@ namespace ASC.Api.Projects #region Create /// - /// Creates a new project using all the necessary (title, description, responsible ID, etc) and some optional parameters specified in the request + /// Creates a new project using all the necessary (title, description, responsible ID, etc) and some optional parameters specified in the request. /// /// - /// Create project + /// Create a project /// /// Projects - /// Title - /// Description - /// Responsible ID - /// Tags - /// Is project private + /// Project title + /// Project description + /// Project responsible ID + /// Project tags + /// Private project or not /// Project participants - /// Notify project manager - /// - /// - /// + /// Notifies a project manager about the project actions or not + /// Project tasks + /// Project milestones + /// Notifies responsibles about the project actions or not /// Newly created project /// [Create("")] @@ -325,6 +325,24 @@ namespace ASC.Api.Projects return new ProjectWrapperFull(this, project, EngineFactory.FileEngine.GetRoot(project.ID)) { ParticipantCount = participantsList.Count() + 1 }; } + /// + /// Creates a new project with team security using all the necessary (title, description, responsible ID, etc) and some optional parameters specified in the request. + /// + /// + /// Create a project with team security + /// + /// Projects + /// Project title + /// Project description + /// Project responsible ID + /// Project tags + /// Private project or not + /// Project participants + /// Notifies a project manager about the project actions or not + /// Project tasks + /// Project milestones + /// Notifies responsibles about the project actions or not + /// Newly created project [Create("withSecurity")] public ProjectWrapperFull CreateProject(string title, string description, @@ -353,21 +371,21 @@ namespace ASC.Api.Projects #region Update /// - ///Updates the existing project information using all the parameters (project ID, title, description, responsible ID, etc) specified in the request + ///Updates the existing project using all the parameters (project ID, title, description, responsible ID, etc) specified in the request. /// /// - ///Update project + ///Update a project /// ///Projects ///Project ID - ///Title - ///Description - ///Responsible ID - ///Tags - ///participants - ///Is project private - ///Status. One of (Open|Closed) - ///Notify project manager + ///New project title + ///New project description + ///New project responsible ID + ///New project tags + ///New project participants + ///New project status: Open, Paused or Closed + ///Private project or not + ///Notifies a project manager about the project actions or not ///Updated project /// /// @@ -389,7 +407,7 @@ namespace ASC.Api.Projects } project.Title = Update.IfNotEmptyAndNotEquals(project.Title, title); - project.StatusChangedOn = DateTime.Now; + project.StatusChangedOn = TenantUtil.DateTimeNow(); if (status.HasValue) { @@ -418,6 +436,23 @@ namespace ASC.Api.Projects return ProjectWrapperFullSelector(project, EngineFactory.FileEngine.GetRoot(id)); } + /// + ///Updates the existing project with team security using all the parameters (project ID, title, description, responsible ID, etc) specified in the request. + /// + /// + ///Update a project with team security + /// + ///Projects + ///Project ID + ///New project title + ///New project description + ///New project responsible ID + ///New project tags + ///New project participants + ///New project status: Open, Paused or Closed + ///Private project or not + ///Notifies a project manager about the project actions or not + ///Updated project [Update(@"{id:[0-9]+}/withSecurityInfo")] public ProjectWrapperFull UpdateProject(int id, string title, string description, Guid responsibleId, string tags, IEnumerable participants, ProjectStatus? status, bool? @private, bool notify) @@ -437,14 +472,14 @@ namespace ASC.Api.Projects } /// - ///Updates the status of the project with the ID specified in the request + ///Updates a status of a project with the ID specified in the request. /// /// - ///Update project status + ///Update a project status /// ///Projects ///Project ID - ///Status. One of (Open|Paused|Closed) + ///New project status: Open, Paused or Closed ///Updated project /// [Update(@"{id:[0-9]+}/status")] @@ -464,10 +499,10 @@ namespace ASC.Api.Projects #region Delete /// - ///Deletes the project with the ID specified in the request from the portal + ///Deletes a project with the ID specified in the request from the portal. /// /// - ///Delete project + ///Delete a project /// ///Projects ///Project ID @@ -489,14 +524,14 @@ namespace ASC.Api.Projects } /// - ///Deletes the project with the ID specified in the request from the portal + ///Deletes the projects with the IDs specified in the request from the portal. /// /// - ///Delete project + ///Delete projects /// ///Projects - ///Project IDs - ///Deleted project + ///List of project IDs + ///Deleted projects /// [Delete(@"")] public IEnumerable DeleteProjects(int[] projectids) @@ -523,10 +558,10 @@ namespace ASC.Api.Projects #region Follow, Tags, Time /// - ///Subscribe or unsubscribe to notifications about the actions performed with the project with the ID specified in the request + ///Subscribes to or unsubscribes from the notifications about the actions performed in the project with the ID specified in the request. /// /// - ///Following/Unfollowing project + ///Project subscription /// ///Projects ///Project ID @@ -556,15 +591,15 @@ namespace ASC.Api.Projects } /// - ///Updates the tags for the project with the selected project ID with the tags specified in the request + ///Updates a tag for the selected project with a tag specified in the request. /// /// - ///Update project tags + ///Update a project tag /// - ///Projects + ///Tags ///Project ID - ///Tags - ///project + ///New project tag + ///Project /// [Update(@"{id:[0-9]+}/tag")] public ProjectWrapperFull UpdateProjectTags(int id, string tags) @@ -578,15 +613,15 @@ namespace ASC.Api.Projects } /// - ///Updates the tags for the project with the selected project ID with the tags specified in the request + ///Updates the tags for the selected project with the tags specified in the request. /// /// ///Update project tags /// - ///Projects + ///Tags ///Project ID - ///Tags - ///project + ///New project tags + ///Project /// [Update(@"{id:[0-9]+}/tags")] public ProjectWrapperFull UpdateProjectTags(int id, IEnumerable tags) @@ -600,14 +635,14 @@ namespace ASC.Api.Projects } /// - ///Returns the detailed information about the time spent on the project with the ID specified in the request + ///Returns the detailed information about the time spent on the project with the ID specified in the request. /// /// - ///Project time spent + ///Get project time /// - ///Projects + ///Time ///Project ID - ///List of time spent + ///List of project time /// [Read(@"{id:[0-9]+}/time")] public IEnumerable GetProjectTime(int id) @@ -617,11 +652,12 @@ namespace ASC.Api.Projects } /// - /// + ///Returns the total time spent on the project with the ID specified in the request. /// - ///Projects + ///Get total project time + ///Time ///Project ID - ///List of time spent + ///Project time /// [Read(@"{id:[0-9]+}/time/total")] public string GetTotalProjectTime(int id) @@ -635,21 +671,21 @@ namespace ASC.Api.Projects #region Milestones /// - ///Creates a new milestone using the parameters (project ID, milestone title, deadline, etc) specified in the request + ///Adds a new milestone using the parameters (project ID, milestone title, deadline, etc) specified in the request. /// /// - ///Add milestone + ///Add a milestone /// - ///Projects + ///Milestones ///Project ID ///Milestone title ///Milestone deadline - ///Is milestone key or not - ///Remind me 48 hours before the due date + ///Specifies if this is a key milestone or not + ///Reminds me 48 hours before the milestone due date ///Milestone description ///Milestone responsible - ///Notify responsible - ///Created milestone + ///Notifies the responsible about the milestone actions or not + ///Added milestone /// /// [Create(@"{id:[0-9]+}/milestone")] @@ -679,12 +715,12 @@ namespace ASC.Api.Projects } /// - ///Returns the list of all the milestones within the project with the ID specified in the request + ///Returns a list of all the milestones within a project with the ID specified in the request. /// /// ///Get milestones by project ID /// - ///Projects + ///Milestones ///Project ID ///List of milestones /// @@ -702,12 +738,12 @@ namespace ASC.Api.Projects } /// - ///Returns the list of all the milestones with the selected status within the project with the ID specified in the request + ///Returns a list of all the milestones with the selected status within a project with the ID specified in the request. /// /// ///Get milestones by project ID and milestone status /// - ///Projects + ///Milestones ///Project ID ///Milestone status ///List of milestones @@ -729,12 +765,12 @@ namespace ASC.Api.Projects #region Team /// - ///Returns the list of all users participating in the project with the ID specified in the request + ///Returns a list of all the users participating in the project with the ID specified in the request. /// /// - ///Project team + ///Get a project team /// - ///Team + ///Teams ///Project ID ///List of team members [Read(@"{projectid:[0-9]+}/team")] @@ -749,12 +785,12 @@ namespace ASC.Api.Projects } /// - ///Returns the list of all users participating in the project with the ID specified in the request + ///Returns a list of all the current and excluded project team members. /// /// - ///Project team + ///Get a project team with excluded members /// - ///Team + ///Teams ///Project ID ///List of team members [Read(@"{projectid:[0-9]+}/teamExcluded")] @@ -769,13 +805,13 @@ namespace ASC.Api.Projects } /// - ///Returns the list of all users participating in the project with the ID specified in the request + ///Returns a list of all the users participating in the projects with the ID specified in the request. /// /// - ///Project team + ///Get team members of projects /// - ///Team - ///Project IDs + ///Teams + ///List of project IDs ///List of team members [Create(@"team")] public IEnumerable GetProjectTeam(List ids) @@ -786,14 +822,14 @@ namespace ASC.Api.Projects } /// - ///Adds the user with the ID specified in the request to the selected project team + ///Adds a user with the ID specified in the request to the selected project team. /// /// - ///Add to team + ///Add a user to the team /// - ///Team + ///Teams ///Project ID - ///ID of the user to add + ///User ID ///List of team members /// [Create(@"{projectid:[0-9]+}/team")] @@ -810,16 +846,16 @@ namespace ASC.Api.Projects } /// - ///Sets the security rights for the user or users with the IDs specified in the request within the selected project + ///Sets the security rights to the user with the ID specified in the request within the selected project. /// /// ///Set team security /// - ///Team + ///Teams ///Project ID - ///ID of the user to set + ///User ID ///Security rights - ///Visible + ///Specifies if the security rights for the user will be visible or not ///List of team members /// [Update(@"{projectid:[0-9]+}/team/security")] @@ -848,14 +884,14 @@ namespace ASC.Api.Projects } /// - ///Removes the user with the ID specified in the request from the selected project team + ///Removes a user with the ID specified in the request from the selected project team. /// /// - ///Remove from team + ///Remove a user from the team /// - ///Team + ///Teams ///Project ID - ///ID of the user to add + ///User ID ///List of team members /// [Delete(@"{projectid:[0-9]+}/team")] @@ -875,16 +911,16 @@ namespace ASC.Api.Projects } /// - ///Updates the project team with the users IDs specified in the request + ///Updates a project team with the user IDs specified in the request. /// /// - ///Updates project team + ///Update a project team /// - ///Team + ///Teams ///Project ID - ///IDs of users to update team - ///Notify project team - ///String with the number of project participants + ///List of user IDs + ///Notifies a project team about the updated project or not + ///Number of project participants /// [Update(@"{projectid:[0-9]+}/team")] public IEnumerable UpdateProjectTeam(int projectId, IEnumerable participants, bool notify) @@ -908,14 +944,14 @@ namespace ASC.Api.Projects #region Task /// - ///Returns the list of all the tasks within the project with the ID specified in the request + ///Returns a list of all the tasks within a project with the ID specified in the request. /// /// - ///Tasks + ///Get tasks /// ///Tasks ///Project ID - /// + ///List of tasks ///List of tasks [Read(@"{projectid:[0-9]+}/task")] public IEnumerable GetProjectTasks(int projectid) @@ -929,22 +965,22 @@ namespace ASC.Api.Projects } /// - ///Adds the task to the selected project with the parameters (responsible user ID, task description, deadline time, etc) specified in the request + ///Adds a task to the selected project with the parameters (responsible user ID, task description, deadline time, etc) specified in the request. /// /// - ///Add task + ///Add a task /// ///Tasks ///Project ID - ///Description - ///Deadline time - ///Priority: Low|Normal|High - ///Title - ///Milestone ID - ///List responsibles - ///Notify responsible - /// - ///Created task + ///Task description + ///Task deadline time + ///Task priority: Low|Normal|High + ///Task title + ///Task milestone ID + ///List of responsibles + ///Notifies the responsibles about the task actions or not + ///Task start date + ///Added task /// [Create(@"{projectid:[0-9]+}/task")] public TaskWrapper AddProjectTask(int projectid, string description, ApiDateTime deadline, @@ -985,15 +1021,15 @@ namespace ASC.Api.Projects } /// - ///Adds the task to the selected project + ///Adds a task to the selected project by message ID specified in the request. /// /// - ///Add task + ///Add a task by message ID /// ///Tasks ///Project ID ///Message ID - ///Created task + ///Added task /// [Create(@"{projectid:[0-9]+}/task/{messageid:[0-9]+}")] public TaskWrapper AddProjectTaskByMessage(int projectid, int messageid) @@ -1069,14 +1105,14 @@ namespace ASC.Api.Projects } /// - ///Returns the list of all tasks with the selected status in the project with the ID specified in the request + ///Returns a list of all the tasks with the selected status in the project with the ID specified in the request. /// /// - ///Tasks with status + ///Get tasks by status /// ///Tasks ///Project ID - ///Task status. Can be one of: notaccept|open|closed|disable|unclassified|notinmilestone + ///Task status: not accept|open|closed|disable|unclassified|not in milestone ///List of tasks /// [Read(@"{projectid:[0-9]+}/task/{status:(notaccept|open|closed|disable|unclassified|notinmilestone)}")] @@ -1089,14 +1125,14 @@ namespace ASC.Api.Projects } /// - ///Returns the list of all tasks for the current user with the selected status in the project with the ID specified in the request + ///Returns a list of all the tasks for the current user with the selected status in the project with the ID specified in the request. /// /// - ///My tasks + ///Get my tasks by status and project ID /// ///Tasks ///Project ID - ///Task status. Can be one of: notaccept|open|closed|disable|unclassified|notinmilestone + ///Task status: not accept|open|closed|disable|unclassified|not in milestone ///List of tasks /// [Read(@"{projectid:[0-9]+}/task/@self/{status:(notaccept|open|closed|disable|unclassified|notinmilestone)}")] @@ -1115,12 +1151,12 @@ namespace ASC.Api.Projects #region Files /// - ///Returns the detailed list of all files and folders for the project with the ID specified in the request + ///Returns the detailed list of all the files and folders for the project with the ID specified in the request. /// /// - ///Project files by project ID + ///Get project files by project ID /// - ///Projects + ///Files ///Project ID ///Project files /// @@ -1136,15 +1172,15 @@ namespace ASC.Api.Projects } /// - ///Returns the list of all files within the entity (project, milestone, task) with the type and ID specified + ///Returns a list of all the files within the entity (message or task) with the type and ID specified in the request. /// /// - ///Entity files + ///Get entity files /// ///Files ///Entity type ///Entity ID - ///Message + ///Files /// [Read(@"{entityID:[0-9]+}/entityfiles")] public IEnumerable GetEntityFiles(EntityType entityType, int entityID) @@ -1162,15 +1198,15 @@ namespace ASC.Api.Projects } /// - ///Uploads the selected files to the entity (project, milestone, task) with the type and ID specified + ///Uploads the selected files to the entity (project, milestone, task) with the type and ID specified in the request. /// /// - ///Upload file to entity + ///Upload files to the entity /// ///Files ///Entity type ///Entity ID - ///File IDs + ///List of file IDs ///Uploaded files /// ///false @@ -1197,13 +1233,13 @@ namespace ASC.Api.Projects } /// - ///Detaches the selected file from the entity (project, milestone, task) with the type and ID specified + ///Detaches the selected file from the entity (project, milestone, task) with the type and ID specified in the request. /// /// - ///Detach file from entity + ///Detach a file from the entity /// ///Files - ///Entity type + ///Entity type ///Entity ID ///File ID ///Detached file @@ -1228,16 +1264,16 @@ namespace ASC.Api.Projects } /// - ///Detaches the selected file from the entity (project, milestone, task) with the type and ID specified + ///Detaches the selected files from the entity (project, milestone, task) with the type and ID specified in the request. /// /// - ///Detach file from entity + ///Detach files from the entity /// ///Files - ///Entity type + ///Entity type ///Entity ID - ///files - ///Detached file + ///List of file IDs + ///Detached files /// ///false [Delete(@"{entityID:[0-9]+}/entityfilesmany")] @@ -1263,21 +1299,21 @@ namespace ASC.Api.Projects } /// - ///Uploads the selected files to the entity (project, milestone, task) with the type and ID specified + ///Uploads the selected files to the entity (project, milestone, task) with the type and ID specified in the request. /// /// - ///Upload file to entity + ///Upload files to the entity /// ///Files - ///Entity type + ///Entity type ///Entity ID - ///ID of the folder to upload to - ///Request enput stream - ///Content-type header - ///Content disposition header + ///Folder ID + ///Request input stream + ///Content-Type header + ///Content-Disposition header ///List of files when posted as multipart/form-data - ///Create new if exist - ///If true, upload documents in original formats as well + ///Creates a new file if it already exists + ///If true, then upload files in the original formats as well ///Uploaded files /// ///false @@ -1324,13 +1360,13 @@ namespace ASC.Api.Projects #region contacts /// - /// Returns the list of all the projects linked with the contact with the ID specified in the request + /// Returns a list of all the projects linked with a contact with the ID specified in the request. /// ///Contact ID ///Contacts - ///Get projects for contact + ///Get contact projects /// - /// Projects list + ///List of projects /// /// [Read("contact/{contactid:[0-9]+}")] @@ -1343,12 +1379,12 @@ namespace ASC.Api.Projects } /// - /// Adds the selected contact to the project with the ID specified in the request + /// Adds the selected contact to the project with the ID specified in the request. /// ///Project ID ///Contact ID ///Contacts - ///Add project contact + ///Add a project contact ///Project /// [Create(@"{projectid:[0-9]+}/contact")] @@ -1371,12 +1407,12 @@ namespace ASC.Api.Projects } /// - /// Deletes the selected contact from the project with the ID specified in the request + /// Deletes the selected contact from the project with the ID specified in the request. /// ///Project ID ///Contact ID ///Contacts - ///Delete project contact + ///Delete a project contact ///Project /// [Delete("{projectid:[0-9]+}/contact")] @@ -1403,12 +1439,12 @@ namespace ASC.Api.Projects #region templates /// - ///Returns the list of all the templates with base information about them + ///Returns a list of all the templates with the base information about them. /// /// - ///Templates + ///Get templates /// - ///Template + ///Templates ///List of templates [Read("template")] public IEnumerable GetAllTemplates() @@ -1417,12 +1453,12 @@ namespace ASC.Api.Projects } /// - ///Returns the detailed information about the template with ID specified in the request + ///Returns the detailed information about a template with ID specified in the request. /// /// - ///Template by ID + ///Get a template by ID /// - ///Template + ///Templates ///Template ID ///Template /// @@ -1434,14 +1470,14 @@ namespace ASC.Api.Projects } /// - ///Creates a new template + ///Creates a new template with the title and description specified in the request. /// /// - ///Create template + ///Create a template /// - ///Template - ///Title - ///JSON template structure. Sample: {"tasks":[{"title":"Task without milestone"}],"milestones":[{"title":"milestone title","duration":0.5,"tasks":[{"title":"task milestone"}]}]} + ///Templates + ///Template title + ///JSON template structure in the following format: {"tasks":[{"title":"Task without milestone"}],"milestones":[{"title":"milestone title","duration":0.5,"tasks":[{"title":"milestone task"}]}]} ///Newly created template /// [Create("template")] @@ -1464,15 +1500,15 @@ namespace ASC.Api.Projects } /// - ///Updates the existing template information + ///Updates the existing template information with the parameters specified in the request. /// /// - ///Update template + ///Update a template /// - ///Template + ///Templates ///Template ID - ///Title - ///JSON template structure. Sample: {"tasks":[{"title":"Task without milestone"}],"milestones":[{"title":"milestone title","duration":0.5,"tasks":[{"title":"task milestone"}]}]} + ///New template title + ///New JSON template structure in the following format: {"tasks":[{"title":"Task without milestone"}],"milestones":[{"title":"milestone title","duration":0.5,"tasks":[{"title":"milestone task"}]}]} ///Updated template /// /// @@ -1495,13 +1531,13 @@ namespace ASC.Api.Projects } /// - ///Deletes the template with the ID specified in the request from the portal + ///Deletes a template with the ID specified in the request from the portal. /// /// - ///Delete template + ///Delete a template /// - ///Template - ///Project ID + ///Templates + ///Template ID ///Deleted template /// [Delete(@"template/{id:[0-9]+}")] @@ -1521,13 +1557,13 @@ namespace ASC.Api.Projects #region HACK: Hidden api methods /// - /// Returns the basic information about the access rights + /// Returns the basic information about the access rights. /// /// - /// Access rights info + /// Get security information /// ///Projects - ///Basic information about access rights + ///Basic information about the access rights ///false [Read("securityinfo")] public CommonSecurityInfo GetProjectSecurityInfo() @@ -1535,6 +1571,14 @@ namespace ASC.Api.Projects return new CommonSecurityInfo(); } + /// + /// Returns the last modified project. + /// + /// + /// Get the last modified project + /// + ///Projects + ///Last modified project ///false [Read("maxlastmodified")] public string GetProjectMaxLastModified() @@ -1545,6 +1589,15 @@ namespace ASC.Api.Projects return result + EngineFactory.ProjectEngine.Count().ToString(); } + /// + /// Returns the current task order in the project with the ID specified in the request. + /// + /// + /// Get the task order + /// + ///Tasks + ///Project ID + ///Task order ///false [Read(@"{id:[0-9]+}/order")] public string GetTaskOrder(int id) @@ -1555,6 +1608,16 @@ namespace ASC.Api.Projects return projectEngine.GetTaskOrder(project); } + /// + /// Sets the task order to the project with the ID specified in the request. + /// + /// + /// Set the task order + /// + ///Tasks + ///Project ID + ///Task order + ///Task order ///false [Update(@"{id:[0-9]+}/order")] public void SetTaskOrder(int id, string order) diff --git a/module/ASC.Api/ASC.Api.Projects/ProjectApi.Reports.cs b/module/ASC.Api/ASC.Api.Projects/ProjectApi.Reports.cs index 77b656371..84d700d92 100644 --- a/module/ASC.Api/ASC.Api.Projects/ProjectApi.Reports.cs +++ b/module/ASC.Api/ASC.Api.Projects/ProjectApi.Reports.cs @@ -37,6 +37,15 @@ namespace ASC.Api.Projects { public partial class ProjectApi { + /// + ///Checks the report data by the URI specified in the request. + /// + /// + ///Check the report data + /// + ///Report + ///Report URI + ///Report state [Create(@"report/create")] public ReportState CheckReportData(string uri) { @@ -49,6 +58,14 @@ namespace ASC.Api.Projects return state; } + /// + ///Returns a report status. + /// + /// + ///Get a report status + /// + ///Report + ///Report status [Read(@"report/status")] public ReportState GetReportStatus() { @@ -57,6 +74,13 @@ namespace ASC.Api.Projects return DocbuilderReportsUtility.Status(ReportOrigin.Projects); } + /// + ///Terminates the reporting process. + /// + /// + ///Terminate the reporting process + /// + ///Report [Read(@"report/terminate")] public void TerminateReport() { @@ -66,28 +90,31 @@ namespace ASC.Api.Projects } /// - ///Creates the project report template with the parameters specified in the request. Most of the parameters are optional and depend on the report type selected. + ///Creates a project report template with the parameters specified in the request. Most of the parameters are optional and depend on the report type selected. /// /// - ///Create report template + ///Create a report template /// ///Report ///Report name - ///Report template generation period. Can be one of the following: Day/Week/Month - ///Period item. Can be: weekday name (for weekly report, starting with Sunday), day of the month (for monthly report). - ///Send time (hour) - ///Automatically generated or not + ///Report template generation period: Day/Week/Month + ///Period item: weekday name (for weekly report, starting with Sunday), day of the month (for monthly report) + ///Report sending time (in hours) + ///Specifies if a report will be automatically generated or not ///Report type - ///Tag - ///Project + ///Report tag + ///Report project ///Task status ///Department/Group ///User GUID ///Report time interval ///Report period start date ///Report period end date - ///View type: view by group or view by project - ///Show or hide tasks without responsible + ///Report view type: by group or by project + ///Specifies if the tasks without responsibles will be shown or hidden + ///Specifies if the average time will be shown or not + ///Average time type (All, ClosingProjects, CompletingTasks) + ///Specifies if the average time of closing projects will be shown or not ///Project report template [Create(@"report")] public ReportTemplateWrapper SaveReportTemplate( @@ -106,7 +133,11 @@ namespace ASC.Api.Projects ApiDateTime fromDate, ApiDateTime toDate, int viewType, - bool noResponsible) + bool noResponsible, + bool isShowAverageTime, + AverageTime typeOfShowAverageTime, + bool projectAverageCompletingTasks + ) { ProjectSecurity.DemandAuthentication(); @@ -121,8 +152,15 @@ namespace ASC.Api.Projects FromDate = fromDate, ToDate = toDate, ViewType = viewType, - NoResponsible = noResponsible + NoResponsible = noResponsible, + IsShowAverageTime = isShowAverageTime, + TypeOfShowAverageTime = typeOfShowAverageTime }; + if (projectAverageCompletingTasks) + { + filter.IsShowAverageTime = true; + filter.TypeOfShowAverageTime = AverageTime.CompletingTasks; + } if (project != 0) { @@ -143,30 +181,30 @@ namespace ASC.Api.Projects } /// - ///Updates the selected project report template with the parameters specified in the request + ///Updates the selected project report template with the parameters specified in the request. /// /// - ///Update report template + ///Update a report template /// ///Report ///Report template ID - ///Report name - ///Report template generation period. Can be one of the following: Day/Week/Month - ///Period item. Can be: weekday name (for weekly report, starting with Sunday), day of the month (for monthly report). - ///Send time (hour) - ///Automatically generated or not - ///Report type - ///Tag - ///Project - ///Task status - ///Department/Group - ///User GUID - ///Report time interval - ///Report period start date - ///Report period end date - ///View type: view by group or view by project - ///Show or hide tasks without responsible - ///Project report template + ///New report name + ///New report template generation period: Day/Week/Month + ///New period item: weekday name (for weekly report, starting with Sunday), day of the month (for monthly report) + ///New report sending time (in hours) + ///Specifies if the report will be automatically generated or not + ///New report type + ///New report tag + ///New report project + ///New task status + ///New department/group + ///New user GUID + ///New report time interval + ///New report period start date + ///New report period end date + ///New view type: by group or by project + ///Specifies if the tasks without responsibles will be shown or hidden + ///Updated project report template [Update(@"report/{reportid:[0-9]+}")] public ReportTemplateWrapper UpdateReportTemplate( int reportid, @@ -221,11 +259,14 @@ namespace ASC.Api.Projects } /// - /// + ///Returns a project report template with the ID specified in the request. /// + /// + ///Get a report template + /// ///Report - /// - /// + ///Report template ID + ///Project report template [Read(@"report/{reportid:[0-9]+}")] public ReportTemplateWrapper GetReportTemplate(int reportid) { @@ -234,10 +275,10 @@ namespace ASC.Api.Projects } /// - ///Deletes the project report template with the ID specified in the request + ///Deletes a project report template with the ID specified in the request. /// /// - ///Delete report template + ///Delete a report template /// ///Report ///Report template ID @@ -257,6 +298,14 @@ namespace ASC.Api.Projects return new ReportTemplateWrapper(reportTemplate); } + /// + ///Returns the generated report files. + /// + /// + ///Get generated reports + /// + ///Report + ///Generated report files [Read(@"report/files")] public IEnumerable GetGeneratedReports() { @@ -269,6 +318,15 @@ namespace ASC.Api.Projects return EngineFactory.FileEngine.GetFiles(fileIds).Select(r => new FileWrapper(r)).OrderByDescending(r => r.Id).ToList(); } + /// + ///Removes the generated report file with the ID specified in the request. + /// + /// + ///Remove the generated report + /// + ///Report + ///Report file ID + ///Generated report file [Delete(@"report/files/{fileid:[0-9]+}")] public ReportFile RemoveGeneratedReport(int fileid) { diff --git a/module/ASC.Api/ASC.Api.Projects/ProjectApi.Tags.cs b/module/ASC.Api/ASC.Api.Projects/ProjectApi.Tags.cs index d00e4a375..407190cac 100644 --- a/module/ASC.Api/ASC.Api.Projects/ProjectApi.Tags.cs +++ b/module/ASC.Api/ASC.Api.Projects/ProjectApi.Tags.cs @@ -29,10 +29,10 @@ namespace ASC.Api.Projects public partial class ProjectApi { /// - ///Returns the list of all available project tags + ///Returns a list of all the available project tags. /// /// - ///Project tags + ///Get project tags /// ///Tags ///List of tags @@ -43,12 +43,13 @@ namespace ASC.Api.Projects } /// - ///Creates new tag + ///Creates a new tag with the data specified in the request. /// /// - ///Tag + ///Create a tag /// ///Tags + ///Tag data ///Created tag [Create(@"tag")] public ObjectWrapperBase CreateNewTag(string data) @@ -62,10 +63,10 @@ namespace ASC.Api.Projects } /// - ///Returns the detailed list of all projects with the specified tag + ///Returns the detailed list of all the projects with a tag specified in the request. /// /// - ///Project by tag + ///Get projects by a tag /// ///Tags ///Tag name @@ -79,20 +80,22 @@ namespace ASC.Api.Projects /// - ///Returns the list of all tags like the specified tag name + ///Returns a list of all the tags by the tag name specified in the request. /// /// - ///Tags by tag name + ///Get tags by a tag name /// ///Tags ///Tag name ///List of tags [Read(@"tag/search")] - public string[] GetTagsByName(string tagName) + public IEnumerable GetTagsByName(string tagName) { - return !string.IsNullOrEmpty(tagName) && tagName.Trim() != string.Empty - ? EngineFactory.TagEngine.GetTags(tagName.Trim()).Select(r => r.Value).ToArray() - : new string[0]; + tagName = (tagName ?? "").Trim(); + + if (string.IsNullOrEmpty(tagName)) return new List(); + + return EngineFactory.TagEngine.GetTags(tagName).Select(x => new ObjectWrapperBase { Id = x.Key, Title = x.Value }); } } } \ No newline at end of file diff --git a/module/ASC.Api/ASC.Api.Projects/ProjectApi.Tasks.cs b/module/ASC.Api/ASC.Api.Projects/ProjectApi.Tasks.cs index 57b4108cb..84d53f82d 100644 --- a/module/ASC.Api/ASC.Api.Projects/ProjectApi.Tasks.cs +++ b/module/ASC.Api/ASC.Api.Projects/ProjectApi.Tasks.cs @@ -39,10 +39,10 @@ namespace ASC.Api.Projects #region tasks /// - ///Returns the list with the detailed information about all tasks for the current user + ///Returns a list with the detailed information about all the tasks for the current user. /// /// - ///My tasks + ///Get my tasks /// ///Tasks ///List of tasks @@ -56,13 +56,13 @@ namespace ASC.Api.Projects } /// - ///Returns the list with the detailed information about the tasks for the current user with the status specified in the request + ///Returns a list with the detailed information about the tasks for the current user with a status specified in the request. /// /// - ///My tasks by status + ///Get my tasks by status /// ///Tasks - ///Status of task. One of notaccept|open|closed|disable|unclassified|notinmilestone + ///Task status: not accept|open|closed|disable|unclassified|not in milestone ///List of tasks [Read(@"task/@self/{status:(notaccept|open|closed|disable|unclassified|notinmilestone)}")] public IEnumerable GetMyTasks(TaskStatus status) @@ -74,10 +74,10 @@ namespace ASC.Api.Projects } /// - ///Returns the detailed information about the task with the ID specified in the request + ///Returns the detailed information about a task with the ID specified in the request. /// /// - ///Get task + ///Get a task /// ///Tasks ///Task ID @@ -106,6 +106,15 @@ namespace ASC.Api.Projects return new TaskWrapper(this, task, milestone); } + /// + ///Returns the detailed information about the tasks with the IDs specified in the request. + /// + /// + ///Get tasks + /// + ///Tasks + ///Task IDs + ///Task ///false [Read(@"task")] public IEnumerable GetTask(IEnumerable taskid) @@ -115,27 +124,27 @@ namespace ASC.Api.Projects } /// - ///Returns the list with the detailed information about all the tasks matching the filter parameters specified in the request + ///Returns a list with the detailed information about all the tasks matching the filter parameters specified in the request. /// /// - ///Get task by filter + ///Get tasks by filter /// ///Tasks - /// Project Id - ///Project Tag - ///Task Status - ///Custom Task Status + /// Project ID + ///Returns tasks only from my projects + ///Milestone ID + ///Returns tasks only from my milestones + ///Returns tasks only without milestones + ///Project tag + ///Task status + ///Custom task status + ///Messages only from followed tasks or not ///Departament GUID ///Participant GUID ///Creator GUID - ///Milestone ID ///Minimum value of task deadline ///Maximum value of task deadline ///Last task ID - ///Tasks in My Projects - ///Tasks in My Milestones - ///Tasks without Milestone - ///Followed tasks ///List of tasks /// [Read(@"task/filter")] @@ -174,25 +183,25 @@ namespace ASC.Api.Projects } /// - ///Returns the list with the detailed information about all the tasks matching the filter parameters specified in the request + ///Returns a list of all the tasks matching the filter parameters specified in the request. /// /// - ///Get task by filter + ///Get tasks without detailed information by filter /// ///Tasks - /// Project Id + /// Project ID + ///Returns tasks only from my projects + ///Milestone ID + ///Returns tasks only from my milestones ///Project Tag ///Task Status + ///Messages only from followed tasks or not ///Departament GUID ///Participant GUID ///Creator GUID - ///Milestone ID ///Minimum value of task deadline ///Maximum value of task deadline ///Last task ID - ///Tasks in My Projects - ///Tasks in My Milestones - ///Followed tasks ///false ///List of tasks /// @@ -229,7 +238,7 @@ namespace ASC.Api.Projects } /// - ///Returns the list of all files attached to the task with the ID specified in the request + ///Returns a list of all the files attached to the task with the ID specified in the request. /// /// ///Get task files @@ -251,15 +260,15 @@ namespace ASC.Api.Projects } /// - ///Uploads the file specified in the request to the selected task + ///Uploads the files specified in the request to the selected task. /// /// - ///Upload file to task + ///Upload files to the task /// ///Files ///Task ID - ///File ID - ///List of tasks + ///File IDs + ///Task /// [Create(@"task/{taskid:[0-9]+}/files")] public TaskWrapper UploadFilesToTask(int taskid, IEnumerable files) @@ -286,10 +295,10 @@ namespace ASC.Api.Projects } /// - ///Detaches the selected file from the task with the ID specified in the request + ///Detaches the selected file from a task with the ID specified in the request. /// /// - ///Detach file from task + ///Detach a file from a task /// ///Files ///Task ID @@ -314,14 +323,14 @@ namespace ASC.Api.Projects } /// - ///Detaches the selected file from the task with the ID specified in the request + ///Detaches the selected files from a task with the ID specified in the request. /// /// - ///Detach file from task + ///Detach files from a task /// ///Files ///Task ID - ///files + ///File IDs ///Task /// ///false @@ -350,15 +359,15 @@ namespace ASC.Api.Projects } /// - /// Updates the status of the task with the ID specified in the request + /// Updates a status of a task with the ID specified in the request. /// /// - /// Update task status + /// Update a task status by task ID /// /// Tasks /// Task ID - /// Status of task. Can be one of: open|closed - /// + /// New task status: Open or Closed + /// Custom status ID /// Updated task /// [Update(@"task/{taskid:[0-9]+}/status")] @@ -376,15 +385,16 @@ namespace ASC.Api.Projects } /// - ///Updates the status of the tasks with the IDs specified in the request + ///Updates a status of the tasks with the IDs specified in the request. /// /// - ///Update tasks status + ///Update a status of tasks /// ///Tasks - ///Tasks ID - ///Status of tasks. Can be one of: open|closed - ///Custom status id + ///Task IDs + ///New task status: Open or Closed + ///New custom status ID + ///Updated tasks [Update(@"task/status")] public IEnumerable UpdateTasks(int[] taskids, TaskStatus status, int statusId = 0) { @@ -406,10 +416,10 @@ namespace ASC.Api.Projects } /// - ///Updates the milestone of the task with the ID specified in the request + ///Updates a milestone of a task with the ID specified in the request. /// /// - ///Update task milestone + ///Update a task milestone /// ///Tasks ///Task ID @@ -441,13 +451,13 @@ namespace ASC.Api.Projects } /// - ///Updates the milestone of the tasks with the IDs specified in the request + ///Updates a milestone of the tasks with the IDs specified in the request. /// /// - ///Update tasks milestone + ///Update a milestone of tasks /// ///Tasks - ///Task ID + ///Task IDs ///Milestone ID ///Updated tasks /// @@ -474,26 +484,26 @@ namespace ASC.Api.Projects } /// - ///Copy task + ///Copies a task with the parameters specified in the request. /// /// - ///Copy task + ///Copy a task /// ///Tasks ///Project ID - ///Description - ///Deadline time - ///Priority: Low|Normal|High - ///Title - ///Milestone ID - ///List responsibles - ///Notify responsible - /// - /// - /// - /// - /// - ///Created task + ///Task description + ///Task deadline + ///Task priority: Low|Normal|High + ///Task title + ///Task milestone ID + ///List of task responsibles + ///Notifies responsibles about the task actions or not + ///Task start date + ///Task ID from which the information is copied + ///Specifies if the subtasks will be copied or not + ///Specifies if the files will be copied or not + ///Specifies if the original task will be removed or not + ///Copied task /// [Create(@"task/{copyFrom:[0-9]+}/copy")] public TaskWrapper CopyTask(int projectid, string description, ApiDateTime deadline, @@ -560,24 +570,24 @@ namespace ASC.Api.Projects } /// - ///Updates the selected task with the parameters (responsible user ID, task description, deadline time, etc) specified in the request + ///Updates the selected task with the parameters (responsible user ID, task description, deadline time, etc) specified in the request. /// /// - ///Update Task + ///Update a task /// ///Tasks - ///task ID - ///description - ///deadline time - ///task start date - ///priority - ///title - ///milestone ID - ///list responsibles - ///Project ID - ///notify responsible - ///status - ///Progress + ///Task ID + ///New task description + ///New task deadline time + ///New task start date + ///New task priority + ///New task title + ///New task milestone ID + ///New list of task responsibles + ///New task project ID + ///Notifies responsibles about the task actions or not + ///New task status + ///New task progress ///Updated task /// [Update(@"task/{taskid:[0-9]+}")] @@ -663,13 +673,13 @@ namespace ASC.Api.Projects } /// - ///Deletes the task with the ID specified in the request from the project + ///Deletes a task with the ID specified in the request from the project. /// /// - ///Delete task + ///Delete a task /// ///Tasks - ///task ID + ///Task ID ///Deleted task /// [Delete(@"task/{taskid:[0-9]+}")] @@ -686,13 +696,13 @@ namespace ASC.Api.Projects } /// - ///Deletes the tasks with the IDs specified in the request from the project + ///Deletes the tasks with the IDs specified in the request from the project. /// /// ///Delete tasks /// ///Tasks - ///task ID + ///Task IDs ///Deleted tasks /// [Delete(@"task")] @@ -716,10 +726,10 @@ namespace ASC.Api.Projects } /// - ///Returns the list of the comments for the task with the ID specified in the request + ///Returns a list of the comments for the task with the ID specified in the request. /// /// - ///Task comments + ///Get task comments /// ///Comments ///Task ID @@ -734,10 +744,10 @@ namespace ASC.Api.Projects /// - ///Adds the comments for the selected task with the comment text and parent comment ID specified in the request + ///Adds a comment to the selected task with the comment text and parent comment ID specified in the request. /// /// - ///Add task comment + ///Add a task comment /// ///Comments ///Task ID @@ -774,10 +784,10 @@ namespace ASC.Api.Projects } /// - ///Notify the responsible for the task with the ID specified in the request about the task + ///Notifies the responsible for the task with the ID specified in the request about the task. /// /// - ///Notify task responsible + ///Notify the task responsible /// ///Tasks ///Task @@ -796,10 +806,10 @@ namespace ASC.Api.Projects } /// - ///Subscribe to notifications about the actions performed with the task with the ID specified in the request + ///Subscribes to the notifications about the actions performed with the selected task. /// /// - ///Subscribe to task action + ///Subscribe to task actions /// ///Tasks ///Task @@ -820,13 +830,14 @@ namespace ASC.Api.Projects } /// - ///Checks subscription to notifications about the actions performed with the task with the ID specified in the request + ///Checks the subscription to the notifications about the actions performed with the selected task. /// /// - ///Check subscription to task action + ///Check the subscription to task actions /// ///Tasks ///Task ID + ///Boolean value: True - subscribed, False - unsubscribed /// [Read(@"task/{taskid:[0-9]+}/subscribe")] public bool IsSubscribeToTask(int taskid) @@ -841,15 +852,16 @@ namespace ASC.Api.Projects } /// - ///Add link between dependenceTaskId and parentTaskId + ///Adds a link between the dependent and parent tasks specified in the request. /// /// - ///Add link + ///Add a link between tasks /// ///Tasks - ///Parent Task ID - ///Dependent Task ID - ///Link Type + ///Parent task ID + ///Dependent task ID + ///Link type + ///Dependent task /// [Create(@"task/{parentTaskId:[0-9]+}/link")] public TaskWrapper AddLink(int parentTaskId, int dependenceTaskId, TaskLinkType linkType) @@ -866,14 +878,15 @@ namespace ASC.Api.Projects } /// - ///Remove link between dependenceTaskId and parentTaskId + ///Removes a link between the dependent and parent tasks specified in the request. /// /// - ///Remove link + ///Remove a link between tasks /// ///Tasks - ///Dependent Task ID - ///Parent Task ID + ///Dependent task ID + ///Parent task ID + ///Dependent task /// [Delete(@"task/{taskid:[0-9]+}/link")] public TaskWrapper RemoveLink(int dependenceTaskId, int parentTaskId) @@ -894,10 +907,10 @@ namespace ASC.Api.Projects #region subtasks /// - ///Creates the subtask with the selected title and responsible within the parent task specified in the request + ///Creates a subtask with the title and responsible within the parent task specified in the request. /// /// - ///Create subtask + ///Create a subtask /// ///Tasks ///Parent task ID @@ -928,15 +941,15 @@ namespace ASC.Api.Projects } /// - ///Copy subtask + ///Copies a subtask with the ID specified in the request. /// /// - ///Copy subtask + ///Copy a subtask /// ///Tasks ///Task ID ///Subtask ID - ///New task + ///New subtask /// [Create(@"task/{taskid:[0-9]+}/{subtaskid:[0-9]+}/copy")] public SubtaskWrapper CopySubtask(int taskid, int subtaskid) @@ -955,17 +968,53 @@ namespace ASC.Api.Projects } /// - ///Updates the subtask with the selected title and responsible with the subtask ID specified in the request + ///Moves a subtask with the ID specified in the request to another task. /// /// - ///Update subtask + ///Move a subtask /// ///Tasks ///Task ID ///Subtask ID - ///Subtask responsible - ///Subtask title - ///Task + ///Updated subtask + /// + [Update(@"task/{taskid:[0-9]+}/{subtaskid:[0-9]+}/move")] + public SubtaskWrapper MoveSubtask(int taskid, int subtaskid) + { + var subtaskEngine = EngineFactory.SubtaskEngine; + var subtask = subtaskEngine.GetById(subtaskid).NotFoundIfNull(); + + var taskEngine = EngineFactory.TaskEngine; + var fromTask = taskEngine.GetByID(subtask.Task).NotFoundIfNull(); + + if (taskid == subtask.Task) + { + return new SubtaskWrapper(this, subtask, fromTask); + } + + var toTask = taskEngine.GetByID(taskid).NotFoundIfNull(); + + var toTaskTeam = EngineFactory.ProjectEngine.GetTeam(toTask.Project.ID); + + EngineFactory.SubtaskEngine.Move(subtask, fromTask, toTask, toTaskTeam); + + MessageService.Send(Request, MessageAction.SubtaskMoved, MessageTarget.Create(subtask.ID), toTask.Project.Title, toTask.Title, subtask.Title); + + return new SubtaskWrapper(this, subtask, toTask); + } + + /// + ///Updates the selected subtask with the title and responsible specified in the request. + /// + /// + ///Update a subtask + /// + ///Tasks + ///Task ID + ///Subtask ID + ///New subtask responsible + ///New subtask title + ///Updated subtask /// [Update(@"task/{taskid:[0-9]+}/{subtaskid:[0-9]+}")] public SubtaskWrapper UpdateSubtask(int taskid, int subtaskid, Guid responsible, string title) @@ -989,14 +1038,15 @@ namespace ASC.Api.Projects } /// - ///Deletes the selected subtask from the parent task with the ID specified in the request + ///Deletes the selected subtask from the parent task with the ID specified in the request. /// /// - ///Delete subtask + ///Delete a subtask /// ///Tasks ///Task ID ///Subtask ID + ///Subtask /// [Delete(@"task/{taskid:[0-9]+}/{subtaskid:[0-9]+}")] public SubtaskWrapper DeleteSubtask(int taskid, int subtaskid) @@ -1011,16 +1061,16 @@ namespace ASC.Api.Projects } /// - ///Updates the selected subtask status in the parent task with the ID specified in the request + ///Updates the selected subtask status of the parent task with the ID specified in the request. /// /// - ///Update subtask status + ///Update a subtask status /// ///Tasks ///Task ID ///Subtask ID - ///Status of task. Can be one of: open|closed|disable|unclassified - ///Updated task + ///New subtask status: open|closed|disable|unclassified + ///Updated subtask /// [Update(@"task/{taskid:[0-9]+}/{subtaskid:[0-9]+}/status")] public SubtaskWrapper UpdateSubtask(int taskid, int subtaskid, TaskStatus status) diff --git a/module/ASC.Api/ASC.Api.Projects/ProjectApi.TimeSpend.cs b/module/ASC.Api/ASC.Api.Projects/ProjectApi.TimeSpend.cs index 6e7191c08..843e25437 100644 --- a/module/ASC.Api/ASC.Api.Projects/ProjectApi.TimeSpend.cs +++ b/module/ASC.Api/ASC.Api.Projects/ProjectApi.TimeSpend.cs @@ -33,24 +33,24 @@ namespace ASC.Api.Projects public partial class ProjectApi { /// - ///Returns the list with the detailed information about all the time spent matching the filter parameters specified in the request + ///Returns a list with the detailed information about all the task time spent matching the filter parameters specified in the request. /// /// - ///Get time spent by filter + ///Get task time by filter /// ///Time - /// Project Id - ///Project Tag + /// Project ID + ///Returns task time only for my projects + ///Milestone ID + ///Returns task time only for my milestones + ///Project tag ///Departament GUID ///Participant GUID - ///Minimum value of create time - ///Maximum value of create time - ///Last time spent ID - ///Tasks time in My Projects - ///Tasks time in My Milestones - ///Milestone ID + ///Starting task creation + ///Finishing task creation + ///Last spent time ID ///Payment status - ///List of time spent + ///List of spent time /// [Read(@"time/filter")] public IEnumerable GetTaskTimeByFilter( @@ -89,24 +89,24 @@ namespace ASC.Api.Projects } /// - ///Returns the total time spent matching the filter parameters specified in the request + ///Returns the total time spent matching the filter parameters specified in the request. /// /// - ///Get total time spent by tilter + ///Get total task time by filter /// ///Time /// Project ID + ///Returns task time only for my projects + ///Milestone ID + ///Returns task time only for my milestones ///Project tag ///Departament GUID ///Participant GUID - ///Minimum value of create time - ///Maximum value of create time - ///Last time spent ID - ///Tasks time in My Projects - ///Tasks time in My Milestones - ///Milestone ID + ///Starting task creation + ///Finishing task creation + ///Last spent time ID ///Payment status - ///Total time spent + ///Total spent time /// [Read(@"time/filter/total")] public float GetTotalTaskTimeByFilter( @@ -147,14 +147,14 @@ namespace ASC.Api.Projects } /// - ///Returns the time spent on the task with the ID specified in the request + ///Returns the time spent on the task with the ID specified in the request. /// /// - ///Get time spent + ///Get task time /// ///Time ///Task ID - /// + ///Task time /// [Read(@"task/{taskid:[0-9]+}/time")] public IEnumerable GetTaskTime(int taskid) @@ -166,17 +166,17 @@ namespace ASC.Api.Projects } /// - ///Adds the time to the selected task with the time parameters specified in the request + ///Adds the time to the selected task with the time parameters specified in the request. /// /// ///Add task time /// ///Time ///Task ID - ///Note + ///Time note ///Date - ///Person that spends time - ///Hours spent + ///Person ID + ///Spent hours ///Project ID ///Created time /// @@ -210,18 +210,18 @@ namespace ASC.Api.Projects } /// - ///Updates the time for the selected task with the time parameters specified in the request + ///Updates the time for the selected task with the time parameters specified in the request. /// /// ///Update task time /// ///Time - ///ID of time spent - ///Note - ///Date - ///Person that spends time - ///Hours spent - ///Created time + ///Time ID + ///New time note + ///New date + ///New person ID + ///New spent hours + ///Updated time /// /// [Update(@"time/{timeid:[0-9]+}")] @@ -246,15 +246,15 @@ namespace ASC.Api.Projects } /// - ///Updates the time status of payment + ///Updates a time status of payment with the parameters specified in the request. /// /// - ///Updates the time status of payment + ///Update a time status of payment /// ///Time - ///List IDs of time spent - ///Status - ///Created time + ///Spent time IDs + ///New payment status + ///Updated times /// [Update(@"time/times/status")] public List UpdateTimes(int[] timeids, PaymentStatus status) @@ -275,14 +275,14 @@ namespace ASC.Api.Projects } /// - ///Deletes the times from the tasks with the ID specified in the request + ///Deletes time from the tasks with the IDs specified in the request. /// /// - ///Delete time spents + ///Delete task time /// ///Time - ///IDs of time spents - /// + ///Spent time IDs + ///Deleted time /// [Delete(@"time/times/remove")] public List DeleteTaskTimes(int[] timeids) diff --git a/module/ASC.Api/ASC.Api.Projects/ProjectApi.cs b/module/ASC.Api/ASC.Api.Projects/ProjectApi.cs index dd26c4095..3dcdd5304 100644 --- a/module/ASC.Api/ASC.Api.Projects/ProjectApi.cs +++ b/module/ASC.Api/ASC.Api.Projects/ProjectApi.cs @@ -37,7 +37,7 @@ using Autofac; namespace ASC.Api.Projects { /// - ///Projects access + ///Project information access. /// public partial class ProjectApi : ProjectApiBase, IApiEntryPoint { @@ -179,6 +179,18 @@ namespace ASC.Api.Projects } } + /// + ///Updates the project settings with the parameters specified in the request. + /// + /// + ///Update settings + /// + ///Settings + ///Specifies if all the portal users can create projects or not + ///Specifies if the entities will be hidden in the paused projects or not + ///Module type: Projects, Tasks, Discussions, TimeTracking + ///Folder ID + ///Updated settings [Update(@"settings")] public ProjectsCommonSettings UpdateSettings(bool? everebodyCanCreate, bool? hideEntitiesInPausedProjects, @@ -226,6 +238,14 @@ namespace ASC.Api.Projects return null; } + /// + ///Returns the common project settings. + /// + /// + ///Get settings + /// + ///Settings + ///Project common settings [Read(@"settings")] public ProjectsCommonSettings GetSettings() { @@ -242,12 +262,30 @@ namespace ASC.Api.Projects } + /// + ///Creates a task status specified in the request. + /// + /// + ///Create a task status + /// + ///Task status + ///Tasks + ///Task status [Create(@"status")] public CustomTaskStatus CreateStatus(CustomTaskStatus status) { return EngineFactory.StatusEngine.Create(status); } + /// + ///Updates a task status with a value specified in the request. + /// + /// + ///Update a task status + /// + ///New task status + ///Tasks + ///Updated task status [Update(@"status")] public CustomTaskStatus UpdateStatus(CustomTaskStatus newStatus) { @@ -272,6 +310,15 @@ namespace ASC.Api.Projects return status; } + /// + ///Updates the task statuses with the values specified in the request. + /// + /// + ///Update task statuses + /// + ///New task statuses + ///Tasks + ///Updated task statuses [Update(@"statuses")] public List UpdateStatuses(List statuses) { @@ -283,12 +330,29 @@ namespace ASC.Api.Projects return statuses; } + /// + ///Returns all the task statuses. + /// + /// + ///Get task statuses + /// + ///Tasks + ///Task statuses [Read(@"status")] public List GetStatuses() { return EngineFactory.StatusEngine.GetWithDefaults(); } + /// + ///Deletes a task status with the ID specified in the request. + /// + /// + ///Delete a task status + /// + ///Task status ID + ///Tasks + ///Task status [Delete(@"status/{id}")] public CustomTaskStatus DeleteStatus(int id) { diff --git a/module/ASC.Api/ASC.Api.Security/ASC.Api.Security.csproj b/module/ASC.Api/ASC.Api.Security/ASC.Api.Security.csproj index 10ede432a..acc964b02 100644 --- a/module/ASC.Api/ASC.Api.Security/ASC.Api.Security.csproj +++ b/module/ASC.Api/ASC.Api.Security/ASC.Api.Security.csproj @@ -32,7 +32,9 @@ + + @@ -53,6 +55,10 @@ {e7be6ce8-f6b0-4b9b-831b-ba0c85c8d130} ASC.Web.Studio + + {8c534af7-5696-4e68-9ff4-ffc311893c10} + ASC.Web.Files + {2c111161-b7c5-4869-9f52-ea725e64ba40} ASC.AuditTrail @@ -74,8 +80,14 @@ + + + + 27.1.1 + + \ No newline at end of file diff --git a/module/ASC.Api/ASC.Api.Security/AuditEventWrapper.cs b/module/ASC.Api/ASC.Api.Security/AuditEventWrapper.cs index e8406b5ee..f3ecf14b9 100644 --- a/module/ASC.Api/ASC.Api.Security/AuditEventWrapper.cs +++ b/module/ASC.Api/ASC.Api.Security/AuditEventWrapper.cs @@ -15,7 +15,13 @@ */ +using System; +using System.Collections.Generic; + using ASC.AuditTrail; +using ASC.AuditTrail.Mappers; +using ASC.AuditTrail.Types; +using ASC.MessagingSystem; using ASC.Specific; namespace ASC.Api.Security @@ -27,15 +33,69 @@ namespace ASC.Api.Security Id = auditEvent.Id; Date = (ApiDateTime)auditEvent.Date; User = auditEvent.UserName; + UserId = auditEvent.UserId; Action = auditEvent.ActionText; + ActionId = (MessageAction)auditEvent.Action; + IP = auditEvent.IP; + Browser = auditEvent.Browser; + Platform = auditEvent.Platform; + Page = auditEvent.Page; + + var maps = AuditActionMapper.GetMessageMaps(auditEvent.Action); + + ActionType = maps.ActionType; + Product = maps.ProductType; + Module = maps.ModuleType; + + var list = new List(2); + + if (maps.EntryType1 != EntryType.None) + { + list.Add(maps.EntryType1); + } + + if (maps.EntryType2 != EntryType.None) + { + list.Add(maps.EntryType2); + } + + Entries = list; + + if (auditEvent.Target != null) + { + Target = auditEvent.Target.GetItems(); + } } - public int Id { get; private set; } + public int Id { get; set; } - public ApiDateTime Date { get; private set; } + public ApiDateTime Date { get; set; } - public string User { get; private set; } + public string User { get; set; } + + public Guid UserId { get; set; } + + public string Action { get; set; } + + public MessageAction ActionId { get; set; } + + public string IP { get; set; } + + public string Browser { get; set; } + + public string Platform { get; set; } + + public string Page { get; set; } + + public ActionType ActionType { get; set; } + + public ProductType Product { get; set; } + + public ModuleType Module { get; set; } + + public IEnumerable Target { get; set; } + + public IEnumerable Entries { get; set; } - public string Action { get; private set; } } } \ No newline at end of file diff --git a/module/ASC.Api/ASC.Api.Security/AuditReportCreator.cs b/module/ASC.Api/ASC.Api.Security/AuditReportCreator.cs new file mode 100644 index 000000000..be87f54d4 --- /dev/null +++ b/module/ASC.Api/ASC.Api.Security/AuditReportCreator.cs @@ -0,0 +1,89 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; + +using ASC.AuditTrail; +using ASC.Common.Logging; +using ASC.Web.Core.Files; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Utils; +using ASC.Web.Studio.Utility; + +using CsvHelper; +using CsvHelper.Configuration; + +namespace ASC.Api.Security +{ + public static class AuditReportCreator + { + private static readonly ILog Log = LogManager.GetLogger("ASC.Messaging"); + + public static string CreateCsvReport(IEnumerable events, string reportName) where TEvent : BaseEvent + { + try + { + using (var stream = new MemoryStream()) + using (var writer = new StreamWriter(stream, Encoding.UTF8)) + using (var csv = new CsvWriter(writer, CultureInfo.CurrentCulture)) + { + csv.Context.RegisterClassMap(new BaseEventMap()); + + csv.WriteHeader(); + csv.NextRecord(); + csv.WriteRecords(events); + writer.Flush(); + + var file = FileUploader.Exec(Global.FolderMy.ToString(), reportName, stream.Length, stream, true); + var fileUrl = CommonLinkUtility.GetFullAbsolutePath(FilesLinkUtility.GetFileWebEditorUrl((int)file.ID)); + + fileUrl += string.Format("&options={{\"codePage\":{0}}}", Encoding.UTF8.CodePage); + return fileUrl; + } + } + catch (Exception ex) + { + Log.Error("Error while generating login report: " + ex); + throw; + } + } + } + + internal class BaseEventMap : ClassMap where T : BaseEvent + { + public BaseEventMap() + { + var eventType = typeof(T); + var eventProps = eventType + .GetProperties() + .Where(r => r.GetCustomAttribute() != null) + .OrderBy(r => r.GetCustomAttribute().Order); + + foreach (var prop in eventProps) + { + var attr = prop.GetCustomAttribute().Resource; + Map(eventType, prop).Name(AuditReportResource.ResourceManager.GetString(attr)); + } + } + } +} \ No newline at end of file diff --git a/module/ASC.Api/ASC.Api.Security/LoginEventWrapper.cs b/module/ASC.Api/ASC.Api.Security/LoginEventWrapper.cs index df71ca123..562ef9b98 100644 --- a/module/ASC.Api/ASC.Api.Security/LoginEventWrapper.cs +++ b/module/ASC.Api/ASC.Api.Security/LoginEventWrapper.cs @@ -15,7 +15,10 @@ */ +using System; + using ASC.AuditTrail; +using ASC.MessagingSystem; using ASC.Specific; namespace ASC.Api.Security @@ -27,15 +30,35 @@ namespace ASC.Api.Security Id = loginEvent.Id; Date = (ApiDateTime)loginEvent.Date; User = loginEvent.UserName; + UserId = loginEvent.UserId; + Login = loginEvent.Login; Action = loginEvent.ActionText; + ActionId = (MessageAction)loginEvent.Action; + IP = loginEvent.IP; + Browser = loginEvent.Browser; + Platform = loginEvent.Platform; + Page = loginEvent.Page; } - public int Id { get; private set; } + public int Id { get; set; } - public ApiDateTime Date { get; private set; } + public ApiDateTime Date { get; set; } - public string User { get; private set; } + public string User { get; set; } - public string Action { get; private set; } + public Guid UserId { get; set; } + public string Login { get; set; } + + public string Action { get; set; } + + public MessageAction ActionId { get; set; } + + public string IP { get; set; } + + public string Browser { get; set; } + + public string Platform { get; set; } + + public string Page { get; set; } } } \ No newline at end of file diff --git a/module/ASC.Api/ASC.Api.Security/ModelTypes.cs b/module/ASC.Api/ASC.Api.Security/ModelTypes.cs new file mode 100644 index 000000000..295498604 --- /dev/null +++ b/module/ASC.Api/ASC.Api.Security/ModelTypes.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace ASC.Api.Security +{ + public class ModelTypes + { + public IEnumerable Actions { get; set; } + public IEnumerable ActionTypes { get; set; } + public IEnumerable ProductTypes { get; set; } + public IEnumerable ModuleTypes { get; set; } + public IEnumerable EntryTypes { get; set; } + } +} diff --git a/module/ASC.Api/ASC.Api.Security/SecurityApi.cs b/module/ASC.Api/ASC.Api.Security/SecurityApi.cs index d2700134a..7662165eb 100644 --- a/module/ASC.Api/ASC.Api.Security/SecurityApi.cs +++ b/module/ASC.Api/ASC.Api.Security/SecurityApi.cs @@ -17,16 +17,30 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; +using System.Security; using System.Web; using ASC.Api.Attributes; +using ASC.Api.Impl; using ASC.Api.Interfaces; using ASC.AuditTrail; using ASC.AuditTrail.Data; +using ASC.AuditTrail.Mappers; +using ASC.AuditTrail.Types; +using ASC.Common.Logging; +using ASC.Core; using ASC.Core.Billing; +using ASC.Core.Data; +using ASC.Core.Security.Authentication; using ASC.Core.Tenants; +using ASC.Core.Users; +using ASC.Geolocation; using ASC.MessagingSystem; +using ASC.Specific; +using ASC.Web.Core; +using ASC.Web.Studio; using ASC.Web.Studio.Core; using ASC.Web.Studio.PublicResources; using ASC.Web.Studio.Utility; @@ -37,6 +51,10 @@ namespace ASC.Api.Security { public class SecurityApi : IApiEntryPoint { + ILog Log = LogManager.GetLogger("ASC.Api"); + GeolocationHelper geoLocHelper = new GeolocationHelper("teamlabsite"); + protected internal ApiContext Context { get; set; } + private static HttpRequest Request { get { return HttpContext.Current.Request; } @@ -47,28 +65,142 @@ namespace ASC.Api.Security get { return "security"; } } + public SecurityApi(ApiContext apiContext) + { + Context = apiContext; + } + [Read("/audit/login/last")] public IEnumerable GetLastLoginEvents() { - if (!SetupInfo.IsVisibleSettings(ManagementType.LoginHistory.ToString())) - { - throw new BillingException(Resource.ErrorNotAllowedOption, "Audit"); - } SecurityContext.DemandPermissions(SecutiryConstants.EditPortalSettings); - return LoginEventsRepository.GetLast(TenantProvider.CurrentTenantID, 20).Select(x => new LoginEventWrapper(x)); + DemandBaseAuditPermission(); + + return LoginEventsRepository.GetByFilter(startIndex: 0, limit: 20).Select(x => new LoginEventWrapper(x)); } [Read("/audit/events/last")] public IEnumerable GetLastAuditEvents() { - if (!SetupInfo.IsVisibleSettings(ManagementType.AuditTrail.ToString())) - { - throw new BillingException(Resource.ErrorNotAllowedOption, "Audit"); - } SecurityContext.DemandPermissions(SecutiryConstants.EditPortalSettings); - return AuditEventsRepository.GetLast(TenantProvider.CurrentTenantID, 20).Select(x => new AuditEventWrapper(x)); + DemandBaseAuditPermission(); + + return AuditEventsRepository.GetByFilter(startIndex: 0, limit: 20).Select(x => new AuditEventWrapper(x)); + } + + /// + /// Returns a list of login events by filter + /// + /// User ID + /// Action + /// From date + /// To date + /// Events + [Read("/audit/login/filter")] + public IEnumerable GetLoginEventsByFilter(Guid userId, + MessageAction action, + ApiDateTime from, + ApiDateTime to) + { + SecurityContext.DemandPermissions(SecutiryConstants.EditPortalSettings); + + var startIndex = (int)Context.StartIndex; + var limit = (int)Context.Count; + Context.SetDataPaginated(); + + action = action == 0 ? MessageAction.None : action; + + if (!TenantExtra.GetTenantQuota().Audit || !SetupInfo.IsVisibleSettings(ManagementType.LoginHistory.ToString())) + { + return GetLastLoginEvents(); + } + else + { + DemandAuditPermission(); + + return LoginEventsRepository.GetByFilter(userId, action, from, to, startIndex, limit).Select(x => new LoginEventWrapper(x)); + } + } + /// + /// Returns a list of audit events by filter + /// + /// User id + /// Product + /// Module + /// Action type + /// Action + /// Entry + /// Target + /// From date + /// To date + /// Actions + [Read("/audit/events/filter")] + public IEnumerable GetAuditEventsByFilter(Guid userId, + ProductType productType, + ModuleType moduleType, + ActionType actionType, + MessageAction action, + EntryType entryType, + string target, + ApiDateTime from, + ApiDateTime to) + { + SecurityContext.DemandPermissions(SecutiryConstants.EditPortalSettings); + + var startIndex = (int)Context.StartIndex; + var limit = (int)Context.Count; + Context.SetDataPaginated(); + + action = action == 0 ? MessageAction.None : action; + + if (!TenantExtra.GetTenantQuota().Audit || !SetupInfo.IsVisibleSettings(ManagementType.LoginHistory.ToString())) + { + return GetLastAuditEvents(); + } + else + { + DemandAuditPermission(); + + return AuditEventsRepository.GetByFilter(userId, productType, moduleType, actionType, action, entryType, target, from, to, startIndex, limit).Select(x => new AuditEventWrapper(x)); + } + } + + [Read("/audit/types", false)] + public ModelTypes GetTypes() + { + return new ModelTypes() + { + Actions = Enum.GetValues(typeof(MessageAction)).Cast().Select(x => x.ToString()), + ActionTypes = Enum.GetValues(typeof(ActionType)).Cast().Select(x => x.ToString()), + ProductTypes = Enum.GetValues(typeof(ProductType)).Cast().Select(x => x.ToString()), + ModuleTypes = Enum.GetValues(typeof(ModuleType)).Cast().Select(x => x.ToString()), + EntryTypes = Enum.GetValues(typeof(EntryType)).Cast().Select(x => x.ToString()) + }; + } + + [Read("/audit/mappers", false)] + public object GetMappers(ProductType? productType, ModuleType? moduleType) + { + return AuditActionMapper.Mappers + .Where(r => !productType.HasValue || r.Product == productType.Value) + .Select(r => new + { + ProductType = r.Product.ToString(), + Modules = r.Mappers + .Where(m => !moduleType.HasValue || m.Module == moduleType.Value) + .Select(x => new + { + ModuleType = x.Module.ToString(), + Actions = x.Actions.Select(a => new + { + MessageAction = a.Key.ToString(), + ActionType = a.Value.ActionType.ToString(), + Entity = a.Value.EntryType1.ToString() + }) + }) + }); } [Create("/audit/login/report")] @@ -76,10 +208,9 @@ namespace ASC.Api.Security { SecurityContext.DemandPermissions(SecutiryConstants.EditPortalSettings); - var tenantId = TenantProvider.CurrentTenantID; + DemandAuditPermission(); - if (!TenantExtra.GetTenantQuota().Audit || !SetupInfo.IsVisibleSettings(ManagementType.LoginHistory.ToString())) - throw new BillingException(Resource.ErrorNotAllowedOption, "Audit"); + var tenantId = TenantProvider.CurrentTenantID; var settings = TenantAuditSettings.LoadForTenant(tenantId); @@ -87,7 +218,7 @@ namespace ASC.Api.Security var from = to.Subtract(TimeSpan.FromDays(settings.LoginHistoryLifeTime)); var reportName = string.Format(AuditReportResource.LoginHistoryReportName + ".csv", from.ToShortDateString(), to.ToShortDateString()); - var events = LoginEventsRepository.Get(tenantId, from, to); + var events = LoginEventsRepository.GetByFilter(from: from, to: to); var result = AuditReportCreator.CreateCsvReport(events, reportName); MessageService.Send(Request, MessageAction.LoginHistoryReportDownloaded); @@ -99,10 +230,9 @@ namespace ASC.Api.Security { SecurityContext.DemandPermissions(SecutiryConstants.EditPortalSettings); - var tenantId = TenantProvider.CurrentTenantID; + DemandAuditPermission(); - if (!TenantExtra.GetTenantQuota().Audit || !SetupInfo.IsVisibleSettings(ManagementType.AuditTrail.ToString())) - throw new BillingException(Resource.ErrorNotAllowedOption, "Audit"); + var tenantId = TenantProvider.CurrentTenantID; var settings = TenantAuditSettings.LoadForTenant(tenantId); @@ -111,7 +241,7 @@ namespace ASC.Api.Security var reportName = string.Format(AuditReportResource.AuditTrailReportName + ".csv", from.ToShortDateString(), to.ToShortDateString()); - var events = AuditEventsRepository.Get(tenantId, from, to); + var events = AuditEventsRepository.GetByFilter(from: from, to: to); var result = AuditReportCreator.CreateCsvReport(events, reportName); MessageService.Send(Request, MessageAction.AuditTrailReportDownloaded); @@ -121,21 +251,20 @@ namespace ASC.Api.Security [Read("/audit/settings/lifetime")] public TenantAuditSettings GetAuditSettings() { - if (!SetupInfo.IsVisibleSettings(ManagementType.LoginHistory.ToString())) - { - throw new BillingException(Resource.ErrorNotAllowedOption, "Audit"); - } SecurityContext.DemandPermissions(SecutiryConstants.EditPortalSettings); + + DemandBaseAuditPermission(); + return TenantAuditSettings.LoadForTenant(TenantProvider.CurrentTenantID); } [Create("/audit/settings/lifetime")] public TenantAuditSettings SetAuditSettings(TenantAuditSettings settings) { - if (!TenantExtra.GetTenantQuota().Audit || !SetupInfo.IsVisibleSettings(ManagementType.LoginHistory.ToString())) - throw new BillingException(Resource.ErrorNotAllowedOption, "Audit"); SecurityContext.DemandPermissions(SecutiryConstants.EditPortalSettings); + DemandAuditPermission(); + if (settings.LoginHistoryLifeTime <= 0 || settings.LoginHistoryLifeTime > TenantAuditSettings.MaxLifeTime) throw new ArgumentException("LoginHistoryLifeTime"); @@ -148,5 +277,210 @@ namespace ASC.Api.Security return settings; } + + private static void DemandAuditPermission() + { + if (!CoreContext.Configuration.Standalone + && (!SetupInfo.IsVisibleSettings(ManagementType.LoginHistory.ToString()) + || !TenantExtra.GetTenantQuota().Audit)) + { + throw new BillingException(Resource.ErrorNotAllowedOption, "Audit"); + } + } + + private static void DemandBaseAuditPermission() + { + if (!CoreContext.Configuration.Standalone + && !SetupInfo.IsVisibleSettings(ManagementType.LoginHistory.ToString())) + { + throw new BillingException(Resource.ErrorNotAllowedOption, "Audit"); + } + } + + [Read("/activeconnections")] + public object GetAllActiveConnections() + { + var user = CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID); + var loginEvents = DbLoginEventsManager.GetLoginEvents(user.Tenant, user.ID); + var listLoginEvents = loginEvents.ConvertAll(Convert); + var loginEventId = GetLoginEventIdFromCookie(); + if (loginEventId != 0) + { + var loginEvent = listLoginEvents.FirstOrDefault(x => x.Id == loginEventId); + if (loginEvent != null) + { + listLoginEvents.Remove(loginEvent); + listLoginEvents.Insert(0, loginEvent); + } + } + else + { + if (listLoginEvents.Count == 0) + { + var request = HttpContext.Current.Request; + var uaHeader = MessageSettings.GetUAHeader(request); + var clientInfo = MessageSettings.GetClientInfo(uaHeader); + var platformAndDevice = MessageSettings.GetPlatformAndDevice(clientInfo); + var browser = MessageSettings.GetBrowser(clientInfo); + var ip = MessageSettings.GetIP(request); + + var baseEvent = new CustomEvent + { + Id = 0, + Platform = platformAndDevice, + Browser = browser, + Date = DateTime.Now, + IP = ip + }; + + listLoginEvents.Add(Convert(baseEvent)); + } + } + + var result = new + { + Items = listLoginEvents, + LoginEvent = loginEventId + }; + return result; + } + + [Update("/activeconnections/logoutallchangepassword")] + public string LogOutAllActiveConnectionsChangePassword() + { + try + { + var user = CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID); + var userName = user.DisplayUserName(false); + + LogOutAllActiveConnections(user.ID); + + Auth.ProcessLogout(); + + var auditEventDate = DateTime.UtcNow; + var hash = auditEventDate.ToString("s"); + var confirmationUrl = CommonLinkUtility.GetConfirmationUrl(user.Email, ConfirmType.PasswordChange, hash); + MessageService.Send(Request, auditEventDate, MessageAction.UserSentPasswordChangeInstructions, MessageTarget.Create(user.ID), userName); + + return confirmationUrl; + } + catch (Exception ex) + { + Log.Error(ex); + return null; + } + } + + [Update("/activeconnections/logoutall/{userId}")] + public void LogOutAllActiveConnectionsForUser(Guid userId) + { + if (!CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID).IsAdmin() + && !WebItemSecurity.IsProductAdministrator(WebItemManager.PeopleProductID, SecurityContext.CurrentAccount.ID)) + throw new SecurityException("Method not available"); + + LogOutAllActiveConnections(userId); + } + + [Update("/activeconnections/logoutallexceptthis")] + public string LogOutAllExceptThisConnection() + { + try + { + var user = CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID); + var userName = user.DisplayUserName(false); + var loginEventFromCookie = GetLoginEventIdFromCookie(); + + DbLoginEventsManager.LogOutAllActiveConnectionsExceptThis(loginEventFromCookie, user.Tenant, user.ID); + + MessageService.Send(Request, MessageAction.UserLogoutActiveConnections, userName); + return userName; + } + catch (Exception ex) + { + Log.Error(ex); + return null; + } + } + + [Update("/activeconnections/logout/{loginEventId}")] + public bool LogOutActiveConnection(int loginEventId) + { + try + { + var user = CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID); + var userName = user.DisplayUserName(false); + + DbLoginEventsManager.LogOutEvent(loginEventId); + + MessageService.Send(Request, MessageAction.UserLogoutActiveConnection, userName); + return true; + } + catch (Exception ex) + { + Log.Error(ex); + return false; + } + } + + public void LogOutAllActiveConnections(Guid? userId = null) + { + var currentUserId = SecurityContext.CurrentAccount.ID; + var user = CoreContext.UserManager.GetUsers(userId ?? currentUserId); + var userName = user.DisplayUserName(false); + var auditEventDate = DateTime.UtcNow; + + MessageService.Send(Request, auditEventDate, + (currentUserId.Equals(user.ID)) ? MessageAction.UserLogoutActiveConnections : MessageAction.UserLogoutActiveConnectionsForUser, + MessageTarget.Create(user.ID), userName); + CookiesManager.ResetUserCookie(user.ID); + } + + public int GetLoginEventIdFromCookie() + { + var cookie = CookiesManager.GetCookies(CookiesType.AuthKey); + int loginEventId = CookieStorage.GetLoginEventIdFromCookie(cookie); + return loginEventId; + } + + private CustomEvent Convert(BaseEvent baseEvent) + { + var location = GetGeolocation(baseEvent.IP); + return new CustomEvent + { + Id = baseEvent.Id, + IP = baseEvent.IP, + Platform = baseEvent.Platform, + Browser = baseEvent.Browser, + Date = baseEvent.Date, + Country = location[0], + City = location[1] + }; + } + + private string[] GetGeolocation(string ip) + { + try + { + var location = geoLocHelper.GetIPGeolocation(ip); + if (string.IsNullOrEmpty(location.Key)) + { + return new string[] { string.Empty, string.Empty }; + } + var regionInfo = new RegionInfo(location.Key).EnglishName; + return new string[] { regionInfo, location.City }; + } + catch (Exception ex) + { + Log.Error(ex); + return new string[] { string.Empty, string.Empty }; + } + } + + private class CustomEvent : BaseEvent + { + public string Country { get; set; } + + public string City { get; set; } + } } } \ No newline at end of file diff --git a/module/ASC.Api/ASC.Api.Settings/ASC.Api.Settings.csproj b/module/ASC.Api/ASC.Api.Settings/ASC.Api.Settings.csproj index 56e08f566..4bfdd373c 100644 --- a/module/ASC.Api/ASC.Api.Settings/ASC.Api.Settings.csproj +++ b/module/ASC.Api/ASC.Api.Settings/ASC.Api.Settings.csproj @@ -62,6 +62,7 @@ + @@ -147,10 +148,10 @@ - 2.11.1 + 2.15.0 - 12.0.3 + 13.0.1 diff --git a/module/ASC.Api/ASC.Api.Settings/BuildVersion.cs b/module/ASC.Api/ASC.Api.Settings/BuildVersion.cs index b1d9a1189..196935c5b 100644 --- a/module/ASC.Api/ASC.Api.Settings/BuildVersion.cs +++ b/module/ASC.Api/ASC.Api.Settings/BuildVersion.cs @@ -31,6 +31,8 @@ namespace ASC.Api.Settings [DataContract(Name = "buildversion", Namespace = "")] public class BuildVersion { + private static ILog Log = LogManager.GetLogger("ASC"); + [DataMember] public string CommunityServer { get; set; } @@ -80,7 +82,7 @@ namespace ASC.Api.Settings } catch (Exception e) { - LogManager.GetLogger("ASC").Warn(e.Message, e); + Log.Warn(e.Message, e); } return null; @@ -97,7 +99,7 @@ namespace ASC.Api.Settings } catch (Exception e) { - LogManager.GetLogger("ASC").Warn(e.Message, e); + Log.Warn(e.Message, e); } return null; diff --git a/module/ASC.Api/ASC.Api.Settings/LdapSettingsApi.cs b/module/ASC.Api/ASC.Api.Settings/LdapSettingsApi.cs index c9c1d9c0b..4076711be 100644 --- a/module/ASC.Api/ASC.Api.Settings/LdapSettingsApi.cs +++ b/module/ASC.Api/ASC.Api.Settings/LdapSettingsApi.cs @@ -18,12 +18,14 @@ using System; using System.Diagnostics; using System.Linq; +using System.Web; using ASC.ActiveDirectory.Base; using ASC.ActiveDirectory.Base.Data; using ASC.ActiveDirectory.Base.Settings; using ASC.ActiveDirectory.ComplexOperations; using ASC.Api.Attributes; +using ASC.Common.Caching; using ASC.Common.Threading; using ASC.Core; using ASC.Core.Billing; @@ -38,13 +40,18 @@ namespace ASC.Api.Settings { public partial class SettingsApi { + + public static readonly ICache Cache = AscCache.Default; + + /// - /// Returns current portal LDAP settings + /// Returns the current portal LDAP settings. /// /// - /// Get LDAP settings + /// Get the LDAP settings /// - /// LDAPSupportSettings object + /// LDAP + /// LDAP settings [Read("ldap")] public LdapSettings GetLdapSettings() { @@ -72,12 +79,13 @@ namespace ASC.Api.Settings } /// - /// Returns current portal LDAP AutoSync cron expression if any + /// Returns the LDAP autosynchronous cron expression of the current portal if it exists. /// /// - /// Get LDAP AutoSync Cron expression + /// Get the LDAP cron expression /// - /// string or null + /// LDAP + /// Cron expression or null [Read("ldap/cron")] public string GetLdapCronSettings() { @@ -95,11 +103,13 @@ namespace ASC.Api.Settings } /// - /// Sets current portal LDAP AutoSync cron expression + /// Sets the LDAP autosynchronous cron expression of the current portal. /// /// - /// Sets LDAP AutoSync Cron expression + /// Set the LDAP cron expression /// + /// LDAP + /// Cron expression [Create("ldap/cron")] public void SetLdapCronSettings(string cron) { @@ -136,11 +146,13 @@ namespace ASC.Api.Settings } /// - /// Start sync users and groups process by LDAP + /// Starts synchronizing users and groups by LDAP. /// /// - /// Sync LDAP + /// Synchronize by LDAP /// + /// LDAP + /// Operation status [Read("ldap/sync")] public LdapOperationStatus SyncLdap() { @@ -174,17 +186,21 @@ namespace ASC.Api.Settings var tenant = CoreContext.TenantManager.GetCurrentTenant(); + Cache.Insert("REWRITE_URL" + tenant.TenantId, HttpContext.Current.Request.GetUrlRewriter().ToString(), TimeSpan.FromMinutes(5)); + var op = new LdapSaveSyncOperation(ldapSettings, tenant, LdapOperationType.Sync, ldapLocalization, CurrentUser.ToString()); return QueueTask(op); } /// - /// Starts the process of collecting preliminary changes on the portal according to the selected LDAP settings + /// Starts the process of collecting preliminary changes on the portal during the synchronization process according to the selected LDAP settings. /// /// - /// Sync LDAP + /// Test the LDAP synchronization /// + /// LDAP + /// Operation status [Read("ldap/sync/test")] public LdapOperationStatus TestLdapSync() { @@ -218,19 +234,23 @@ namespace ASC.Api.Settings var tenant = CoreContext.TenantManager.GetCurrentTenant(); + Cache.Insert("REWRITE_URL" + tenant.TenantId, HttpContext.Current.Request.GetUrlRewriter().ToString(), TimeSpan.FromMinutes(5)); + var op = new LdapSaveSyncOperation(ldapSettings, tenant, LdapOperationType.SyncTest, ldapLocalization); return QueueTask(op); } /// - /// Save LDAP settings and start import/sync users and groups process by LDAP + /// Saves the LDAP settings specified in the request and starts importing/synchronizing users and groups by LDAP. /// /// - /// Save LDAP settings + /// Save the LDAP settings /// - /// LDAPSupportSettings serialized string - /// Flag permits errors of checking certificates + /// LDAP + /// LDAP settings in the serialized string format + /// Specifies if the errors of checking certificates are allowed (true) or not (false) + /// Operation status [Create("ldap")] public LdapOperationStatus SaveLdapSettings(string settings, bool acceptCertificate) { @@ -265,17 +285,23 @@ namespace ASC.Api.Settings var tenant = CoreContext.TenantManager.GetCurrentTenant(); + Cache.Insert("REWRITE_URL" + tenant.TenantId, HttpContext.Current.Request.GetUrlRewriter().ToString(), TimeSpan.FromMinutes(5)); + var op = new LdapSaveSyncOperation(ldapSettings, tenant, LdapOperationType.Save, ldapLocalization, CurrentUser.ToString()); return QueueTask(op); } /// - /// Starts the process of collecting preliminary changes on the portal according to the LDAP settings + /// Starts the process of collecting preliminary changes on the portal during the saving process according to the LDAP settings. /// /// - /// Save LDAP settings + /// Test the LDAP saving process /// + /// LDAP + /// LDAP settings in the serialized string format + /// Specifies if the errors of checking certificates are allowed (true) or not (false) + /// Operation status [Create("ldap/save/test")] public LdapOperationStatus TestLdapSave(string settings, bool acceptCertificate) { @@ -311,18 +337,21 @@ namespace ASC.Api.Settings var tenant = CoreContext.TenantManager.GetCurrentTenant(); + Cache.Insert("REWRITE_URL" + tenant.TenantId, HttpContext.Current.Request.GetUrlRewriter().ToString(), TimeSpan.FromMinutes(5)); + var op = new LdapSaveSyncOperation(ldapSettings, tenant, LdapOperationType.SaveTest, ldapLocalization, CurrentUser.ToString()); return QueueTask(op); } /// - /// Returns LDAP sync process status + /// Returns the LDAP synchronization process status. /// /// - /// Get LDAP sync process status + /// Get the LDAP synchronization process status /// - /// LDAPSupportSettingsResult object + /// LDAP + /// Operation status [Read("ldap/status")] public LdapOperationStatus GetLdapOperationStatus() { @@ -332,12 +361,13 @@ namespace ASC.Api.Settings } /// - /// Returns LDAP default settings + /// Returns the LDAP default settings. /// /// - /// Get LDAP default settings + /// Get the LDAP default settings /// - /// LDAPSupportSettings object + /// LDAP + /// LDAP default settings [Read("ldap/default")] public LdapSettings GetDefaultLdapSettings() { @@ -403,8 +433,9 @@ namespace ASC.Api.Settings { SecurityContext.DemandPermissions(SecutiryConstants.EditPortalSettings); - if ((!SetupInfo.IsVisibleSettings(ManagementType.LdapSettings.ToString()) && !CoreContext.Configuration.Standalone) || - !CoreContext.TenantManager.GetTenantQuota(TenantProvider.CurrentTenantID).Ldap) + if (!CoreContext.Configuration.Standalone + && (!SetupInfo.IsVisibleSettings(ManagementType.LdapSettings.ToString()) + || !CoreContext.TenantManager.GetTenantQuota(TenantProvider.CurrentTenantID).Ldap)) { throw new BillingException(Resource.ErrorNotAllowedOption, "Ldap"); } diff --git a/module/ASC.Api/ASC.Api.Settings/RadicaleSettingsAPI.cs b/module/ASC.Api/ASC.Api.Settings/RadicaleSettingsAPI.cs new file mode 100644 index 000000000..f9db14091 --- /dev/null +++ b/module/ASC.Api/ASC.Api.Settings/RadicaleSettingsAPI.cs @@ -0,0 +1,163 @@ +using System; +using System.Text; +using System.Threading.Tasks; +using System.Web; + +using ASC.Api.Attributes; +using ASC.Common.Radicale; +using ASC.Common.Radicale.Core; +using ASC.Core; +using ASC.Core.Users; +using ASC.Security.Cryptography; +using ASC.Web.Core; +using ASC.Web.Studio.PublicResources; + +namespace ASC.Api.Settings +{ + public partial class SettingsApi + { + + /// + /// Creates a CardDav address book for a user with all portal users and returns a link to this address book. + /// + /// + /// Get a link to the CardDav address book + /// + /// CardDav address book + /// false + [Read("carddavurl")] + public async Task GetCardDavUrl() + { + + if (WebItemManager.Instance[WebItemManager.PeopleProductID].IsDisabled()) + { + await DeleteCardDavAddressBook().ConfigureAwait(false); + throw new MethodAccessException("Method not available"); + } + + var myUri = HttpContext.Current.Request.GetUrlRewriter(); + var currUser = CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID); + var userName = currUser.Email.ToLower(); + var currentAccountPaswd = InstanceCrypto.Encrypt(userName); + var cardBuilder = CardDavAllSerialization(myUri); + + + var cardDavAddBook = new CardDavAddressbook(); + var userAuthorization = userName + ":" + currentAccountPaswd; + var rootAuthorization = cardDavAddBook.GetSystemAuthorization(); + var sharedCardUrl = cardDavAddBook.GetRadicaleUrl(myUri.ToString(), userName, true, true, true); + var getResponse = cardDavAddBook.GetCollection(sharedCardUrl, userAuthorization, myUri.ToString()).Result; + if (getResponse.Completed) + { + return new DavResponse() + { + Completed = true, + Data = sharedCardUrl + }; + } + else if (getResponse.StatusCode == 404) + { + var cardAB = new CardDavAddressbook(); + var createResponse = cardAB.Create("", "", "", sharedCardUrl, rootAuthorization).Result; + if (createResponse.Completed) + { + try + { + var dbConn = new DbRadicale(); + dbConn.SaveCardDavUser(CurrentTenant, currUser.ID.ToString()); + } + catch (Exception ex) + { + Log.Error("ERROR: " + ex.Message); + } + + await cardAB.UpdateItem(sharedCardUrl, rootAuthorization, cardBuilder, myUri.ToString()).ConfigureAwait(false); + return new DavResponse() + { + Completed = true, + Data = sharedCardUrl + }; + } + Log.Error("ERROR: " + createResponse.Error); + throw new RadicaleException(createResponse.Error); + } + else + { + Log.Error("ERROR: " + getResponse.Error); + throw new RadicaleException(getResponse.Error); + } + + } + + + /// + /// Deletes a CardDav address book with all portal users. + /// + /// + /// Delete a CardDav address book + /// + /// CardDav address book + /// false + [Delete("deletebook")] + public async Task DeleteCardDavAddressBook() + { + var currUser = CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID); + var currentUserEmail = currUser.Email; + var cardDavAB = new CardDavAddressbook(); + var authorization = cardDavAB.GetSystemAuthorization(); + var myUri = HttpContext.Current.Request.GetUrlRewriter(); + var requestUrlBook = cardDavAB.GetRadicaleUrl(myUri.ToString(), currentUserEmail, true, true); + var tenant = CurrentTenant; + var davRequest = new DavRequest() + { + Url = requestUrlBook, + Authorization = authorization, + Header = myUri.ToString() + }; + await RadicaleClient.RemoveAsync(davRequest).ConfigureAwait(false); + + try + { + var dbConn = new DbRadicale(); + dbConn.RemoveCardDavUser(tenant, currUser.ID.ToString()); + + return new DavResponse() + { + Completed = true, + Data = Resource.RadicaleCardDavDeleteMessage + }; + } + catch (Exception ex) + { + Log.Error("ERROR: " + ex.Message); + return new DavResponse() + { + Completed = false, + Error = ex.Message + }; + } + + + } + + public string CardDavAllSerialization(Uri uri) + { + var builder = new StringBuilder(); + var users = CoreContext.UserManager.GetUsers(); + var addbook = new CardDavAddressbook(); + + foreach (var user in users) + { + builder.AppendLine(addbook.GetUserSerialization(ItemFromUserInfo(user))); + } + + return builder.ToString(); + } + + public static CardDavItem ItemFromUserInfo(UserInfo u) + { + return new CardDavItem(u.ID, u.FirstName, u.LastName, u.UserName, u.BirthDate, u.Sex, u.Title, u.Email, u.Contacts, u.MobilePhone); + + } + } +} \ No newline at end of file diff --git a/module/ASC.Api/ASC.Api.Settings/SettingsApi.cs b/module/ASC.Api/ASC.Api.Settings/SettingsApi.cs index a64feecdc..8b5c5fa2a 100644 --- a/module/ASC.Api/ASC.Api.Settings/SettingsApi.cs +++ b/module/ASC.Api/ASC.Api.Settings/SettingsApi.cs @@ -71,11 +71,12 @@ using StorageHelper = ASC.Web.Studio.UserControls.CustomNavigation.StorageHelper namespace ASC.Api.Settings { /// - /// Portal settings + /// Portal settings. /// public partial class SettingsApi : IApiEntryPoint { private const int ONE_THREAD = 1; + private ILog Log = LogManager.GetLogger("ASC"); private static readonly DistributedTaskQueue ldapTasks = new DistributedTaskQueue("ldapOperations"); private static readonly DistributedTaskQueue quotaTasks = new DistributedTaskQueue("quotaOperations", ONE_THREAD); private static readonly DistributedTaskQueue smtpTasks = new DistributedTaskQueue("smtpOperations"); @@ -111,11 +112,12 @@ namespace ASC.Api.Settings } /// - /// Returns the list of all available portal settings with the current values for each one + /// Returns a list of all the available portal settings with the current values for each parameter. /// /// - /// Portal settings + /// Get the portal settings /// + ///Common settings ///Settings [Read("")] public SettingsWrapper GetSettings() @@ -132,11 +134,12 @@ namespace ASC.Api.Settings } /// - /// Returns space usage quota for portal with the space usage of each module + /// Returns the space usage quota for the portal with the space usage of each module. /// /// - /// Space usage + /// Get the space usage /// + ///Quota ///Space usage and limits for upload [Read("quota")] public QuotaWrapper GetQuotaUsed() @@ -145,12 +148,12 @@ namespace ASC.Api.Settings } /// - /// Start Recalculate Quota Task + /// Starts the process of recalculating quota. /// /// - /// Recalculate Quota + /// Recalculates quota /// - /// + ///Quota [Read("recalculatequota")] public void RecalculateQuota() { @@ -170,12 +173,13 @@ namespace ASC.Api.Settings } /// - /// Check Recalculate Quota Task + /// Checks the process of recalculating quota. /// /// - /// Check Recalculate Quota Task + /// Check quota recalculating /// - ///Check Recalculate Quota Task Status + ///Quota + ///Boolean value: True - quota recalculating process is enabled, False - quota recalculating process is disabled [Read("checkrecalculatequota")] public bool CheckRecalculateQuota() { @@ -193,24 +197,27 @@ namespace ASC.Api.Settings } /// - /// Get build version + /// Returns the current build version. /// + /// Get the current build version + /// Versions /// false /// Current onlyoffice, editor, mailserver versions - [Read("version/build", false, false)] //NOTE: this method doesn't requires auth!!! //NOTE: this method doesn't check payment!!! + [Read("version/build", false, false)] //NOTE: this method doesn't require auth!!! //NOTE: this method doesn't check payment!!! public BuildVersion GetBuildVersions() { return BuildVersion.GetCurrentBuildVersion(); } /// - /// Get list of availibe portal versions including current version + /// Returns a list of the availibe portal versions including the current version. /// /// - /// Portal versions + /// Get the portal versions /// + /// Versions /// false - /// List of availibe portal versions including current version + /// List of availibe portal versions including the current version [Read("version")] public TenantVersionWrapper GetVersions() { @@ -218,14 +225,15 @@ namespace ASC.Api.Settings } /// - /// Set current portal version to the one with the ID specified in the request + /// Sets the current portal version to the one with the ID specified in the request. /// /// - /// Change portal version + /// Change the portal version /// + /// Versions /// Version ID /// false - /// List of availibe portal versions including current version + /// List of availibe portal versions including the current version [Update("version")] public TenantVersionWrapper SetVersion(int versionId) { @@ -238,13 +246,14 @@ namespace ASC.Api.Settings } /// - /// Returns security settings about product, module or addons + /// Returns the security settings for the product, module or add-ons specified in the request. /// /// - /// Get security settings + /// Get the security settings /// - /// Module ID list - /// + /// Security + /// List of module IDs + /// Security settings [Read("security")] public IEnumerable GetWebItemSecurityInfo(IEnumerable ids) { @@ -266,6 +275,15 @@ namespace ASC.Api.Settings }).ToList(); } + /// + /// Returns the availability of the module with the ID specified in the request. + /// + /// + /// Get the module availability + /// + /// Security + /// Module ID + /// Boolean value: True - module is enabled, False - module is disabled [Read("security/{id}")] public bool GetWebItemSecurityInfo(Guid id) { @@ -275,11 +293,13 @@ namespace ASC.Api.Settings } /// - /// Return list of enabled modules + /// Returns a list of all the enabled modules. /// /// - /// Enabled modules + /// Get the enabled modules /// + /// Security + /// List of enabled modules /// false [Read("security/modules")] public object GetEnabledModules() @@ -297,11 +317,13 @@ namespace ASC.Api.Settings } /// - /// Get portal password settings + /// Returns the portal password settings. /// /// - /// Password settings + /// Get the password settings /// + /// Security + /// Password settings [Read("security/password")] public object GetPasswordSettings() { @@ -311,14 +333,16 @@ namespace ASC.Api.Settings } /// - /// Set security settings for product, module or addons + /// Sets the security settings to the product, module or add-ons. /// /// - /// Set security settings + /// Set the security settings /// + /// Security /// Module ID - /// Enabled - /// User (Group) ID list + /// Specifies if the module is enabled or not + /// List of user/group IDs + /// Security settings [Update("security")] public IEnumerable SetWebItemSecurity(string id, bool enabled, IEnumerable subjects) { @@ -354,12 +378,14 @@ namespace ASC.Api.Settings } /// - /// Set access to products, modules or addons + /// Sets access to the products, modules or add-ons specified in the request. /// /// - /// Set access + /// Set access to web items /// - /// + /// Security + /// Modules, products or add-ons with security information + /// Security settings [Update("security/access")] public IEnumerable SetAccessToWebItems(IEnumerable> items) { @@ -408,6 +434,15 @@ namespace ASC.Api.Settings return GetWebItemSecurityInfo(itemList.Keys.ToList()); } + /// + /// Returns a list of all the administrators of the product with the ID specified in the request. + /// + /// + /// Get the product administrators + /// + /// Security + /// Product ID + /// List of product administrators [Read("security/administrator/{productid}")] public IEnumerable GetProductAdministrators(Guid productid) { @@ -416,6 +451,16 @@ namespace ASC.Api.Settings .ToList(); } + /// + /// Checks if the selected user is an administrator of a product with the ID specified in the request. + /// + /// + /// Check the product administrator + /// + /// Security + /// Product ID + /// User ID + /// Object with user security information [Read("security/administrator")] public object IsProductAdministrator(Guid productid, Guid userid) { @@ -423,11 +468,28 @@ namespace ASC.Api.Settings return new { ProductId = productid, UserId = userid, Administrator = result, }; } + /// + /// Sets the selected user as an administrator of a product with the ID specified in the request. + /// + /// + /// Set the product administrator + /// + /// Security + /// Product ID + /// User ID + /// Specifies if the user will be added to the product as an administrator or deleted from it + /// Object with user security information [Update("security/administrator")] public object SetProductAdministrator(Guid productid, Guid userid, bool administrator) { SecurityContext.DemandPermissions(SecutiryConstants.EditPortalSettings); + var isStartup = !CoreContext.Configuration.CustomMode && TenantExtra.Saas && TenantExtra.GetTenantQuota().Free; + if (isStartup) + { + throw new BillingException(Resource.ErrorNotAllowedOption, "Administrator"); + } + WebItemSecurity.SetProductAdministrator(productid, userid, administrator); var admin = CoreContext.UserManager.GetUsers(userid); @@ -447,11 +509,12 @@ namespace ASC.Api.Settings } /// - /// Get portal logo image URL + /// Returns the portal logo image URL. /// /// - /// Portal logo + /// Get a portal logo /// + /// Common settings /// Portal logo image URL [Read("logo")] public string GetLogo() @@ -460,16 +523,23 @@ namespace ASC.Api.Settings } + /// + /// Saves the white label settings specified in the request. + /// + /// + /// Save the white label settings + /// + /// Rebranding + /// Logo text + /// Tenant IDs with their logos + /// Specifies if the default settings will be saved or not ///false [Create("whitelabel/save")] public void SaveWhiteLabelSettings(string logoText, IEnumerable> logo, bool isDefault) { SecurityContext.DemandPermissions(SecutiryConstants.EditPortalSettings); - if (!TenantLogoManager.WhiteLabelEnabled || !TenantLogoManager.WhiteLabelPaid) - { - throw new BillingException(Resource.ErrorNotAllowedOption, "WhiteLabel"); - } + DemandWhiteLabelPermission(); if (isDefault) { @@ -512,16 +582,22 @@ namespace ASC.Api.Settings } + /// + /// Saves the white label settings from files specified in the request. + /// + /// + /// Save the white label settings from files + /// + /// Rebranding + /// Files with white label settings + /// Specifies if the default settings will be saved or not ///false [Create("whitelabel/savefromfiles")] public void SaveWhiteLabelSettingsFromFiles(IEnumerable attachments, bool isDefault) { SecurityContext.DemandPermissions(SecutiryConstants.EditPortalSettings); - if (!TenantLogoManager.WhiteLabelEnabled || !TenantLogoManager.WhiteLabelPaid) - { - throw new BillingException(Resource.ErrorNotAllowedOption, "WhiteLabel"); - } + DemandWhiteLabelPermission(); if (attachments == null || !attachments.Any()) { @@ -568,16 +644,21 @@ namespace ASC.Api.Settings } + /// + /// Returns the white label sizes. + /// + /// + /// Get the white label sizes + /// + /// Rebranding + /// White label sizes ///false [Read("whitelabel/sizes")] public object GetWhiteLabelSizes() { SecurityContext.DemandPermissions(SecutiryConstants.EditPortalSettings); - if (!TenantLogoManager.WhiteLabelEnabled) - { - throw new BillingException(Resource.ErrorNotAllowedOption, "WhiteLabel"); - } + DemandWhiteLabelPermission(); return new[] @@ -592,16 +673,23 @@ namespace ASC.Api.Settings + /// + /// Returns the white label logos. + /// + /// + /// Get the white label logos + /// + /// Rebranding + /// Specifies if the logos will be for the retina screens or not + /// Specifies if the default settings will be saved or not + /// White label logos ///false [Read("whitelabel/logos")] public Dictionary GetWhiteLabelLogos(bool retina, bool isDefault) { SecurityContext.DemandPermissions(SecutiryConstants.EditPortalSettings); - if (!TenantLogoManager.WhiteLabelEnabled) - { - throw new BillingException(Resource.ErrorNotAllowedOption, "WhiteLabel"); - } + DemandWhiteLabelPermission(); var result = new Dictionary(); @@ -627,14 +715,22 @@ namespace ASC.Api.Settings return result; } + /// + /// Returns the white label logo text. + /// + /// + /// Get the white label logo text + /// + /// Rebranding + /// Specifies if the default settings will be saved or not + /// Logo text ///false [Read("whitelabel/logotext")] public string GetWhiteLabelLogoText(bool isDefault) { - if (!TenantLogoManager.WhiteLabelEnabled) - { - throw new BillingException(Resource.ErrorNotAllowedOption, "WhiteLabel"); - } + SecurityContext.DemandPermissions(SecutiryConstants.EditPortalSettings); + + DemandWhiteLabelPermission(); var settings = isDefault ? TenantWhiteLabelSettings.LoadForDefaultTenant() : TenantWhiteLabelSettings.Load(); @@ -642,16 +738,21 @@ namespace ASC.Api.Settings } + /// + /// Restores the white label options. + /// + /// + /// Restore the white label options + /// + /// Rebranding + /// Specifies if the default settings will be saved or not ///false [Update("whitelabel/restore")] public void RestoreWhiteLabelOptions(bool isDefault) { SecurityContext.DemandPermissions(SecutiryConstants.EditPortalSettings); - if (!TenantLogoManager.WhiteLabelEnabled || !TenantLogoManager.WhiteLabelPaid) - { - throw new BillingException(Resource.ErrorNotAllowedOption, "WhiteLabel"); - } + DemandWhiteLabelPermission(); if (isDefault) { @@ -689,9 +790,11 @@ namespace ASC.Api.Settings } /// - /// Get portal ip restrictions + /// Returns the IP portal restrictions. /// - /// + /// Get the IP portal restrictions + /// IP restrictions + /// IP restrictions [Read("/iprestrictions")] public IEnumerable GetIpRestrictions() { @@ -700,10 +803,12 @@ namespace ASC.Api.Settings } /// - /// save new portal ip restrictions + /// Saves the new portal IP restrictions specified in the request. /// - /// ip restrictions - /// + /// Save the IP restrictions + /// IP restrictions + /// New IP restrictions + /// New IP restrictions [Update("iprestrictions")] public IEnumerable SaveIpRestrictions(IEnumerable ips) { @@ -712,10 +817,12 @@ namespace ASC.Api.Settings } /// - /// update ip restrictions settings + /// Updates the IP restriction settings with a parameter specified in the request. /// - /// enable ip restrictions settings - /// + /// Update the IP restriction settings + /// IP restrictions + /// Enables IP restrictions + /// Updated IP restriction settings [Update("iprestrictions/settings")] public IPRestrictionsSettings UpdateIpRestrictionsSettings(bool enable) { @@ -728,10 +835,12 @@ namespace ASC.Api.Settings } /// - /// update tips settings + /// Updates the tip settings with a parameter specified in the request. /// - /// show tips for user - /// + /// Update the tip settings + /// Tips + /// Shows tips for the user + /// Updated tip settings [Update("tips")] public TipsSettings UpdateTipsSettings(bool show) { @@ -753,7 +862,7 @@ namespace ASC.Api.Settings } catch (Exception e) { - LogManager.GetLogger("ASC").Error(e.Message, e); + Log.Error(e.Message, e); } } @@ -761,9 +870,11 @@ namespace ASC.Api.Settings } /// - /// change tips&tricks subscription + /// Updates the tip subscription. /// - /// subscription state + /// Update the tip subscription + /// Tips + /// Updated tip subscription [Update("tips/change/subscription")] public bool UpdateTipsSubscription() { @@ -771,9 +882,11 @@ namespace ASC.Api.Settings } /// - /// Complete Wizard + /// Completes the Wizard settings. /// - /// WizardSettings + /// Complete the Wizard settings + /// Wizard + /// Wizard settings /// false [Update("wizard/complete")] public WizardSettings CompleteWizard() @@ -792,10 +905,12 @@ namespace ASC.Api.Settings } /// - /// Update two-factor authentication settings + /// Updates the two-factor authentication settings with the type specified in the request. /// - /// sms, app or none - /// true if success + /// Update the TFA settings + /// TFA settings + /// TFA type: sms, app or none + /// True if an operation is successful [Update("tfaapp")] public bool TfaSettings(string type) { @@ -807,7 +922,7 @@ namespace ASC.Api.Settings switch (type) { case "sms": - if (!StudioSmsNotificationSettings.IsVisibleSettings) + if (!StudioSmsNotificationSettings.IsVisibleAndAvailableSettings) throw new Exception(Resource.SmsNotAvailable); if (!SmsProviderManager.Enabled()) @@ -834,7 +949,7 @@ namespace ASC.Api.Settings TfaAppAuthSettings.Enable = true; action = MessageAction.TwoFactorAuthenticationEnabledByTfaApp; - if (StudioSmsNotificationSettings.IsVisibleSettings && StudioSmsNotificationSettings.Enable) + if (StudioSmsNotificationSettings.IsVisibleAndAvailableSettings && StudioSmsNotificationSettings.Enable) { StudioSmsNotificationSettings.Enable = false; } @@ -849,7 +964,7 @@ namespace ASC.Api.Settings TfaAppAuthSettings.Enable = false; } - if (StudioSmsNotificationSettings.IsVisibleSettings && StudioSmsNotificationSettings.Enable) + if (StudioSmsNotificationSettings.IsVisibleAndAvailableSettings && StudioSmsNotificationSettings.Enable) { StudioSmsNotificationSettings.Enable = false; } @@ -868,6 +983,12 @@ namespace ASC.Api.Settings return result; } + /// + /// Returns the two-factor authentication application codes. + /// + /// Get the TFA codes + /// TFA settings + /// List of TFA application codes ///false [Read("tfaappcodes")] public IEnumerable TfaAppGetCodes() @@ -884,8 +1005,10 @@ namespace ASC.Api.Settings } /// - /// Requests new backup codes for two-factor application + /// Requests the new backup codes for the two-factor authentication application. /// + /// Request the new TFA codes + /// TFA settings /// New backup codes [Update("tfaappnewcodes")] public IEnumerable TfaAppRequestNewCodes() @@ -904,9 +1027,12 @@ namespace ASC.Api.Settings } /// - /// Unlinks current two-factor auth application + /// Unlinks the current two-factor authentication application from the user account specified in the request. /// - /// Login url + /// Unlink the TFA application + /// TFA settings + /// User ID + /// Login URL [Update("tfaappnewapp")] public string TfaAppNewApp(Guid id) { @@ -934,11 +1060,14 @@ namespace ASC.Api.Settings return string.Empty; } - /// false + /// - /// Gets a link that will connect TelegramBot to your account + /// Returns a link that will connect Telegram Bot to your account. /// - /// url + /// Get the Telegram link + /// Telegram + /// Telegram link + /// false [Read("telegramlink")] public string TelegramLink() { @@ -955,9 +1084,11 @@ namespace ASC.Api.Settings } /// - /// Checks if user has connected TelegramBot + /// Checks if the current user is connected to Telegram Bot or not. /// - /// 0 - not connected, 1 - connected, 2 - awaiting confirmation + /// Check the Telegram connection + /// Telegram + /// Integer value: 0 - not connected, 1 - connected, 2 - awaiting confirmation [Read("telegramisconnected")] public int TelegramIsConnected() { @@ -965,15 +1096,21 @@ namespace ASC.Api.Settings } /// - /// Unlinks TelegramBot from your account + /// Unlinks Telegram Bot from your account. /// + /// Unlink Telegram Bot + /// Telegram [Delete("telegramdisconnect")] public void TelegramDisconnect() { TelegramHelper.Instance.Disconnect(CurrentUser, CurrentTenant); } - ///false + /// + /// Closes the welcome pop-up notification. + /// + /// Close the welcome pop-up notification + /// Common settings [Update("welcome/close")] public void CloseWelcomePopup() { @@ -988,6 +1125,29 @@ namespace ASC.Api.Settings collaboratorPopupSettings.SaveForCurrentUser(); } + /// + /// Close the admin helper notification. + /// + /// Close the admin helper notification + /// Common settings + [Update("closeadminhelper")] + public void CloseAdminHelper() + { + if(!CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID).IsAdmin() || CoreContext.Configuration.CustomMode || !CoreContext.Configuration.Standalone) + throw new NotSupportedException("Not available."); + + var adminHelperSettings = AdminHelperSettings.LoadForCurrentUser(); + adminHelperSettings.Viewed = true; + adminHelperSettings.SaveForCurrentUser(); + } + + + /// + /// Saves a portal color theme specified in the request. + /// + /// Save a color theme + /// Common settings + /// Portal theme ///false [Update("colortheme")] public void SaveColorTheme(string theme) @@ -997,6 +1157,14 @@ namespace ASC.Api.Settings MessageService.Send(HttpContext.Current.Request, MessageAction.ColorThemeChanged); } + /// + /// Sets the portal time zone and language specified in the request. + /// + /// Set time zone and language + /// Common settings + /// Language + /// Time zone ID + /// Operation result ///false [Update("timeandlanguage")] public string TimaAndLanguage(string lng, string timeZoneID) @@ -1041,6 +1209,13 @@ namespace ASC.Api.Settings return Resource.SuccessfullySaveSettingsMessage; } + /// + /// Sets the default product page. + /// + /// Set the default product page + /// Common settings + /// Default product ID + /// Operation result ///false [Update("defaultpage")] public string SaveDefaultPageSettings(string defaultProductID) @@ -1062,8 +1237,11 @@ namespace ASC.Api.Settings } /// - /// Refresh license + /// Refreshes the license. /// + /// Refresh the license + /// Common settings + /// Boolean value: True - an operation is successful, False - an operation is unsuccessful /// false [Read("license/refresh")] public bool RefreshLicense() @@ -1075,9 +1253,11 @@ namespace ASC.Api.Settings /// - /// Get Custom Navigation Items + /// Returns a list of the custom navigation items. /// - /// CustomNavigationItem List + /// Get the custom navigation items + /// Custom navigation + /// List of the custom navigation items [Read("customnavigation/getall")] public List GetCustomNavigationItems() { @@ -1085,9 +1265,11 @@ namespace ASC.Api.Settings } /// - /// Get Custom Navigation Items Sample + /// Returns a custom navigation item sample. /// - /// CustomNavigationItem Sample + /// Get a custom navigation item sample + /// Custom navigation + /// Custom navigation item [Read("customnavigation/getsample")] public CustomNavigationItem GetCustomNavigationItemSample() { @@ -1095,10 +1277,12 @@ namespace ASC.Api.Settings } /// - /// Get Custom Navigation Item by Id + /// Returns a custom navigation item by the ID specified in the request. /// - /// Item id - /// CustomNavigationItem + /// Get a custom navigation item by ID + /// Custom navigation + /// Item ID + /// Custom navigation item [Read("customnavigation/get/{id}")] public CustomNavigationItem GetCustomNavigationItem(Guid id) { @@ -1106,10 +1290,12 @@ namespace ASC.Api.Settings } /// - /// Add Custom Navigation Item + /// Adds a custom navigation item with the parameters specified in the request. /// - /// Item - /// CustomNavigationItem + /// Add a custom navigation item + /// Custom navigation + /// Item parameters + /// Custom navigation item [Create("customnavigation/create")] public CustomNavigationItem CreateCustomNavigationItem(CustomNavigationItem item) { @@ -1161,9 +1347,11 @@ namespace ASC.Api.Settings } /// - /// Delete Custom Navigation Item by Id + /// Deletes a custom navigation item by the ID specified in the request. /// - /// Item id + /// Delete a custom navigation item + /// Custom navigation + /// Item ID [Delete("customnavigation/delete/{id}")] public void DeleteCustomNavigationItem(Guid id) { @@ -1185,10 +1373,12 @@ namespace ASC.Api.Settings } /// - /// update email activation settings + /// Updates the email activation settings. /// - /// show email activation panel for user - /// + /// Update the email activation settings + /// Common settings + /// Shows the email activation panel for the user + /// Updated email activation settings [Update("emailactivation")] public EmailActivationSettings UpdateEmailActivationSettings(bool show) { @@ -1199,6 +1389,12 @@ namespace ASC.Api.Settings return settings; } + /// + /// Returns the licensor data. + /// + /// Get the licensor data + /// Common settings + /// List of company white label settings ///false [Read("companywhitelabel")] public List GetLicensorData() @@ -1218,10 +1414,12 @@ namespace ASC.Api.Settings } /// - /// Get WebItem Space Usage Statistics + /// Returns the space usage statistics of the web item with the ID specified in the request. /// - /// WebItem id - /// UsageSpaceStatItemWrapper List + /// Statistics + /// Get the space usage statistics + /// Web item ID + /// Space usage statistics of the web item [Read("statistics/spaceusage/{id}")] public List GetSpaceUsageStatistics(Guid id) { @@ -1248,11 +1446,13 @@ namespace ASC.Api.Settings } /// - /// Get User Visit Statistics + /// Returns the user visit statistics for the period specified in the request. /// - /// From Date - /// To Date - /// ChartPointWrapper List + /// Statistics + /// Get the visit statistics + /// Start period date + /// End period date + /// List of point charts [Read("statistics/visit")] public List GetVisitStatistics(ApiDateTime fromDate, ApiDateTime toDate) { @@ -1302,9 +1502,11 @@ namespace ASC.Api.Settings } /// - /// Get Storage + /// Returns a list of all the portal storages. /// - /// Consumer + /// Storage + /// Get storages + /// List of storages [Read("storage")] public List GetAllStorages() { @@ -1318,9 +1520,11 @@ namespace ASC.Api.Settings } /// - /// Get Storage + /// Returns the storage progress. /// - /// Consumer + /// Storage + /// Get the storage progress + /// Storage progress [Read("storage/progress", checkPayment: false)] public double GetStorageProgress() { @@ -1335,16 +1539,20 @@ namespace ASC.Api.Settings } /// - /// Get Storage + /// Updates a storage with the parameters specified in the request. /// - /// Consumer + /// Storage + /// Update a storage + /// Storage name + /// New storage properties + /// Updated storage [Update("storage")] public StorageSettings UpdateStorage(string module, IEnumerable> props) { try { - LogManager.GetLogger("ASC").Debug("UpdateStorage"); + Log.Debug("UpdateStorage"); SecurityContext.DemandPermissions(SecutiryConstants.EditPortalSettings); if (!CoreContext.Configuration.Standalone) return null; @@ -1365,17 +1573,22 @@ namespace ASC.Api.Settings } catch (Exception e) { - LogManager.GetLogger("ASC").Error("UpdateStorage", e); + Log.Error("UpdateStorage", e); throw; } } + /// + /// Resets the storage settings to the default parameters. + /// + /// Storage + /// Reset the storage settings [Delete("storage")] public void ResetStorageToDefault() { try { - LogManager.GetLogger("ASC").Debug("ResetStorageToDefault"); + Log.Debug("ResetStorageToDefault"); SecurityContext.DemandPermissions(SecutiryConstants.EditPortalSettings); if (!CoreContext.Configuration.Standalone) return; @@ -1391,15 +1604,17 @@ namespace ASC.Api.Settings } catch (Exception e) { - LogManager.GetLogger("ASC").Error("ResetStorageToDefault", e); + Log.Error("ResetStorageToDefault", e); throw; } } /// - /// Get Storage + /// Returns a list of all the CDN storages. /// - /// Consumer + /// Storage + /// Get the CDN storages + /// List of the CDN storages [Read("storage/cdn")] public List GetAllCdnStorages() { @@ -1414,9 +1629,13 @@ namespace ASC.Api.Settings } /// - /// Get Storage + /// Updates the CDN storage with the parameters specified in the request. /// - /// Consumer + /// Storage + /// Update the CDN storage + /// CDN storage name + /// New CDN storage properties + /// Updated CDN storage [Update("storage/cdn")] public CdnStorageSettings UpdateCdn(string module, IEnumerable> props) { @@ -1446,13 +1665,18 @@ namespace ASC.Api.Settings } catch (Exception e) { - LogManager.GetLogger("ASC").Error("UpdateCdn", e); + Log.Error("UpdateCdn", e); throw; } return settings; } + /// + /// Resets the CDN storage settings to the default parameters. + /// + /// Storage + /// Reset the CDN storage settings [Delete("storage/cdn")] public void ResetCdnToDefault() { @@ -1465,9 +1689,11 @@ namespace ASC.Api.Settings } /// - /// Get Storage + /// Returns a list of all the backup storages. /// - /// Consumer + /// Storage + /// Get the backup storages + /// List of the backup storages [Read("storage/backup")] public List GetAllBackupStorages() { @@ -1507,6 +1733,12 @@ namespace ASC.Api.Settings } + /// + /// Returns the socket settings. + /// + /// Common settings + /// Get the socket settings + /// Socket settings [Read("socket")] public object GetSocketSettings() { @@ -1522,6 +1754,12 @@ namespace ASC.Api.Settings return new { Url = hubUrl }; } + /// + /// Returns the tenant control panel settings. + /// + /// Common settings + /// Get the tenant control panel settings + /// Tenant control panel settings ///false [Read("controlpanel")] public TenantControlPanelSettings GetTenantControlPanelSettings() @@ -1529,6 +1767,12 @@ namespace ASC.Api.Settings return TenantControlPanelSettings.Instance; } + /// + /// Saves the company white label settings specified in the request. + /// + /// Rebranding + /// Save the white label settings + /// White label settings ///false [Create("rebranding/company")] public void SaveCompanyWhiteLabelSettings(CompanyWhiteLabelSettings settings) @@ -1542,6 +1786,12 @@ namespace ASC.Api.Settings settings.SaveForDefaultTenant(); } + /// + /// Returns the company white label settings. + /// + /// Rebranding + /// Get the white label settings + /// Company white label settings ///false [Read("rebranding/company")] public CompanyWhiteLabelSettings GetCompanyWhiteLabelSettings() @@ -1549,6 +1799,12 @@ namespace ASC.Api.Settings return CompanyWhiteLabelSettings.Instance; } + /// + /// Deletes the company white label settings. + /// + /// Rebranding + /// Delete the white label settings + /// Default company white label settings ///false [Delete("rebranding/company")] public CompanyWhiteLabelSettings DeleteCompanyWhiteLabelSettings() @@ -1562,6 +1818,12 @@ namespace ASC.Api.Settings return defaultSettings; } + /// + /// Saves the additional white label settings specified in the request. + /// + /// Rebranding + /// Save the additional white label settings + /// Additional white label settings ///false [Create("rebranding/additional")] public void SaveAdditionalWhiteLabelSettings(AdditionalWhiteLabelSettings settings) @@ -1573,6 +1835,12 @@ namespace ASC.Api.Settings settings.SaveForDefaultTenant(); } + /// + /// Returns the additional white label settings. + /// + /// Rebranding + /// Get the additional white label settings + /// Additional white label settings ///false [Read("rebranding/additional")] public AdditionalWhiteLabelSettings GetAdditionalWhiteLabelSettings() @@ -1580,6 +1848,12 @@ namespace ASC.Api.Settings return AdditionalWhiteLabelSettings.Instance; } + /// + /// Deletes the additional white label settings. + /// + /// Rebranding + /// Delete the additional white label settings + /// Default white label settings ///false [Delete("rebranding/additional")] public AdditionalWhiteLabelSettings DeleteAdditionalWhiteLabelSettings() @@ -1593,6 +1867,12 @@ namespace ASC.Api.Settings return defaultSettings; } + /// + /// Saves the mail white label settings specified in the request. + /// + /// Rebranding + /// Save the mail white label settings + /// Mail white label settings ///false [Create("rebranding/mail")] public void SaveMailWhiteLabelSettings(MailWhiteLabelSettings settings) @@ -1604,6 +1884,12 @@ namespace ASC.Api.Settings settings.SaveForDefaultTenant(); } + /// + /// Updates the mail white label settings with a paramater specified in the request. + /// + /// Rebranding + /// Update the mail white label settings + /// Specified if the footer will be enabled or not ///false [Update("rebranding/mail")] public void UpdateMailWhiteLabelSettings(bool footerEnabled) @@ -1617,6 +1903,12 @@ namespace ASC.Api.Settings settings.SaveForDefaultTenant(); } + /// + /// Returns the mail white label settings. + /// + /// Rebranding + /// Get the mail white label settings + /// Mail white label settings ///false [Read("rebranding/mail")] public MailWhiteLabelSettings GetMailWhiteLabelSettings() @@ -1624,6 +1916,12 @@ namespace ASC.Api.Settings return MailWhiteLabelSettings.Instance; } + /// + /// Deletes the mail white label settings. + /// + /// Rebranding + /// Delete the mail white label settings + /// Default mail white label settings ///false [Delete("rebranding/mail")] public MailWhiteLabelSettings DeleteMailWhiteLabelSettings() @@ -1637,11 +1935,20 @@ namespace ASC.Api.Settings return defaultSettings; } + private static void DemandWhiteLabelPermission() + { + if (!CoreContext.Configuration.Standalone + && (!TenantLogoManager.WhiteLabelEnabled || !TenantLogoManager.WhiteLabelPaid)) + { + throw new BillingException(Resource.ErrorNotAllowedOption, "WhiteLabel"); + } + } + private static void DemandRebrandingPermission() { TenantExtra.DemandControlPanelPermission(); - if (!CoreContext.TenantManager.GetTenantQuota(TenantProvider.CurrentTenantID).SSBranding) + if (!CoreContext.Configuration.Standalone) { throw new BillingException(Resource.ErrorNotAllowedOption, "SSBranding"); } @@ -1653,9 +1960,11 @@ namespace ASC.Api.Settings } /// - /// Get storage encryption settings + /// Returns the storage encryption settings. /// - /// EncryptionSettings + /// Get the storage encryption settings + /// Encryption + /// Storage encryption settings /// false [Read("encryption/settings")] public EncryptionSettings GetStorageEncryptionSettings() @@ -1681,11 +1990,6 @@ namespace ASC.Api.Settings TenantExtra.DemandControlPanelPermission(); - if (!CoreContext.TenantManager.GetTenantQuota(TenantProvider.CurrentTenantID).DiscEncryption) - { - throw new BillingException(Resource.ErrorNotAllowedOption, "DiscEncryption"); - } - var settings = EncryptionSettings.Load(); settings.Password = string.Empty; // Don't show password @@ -1694,15 +1998,17 @@ namespace ASC.Api.Settings } catch (Exception e) { - LogManager.GetLogger("ASC").Error("GetStorageEncryptionSettings", e); + Log.Error("GetStorageEncryptionSettings", e); return null; } } /// - /// Get storage encryption progress + /// Returns the storage encryption progress. /// - /// Progress + /// Get the storage encryption progress + /// Encryption + /// Storage encryption progress /// false [Read("encryption/progress", checkPayment: false)] public double GetStorageEncryptionProgress() @@ -1722,11 +2028,6 @@ namespace ASC.Api.Settings throw new NotSupportedException(Resource.ErrorServerEditionMethod); } - if (!CoreContext.TenantManager.GetTenantQuota(TenantProvider.CurrentTenantID).DiscEncryption) - { - throw new BillingException(Resource.ErrorNotAllowedOption, "DiscEncryption"); - } - using (var encryptionClient = new EncryptionServiceClient()) { return encryptionClient.GetProgress(); @@ -1736,9 +2037,11 @@ namespace ASC.Api.Settings public static readonly object Locker = new object(); /// - /// Start storage encryption process + /// Starts the storage encryption process. /// - /// + /// Start the storage encryption process + /// Encryption + /// Specifies if the users will be notified about the encryption process or not /// false [Create("encryption/start")] public void StartStorageEncryption(bool notifyUsers) @@ -1775,11 +2078,6 @@ namespace ASC.Api.Settings TenantExtra.DemandControlPanelPermission(); - if (!CoreContext.TenantManager.GetTenantQuota(TenantProvider.CurrentTenantID).DiscEncryption) - { - throw new BillingException(Resource.ErrorNotAllowedOption, "DiscEncryption"); - } - var storages = GetAllStorages(); if (storages.Any(s => s.Current)) diff --git a/module/ASC.Api/ASC.Api.Settings/Smtp/SmtpOperation.cs b/module/ASC.Api/ASC.Api.Settings/Smtp/SmtpOperation.cs index b42e3cdeb..055097c13 100644 --- a/module/ASC.Api/ASC.Api.Settings/Smtp/SmtpOperation.cs +++ b/module/ASC.Api/ASC.Api.Settings/Smtp/SmtpOperation.cs @@ -102,7 +102,7 @@ namespace ASC.Api.Settings.Smtp SetProgress(10, "Setup user"); - SecurityContext.AuthenticateMe(CurrentUser); //Core.Configuration.Constants.CoreSystem); + SecurityContext.CurrentUser = CurrentUser; //Core.Configuration.Constants.CoreSystem); SetProgress(15, "Find user data"); diff --git a/module/ASC.Api/ASC.Api.Settings/SmtpSettingsApi.cs b/module/ASC.Api/ASC.Api.Settings/SmtpSettingsApi.cs index 85900c4d4..b6d14e759 100644 --- a/module/ASC.Api/ASC.Api.Settings/SmtpSettingsApi.cs +++ b/module/ASC.Api/ASC.Api.Settings/SmtpSettingsApi.cs @@ -34,12 +34,13 @@ namespace ASC.Api.Settings public partial class SettingsApi { /// - /// Returns current portal SMTP settings + /// Returns the current portal SMTP settings. /// /// - /// Get SMTP settings + /// Get the SMTP settings /// - /// SmtpSettings data + /// SMTP + /// SMTP settings [Read("smtp")] public SmtpSettingsWrapper GetSmtpSettings() { @@ -51,13 +52,14 @@ namespace ASC.Api.Settings } /// - /// Save SMTP settings for current portal + /// Saves the SMTP settings for the current portal. /// /// - /// Save SMTP settings + /// Save the SMTP settings /// - /// SMTP settings data - /// SmtpSettings data + /// SMTP + /// SMTP settings + /// SMTP settings [Create("smtp")] public SmtpSettingsWrapper SaveSmtpSettings(SmtpSettingsWrapper smtpSettings) { @@ -80,12 +82,13 @@ namespace ASC.Api.Settings } /// - /// Reset SMTP settings for current portal + /// Resets SMTP settings of the current portal. /// /// - /// Reset SMTP settings + /// Reset the SMTP settings /// - /// SmtpSettings data + /// SMTP + /// Default SMTP settings [Delete("smtp")] public SmtpSettingsWrapper ResetSmtpSettings() { @@ -103,12 +106,13 @@ namespace ASC.Api.Settings } /// - /// Test SMTP settings for current portal (send test message to user email) + /// Tests the SMTP settings for the current portal (send test message to the user email). /// /// - /// Test SMTP settings + /// Test the SMTP settings /// - /// SmtpOperationStatus data + /// SMTP + /// SMTP operation status [Read("smtp/test")] public SmtpOperationStatus TestSmtpSettings() { @@ -124,12 +128,13 @@ namespace ASC.Api.Settings } /// - /// Returns SMTP test process status + /// Returns the SMTP test process status. /// /// - /// Get SMTP test process status + /// Get the SMTP test process status /// - /// SmtpOperationStatus object + /// SMTP + /// SMTP operation status [Read("smtp/test/status")] public SmtpOperationStatus GetSmtpOperationStatus() { diff --git a/module/ASC.Api/ASC.Api.Settings/SsoSettingsV2Api.cs b/module/ASC.Api/ASC.Api.Settings/SsoSettingsV2Api.cs index 8bee9a586..afe20569f 100644 --- a/module/ASC.Api/ASC.Api.Settings/SsoSettingsV2Api.cs +++ b/module/ASC.Api/ASC.Api.Settings/SsoSettingsV2Api.cs @@ -36,12 +36,13 @@ namespace ASC.Api.Settings public partial class SettingsApi { /// - /// Returns current portal SSO settings + /// Returns the current portal SSO settings. /// /// - /// Get SSO settings + /// Get the SSO settings /// - /// SsoSettingsV2 object + /// SSO + /// SSO settings [Read("ssov2")] public SsoSettingsV2 GetSsoSettingsV2() { @@ -56,12 +57,13 @@ namespace ASC.Api.Settings } /// - /// Returns default portal SSO settings + /// Returns the default portal SSO settings. /// /// - /// Get default SSO settings + /// Get the default SSO settings /// - /// SsoSettingsV2 object + /// SSO + /// Default SSO settings [Read("ssov2/default")] public SsoSettingsV2 GetDefaultSsoSettingsV2() { @@ -71,12 +73,13 @@ namespace ASC.Api.Settings } /// - /// Returns SSO settings constants + /// Returns the constants of the SSO settings. /// /// - /// Get SSO settings constants + /// Get the constants of the SSO settings /// - /// object + /// SSO + /// Constants of the SSO settings [Read("ssov2/constants")] public object GetSsoSettingsV2Constants() { @@ -92,13 +95,14 @@ namespace ASC.Api.Settings } /// - /// Save SSO settings for current portal + /// Saves the SSO settings for the current portal. /// /// - /// Save SSO settings + /// Save the SSO settings /// - /// serialized SsoSettingsV2 object - /// SsoSettingsV2 object + /// SSO + /// Serialized SSO settings + /// SSO settings [Create("ssov2")] public SsoSettingsV2 SaveSsoSettingsV2(string serializeSettings) { @@ -151,12 +155,13 @@ namespace ASC.Api.Settings } /// - /// Reset SSO settings for current portal + /// Resets the SSO settings of the current portal. /// /// - /// Reset SSO settings + /// Reset the SSO settings /// - /// SsoSettingsV2 object + /// SSO + /// Default SSO settings [Delete("ssov2")] public SsoSettingsV2 ResetSsoSettingsV2() { @@ -203,8 +208,9 @@ namespace ASC.Api.Settings { SecurityContext.DemandPermissions(SecutiryConstants.EditPortalSettings); - if ((!SetupInfo.IsVisibleSettings(ManagementType.SingleSignOnSettings.ToString()) && !CoreContext.Configuration.Standalone) || - !CoreContext.TenantManager.GetTenantQuota(TenantProvider.CurrentTenantID).Sso) + if (!CoreContext.Configuration.Standalone + && (!SetupInfo.IsVisibleSettings(ManagementType.SingleSignOnSettings.ToString()) + || !CoreContext.TenantManager.GetTenantQuota(TenantProvider.CurrentTenantID).Sso)) { throw new BillingException(Resource.ErrorNotAllowedOption, "Sso"); } diff --git a/module/ASC.Api/ASC.Api/ASC.Api.Core.csproj b/module/ASC.Api/ASC.Api/ASC.Api.Core.csproj index 32b72631a..65ab4e90a 100644 --- a/module/ASC.Api/ASC.Api/ASC.Api.Core.csproj +++ b/module/ASC.Api/ASC.Api/ASC.Api.Core.csproj @@ -117,7 +117,7 @@ - + @@ -176,10 +176,10 @@ - 5.1.2 + 6.2.0 - 12.0.3 + 13.0.1 diff --git a/module/ASC.Api/ASC.Api/ApiServer.cs b/module/ASC.Api/ASC.Api/ApiServer.cs index c63ede503..9b52fb62c 100644 --- a/module/ASC.Api/ASC.Api/ApiServer.cs +++ b/module/ASC.Api/ASC.Api/ApiServer.cs @@ -126,7 +126,7 @@ namespace ASC.Api public ApiBatchResponse CallApiMethod(ApiBatchRequest request, bool encode) { - var response = _batchHandler.ProcessBatchRequest(_context, request); + var response = _batchHandler.ProcessBatchRequest(_context, request).Result; if (encode && response != null && response.Data != null) response.Data = Convert.ToBase64String(Encoding.UTF8.GetBytes(response.Data)); return response; diff --git a/module/ASC.Api/ASC.Api/Batch/ApiBatchHttpHandler.cs b/module/ASC.Api/ASC.Api/Batch/ApiBatchHttpHandler.cs index edc6af96e..6335d0f08 100644 --- a/module/ASC.Api/ASC.Api/Batch/ApiBatchHttpHandler.cs +++ b/module/ASC.Api/ASC.Api/Batch/ApiBatchHttpHandler.cs @@ -22,6 +22,7 @@ using System.Linq; using System.Net; using System.Net.Mime; using System.Text; +using System.Threading.Tasks; using System.Web; using System.Web.Routing; @@ -43,7 +44,7 @@ namespace ASC.Api.Batch { } - protected override void DoProcess(HttpContextBase context) + protected override async Task DoProcess(HttpContextBase context) { //Read body var batch = context.Request["batch"]; @@ -71,7 +72,7 @@ namespace ASC.Api.Batch try { Log.Debug("processing batch started"); - ProcessBatch(context, requests); + await ProcessBatch(context, requests); Log.Debug("processing batch finished"); } catch (Exception e) @@ -91,15 +92,21 @@ namespace ASC.Api.Batch RespondTo(null, context); } - private void ProcessBatch(HttpContextBase context, IEnumerable requests) + private async Task ProcessBatch(HttpContextBase context, IEnumerable requests) { - var resonse = requests.OrderBy(x => x.Order).Select(x => ProcessBatchRequest(context, x)).ToList(); + var response = new List(); - ApiResponce.Response = resonse; - PostProcessResponse(context, resonse); + foreach (ApiBatchRequest request in requests.OrderBy(x => x.Order)) + { + response.Add(await ProcessBatchRequest(context, request)); + } + + + ApiResponce.Response = response; + PostProcessResponse(context, response); } - internal ApiBatchResponse ProcessBatchRequest(HttpContextBase context, ApiBatchRequest apiBatchRequest) + internal async Task ProcessBatchRequest(HttpContextBase context, ApiBatchRequest apiBatchRequest) { if (context.Request == null) throw new InvalidOperationException("Request is empty"); if (context.Request.Url == null) throw new InvalidOperationException("Url is empty"); @@ -130,7 +137,7 @@ namespace ASC.Api.Batch if (routeData != null) { //Construct new context - Container.BeginLifetimeScope().Resolve(new TypedParameter(typeof(RouteData), routeData)).Process(newContext); + await Container.BeginLifetimeScope().Resolve(new TypedParameter(typeof(RouteData), routeData)).ProcessRequestAsync(workContext); newContext.Response.Flush(); //Form response diff --git a/module/ASC.Api/ASC.Api/Impl/ApiArgumentBuilder.cs b/module/ASC.Api/ASC.Api/Impl/ApiArgumentBuilder.cs index 9b2a3abfe..f78d47e78 100644 --- a/module/ASC.Api/ASC.Api/Impl/ApiArgumentBuilder.cs +++ b/module/ASC.Api/ASC.Api/Impl/ApiArgumentBuilder.cs @@ -240,7 +240,6 @@ namespace ASC.Api.Impl FillCollectionFromXElement(element.Elements(), prefix + "." + element.Name.LocalName, collection); } } - } else { @@ -275,7 +274,17 @@ namespace ASC.Api.Impl var additional = string.Empty; if (prefixes.Length > 1) { - additional = string.Join("", prefix.Skip(1).Select(x => "[" + x + "]").ToArray()); + additional = string.Join("", prefixes.Skip(1) + .Select(subprefix => + { + var indexPos = subprefix.IndexOf('['); + if (indexPos < 0) + return "[" + subprefix + "]"; + + //"param[0]" => "[param][0]" + return "[" + subprefix.Substring(0, indexPos) + "]" + subprefix.Substring(indexPos); + }) + .ToArray()); } collection.Add(prefixes[0] + additional + "[" + element.Name.LocalName + "]", element.Value); } diff --git a/module/ASC.Api/ASC.Api/Impl/ApiHttpHandler.cs b/module/ASC.Api/ASC.Api/Impl/ApiHttpHandler.cs index d803a92c0..61b6b7103 100644 --- a/module/ASC.Api/ASC.Api/Impl/ApiHttpHandler.cs +++ b/module/ASC.Api/ASC.Api/Impl/ApiHttpHandler.cs @@ -20,11 +20,13 @@ using System.Net; using System.Reflection; using System.Security; using System.Threading; +using System.Threading.Tasks; using System.Web; using System.Web.Routing; using ASC.Api.Exceptions; using ASC.Api.Interfaces; +using ASC.Core; using Autofac; @@ -41,7 +43,7 @@ namespace ASC.Api.Impl } - protected override void DoProcess(HttpContextBase context) + protected override async Task DoProcess(HttpContextBase context) { Log.DebugFormat("strating request. context: '{0}'", ApiContext); @@ -49,16 +51,17 @@ namespace ASC.Api.Impl context.Response.Buffer = true; context.Response.BufferOutput = true; - IApiEntryPoint instance = null; - try { + var currentTenantId = CoreContext.TenantManager.GetCurrentTenant().TenantId; Log.Debug("method invoke"); ApiResponce.Count = ApiContext.Count; ApiResponce.StartIndex = ApiContext.StartIndex; if (Method != null) { + + IApiEntryPoint instance; if (!string.IsNullOrEmpty(Method.Name)) { instance = Container.ResolveNamed(Method.Name, new TypedParameter(typeof(ApiContext), ApiContext)); @@ -69,6 +72,21 @@ namespace ASC.Api.Impl } var responce = ApiManager.InvokeMethod(Method, ApiContext, instance); + + if (responce is Task) + { + if (Method.MethodCall.ReturnType.IsGenericType) + { + responce = await (dynamic)responce; + CoreContext.TenantManager.SetCurrentTenant(currentTenantId); + } + else + { + await (Task)responce; + CoreContext.TenantManager.SetCurrentTenant(currentTenantId); + responce = null; + } + } if (responce is Exception) { SetError(context, (Exception)responce, HttpStatusCode.InternalServerError); diff --git a/module/ASC.Api/ASC.Api/Impl/ApiHttpHandlerBase.cs b/module/ASC.Api/ASC.Api/Impl/ApiHttpHandlerBase.cs index 52674a326..9c43ecd60 100644 --- a/module/ASC.Api/ASC.Api/Impl/ApiHttpHandlerBase.cs +++ b/module/ASC.Api/ASC.Api/Impl/ApiHttpHandlerBase.cs @@ -21,6 +21,7 @@ using System.IO.Compression; using System.Linq; using System.Net; using System.Threading; +using System.Threading.Tasks; using System.Web; using System.Web.Routing; @@ -35,7 +36,7 @@ using Autofac; namespace ASC.Api.Impl { - public abstract class ApiHttpHandlerBase : IApiHttpHandler + public abstract class ApiHttpHandlerBase : ApiHttpAsyncHandler { public ILog Log { get; set; } @@ -47,14 +48,10 @@ namespace ASC.Api.Impl protected IApiMethodCall Method { get; private set; } - public IApiStandartResponce ApiResponce { get; private set; } public ApiContext ApiContext { get; private set; } - - - protected ApiHttpHandlerBase(RouteData routeData) { RouteData = routeData; @@ -111,7 +108,7 @@ namespace ASC.Api.Impl } } - public void Process(HttpContextBase context) + public async Task Process(HttpContextBase context) { using (Container) { @@ -126,25 +123,25 @@ namespace ASC.Api.Impl Method = ApiManager.GetMethod(((Route)RouteData.Route).Url, context.Request.RequestType); //Set method - DoProcess(context); + await DoProcess(context); } } protected RequestContext RouteContext { get; private set; } - protected abstract void DoProcess(HttpContextBase context); + protected abstract Task DoProcess(HttpContextBase context); - public void ProcessRequest(HttpContext context) + public override async Task ProcessRequestAsync(HttpContext context) { var contextWrapper = new HttpContextWrapper(context); - ProcessInternal(contextWrapper); + await ProcessInternal(contextWrapper); } - private void ProcessInternal(HttpContextWrapper contextWrapper) + private async Task ProcessInternal(HttpContextWrapper contextWrapper) { try { - Process(contextWrapper); + await Process(contextWrapper); } catch (ThreadAbortException e) { @@ -160,7 +157,7 @@ namespace ASC.Api.Impl } } - public bool IsReusable + public override bool IsReusable { get { return false; } } diff --git a/module/ASC.Api/ASC.Api/Impl/ApiRouteHandler.cs b/module/ASC.Api/ASC.Api/Impl/ApiRouteHandler.cs index df4a62c5f..a9455cb6e 100644 --- a/module/ASC.Api/ASC.Api/Impl/ApiRouteHandler.cs +++ b/module/ASC.Api/ASC.Api/Impl/ApiRouteHandler.cs @@ -72,7 +72,7 @@ namespace ASC.Api.Impl public virtual IHttpHandler GetHandler(RequestContext requestContext) { - return Container.BeginLifetimeScope().Resolve(new TypedParameter(typeof(RouteData), requestContext.RouteData)); + return Container.BeginLifetimeScope().Resolve(new TypedParameter(typeof(RouteData), requestContext.RouteData)); } } diff --git a/module/ASC.Api/ASC.Api/Impl/Responders/ContentResponder.cs b/module/ASC.Api/ASC.Api/Impl/Responders/ContentResponder.cs index 1c89f031d..76cd41be6 100644 --- a/module/ASC.Api/ASC.Api/Impl/Responders/ContentResponder.cs +++ b/module/ASC.Api/ASC.Api/Impl/Responders/ContentResponder.cs @@ -21,7 +21,6 @@ using System.Web; using ASC.Api.Interfaces; using ASC.Api.Interfaces.ResponseTypes; -using ASC.Api.Utils; namespace ASC.Api.Impl.Responders { @@ -69,7 +68,11 @@ namespace ASC.Api.Impl.Responders { context.Response.ContentEncoding = contentResponce.ContentEncoding; } - context.Response.WriteStreamToResponce(contentResponce.ContentStream); + + contentResponce.ContentStream.CopyTo(context.Response.OutputStream); + + contentResponce.ContentStream.Close(); + contentResponce.ContentStream.Dispose(); } #endregion diff --git a/module/ASC.Api/ASC.Api/Interfaces/ApiHttpAsyncHandler.cs b/module/ASC.Api/ASC.Api/Interfaces/ApiHttpAsyncHandler.cs new file mode 100644 index 000000000..4a84c83b1 --- /dev/null +++ b/module/ASC.Api/ASC.Api/Interfaces/ApiHttpAsyncHandler.cs @@ -0,0 +1,26 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System.Web; + + +namespace ASC.Api.Interfaces +{ + public abstract class ApiHttpAsyncHandler : HttpTaskAsyncHandler + { + } +} \ No newline at end of file diff --git a/module/ASC.Api/ASC.Api/Interfaces/IApiHttpHandler.cs b/module/ASC.Api/ASC.Api/Interfaces/IApiHttpHandler.cs deleted file mode 100644 index f248a7699..000000000 --- a/module/ASC.Api/ASC.Api/Interfaces/IApiHttpHandler.cs +++ /dev/null @@ -1,27 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System.Web; - - -namespace ASC.Api.Interfaces -{ - public interface IApiHttpHandler : IHttpHandler - { - void Process(HttpContextBase context); - } -} \ No newline at end of file diff --git a/module/ASC.Api/ASC.Api/Utils/Binder.cs b/module/ASC.Api/ASC.Api/Utils/Binder.cs index 7a61b8daa..b08789436 100644 --- a/module/ASC.Api/ASC.Api/Utils/Binder.cs +++ b/module/ASC.Api/ASC.Api/Utils/Binder.cs @@ -185,7 +185,8 @@ namespace ASC.Api.Utils { name, StringUtils.ToCamelCase(name), - name.ToLower() + name.ToLower(), + name.ToLowerInvariant() }; } @@ -210,8 +211,9 @@ namespace ASC.Api.Utils private static readonly ConcurrentDictionary CollectionPrefixCache = new ConcurrentDictionary(); - private static void BindCollection(string prefix, IList collection, NameValueCollection values) + private static bool BindCollection(string prefix, IList collection, NameValueCollection values) { + var isBinded = false; Regex parse = GetParseRegex(prefix); //Parse values related to collection @@ -220,6 +222,7 @@ namespace ASC.Api.Utils let match = parse.Match(key) group key by string.IsNullOrEmpty(match.Groups["pos"].Value) ? (match.Groups["arrleft"].Success ? string.Empty : simple) : match.Groups["pos"].Value into key select key; + foreach (var key in keys) { int index; @@ -239,7 +242,6 @@ namespace ASC.Api.Utils if (genericType != null) { - var newprefix = simple.Equals(key.Key) ? prefix : prefix + "[" + (indexed ? index.ToString(CultureInfo.InvariantCulture) : "") + "]"; if (IsSimple(genericType)) { @@ -250,19 +252,23 @@ namespace ASC.Api.Utils foreach (var collectionValue in collectionValues) { collection.Add(ConvertUtils.GetConverted(collectionValue, genericType)); + isBinded = true; } } - } else { var constructed = Bind(genericType, values, newprefix); if (constructed != null) + { collection.Insert(index, constructed); + isBinded = true; + } } } } + return isBinded; } private static Regex GetParseRegex(string prefix) diff --git a/module/ASC.Api/ASC.Employee/EmployeeApi.cs b/module/ASC.Api/ASC.Employee/EmployeeApi.cs index 7a1c35501..b8bcb2bb4 100644 --- a/module/ASC.Api/ASC.Employee/EmployeeApi.cs +++ b/module/ASC.Api/ASC.Employee/EmployeeApi.cs @@ -17,17 +17,22 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Net.Mail; using System.Security; +using System.Text; +using System.Threading.Tasks; using System.Web; using ASC.Api.Attributes; using ASC.Api.Exceptions; using ASC.Api.Impl; +using ASC.Common.Caching; using ASC.Common.Logging; +using ASC.Common.Radicale; using ASC.Common.Threading.Progress; using ASC.Core; using ASC.Core.Tenants; @@ -40,6 +45,7 @@ using ASC.Security.Cryptography; using ASC.Specific; using ASC.Web.Core; using ASC.Web.Core.Users; +using ASC.Web.People; using ASC.Web.People.Core.Import; using ASC.Web.Studio.Core.Notify; using ASC.Web.Studio.Core.Users; @@ -52,11 +58,14 @@ using SecurityContext = ASC.Core.SecurityContext; namespace ASC.Api.Employee { /// - ///User profiles access + ///Access to user profiles /// public class EmployeeApi : Interfaces.IApiEntryPoint { private static readonly ProgressQueue progressQueue = new ProgressQueue(1, TimeSpan.FromMinutes(5), true); + private ILog Log = LogManager.GetLogger("ASC.Api"); + + public static readonly ICache Cache = AscCache.Default; private static Dictionary GetHttpHeaders(HttpRequest httpRequest) { @@ -84,12 +93,13 @@ namespace ASC.Api.Employee } /// - ///Returns the detailed information about the current user profile + ///Returns the detailed information about the current user profile. /// /// - ///My profile + ///Get my profile /// - ///Profile + ///Profiles + ///Detailed information about my profile [Read("@self")] public EmployeeWraperFull GetMe() { @@ -97,19 +107,29 @@ namespace ASC.Api.Employee } /// - ///Returns the list of profiles for all portal users + ///Returns a list of profiles for all the portal users. /// /// - ///All profiles + ///Get all profiles /// + ///Profiles ///List of profiles - /// This method returns a partial profile. Use more specific method to get full profile + /// This method returns the partial profiles. Use more specific method to get full profiles. [Read("")] public IEnumerable GetAll() { return GetByStatus(EmployeeStatus.Active); } + /// + ///Returns a list of profiles filtered by user status. + /// + /// + ///Get profiles by status + /// + ///User status + ///User status + ///List of profiles [Read("status/{status}")] public IEnumerable GetByStatus(EmployeeStatus status) { @@ -126,11 +146,12 @@ namespace ASC.Api.Employee } /// - ///Returns the detailed information about the profile of the user with the name specified in the request + ///Returns the detailed information about a profile of the user with the name specified in the request. /// /// - ///Specific profile + ///Get a profile by user name /// + ///Profiles ///User name ///User profile [Read("{username}")] @@ -147,9 +168,8 @@ namespace ASC.Api.Employee } else { - LogManager.GetLogger("ASC.Api").Error( - string.Format("Account {0} сould not get user by name {1}", - SecurityContext.CurrentAccount.ID, username)); + Log.Error(string.Format("Account {0} сould not get user by name {1}", + SecurityContext.CurrentAccount.ID, username)); } } @@ -161,12 +181,14 @@ namespace ASC.Api.Employee return new EmployeeWraperFull(user); } + /// - ///Returns the detailed information about the profile of the user with the name specified in the request + ///Returns the detailed information about a profile of the user with the email specified in the request. /// /// - ///Specific profile + ///Get a profile by user email /// + ///Profiles ///User email ///User profile [Read("email")] @@ -183,13 +205,14 @@ namespace ASC.Api.Employee return new EmployeeWraperFull(user); } /// - ///Returns the list of profiles for all portal users matching the search query + ///Returns a list of profiles for all the portal users matching the search query. /// /// - ///Search users + ///Search user profiles /// - ///Query - ///List of users + ///Search + ///Search query + ///List of user profiles [Read("@search/{query}")] public IEnumerable GetSearch(string query) { @@ -207,19 +230,20 @@ namespace ASC.Api.Employee } catch (Exception error) { - LogManager.GetLogger("ASC.Api").Error(error); + Log.Error(error); } return null; } /// - ///Returns the list of users matching the search criteria + ///Returns a list of users matching the search query. /// /// - ///User search + ///Search users /// - ///Search text - ///User list + ///Search + ///Search query + ///List of users [Read("search")] public IEnumerable GetPeopleSearch(string query) { @@ -227,14 +251,15 @@ namespace ASC.Api.Employee } /// - ///Returns the list of users matching the status filter and text search + ///Returns a list of users matching the status filter and search query. /// /// - ///User search by filter + ///Search users by status filter /// + ///Search ///User status - ///Search text - ///User list + ///Search query + ///List of users [Read("status/{status}/search")] public IEnumerable GetAdvanced(EmployeeStatus status, string query) { @@ -258,26 +283,29 @@ namespace ASC.Api.Employee } catch (Exception error) { - LogManager.GetLogger("ASC.Api").Error(error); + Log.Error(error); } return null; } /// - /// Adds a new portal user from import with the first and last name, email address + /// Imports the new portal users with the first name, last name and email address. /// /// - /// Add new import user + /// Import users /// - /// The list of users to add - /// Add users as guests (bool type: false|true) - /// Newly created users + /// Profiles + /// List of users + /// Imports users as guests (true) or not (false) + /// Newly added users [Create("import/save")] public void SaveUsers(string userList, bool importUsersAsCollaborators) { lock (progressQueue.SynchRoot) { var task = progressQueue.GetItems().OfType().FirstOrDefault(t => (int)t.Id == TenantProvider.CurrentTenantID); + var tenant = CoreContext.TenantManager.GetCurrentTenant(); + Cache.Insert("REWRITE_URL" + tenant.TenantId, HttpContext.Current.Request.GetUrlRewriter().ToString(), TimeSpan.FromMinutes(5)); if (task != null && task.IsCompleted) { progressQueue.Remove(task); @@ -292,9 +320,18 @@ namespace ASC.Api.Employee Percentage = 0 }); } + } } + /// + /// Returns a status of the current user. + /// + /// + /// Get a user status + /// + /// User status + /// Current user information [Read("import/status")] public object GetStatus() { @@ -317,18 +354,19 @@ namespace ASC.Api.Employee /// - /// Returns the list of users matching the filter with the parameters specified in the request + /// Returns a list of users with full information about them matching the parameters specified in the request. /// /// - /// User search by extended filter + /// Search users and their information by extended filter /// + /// Search /// User status /// Group ID /// Activation status /// User type - /// Administrator(bool type) + /// Specifies if the user is administrator or not /// - /// User list + /// List of users with their information /// [Read("filter")] public IEnumerable GetFullByFilter(EmployeeStatus? employeeStatus, Guid? groupId, EmployeeActivationStatus? activationStatus, EmployeeType? employeeType, bool? isAdministrator) @@ -339,18 +377,19 @@ namespace ASC.Api.Employee } /// - /// Returns the list of users matching the filter with the parameters specified in the request + /// Returns a list of users matching the parameters specified in the request. /// /// - /// User search by extended filter + /// Search users by extended filter /// + /// Search /// User status /// Group ID /// Activation status /// User type - /// Administrator(bool type) + /// Specifies if the user is administrator or not /// - /// User list + /// List of users /// [Read("simple/filter")] public IEnumerable GetSimpleByFilter(EmployeeStatus? employeeStatus, Guid? groupId, EmployeeActivationStatus? activationStatus, EmployeeType? employeeType, bool? isAdministrator) @@ -439,27 +478,28 @@ namespace ASC.Api.Employee } /// - /// Adds a new portal user with the first and last name, email address and several optional parameters specified in the request + /// Adds a new portal user with the first name, last name, email address and several optional parameters specified in the request. /// /// - /// Add new user + /// Add a user /// - /// User or Visitor (bool type: false|true) - /// Email - /// First name - /// Last name - /// Department - /// Title - /// Location - /// Sex (male|female) - /// Birthday - /// Works from date. If not specified - current will be set - /// Comment for user - /// List of contacts - /// Avatar photo url - /// User Password - /// - /// Newly created user + /// Profiles + /// Specifies if this is a guest (true) or user (false) + /// User email + /// User first name + /// User last name + /// User department + /// User title + /// User location + /// User sex (male or female) + /// User birthday + /// User registration date. If it is not specified, then the current date will be set + /// User comments + /// Contact list + /// Avatar photo URL + /// User password + /// Password hash + /// Newly added user [Create("")] public EmployeeWraperFull AddMember(bool isVisitor, string email, string firstname, string lastname, Guid[] department, string title, string location, string sex, ApiDateTime birthday, ApiDateTime worksfrom, string comment, IEnumerable contacts, string files, string password, string passwordHash) { @@ -500,9 +540,10 @@ namespace ASC.Api.Employee user.BirthDate = birthday != null ? TenantUtil.DateTimeFromUtc(Convert.ToDateTime(birthday)) : (DateTime?)null; user.WorkFromDate = worksfrom != null ? TenantUtil.DateTimeFromUtc(Convert.ToDateTime(worksfrom)) : DateTime.UtcNow.Date; - UpdateContacts(contacts, user); - user = UserManagerWrapper.AddUser(user, passwordHash, false, true, isVisitor); + UpdateContacts(contacts, user); + Cache.Insert("REWRITE_URL" + CoreContext.TenantManager.GetCurrentTenant().TenantId, HttpContext.Current.Request.GetUrlRewriter().ToString(), TimeSpan.FromMinutes(5)); + user = UserManagerWrapper.AddUser(user, passwordHash, false, true, isVisitor, false, true, true); var messageAction = isVisitor ? MessageAction.GuestCreated : MessageAction.UserCreated; MessageService.Send(Request, messageAction, MessageTarget.Create(user.ID), user.DisplayUserName(false)); @@ -514,30 +555,35 @@ namespace ASC.Api.Employee UpdatePhotoUrl(files, user); } + //this eject + return new EmployeeWraperFull(user); } + + /// - /// Adds a new portal user with the first and last name, email address and several optional parameters specified in the request + /// Adds a new activated portal user with the first name, last name, email address and several optional parameters specified in the request. /// /// - /// Add new user + /// Add an activated user /// - /// User or Visitor (bool type: false|true) - /// Email - /// First name - /// Last name - /// Department - /// Title - /// Location - /// Sex (male|female) - /// Birthday - /// Works from date. If not specified - current will be set - /// Comment for user - /// List of contacts - /// Avatar photo url - /// User Password - /// Newly created user + /// Profiles + /// Specifies if this is a guest (true) or user (false) + /// User email + /// User first name + /// User last name + /// User department + /// User title + /// User location + /// User sex (male or female) + /// User birthday + /// User registration date. If it is not specified, then the current date will be set + /// User comments + /// Contact list + /// Avatar photo URL + /// User password + /// Newly added user /// false [Create("active")] public EmployeeWraperFull AddMemberAsActivated( @@ -590,8 +636,8 @@ namespace ASC.Api.Employee user.WorkFromDate = worksfrom != null ? TenantUtil.DateTimeFromUtc(Convert.ToDateTime(worksfrom)) : DateTime.UtcNow.Date; UpdateContacts(contacts, user); - - user = UserManagerWrapper.AddUser(user, passwordHash, false, false, isVisitor); + Cache.Insert("REWRITE_URL" + CoreContext.TenantManager.GetCurrentTenant().TenantId, HttpContext.Current.Request.GetUrlRewriter().ToString(), TimeSpan.FromMinutes(5)); + user = UserManagerWrapper.AddUser(user, passwordHash, false, false, isVisitor, false, true, true); user.ActivationStatus = EmployeeActivationStatus.Activated; @@ -698,26 +744,27 @@ namespace ASC.Api.Employee } /// - /// Updates the data for the selected portal user with the first and last name, email address and/or optional parameters specified in the request + /// Updates the data for the selected portal user with the first name, last name, email address and/or optional parameters specified in the request. /// /// - /// Update user + /// Update a user /// - /// User or Visitor (bool type: false|true) - /// User ID to update - /// First name - /// Last name - /// Comment for user - /// Department - /// Title - /// Location - /// Sex (male|female) - /// Birthday - /// Works from date. If not specified - current will be set - /// List fo contacts - /// Avatar photo url - /// - /// Newly created user + /// Profiles + /// Specifies if this is a guest (true) or user (false) + /// User ID + /// New user first name + /// New user last name + /// New user comments + /// New user department + /// New user title + /// New user location + /// New user sex (male or female) + /// New user birthday + /// New user registration date. If it is not specified, then the current date will be set + /// New contact list + /// New avatar photo URL + /// Disable user + /// Updated user [Update("{userid}")] public EmployeeWraperFull UpdateMember(bool isVisitor, string userid, string firstname, string lastname, string comment, Guid[] department, string title, string location, string sex, ApiDateTime birthday, ApiDateTime worksfrom, IEnumerable contacts, string files, bool? disable) { @@ -815,7 +862,7 @@ namespace ASC.Api.Employee } } - CoreContext.UserManager.SaveUserInfo(user, isVisitor); + CoreContext.UserManager.SaveUserInfo(user, isVisitor, true); MessageService.Send(Request, MessageAction.UserUpdated, MessageTarget.Create(user.ID), user.DisplayUserName(false)); if (disable.HasValue && disable.Value) @@ -828,13 +875,14 @@ namespace ASC.Api.Employee } /// - /// Deletes the user with the ID specified in the request from the portal + /// Deletes a user with the ID specified in the request from the portal. /// /// - /// Delete user + /// Delete a user /// - /// ID of user to delete - /// + /// Profiles + /// User ID + /// Deleted user [Delete("{userid}")] public EmployeeWraperFull DeleteMember(string userid) { @@ -862,13 +910,14 @@ namespace ASC.Api.Employee } /// - /// Updates the specified user contact information merging the sent data with the present on the portal + /// Updates the contact information of the user with the ID specified in the request merging new data with the present portal data. /// /// /// Update user contacts /// + /// Contacts /// User ID - /// Contacts list + /// List of new contacts /// Updated user profile [Update("{userid}/contacts")] public EmployeeWraperFull UpdateMemberContacts(string userid, IEnumerable contacts) @@ -879,18 +928,19 @@ namespace ASC.Api.Employee throw new SecurityException(); UpdateContacts(contacts, user); - CoreContext.UserManager.SaveUserInfo(user); + CoreContext.UserManager.SaveUserInfo(user, syncCardDav: true); return new EmployeeWraperFull(user); } /// - /// Updates the specified user contact information changing the data present on the portal for the sent data + /// Sets the contacts of the user with the ID specified in the request replacing the present portal data with the new data. /// /// /// Set user contacts /// + /// Contacts /// User ID - /// Contacts list + /// List of new contacts /// Updated user profile [Create("{userid}/contacts")] public EmployeeWraperFull SetMemberContacts(string userid, IEnumerable contacts) @@ -902,18 +952,19 @@ namespace ASC.Api.Employee user.Contacts.Clear(); UpdateContacts(contacts, user); - CoreContext.UserManager.SaveUserInfo(user); + CoreContext.UserManager.SaveUserInfo(user, syncCardDav: true); return new EmployeeWraperFull(user); } /// - /// Updates the specified user contact information deleting the data specified in the request from the portal + /// Deletes the contacts of the user with the ID specified in the request from the portal. /// /// /// Delete user contacts /// + /// Contacts /// User ID - /// Contacts list + /// List of contacts /// Updated user profile [Delete("{userid}/contacts")] public EmployeeWraperFull DeleteMemberContacts(string userid, IEnumerable contacts) @@ -924,18 +975,19 @@ namespace ASC.Api.Employee throw new SecurityException(); DeleteContacts(contacts, user); - CoreContext.UserManager.SaveUserInfo(user); + CoreContext.UserManager.SaveUserInfo(user, syncCardDav: true); return new EmployeeWraperFull(user); } /// - /// Get user photoes + /// Returns a photo of the user with the ID specified in the request. /// /// - /// Get user photoes + /// Get a user photo /// + /// Photos /// User ID - /// + /// User photo [Read("{userid}/photo")] public ThumbnailsDataWrapper GetMemberPhoto(string userid) { @@ -948,14 +1000,15 @@ namespace ASC.Api.Employee } /// - /// Updates the specified user photo with the pathname + /// Updates a photo of the user with the ID specified in the request. /// /// - /// Update user photo + /// Update a user photo /// + /// Photos /// User ID - /// Avatar photo url - /// + /// New avatar photo URL + /// Updated user photo [Update("{userid}/photo")] public ThumbnailsDataWrapper UpdateMemberPhoto(string userid, string files) { @@ -969,20 +1022,21 @@ namespace ASC.Api.Employee UpdatePhotoUrl(files, user); } - CoreContext.UserManager.SaveUserInfo(user); + CoreContext.UserManager.SaveUserInfo(user, syncCardDav: true); MessageService.Send(Request, MessageAction.UserAddedAvatar, MessageTarget.Create(user.ID), user.DisplayUserName(false)); return new ThumbnailsDataWrapper(user.ID); } /// - /// Deletes the photo of the user with the ID specified in the request + /// Deletes a photo of the user with the ID specified in the request. /// /// - /// Delete user photo + /// Delete a user photo /// + /// Photos /// User ID - /// + /// Deleted user photo [Delete("{userid}/photo")] public ThumbnailsDataWrapper DeleteMemberPhoto(string userid) { @@ -995,25 +1049,26 @@ namespace ASC.Api.Employee UserPhotoManager.RemovePhoto(user.ID); - CoreContext.UserManager.SaveUserInfo(user); + CoreContext.UserManager.SaveUserInfo(user, syncCardDav: true); MessageService.Send(Request, MessageAction.UserDeletedAvatar, MessageTarget.Create(user.ID), user.DisplayUserName(false)); return new ThumbnailsDataWrapper(user.ID); } /// - /// Create photo thumbnails by coordinates of original image + /// Creates a photo thumbnail by coordinates of the original image specified in the request. /// /// - /// Create user photo thumbnails + /// Create a photo thumbnail /// + /// Photos /// User ID /// Path to the temporary file - /// X - /// Y - /// Width - /// Height - /// + /// Horizontal coordinate + /// Vertical coordinate + /// Thumbnail width + /// Thumbnail height + /// Thumbnail [Create("{userid}/photo/thumbnails")] public ThumbnailsDataWrapper CreateMemberPhotoThumbnails(string userid, string tmpFile, int x, int y, int width, int height) { @@ -1040,38 +1095,41 @@ namespace ASC.Api.Employee UserPhotoThumbnailManager.SaveThumbnails(x, y, width, height, user.ID); } - CoreContext.UserManager.SaveUserInfo(user); + CoreContext.UserManager.SaveUserInfo(user, syncCardDav: true); MessageService.Send(HttpContext.Current.Request, MessageAction.UserUpdatedAvatarThumbnails, MessageTarget.Create(user.ID), user.DisplayUserName(false)); return new ThumbnailsDataWrapper(user.ID); } /// - /// Remind password for the user with email specified in the request + /// Reminds a password to the user using the email specified in the request. /// /// - /// Remind user password + /// Remind a user password /// + /// Password /// User email - /// + /// Email with the password /// false - [Create("password", false, false)] //NOTE: this method doesn't requires auth!!! //NOTE: this method doesn't check payment!!! + [Create("password", false, false)] //NOTE: this method doesn't require auth!!! //NOTE: this method doesn't check payment!!! public string SendUserPassword(string email) { string error; if (!string.IsNullOrEmpty(error = UserManagerWrapper.SendUserPassword(email))) { - LogManager.GetLogger("ASC.Api").ErrorFormat("Password recovery ({0}): {1}", email, error); + Log.ErrorFormat("Password recovery ({0}): {1}", email, error); } return String.Format(Resource.MessageYourPasswordSendedToEmail, email); } /// - /// Sets the password and email for the user with the ID specified in the request + /// Sets a new password and email to the user with the ID specified in the request. /// + /// Change a user password + /// Password /// User ID - /// Password + /// New password /// New email /// Detailed user information /// false @@ -1094,7 +1152,7 @@ namespace ASC.Api.Employee { user.Email = address.Address.ToLowerInvariant(); user.ActivationStatus = EmployeeActivationStatus.Activated; - CoreContext.UserManager.SaveUserInfo(user); + CoreContext.UserManager.SaveUserInfo(user, syncCardDav: true); } } @@ -1129,13 +1187,14 @@ namespace ASC.Api.Employee } /// - /// Sets the required activation status to the list of users with the ID specified in the request + /// Sets the required activation status to the list of users with the IDs specified in the request. /// - /// - /// Set activation status - /// - /// User list ID - /// Required status + /// + /// Set an activation status + /// + /// Activation status + /// List of user IDs + /// Required activation status /// List of users /// false [Update("activationstatus/{activationstatus}")] @@ -1157,14 +1216,15 @@ namespace ASC.Api.Employee } /// - /// Changes the type between user and guest for the user with the ID specified in the request + /// Changes a type (user or visitor) for the users with the IDs specified in the request. /// /// - /// User type change + /// Change a user type /// + /// User type /// New user type - /// User ID list - /// User list + /// List of user IDs + /// List of users [Update("type/{type}")] public IEnumerable UpdateUserType(EmployeeType type, IEnumerable userIds) { @@ -1206,14 +1266,15 @@ namespace ASC.Api.Employee } /// - /// Changes the status between active and disabled for the user with the ID specified in the request + /// Changes a status for the users with the IDs specified in the request. /// /// - /// User status change + /// Change a user status /// + /// User status /// New user status - /// User ID list - /// User list + /// List of user IDs + /// List of users [Update("status/{status}")] public IEnumerable UpdateUserStatus(EmployeeStatus status, IEnumerable userIds) { @@ -1236,13 +1297,13 @@ namespace ASC.Api.Employee if (TenantStatisticsProvider.GetUsersCount() < TenantExtra.GetTenantQuota().ActiveUsers || user.IsVisitor()) { user.Status = EmployeeStatus.Active; - CoreContext.UserManager.SaveUserInfo(user); + CoreContext.UserManager.SaveUserInfo(user, syncCardDav: true); } } break; case EmployeeStatus.Terminated: user.Status = EmployeeStatus.Terminated; - CoreContext.UserManager.SaveUserInfo(user); + CoreContext.UserManager.SaveUserInfo(user, syncCardDav: true); CookiesManager.ResetUserCookie(user.ID); MessageService.Send(HttpContext.Current.Request, MessageAction.CookieSettingsUpdated); @@ -1256,13 +1317,14 @@ namespace ASC.Api.Employee } /// - /// Sends emails once again for the users who have not activated their emails + /// Resends emails to the users who have not activated their emails. /// /// - /// Send activation email + /// Resend an activation email /// - /// User ID list - /// User list + /// Profiles + /// List of user IDs + /// List of users [Update("invite")] public IEnumerable ResendUserInvites(IEnumerable userIds) { @@ -1298,13 +1360,14 @@ namespace ASC.Api.Employee } /// - /// Delete the list of selected users + /// Deletes a list of the users with the IDs specified in the request. /// /// /// Delete users /// - /// User ID list - /// User list + /// Profiles + /// List of user IDs + /// List of users [Update("delete")] public IEnumerable RemoveUsers(IEnumerable userIds) { @@ -1325,6 +1388,7 @@ namespace ASC.Api.Employee UserPhotoManager.RemovePhoto(user.ID); CoreContext.UserManager.DeleteUser(user.ID); QueueWorker.StartRemove(HttpContext.Current, TenantProvider.CurrentTenantID, user, SecurityContext.CurrentAccount.ID, false); + } MessageService.Send(Request, MessageAction.UsersDeleted, MessageTarget.Create(users.Select(x => x.ID)), userNames); @@ -1334,12 +1398,13 @@ namespace ASC.Api.Employee /// - /// Send instructions for delete user own profile + /// Sends instructions for deleting a user profile. /// /// - /// Send delete instructions + /// Send the delete instructions /// - /// Info message + /// Profiles + /// Information message [Update("self/delete")] public string SendInstructionsToDelete() { @@ -1354,21 +1419,32 @@ namespace ASC.Api.Employee return String.Format(Resource.SuccessfullySentNotificationDeleteUserInfoMessage, "" + user.Email + ""); } + /// - /// Join to affiliate programm + /// Subscribes to or unsubscribes from the birthday of a user with the ID specified. /// - /// - /// Join to affiliate programm - /// - /// Link to affiliate programm - [Update("self/joinaffiliate")] - public string JoinToAffiliateProgram() + /// Birthday subscription + /// User ID + /// Defines if the user will be notified about other user's birthday or not + /// Bool value: true means that the user will get the notification + /// Birthday + [Create("birthdays/reminder")] + public bool RemindAboutBirthday(Guid userid, bool onRemind) { - return AffiliateHelper.Join(); + BirthdaysNotifyClient.Instance.SetSubscription(SecurityContext.CurrentAccount.ID, userid, onRemind); + return onRemind; } #region Auth page hidden methods + /// + /// Links a third-party account specified in the request to the user profile. + /// + /// + /// Link a third-pary account + /// + /// Third-party accounts + /// Third-party profile in the serialized format ///false [Update("thirdparty/linkaccount")] public void LinkAccount(string serializedProfile) @@ -1395,6 +1471,14 @@ namespace ASC.Api.Employee } } + /// + /// Unlinks a third-party account specified in the request from the user profile. + /// + /// + /// Unlink a third-pary account + /// + /// Third-party accounts + /// Provider name ///false [Delete("thirdparty/unlinkaccount")] public void UnlinkAccount(string provider) @@ -1432,11 +1516,12 @@ namespace ASC.Api.Employee #region Reassign user data /// - /// Returns the progress of the started reassign process + /// Returns the progress of the started reassigning process for the user with the ID specified in the request. /// + /// Get the reassigning progress /// User ID whose data is reassigned /// Reassign user data - /// Reassign Progress + /// Reassigning progress [Read(@"reassign/progress")] public ReassignProgressItem GetReassignProgress(Guid userId) { @@ -1446,8 +1531,9 @@ namespace ASC.Api.Employee } /// - /// Terminate reassign process + /// Terminates the reassigning process for the user with the ID specified in the request. /// + /// Terminate the reassigning process /// User ID whose data is reassigned /// Reassign user data [Update(@"reassign/terminate")] @@ -1459,13 +1545,14 @@ namespace ASC.Api.Employee } /// - /// Start a reassign process + /// Starts the reassigning process for the user with the ID specified in the request. /// - /// From User ID - /// To User ID - /// Delete profile when reassignment will be finished + /// Start the reassigning process + /// User ID whose data will be reassigned to another user + /// User ID to whom all the data will be reassigned + /// Deletes a profile when reassignment will be finished or not /// Reassign user data - /// Reassign Progress + /// Reassigning progress [Create(@"reassign/start")] public ReassignProgressItem StartReassign(Guid fromUserId, Guid toUserId, bool deleteProfile) { @@ -1509,11 +1596,12 @@ namespace ASC.Api.Employee #region Remove user data /// - /// Returns the progress of the started remove process + /// Returns the progress of the started removing process for the user with the ID specified in the request. /// + /// Get the removing progress /// User ID /// Remove user data - /// Remove Progress + /// Removing progress [Read(@"remove/progress")] public RemoveProgressItem GetRemoveProgress(Guid userId) { @@ -1523,8 +1611,9 @@ namespace ASC.Api.Employee } /// - /// Terminate remove process + /// Terminates the removing process for the user with the ID specified in the request. /// + /// Terminate the removing process /// User ID /// Remove user data [Update(@"remove/terminate")] @@ -1536,11 +1625,12 @@ namespace ASC.Api.Employee } /// - /// Start a remove process + /// Starts the removing process for the user with the ID specified in the request. /// + /// Start the removing process /// User ID /// Remove user data - /// Remove Progress + /// Removing progress [Create(@"remove/start")] public RemoveProgressItem StartRemove(Guid userId) { diff --git a/module/ASC.Api/ASC.Employee/GroupsApi.cs b/module/ASC.Api/ASC.Employee/GroupsApi.cs index 4374fd612..877d25e5a 100644 --- a/module/ASC.Api/ASC.Employee/GroupsApi.cs +++ b/module/ASC.Api/ASC.Employee/GroupsApi.cs @@ -32,7 +32,7 @@ using ASC.MessagingSystem; namespace ASC.Api.Employee { /// - ///Groups access + /// Access to the groups. /// public class GroupsApi : IApiEntryPoint { @@ -51,14 +51,14 @@ namespace ASC.Api.Employee } /// - ///Returns the general information about all groups, such as group ID and group manager + ///Returns the general information about all the groups, such as group ID and group manager. /// /// - ///All groups + ///Get all groups /// ///List of groups /// - /// This method returns partial group info + /// This method returns partial group information. /// [Read("")] public IEnumerable GetAll() @@ -67,15 +67,35 @@ namespace ASC.Api.Employee } /// - ///Returns the detailed information about the selected group: group name, category, description, manager, users and parent group if any + ///Returns a list of all the groups by the group name specified in the request. /// /// - ///Specific group + ///Get groups by a group name + /// + ///Group name + ///List of groups + [Read("search")] + public IEnumerable GetTagsByName(string groupName) + { + groupName = (groupName ?? "").Trim(); + + if (string.IsNullOrEmpty(groupName)) return new List(); + + return CoreContext.UserManager.GetDepartments() + .Where(x => x.Name.Contains(groupName)) + .Select(x => new GroupWrapperSummary(x)); + } + + /// + ///Returns the detailed information about the selected group: group name, category, description, manager, members and parent group if it exists. + /// + /// + ///Get a group /// ///Group ID ///Group /// - /// That method returns full group info + /// That method returns full group information. /// [Read("{groupid}")] public GroupWrapperFull GetById(Guid groupid) @@ -84,16 +104,13 @@ namespace ASC.Api.Employee } /// - ///Returns the group list for user + ///Returns a list of groups for the user with the ID specified in the request. /// /// - ///User groups + ///Get user groups /// ///User ID ///Group - /// - /// That method returns user groups - /// [Read("user/{userid}")] public IEnumerable GetByUserId(Guid userid) { @@ -101,14 +118,14 @@ namespace ASC.Api.Employee } /// - /// Adds a new group with the group manager, name and users specified + /// Adds a new group with the group manager, name and members specified in the request. /// /// - /// Add new group + /// Add a new group /// /// Group manager /// Group name - /// List of group users + /// List of group members /// Newly created group [Create("")] public GroupWrapperFull AddGroup(Guid groupManager, string groupName, IEnumerable members) @@ -132,16 +149,16 @@ namespace ASC.Api.Employee } /// - /// Updates an existing group changing the group manager, name and/or users + /// Updates the existing group changing the group manager, name and/or members. /// /// - /// Update existing group + /// Update a group /// /// Group ID - /// Group manager - /// Group name - /// List of group users - /// Newly created group + /// New group manager + /// New group name + /// New list of group members + /// Updated group [Update("{groupid}")] public GroupWrapperFull UpdateGroup(Guid groupid, Guid groupManager, string groupName, IEnumerable members) { @@ -172,13 +189,13 @@ namespace ASC.Api.Employee } /// - /// Deletes the selected group from the list of groups on the portal + /// Deletes a group with the ID specified in the request from the list of groups on the portal. /// /// - /// Delete group + /// Delete a group /// /// Group ID - /// + /// Group [Delete("{groupid}")] public GroupWrapperFull DeleteGroup(Guid groupid) { @@ -204,14 +221,14 @@ namespace ASC.Api.Employee } /// - /// Move all the users from the selected group to another one specified + /// Moves all the members from the selected group to another one specified in the request. /// /// - /// Move group users + /// Move group members /// - /// ID of group to move from - /// ID of group to move to - /// Group info which users were moved + /// Group ID to move from + /// Group ID to move to + /// New group information [Update("{groupid}/members/{newgroupid}")] public GroupWrapperFull TransferMembersTo(Guid groupid, Guid newgroupid) { @@ -229,14 +246,14 @@ namespace ASC.Api.Employee } /// - /// Manages the group users deleting the current users and setting new ones instead + /// Manages the group members deleting the current members and setting new ones specified in the request instead. /// /// - /// Set group users + /// Set group members /// /// Group ID - /// User list - /// + /// List of new members + /// Group information [Create("{groupid}/members")] public GroupWrapperFull SetMembersTo(Guid groupid, IEnumerable members) { @@ -246,14 +263,14 @@ namespace ASC.Api.Employee } /// - /// Add new group users keeping the current users and adding new ones + /// Manages the group members keeping the current members and adding new ones specified in the request instead. /// /// - /// Add group users + /// Add group members /// /// Group ID - /// User list - /// + /// List of new members + /// Group information [Update("{groupid}/members")] public GroupWrapperFull AddMembersTo(Guid groupid, IEnumerable members) { @@ -268,14 +285,14 @@ namespace ASC.Api.Employee } /// - /// Sets the user with the ID sent as a manager to the selected group + /// Sets a user with the ID specified in the request as a manager. /// /// - /// Set group manager + /// Set a group manager /// /// Group ID - /// User ID to become manager - /// + /// User ID + /// Group information /// [Update("{groupid}/manager")] public GroupWrapperFull SetManager(Guid groupid, Guid userid) @@ -293,14 +310,14 @@ namespace ASC.Api.Employee } /// - /// Removes the specified group users with all the rest current group users retained + /// Removes the group members specified in the request from the selected group. /// /// - /// Remove group users + /// Remove group members /// /// Group ID - /// User list - /// + /// List of members + /// Group information [Delete("{groupid}/members")] public GroupWrapperFull RemoveMembersFrom(Guid groupid, IEnumerable members) { diff --git a/module/ASC.Api/ASC.Specific/ASC.Specific.csproj b/module/ASC.Api/ASC.Specific/ASC.Specific.csproj index 0b0e28665..739770cb1 100644 --- a/module/ASC.Api/ASC.Specific/ASC.Specific.csproj +++ b/module/ASC.Api/ASC.Specific/ASC.Specific.csproj @@ -115,10 +115,10 @@ 4.3.0 - 2.1.1 + 2.2.0 - 12.0.3 + 13.0.1 diff --git a/module/ASC.Api/ASC.Specific/AuthorizationApi/AuthenticationEntryPoint.cs b/module/ASC.Api/ASC.Specific/AuthorizationApi/AuthenticationEntryPoint.cs index 4cd9a14f6..317e34278 100644 --- a/module/ASC.Api/ASC.Specific/AuthorizationApi/AuthenticationEntryPoint.cs +++ b/module/ASC.Api/ASC.Specific/AuthorizationApi/AuthenticationEntryPoint.cs @@ -35,10 +35,12 @@ using ASC.Common.Logging; using ASC.Core; using ASC.Core.Tenants; using ASC.Core.Users; +using ASC.FederatedLogin.Helpers; using ASC.FederatedLogin.LoginProviders; using ASC.IPSecurity; using ASC.MessagingSystem; using ASC.Security.Cryptography; +using ASC.Web.Core; using ASC.Web.Core.Sms; using ASC.Web.Studio.Core; using ASC.Web.Studio.Core.Notify; @@ -55,15 +57,16 @@ using SecurityContext = ASC.Core.SecurityContext; namespace ASC.Specific.AuthorizationApi { /// - /// Authorization for api + /// Authorization for api. /// public class AuthenticationEntryPoint : IApiEntryPoint { private static readonly ICache Cache = AscCache.Memory; + private ILog Log = LogManager.GetLogger("ASC.Web"); /// - /// Entry point name + /// Entry point name. /// public string Name { @@ -76,24 +79,25 @@ namespace ASC.Specific.AuthorizationApi } /// - /// Gets authentication token for use in api authorization + /// Returns the authentication token for use in api authorization. /// /// - /// Get token + /// Get the authentication token /// - /// user name or email - /// password - /// social media provider type - /// provider token - /// tokent to use in 'Authorization' header when calling API methods - /// Thrown when not authenticated - [Create(@"", false, false)] //NOTE: this method doesn't requires auth!!! //NOTE: this method doesn't check payment!!! - public AuthenticationTokenData AuthenticateMe(string userName, string password, string provider, string accessToken) + /// User name or email + /// Password + /// Social media provider type + /// Provider token + /// Code for take token + /// Authentication token to use in the 'Authorization' header when calling API methods + /// Thrown when not authenticated. + [Create(@"", false, false)] //NOTE: This method doesn't require auth!!! //NOTE: This method doesn't check payment!!! + public AuthenticationTokenData AuthenticateMe(string userName, string password, string provider, string accessToken, string codeOAuth) { bool viaEmail; - var user = GetUser(userName, password, provider, accessToken, out viaEmail); + var user = GetUser(userName, password, provider, accessToken, out viaEmail, codeOAuth); - if (StudioSmsNotificationSettings.IsVisibleSettings && StudioSmsNotificationSettings.Enable) + if (StudioSmsNotificationSettings.IsVisibleAndAvailableSettings && StudioSmsNotificationSettings.Enable) { if (string.IsNullOrEmpty(user.MobilePhone) || user.MobilePhoneActivationStatus == MobilePhoneActivationStatus.NotActivated) return new AuthenticationTokenData @@ -128,9 +132,8 @@ namespace ASC.Specific.AuthorizationApi try { - var token = SecurityContext.AuthenticateMe(user.ID); - - MessageService.Send(Request, viaEmail ? MessageAction.LoginSuccessViaApi : MessageAction.LoginSuccessViaApiSocialAccount); + var action = viaEmail ? MessageAction.LoginSuccessViaApi : MessageAction.LoginSuccessViaApiSocialAccount; + var token = CookiesManager.AuthenticateMeAndSetCookies(user.Tenant, user.ID, action); var tenant = CoreContext.TenantManager.GetCurrentTenant().TenantId; var expires = TenantCookieSettings.GetExpiresTime(tenant); @@ -153,15 +156,16 @@ namespace ASC.Specific.AuthorizationApi } /// - /// Set mobile phone for user + /// Sets a mobile phone for the user with the name specified in the request. /// - /// user name or email - /// password - /// social media provider type - /// provider token - /// new mobile phone - /// mobile phone - [Create(@"setphone", false, false)] //NOTE: this method doesn't requires auth!!! //NOTE: this method doesn't check payment!!! + /// Set a mobile phone + /// User name or email + /// Password + /// Social media provider type + /// Provider token + /// New mobile phone + /// Mobile phone + [Create(@"setphone", false, false)] //NOTE: This method doesn't require auth!!! //NOTE: This method doesn't check payment!!! public AuthenticationTokenData SaveMobilePhone(string userName, string password, string provider, string accessToken, string mobilePhone) { bool viaEmail; @@ -178,14 +182,15 @@ namespace ASC.Specific.AuthorizationApi } /// - /// Send sms code again + /// Sends sms with the authentication code. /// - /// user name or email - /// password - /// social media provider type - /// provider token - /// mobile phone - [Create(@"sendsms", false, false)] //NOTE: this method doesn't requires auth!!! //NOTE: this method doesn't check payment!!! + /// Send sms code + /// User name or email + /// Password + /// Social media provider type + /// Provider token + /// Mobile phone + [Create(@"sendsms", false, false)] //NOTE: This method doesn't require auth!!! //NOTE: This method doesn't check payment!!! public AuthenticationTokenData SendSmsCode(string userName, string password, string provider, string accessToken) { bool viaEmail; @@ -201,36 +206,37 @@ namespace ASC.Specific.AuthorizationApi } /// - /// Gets two-factor authentication token for use in api authorization + /// Returns the two-factor authentication token for use in api authorization. /// /// - /// Get token + /// Get the two-factor authentication token /// - /// user name or email - /// password - /// social media provider type - /// provider token - /// two-factor authentication code - /// tokent to use in 'Authorization' header when calling API methods - [Create(@"{code}", false, false)] //NOTE: this method doesn't requires auth!!! //NOTE: this method doesn't check payment!!! - public AuthenticationTokenData AuthenticateMe(string userName, string password, string provider, string accessToken, string code) + /// User name or email + /// Password + /// Social media provider type + /// Provider token + /// Two-factor authentication code + /// Code for take token + /// Two-factor authentication token to use in 'Authorization' header when calling API methods + [Create(@"{code}", false, false)] //NOTE: This method doesn't require auth!!! //NOTE: This method doesn't check payment!!! + public AuthenticationTokenData AuthenticateMe(string userName, string password, string provider, string accessToken, string code, string codeOAuth) { bool viaEmail; - var user = GetUser(userName, password, provider, accessToken, out viaEmail); + var user = GetUser(userName, password, provider, accessToken, out viaEmail, codeOAuth); var sms = false; try { - if (StudioSmsNotificationSettings.IsVisibleSettings && StudioSmsNotificationSettings.Enable) + if (StudioSmsNotificationSettings.IsVisibleAndAvailableSettings && StudioSmsNotificationSettings.Enable) { sms = true; - SmsManager.ValidateSmsCode(user, code); + SmsManager.ValidateSmsCode(user, code, true); } else if (TfaAppAuthSettings.IsVisibleSettings && TfaAppAuthSettings.Enable) { - if (user.ValidateAuthCode(code)) + if (user.ValidateAuthCode(code, true, true)) { - MessageService.Send(HttpContext.Current.Request, MessageAction.UserConnectedTfaApp, MessageTarget.Create(user.ID)); + MessageService.Send(Request, MessageAction.UserConnectedTfaApp, MessageTarget.Create(user.ID)); } } else @@ -238,10 +244,7 @@ namespace ASC.Specific.AuthorizationApi throw new SecurityException("Auth code is not available"); } - var token = SecurityContext.AuthenticateMe(user.ID); - - MessageService.Send(Request, sms ? MessageAction.LoginSuccessViaApiSms : MessageAction.LoginSuccessViaApiTfa); - + var token = CookiesManager.AuthenticateMeAndSetCookies(user.Tenant, user.ID, MessageAction.LoginSuccess); var tenant = CoreContext.TenantManager.GetCurrentTenant().TenantId; var expires = TenantCookieSettings.GetExpiresTime(tenant); @@ -278,14 +281,15 @@ namespace ASC.Specific.AuthorizationApi } /// - /// Request of invitation by email on personal.onlyoffice.com + /// Requests an invitation by email on personal.onlyoffice.com. /// + /// Register a user on the Personal portal /// Email address /// Culture /// User consent to subscribe to the ONLYOFFICE newsletter - /// recaptcha token + /// ReCAPTCHA token /// false - [Create(@"register", false)] //NOTE: this method doesn't requires auth!!! + [Create(@"register", false)] //NOTE: This method doesn't require auth!!! public string RegisterUserOnPersonal(string email, string lang, bool spam, string recaptchaResponse) { if (!CoreContext.Configuration.Personal) throw new MethodAccessException("Method is only available on personal.onlyoffice.com"); @@ -328,7 +332,7 @@ namespace ASC.Specific.AuthorizationApi try { - SecurityContext.AuthenticateMe(Constants.CoreSystem); + SecurityContext.CurrentAccount = Constants.CoreSystem; CoreContext.UserManager.DeleteUser(newUserInfo.ID); } finally @@ -347,12 +351,12 @@ namespace ASC.Specific.AuthorizationApi .InColumnValue("email", email.ToLowerInvariant()) .InColumnValue("reason", "personal") ); - LogManager.GetLogger("ASC.Web").Debug(String.Format("Write to template_unsubscribe {0}", email.ToLowerInvariant())); + Log.Debug(String.Format("Write to template_unsubscribe {0}", email.ToLowerInvariant())); } } catch (Exception ex) { - LogManager.GetLogger("ASC.Web").Debug(String.Format("ERROR write to template_unsubscribe {0}, email:{1}", ex.Message, email.ToLowerInvariant())); + Log.Debug(String.Format("ERROR write to template_unsubscribe {0}, email:{1}", ex.Message, email.ToLowerInvariant())); } } StudioNotifyService.Instance.SendInvitePersonal(email); @@ -365,14 +369,15 @@ namespace ASC.Specific.AuthorizationApi } /// - /// Check userName and password + /// Checks user name and password when logging. /// - /// user name or email - /// password - /// - /// Thrown when not authenticated + /// Log in + /// User name or email + /// Password + /// Email key + /// Thrown when not authenticated. /// false - [Create(@"login", false, false)] //NOTE: this method doesn't requires auth!!! //NOTE: this method doesn't check payment!!! + [Create(@"login", false, false)] //NOTE: This method doesn't require auth!!! //NOTE: This method doesn't check payment!!! public bool AuthenticateMe(string userName, string password, string key) { var authInterval = TimeSpan.FromMinutes(5); @@ -384,7 +389,7 @@ namespace ASC.Specific.AuthorizationApi return user != null; } - private static UserInfo GetUser(string userName, string password, string provider, string accessToken, out bool viaEmail) + private static UserInfo GetUser(string userName, string password, string provider, string accessToken, out bool viaEmail, string codeOAuth = null) { viaEmail = true; var action = MessageAction.LoginFailViaApi; @@ -430,10 +435,15 @@ namespace ASC.Specific.AuthorizationApi } else { + if (!(CoreContext.Configuration.Standalone || CoreContext.TenantManager.GetTenantQuota(TenantProvider.CurrentTenantID).Oauth)) + { + throw new Exception(Resource.ErrorNotAllowedOption); + } viaEmail = false; action = MessageAction.LoginFailViaApiSocialAccount; - var thirdPartyProfile = ProviderManager.GetLoginProfile(provider, accessToken); + + var thirdPartyProfile = ProviderManager.GetLoginProfile(provider, accessToken, codeOAuth); userName = thirdPartyProfile.EMail; user = LoginWithThirdParty.GetUserByThirdParty(thirdPartyProfile); @@ -464,8 +474,9 @@ namespace ASC.Specific.AuthorizationApi { get { - if ((!SetupInfo.IsVisibleSettings(ManagementType.LdapSettings.ToString()) && !CoreContext.Configuration.Standalone) || - !CoreContext.TenantManager.GetTenantQuota(TenantProvider.CurrentTenantID).Ldap) + if (!CoreContext.Configuration.Standalone + && (!SetupInfo.IsVisibleSettings(ManagementType.LdapSettings.ToString()) + || !CoreContext.TenantManager.GetTenantQuota(TenantProvider.CurrentTenantID).Ldap)) { return false; } diff --git a/module/ASC.Api/ASC.Specific/CapabilitiesApi/CapabilitiesData.cs b/module/ASC.Api/ASC.Specific/CapabilitiesApi/CapabilitiesData.cs index 3c74062f8..4966d3b4b 100644 --- a/module/ASC.Api/ASC.Specific/CapabilitiesApi/CapabilitiesData.cs +++ b/module/ASC.Api/ASC.Specific/CapabilitiesApi/CapabilitiesData.cs @@ -28,6 +28,9 @@ namespace ASC.Specific.CapabilitiesApi [DataMember] public bool LdapEnabled { get; set; } + [DataMember] + public bool OauthEnabled { get; set; } + [DataMember] public List Providers { get; set; } diff --git a/module/ASC.Api/ASC.Specific/CapabilitiesApi/CapabilitiesEntryPoint.cs b/module/ASC.Api/ASC.Specific/CapabilitiesApi/CapabilitiesEntryPoint.cs index 99a581e70..1a57d03fb 100644 --- a/module/ASC.Api/ASC.Specific/CapabilitiesApi/CapabilitiesEntryPoint.cs +++ b/module/ASC.Api/ASC.Specific/CapabilitiesApi/CapabilitiesEntryPoint.cs @@ -16,6 +16,7 @@ using System; +using System.Collections.Generic; using System.Configuration; using System.Linq; using System.Web; @@ -26,6 +27,7 @@ using ASC.Api.Impl; using ASC.Api.Interfaces; using ASC.Common.Logging; using ASC.Core; +using ASC.FederatedLogin; using ASC.FederatedLogin.LoginProviders; using ASC.Web.Studio.Core; using ASC.Web.Studio.UserControls.Management.SingleSignOnSettings; @@ -35,12 +37,15 @@ using ASC.Web.Studio.Utility; namespace ASC.Specific.CapabilitiesApi { /// - /// Capabilities for api + /// Portal capabilities for api. /// public class CapabilitiesEntryPoint : IApiEntryPoint { + + private ILog Log = LogManager.GetLogger("ASC"); + /// - /// Entry point name + /// Entry point name. /// public string Name { @@ -56,28 +61,29 @@ namespace ASC.Specific.CapabilitiesApi } /// - ///Returns the information about portal capabilities + ///Returns the information about portal capabilities. /// /// ///Get portal capabilities /// - ///CapabilitiesData - [Read("", false, false)] //NOTE: this method doesn't requires auth!!! //NOTE: this method doesn't check payment!!! + ///Portal capabilities + [Read("", false, false)] //NOTE: This method doesn't require auth!!! //NOTE: This method doesn't check payment!!! public CapabilitiesData GetPortalCapabilities() { var result = new CapabilitiesData { LdapEnabled = false, - Providers = null, + OauthEnabled = CoreContext.Configuration.Standalone || CoreContext.TenantManager.GetTenantQuota(TenantProvider.CurrentTenantID).Oauth, + Providers = new List(0), SsoLabel = string.Empty, SsoUrl = string.Empty }; try { - if (SetupInfo.IsVisibleSettings(ManagementType.LdapSettings.ToString()) - && (!CoreContext.Configuration.Standalone - || CoreContext.TenantManager.GetTenantQuota(TenantProvider.CurrentTenantID).Ldap)) + if (CoreContext.Configuration.Standalone + || SetupInfo.IsVisibleSettings(ManagementType.LdapSettings.ToString()) + && CoreContext.TenantManager.GetTenantQuota(TenantProvider.CurrentTenantID).Ldap) { var settings = LdapSettings.Load(); @@ -86,28 +92,37 @@ namespace ASC.Specific.CapabilitiesApi } catch (Exception ex) { - LogManager.GetLogger("ASC").Error(ex.Message); + Log.Error(ex.Message); } try { - result.Providers = AccountLinkControl.AuthProviders - .Where(loginProvider => - { - var provider = ProviderManager.GetLoginProvider(loginProvider); - return provider != null && provider.IsEnabled; - }) - .ToList(); + if (result.OauthEnabled) + { + result.Providers = AccountLinkControl.AuthProviders + .Where(loginProvider => + { + if ((loginProvider == ProviderConstants.Facebook || loginProvider == ProviderConstants.AppleId) + && CoreContext.Configuration.Standalone && HttpContext.Current.Request.MobileApp()) + { + return false; + } + var provider = ProviderManager.GetLoginProvider(loginProvider); + return provider != null && provider.IsEnabled; + }) + .ToList(); + } } catch (Exception ex) { - LogManager.GetLogger("ASC").Error(ex.Message); + Log.Error(ex.Message); } try { - if (SetupInfo.IsVisibleSettings(ManagementType.SingleSignOnSettings.ToString()) - && CoreContext.TenantManager.GetTenantQuota(TenantProvider.CurrentTenantID).Sso) + if (CoreContext.Configuration.Standalone + || SetupInfo.IsVisibleSettings(ManagementType.SingleSignOnSettings.ToString()) + && CoreContext.TenantManager.GetTenantQuota(TenantProvider.CurrentTenantID).Sso) { var settings = SsoSettingsV2.Load(); @@ -126,7 +141,7 @@ namespace ASC.Specific.CapabilitiesApi } catch (Exception ex) { - LogManager.GetLogger("ASC").Error(ex.Message); + Log.Error(ex.Message); } return result; diff --git a/module/ASC.AuditTrail/ASC.AuditTrail.csproj b/module/ASC.AuditTrail/ASC.AuditTrail.csproj index 8bee66e49..b581cf7e5 100644 --- a/module/ASC.AuditTrail/ASC.AuditTrail.csproj +++ b/module/ASC.AuditTrail/ASC.AuditTrail.csproj @@ -30,11 +30,11 @@ - - + + @@ -45,8 +45,15 @@ + + + + + + AuditReportResource.resx + AuditReportResource.resx @@ -145,14 +152,8 @@ False - {02c40a64-fe22-41d0-9037-69f0d6f787a9} + {02C40A64-FE22-41D0-9037-69F0D6F787A9} ASC.Web.Core - False - - - {8c534af7-5696-4e68-9ff4-ffc311893c10} - ASC.Web.Files - False {bd8a18a5-60c5-4411-9719-0aa11b4be0e9} @@ -164,7 +165,10 @@ - 15.0.4 + 27.1.1 + + + 13.0.1 diff --git a/module/ASC.AuditTrail/AuditActionsMapper.cs b/module/ASC.AuditTrail/AuditActionsMapper.cs deleted file mode 100644 index d6fd0770a..000000000 --- a/module/ASC.AuditTrail/AuditActionsMapper.cs +++ /dev/null @@ -1,2746 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using ASC.AuditTrail.Model; -using ASC.MessagingSystem.Contracts; -using System.Linq; - -namespace ASC.AuditTrail.Data -{ - public class AuditActionsMapper - { - private static readonly Dictionary actions = new Dictionary - - { - #region login - - {MessageAction.LoginSuccess, new MessageMaps {ActionText = AuditReportResource.LoginSuccess}}, - {MessageAction.LoginSuccessThirdParty, new MessageMaps {ActionText = AuditReportResource.LoginSuccessThirdParty}}, - {MessageAction.LoginFail, new MessageMaps {ActionText = AuditReportResource.LoginFail}}, - {MessageAction.LoginFailInvalidCombination, new MessageMaps {ActionText = AuditReportResource.LoginFailInvalidCombination}}, - {MessageAction.LoginFailThirdPartyNotFound, new MessageMaps {ActionText = AuditReportResource.LoginFailThirdPartyNotFound}}, - {MessageAction.LoginFailDisabledProfile, new MessageMaps {ActionText = AuditReportResource.LoginFailDisabledProfile}}, - {MessageAction.Logout, new MessageMaps {ActionText = AuditReportResource.Logout}}, - - #endregion - - #region projects - - { - MessageAction.ProjectCreated, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.ProjectCreated, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.ProjectsModule - } - }, - { - MessageAction.ProjectCreatedFromTemplate, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.ProjectCreatedFromTemplate, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.ProjectsModule - } - }, - { - MessageAction.ProjectUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.ProjectUpdated, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.ProjectsModule - } - }, - { - MessageAction.ProjectUpdatedStatus, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.ProjectUpdatedStatus, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.ProjectsModule - } - }, - { - MessageAction.ProjectDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.ProjectDeleted, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.ProjectsModule - } - }, - { - MessageAction.ProjectFollowed, new MessageMaps - { - ActionTypeText = AuditReportResource.FollowActionType, - ActionText = AuditReportResource.ProjectFollowed, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.ProjectsModule - } - }, - { - MessageAction.ProjectUnfollowed, new MessageMaps - { - ActionTypeText = AuditReportResource.UnfollowActionType, - ActionText = AuditReportResource.ProjectUnfollowed, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.ProjectsModule - } - }, - { - MessageAction.ProjectExcludedMember, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.ProjectExcludedMember, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.ProjectsModule - } - }, - { - MessageAction.ProjectUpdatedMembers, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.ProjectUpdatedMembers, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.ProjectsModule - } - }, - { - MessageAction.ProjectUpdatedMemberRights, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateAccessActionType, - ActionText = AuditReportResource.ProjectUpdatedMemberRights, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.ProjectsModule - } - }, - { - MessageAction.MilestoneCreated, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.MilestoneCreated, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.MilestonesModule - } - }, - { - MessageAction.MilestoneUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.MilestoneUpdated, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.MilestonesModule - } - }, - { - MessageAction.MilestoneUpdatedStatus, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.MilestoneUpdatedStatus, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.MilestonesModule - } - }, - { - MessageAction.MilestoneDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.MilestoneDeleted, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.MilestonesModule - } - }, - { - MessageAction.TaskCreated, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.TaskCreated, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.TasksModule - } - }, - { - MessageAction.TaskCreatedFromDiscussion, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.TaskCreatedFromDiscussion, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.TasksModule - } - }, - { - MessageAction.TaskUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.TaskUpdated, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.TasksModule - } - }, - { - MessageAction.TaskUpdatedStatus, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.TaskUpdatedStatus, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.TasksModule - } - }, - { - MessageAction.TaskMovedToMilestone, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.TaskMovedToMilestone, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.TasksModule - } - }, - { - MessageAction.TaskDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.TaskDeleted, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.TasksModule - } - }, - { - MessageAction.TaskUpdatedFollowing, new MessageMaps - { - ActionTypeText = AuditReportResource.FollowActionType, - ActionText = AuditReportResource.TaskUpdatedFollowing, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.TasksModule - } - }, - { - MessageAction.TaskCommentCreated, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.TaskCommentCreated, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.TasksModule - } - }, - { - MessageAction.TaskCommentUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.TaskCommentUpdated, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.TasksModule - } - }, - { - MessageAction.TaskCommentDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.TaskCommentDeleted, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.TasksModule - } - }, - { - MessageAction.TasksLinked, new MessageMaps - { - ActionTypeText = AuditReportResource.LinkActionType, - ActionText = AuditReportResource.TasksLinked, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.TasksModule - } - }, - { - MessageAction.TasksUnlinked, new MessageMaps - { - ActionTypeText = AuditReportResource.UnlinkActionType, - ActionText = AuditReportResource.TasksUnlinked, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.TasksModule - } - }, - { - MessageAction.TaskAttachedFiles, new MessageMaps - { - ActionTypeText = AuditReportResource.AttachActionType, - ActionText = AuditReportResource.TaskAttachedFiles, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.TasksModule - } - }, - { - MessageAction.TaskDetachedFile, new MessageMaps - { - ActionTypeText = AuditReportResource.DetachActionType, - ActionText = AuditReportResource.TaskDetachedFile, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.TasksModule - } - }, - { - MessageAction.TaskUnlinkedMilestone, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.TaskUnlinkedMilestone, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.TasksModule - } - }, - { - MessageAction.SubtaskCreated, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.SubtaskCreated, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.TasksModule - } - }, - { - MessageAction.SubtaskUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.SubtaskUpdated, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.TasksModule - } - }, - { - MessageAction.SubtaskUpdatedStatus, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.SubtaskUpdatedStatus, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.TasksModule - } - }, - { - MessageAction.SubtaskDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.SubtaskDeleted, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.TasksModule - } - }, - { - MessageAction.TaskTimeCreated, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.TaskTimeCreated, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.TimeTrackingModule - } - }, - { - MessageAction.TaskTimeUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.TaskTimeUpdated, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.TimeTrackingModule - } - }, - { - MessageAction.TaskTimesUpdatedStatus, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.TaskTimesUpdatedStatus, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.TimeTrackingModule - } - }, - { - MessageAction.TaskTimesDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.TaskTimesDeleted, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.TimeTrackingModule - } - }, - { - MessageAction.DiscussionCreated, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.DiscussionCreated, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.DiscussionsModule - } - }, - { - MessageAction.DiscussionUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.DiscussionUpdated, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.DiscussionsModule - } - }, - { - MessageAction.DiscussionDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.DiscussionDeleted, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.DiscussionsModule - } - }, - { - MessageAction.DiscussionUpdatedFollowing, new MessageMaps - { - ActionTypeText = AuditReportResource.FollowActionType, - ActionText = AuditReportResource.DiscussionUpdatedFollowing, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.DiscussionsModule - } - }, - { - MessageAction.DiscussionCommentCreated, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.DiscussionCommentCreated, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.DiscussionsModule - } - }, - { - MessageAction.DiscussionCommentUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.DiscussionCommentUpdated, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.DiscussionsModule - } - }, - { - MessageAction.DiscussionCommentDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.DiscussionCommentDeleted, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.DiscussionsModule - } - }, - { - MessageAction.DiscussionAttachedFiles, new MessageMaps - { - ActionTypeText = AuditReportResource.AttachActionType, - ActionText = AuditReportResource.DiscussionAttachedFiles, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.DiscussionsModule - } - }, - { - MessageAction.DiscussionDetachedFile, new MessageMaps - { - ActionTypeText = AuditReportResource.DetachActionType, - ActionText = AuditReportResource.DiscussionDetachedFile, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.DiscussionsModule - } - }, - { - MessageAction.ProjectTemplateCreated, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.ProjectTemplateCreated, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.TemplatesModule - } - }, - { - MessageAction.ProjectTemplateUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.ProjectTemplateUpdated, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.TemplatesModule - } - }, - { - MessageAction.ProjectTemplateDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.ProjectTemplateDeleted, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.TemplatesModule - } - }, - { - MessageAction.ReportTemplateCreated, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.ReportTemplateCreated, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.ReportsModule - } - }, - { - MessageAction.ReportTemplateUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.ReportTemplateUpdated, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.ReportsModule - } - }, - { - MessageAction.ReportTemplateDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.ReportTemplateDeleted, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.ReportsModule - } - }, - { - MessageAction.ProjectLinkedCompany, new MessageMaps - { - ActionTypeText = AuditReportResource.LinkActionType, - ActionText = AuditReportResource.ProjectLinkedCompany, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.ProjectsModule - } - }, - { - MessageAction.ProjectUnlinkedCompany, new MessageMaps - { - ActionTypeText = AuditReportResource.UnlinkActionType, - ActionText = AuditReportResource.ProjectUnlinkedCompany, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.ProjectsModule - } - }, - { - MessageAction.ProjectLinkedPerson, new MessageMaps - { - ActionTypeText = AuditReportResource.LinkActionType, - ActionText = AuditReportResource.ProjectLinkedPerson, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.ProjectsModule - } - }, - { - MessageAction.ProjectUnlinkedPerson, new MessageMaps - { - ActionTypeText = AuditReportResource.UnlinkActionType, - ActionText = AuditReportResource.ProjectUnlinkedPerson, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.ProjectsModule - } - }, - { - MessageAction.ProjectLinkedContacts, new MessageMaps - { - ActionTypeText = AuditReportResource.LinkActionType, - ActionText = AuditReportResource.ProjectLinkedContacts, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.ProjectsModule - } - }, - { - MessageAction.ProjectsImportedFromBasecamp, new MessageMaps - { - ActionTypeText = AuditReportResource.ImportActionType, - ActionText = AuditReportResource.ProjectsImportedFromBasecamp, - Product = AuditReportResource.ProjectsProduct, - Module = AuditReportResource.ProjectsModule - } - }, - - #endregion - - #region crm - - #region companies - - { - MessageAction.CompanyCreated, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.CompanyCreated, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CompaniesModule - } - }, - { - MessageAction.CompanyUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.CompanyUpdated, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CompaniesModule - } - }, - { - MessageAction.CompanyUpdatedStatus, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.CompanyUpdatedStatus, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CompaniesModule - } - }, - { - MessageAction.CompanyUpdatedPersonsStatus, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.CompanyUpdatedPersonsStatus, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CompaniesModule - } - }, - { - MessageAction.CompanyUpdatedPrincipalInfo, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.CompanyUpdatedPrincipalInfo, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CompaniesModule - } - }, - { - MessageAction.CompanyUpdatedPhoto, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.CompanyUpdatedPhoto, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CompaniesModule - } - }, - { - MessageAction.CompanyLinkedProject, new MessageMaps - { - ActionTypeText = AuditReportResource.LinkActionType, - ActionText = AuditReportResource.CompanyLinkedProject, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CompaniesModule - } - }, - { - MessageAction.CompanyUnlinkedProject, new MessageMaps - { - ActionTypeText = AuditReportResource.UnlinkActionType, - ActionText = AuditReportResource.CompanyUnlinkedProject, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CompaniesModule - } - }, - { - MessageAction.CompaniesMerged, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.CompaniesMerged, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CompaniesModule - } - }, - { - MessageAction.CompanyCreatedTag, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.CompanyCreatedTag, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CompaniesModule - } - }, - { - MessageAction.CompanyCreatedPersonsTag, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.CompanyCreatedPersonsTag, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CompaniesModule - } - }, - { - MessageAction.CompanyDeletedTag, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.CompanyDeletedTag, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CompaniesModule - } - }, - { - MessageAction.CompanyCreatedHistory, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.CompanyCreatedHistory, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CompaniesModule - } - }, - { - MessageAction.CompanyDeletedHistory, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.CompanyDeletedHistory, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CompaniesModule - } - }, - { - MessageAction.CompanyLinkedPerson, new MessageMaps - { - ActionTypeText = AuditReportResource.LinkActionType, - ActionText = AuditReportResource.CompanyLinkedPerson, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CompaniesModule - } - }, - { - MessageAction.CompanyUnlinkedPerson, new MessageMaps - { - ActionTypeText = AuditReportResource.UnlinkActionType, - ActionText = AuditReportResource.CompanyUnlinkedPerson, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CompaniesModule - } - }, - { - MessageAction.CompanyUploadedFiles, new MessageMaps - { - ActionTypeText = AuditReportResource.AttachActionType, - ActionText = AuditReportResource.CompanyUploadedFiles, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CompaniesModule - } - }, - { - MessageAction.CompanyAttachedFiles, new MessageMaps - { - ActionTypeText = AuditReportResource.AttachActionType, - ActionText = AuditReportResource.CompanyAttachedFiles, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CompaniesModule - } - }, - { - MessageAction.CompanyDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.CompanyDeleted, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CompaniesModule - } - }, - - #endregion - - #region persons - - { - MessageAction.PersonCreated, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.PersonCreated, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.PersonsModule - } - }, - { - MessageAction.PersonsCreated, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.PersonsCreated, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.PersonsModule - } - }, - { - MessageAction.PersonUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.PersonUpdated, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.PersonsModule - } - }, - { - MessageAction.PersonUpdatedStatus, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.PersonUpdatedStatus, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.PersonsModule - } - }, - { - MessageAction.PersonUpdatedCompanyStatus, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.PersonUpdatedCompanyStatus, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.PersonsModule - } - }, - { - MessageAction.PersonUpdatedPrincipalInfo, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.PersonUpdatedPrincipalInfo, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.PersonsModule - } - }, - { - MessageAction.PersonUpdatedPhoto, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.PersonUpdatedPhoto, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.PersonsModule - } - }, - { - MessageAction.PersonLinkedProject, new MessageMaps - { - ActionTypeText = AuditReportResource.LinkActionType, - ActionText = AuditReportResource.PersonLinkedProject, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.PersonsModule - } - }, - { - MessageAction.PersonUnlinkedProject, new MessageMaps - { - ActionTypeText = AuditReportResource.UnlinkActionType, - ActionText = AuditReportResource.PersonUnlinkedProject, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.PersonsModule - } - }, - { - MessageAction.PersonsMerged, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.PersonsMerged, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.PersonsModule - } - }, - { - MessageAction.PersonCreatedTag, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.PersonCreatedTag, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.PersonsModule - } - }, - { - MessageAction.PersonCreatedCompanyTag, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.PersonCreatedCompanyTag, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.PersonsModule - } - }, - { - MessageAction.PersonDeletedTag, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.PersonDeletedTag, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.PersonsModule - } - }, - { - MessageAction.PersonCreatedHistory, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.PersonCreatedHistory, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.PersonsModule - } - }, - { - MessageAction.PersonDeletedHistory, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.PersonDeletedHistory, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.PersonsModule - } - }, - { - MessageAction.PersonUploadedFiles, new MessageMaps - { - ActionTypeText = AuditReportResource.AttachActionType, - ActionText = AuditReportResource.PersonUploadedFiles, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.PersonsModule - } - }, - { - MessageAction.PersonAttachedFiles, new MessageMaps - { - ActionTypeText = AuditReportResource.AttachActionType, - ActionText = AuditReportResource.PersonAttachedFiles, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.PersonsModule - } - }, - { - MessageAction.PersonDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.PersonDeleted, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.PersonsModule - } - }, - - #endregion - - #region contacts - - { - MessageAction.ContactLinkedProject, new MessageMaps - { - ActionTypeText = AuditReportResource.LinkActionType, - ActionText = AuditReportResource.ContactLinkedProject, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.ContactsModule - } - }, - { - MessageAction.ContactUnlinkedProject, new MessageMaps - { - ActionTypeText = AuditReportResource.UnlinkActionType, - ActionText = AuditReportResource.ContactUnlinkedProject, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.ContactsModule - } - }, - { - MessageAction.ContactsDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.ContactsDeleted, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.ContactsModule - } - }, - - #endregion - - #region tasks - - { - MessageAction.CrmTaskCreated, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.CrmTaskCreated, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CrmTasksModule - } - }, - { - MessageAction.CrmTaskUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.CrmTaskUpdated, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CrmTasksModule - } - }, - { - MessageAction.CrmTaskOpened, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.CrmTaskOpened, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CrmTasksModule - } - }, - { - MessageAction.CrmTaskClosed, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.CrmTaskClosed, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CrmTasksModule - } - }, - { - MessageAction.CrmTaskDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.CrmTaskDeleted, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CrmTasksModule - } - }, - { - MessageAction.ContactsCreatedCrmTasks, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.ContactsCreatedCrmTasks, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CrmTasksModule - } - }, - - #endregion - - #region opportunities - - { - MessageAction.OpportunityCreated, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.OpportunityCreated, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OpportunitiesModule - } - }, - { - MessageAction.OpportunityUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.OpportunityUpdated, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OpportunitiesModule - } - }, - { - MessageAction.OpportunityUpdatedStage, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.OpportunityUpdatedStage, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OpportunitiesModule - } - }, - { - MessageAction.OpportunityCreatedTag, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.OpportunityCreatedTag, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OpportunitiesModule - } - }, - { - MessageAction.OpportunityDeletedTag, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.OpportunityDeletedTag, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OpportunitiesModule - } - }, - { - MessageAction.OpportunityCreatedHistory, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.OpportunityCreatedHistory, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OpportunitiesModule - } - }, - { - MessageAction.OpportunityDeletedHistory, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.OpportunityDeletedHistory, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OpportunitiesModule - } - }, - { - MessageAction.OpportunityLinkedCompany, new MessageMaps - { - ActionTypeText = AuditReportResource.LinkActionType, - ActionText = AuditReportResource.OpportunityLinkedCompany, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OpportunitiesModule - } - }, - { - MessageAction.OpportunityUnlinkedCompany, new MessageMaps - { - ActionTypeText = AuditReportResource.UnlinkActionType, - ActionText = AuditReportResource.OpportunityUnlinkedCompany, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OpportunitiesModule - } - }, - { - MessageAction.OpportunityLinkedPerson, new MessageMaps - { - ActionTypeText = AuditReportResource.LinkActionType, - ActionText = AuditReportResource.OpportunityLinkedPerson, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OpportunitiesModule - } - }, - { - MessageAction.OpportunityUnlinkedPerson, new MessageMaps - { - ActionTypeText = AuditReportResource.UnlinkActionType, - ActionText = AuditReportResource.OpportunityUnlinkedPerson, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OpportunitiesModule - } - }, - { - MessageAction.OpportunityUploadedFiles, new MessageMaps - { - ActionTypeText = AuditReportResource.AttachActionType, - ActionText = AuditReportResource.OpportunityUploadedFiles, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OpportunitiesModule - } - }, - { - MessageAction.OpportunityAttachedFiles, new MessageMaps - { - ActionTypeText = AuditReportResource.AttachActionType, - ActionText = AuditReportResource.OpportunityAttachedFiles, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OpportunitiesModule - } - }, - { - MessageAction.OpportunityOpenedAccess, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateAccessActionType, - ActionText = AuditReportResource.OpportunityOpenedAccess, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OpportunitiesModule - } - }, - { - MessageAction.OpportunityRestrictedAccess, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateAccessActionType, - ActionText = AuditReportResource.OpportunityRestrictedAccess, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OpportunitiesModule - } - }, - { - MessageAction.OpportunityDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.OpportunityDeleted, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OpportunitiesModule - } - }, - { - MessageAction.OpportunitiesDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.OpportunitiesDeleted, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OpportunitiesModule - } - }, - - #endregion - - #region cases - - { - MessageAction.CaseCreated, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.CaseCreated, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CasesModule - } - }, - { - MessageAction.CaseUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.CaseUpdated, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CasesModule - } - }, - { - MessageAction.CaseOpened, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.CaseOpened, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CasesModule - } - }, - { - MessageAction.CaseClosed, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.CaseClosed, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CasesModule - } - }, - { - MessageAction.CaseCreatedTag, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.CaseCreatedTag, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CasesModule - } - }, - { - MessageAction.CaseDeletedTag, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.CaseDeletedTag, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CasesModule - } - }, - { - MessageAction.CaseCreatedHistory, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.CaseCreatedHistory, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CasesModule - } - }, - { - MessageAction.CaseDeletedHistory, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.CaseDeletedHistory, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CasesModule - } - }, - { - MessageAction.CaseLinkedCompany, new MessageMaps - { - ActionTypeText = AuditReportResource.LinkActionType, - ActionText = AuditReportResource.CaseLinkedCompany, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CasesModule - } - }, - { - MessageAction.CaseUnlinkedCompany, new MessageMaps - { - ActionTypeText = AuditReportResource.UnlinkActionType, - ActionText = AuditReportResource.CaseUnlinkedCompany, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CasesModule - } - }, - { - MessageAction.CaseLinkedPerson, new MessageMaps - { - ActionTypeText = AuditReportResource.LinkActionType, - ActionText = AuditReportResource.CaseLinkedPerson, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CasesModule - } - }, - { - MessageAction.CaseUnlinkedPerson, new MessageMaps - { - ActionTypeText = AuditReportResource.UnlinkActionType, - ActionText = AuditReportResource.CaseUnlinkedPerson, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CasesModule - } - }, - { - MessageAction.CaseUploadedFiles, new MessageMaps - { - ActionTypeText = AuditReportResource.AttachActionType, - ActionText = AuditReportResource.CaseUploadedFiles, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CasesModule - } - }, - { - MessageAction.CaseAttachedFiles, new MessageMaps - { - ActionTypeText = AuditReportResource.AttachActionType, - ActionText = AuditReportResource.CaseAttachedFiles, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CasesModule - } - }, - { - MessageAction.CaseOpenedAccess, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateAccessActionType, - ActionText = AuditReportResource.CaseOpenedAccess, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CasesModule - } - }, - { - MessageAction.CaseRestrictedAccess, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateAccessActionType, - ActionText = AuditReportResource.CaseRestrictedAccess, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CasesModule - } - }, - { - MessageAction.CaseDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.CaseDeleted, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CasesModule - } - }, - { - MessageAction.CasesDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.CasesDeleted, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CasesModule - } - }, - - #endregion - - #region import - - { - MessageAction.ContactsImportedFromCSV, new MessageMaps - { - ActionTypeText = AuditReportResource.ImportActionType, - ActionText = AuditReportResource.ContactsImportedFromCSV, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.ContactsModule - } - }, - { - MessageAction.CrmTasksImportedFromCSV, new MessageMaps - { - ActionTypeText = AuditReportResource.ImportActionType, - ActionText = AuditReportResource.CrmTasksImportedFromCSV, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CrmTasksModule - } - }, - { - MessageAction.OpportunitiesImportedFromCSV, new MessageMaps - { - ActionTypeText = AuditReportResource.ImportActionType, - ActionText = AuditReportResource.OpportunitiesImportedFromCSV, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OpportunitiesModule - } - }, - { - MessageAction.CasesImportedFromCSV, new MessageMaps - { - ActionTypeText = AuditReportResource.ImportActionType, - ActionText = AuditReportResource.CasesImportedFromCSV, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CasesModule - } - }, - - #endregion - - #region export - - { - MessageAction.ContactsExportedToCsv, new MessageMaps - { - ActionTypeText = AuditReportResource.ExportActionType, - ActionText = AuditReportResource.ContactsExportedToCsv, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.ContactsModule - } - }, - { - MessageAction.CrmTasksExportedToCsv, new MessageMaps - { - ActionTypeText = AuditReportResource.ExportActionType, - ActionText = AuditReportResource.CrmTasksExportedToCsv, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CrmTasksModule - } - }, - { - MessageAction.OpportunitiesExportedToCsv, new MessageMaps - { - ActionTypeText = AuditReportResource.ExportActionType, - ActionText = AuditReportResource.OpportunitiesExportedToCsv, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OpportunitiesModule - } - }, - { - MessageAction.CasesExportedToCsv, new MessageMaps - { - ActionTypeText = AuditReportResource.ExportActionType, - ActionText = AuditReportResource.CasesExportedToCsv, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CasesModule - } - }, - - #endregion - - #region common settings - - { - MessageAction.CrmSmtpSettingsUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.CrmSmtpSettingsUpdated, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CommonCrmSettingsModule - } - }, - { - MessageAction.CrmTestMailSent, new MessageMaps - { - ActionTypeText = AuditReportResource.SendActionType, - ActionText = AuditReportResource.CrmTestMailSent, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CommonCrmSettingsModule - } - }, - { - MessageAction.CrmDefaultCurrencyUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.CrmDefaultCurrencyUpdated, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CommonCrmSettingsModule - } - }, - { - MessageAction.CrmAllDataExported, new MessageMaps - { - ActionTypeText = AuditReportResource.ExportActionType, - ActionText = AuditReportResource.CrmAllDataExported, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CommonCrmSettingsModule - } - }, - { - MessageAction.ContactsTemperatureLevelSettingsUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.ContactsTemperatureLevelSettingsUpdated, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CommonCrmSettingsModule - } - }, - { - MessageAction.ContactsTagSettingsUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.ContactsTagSettingsUpdated, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CommonCrmSettingsModule - } - }, - - #endregion - - #region contact settings - - { - MessageAction.ContactsTemperatureLevelCreated, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.ContactsTemperatureLevelCreated, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.ContactsSettingsModule - } - }, - { - MessageAction.ContactsTemperatureLevelUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.ContactsTemperatureLevelUpdated, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.ContactsSettingsModule - } - }, - { - MessageAction.ContactsTemperatureLevelUpdatedColor, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.ContactsTemperatureLevelUpdatedColor, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.ContactsSettingsModule - } - }, - { - MessageAction.ContactsTemperatureLevelsUpdatedOrder, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.ContactsTemperatureLevelsUpdatedOrder, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.ContactsSettingsModule - } - }, - { - MessageAction.ContactsTemperatureLevelDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.ContactsTemperatureLevelDeleted, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.ContactsSettingsModule - } - }, - { - MessageAction.ContactTypeCreated, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.ContactTypeCreated, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.ContactTypesModule - } - }, - { - MessageAction.ContactTypeUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.ContactTypeUpdated, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.ContactTypesModule - } - }, - { - MessageAction.ContactTypesUpdatedOrder, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.ContactTypesUpdatedOrder, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.ContactTypesModule - } - }, - { - MessageAction.ContactTypeDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.ContactTypeDeleted, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.ContactTypesModule - } - }, - - #endregion - - #region user fields settings - - { - MessageAction.ContactsCreatedUserField, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.ContactsCreatedUserField, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.ContactsUpdatedUserField, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.ContactsUpdatedUserField, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.ContactsUpdatedUserFieldsOrder, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.ContactsUpdatedUserFieldsOrder, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.ContactsDeletedUserField, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.ContactsDeletedUserField, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.PersonsCreatedUserField, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.PersonsCreatedUserField, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.PersonsUpdatedUserField, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.PersonsUpdatedUserField, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.PersonsUpdatedUserFieldsOrder, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.PersonsUpdatedUserFieldsOrder, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.PersonsDeletedUserField, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.PersonsDeletedUserField, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.CompaniesCreatedUserField, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.CompaniesCreatedUserField, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.CompaniesUpdatedUserField, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.CompaniesUpdatedUserField, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.CompaniesUpdatedUserFieldsOrder, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.CompaniesUpdatedUserFieldsOrder, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.CompaniesDeletedUserField, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.CompaniesDeletedUserField, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.OpportunitiesCreatedUserField, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.OpportunitiesCreatedUserField, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.OpportunitiesUpdatedUserField, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.OpportunitiesUpdatedUserField, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.OpportunitiesUpdatedUserFieldsOrder, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.OpportunitiesUpdatedUserFieldsOrder, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.OpportunitiesDeletedUserField, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.OpportunitiesDeletedUserField, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.CasesCreatedUserField, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.CasesCreatedUserField, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.CasesUpdatedUserField, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.CasesUpdatedUserField, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.CasesUpdatedUserFieldsOrder, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.CasesUpdatedUserFieldsOrder, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.CasesDeletedUserField, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.CasesDeletedUserField, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - - #endregion - - #region history categories settings - - { - MessageAction.HistoryCategoryCreated, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.HistoryCategoryCreated, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.HistoryCategoryUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.HistoryCategoryUpdated, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.HistoryCategoryUpdatedIcon, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.HistoryCategoryUpdatedIcon, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.HistoryCategoriesUpdatedOrder, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.HistoryCategoriesUpdatedOrder, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.HistoryCategoryDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.HistoryCategoryDeleted, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - - #endregion - - #region task actegories settings - - { - MessageAction.CrmTaskCategoryCreated, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.CrmTaskCategoryCreated, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.CrmTaskCategoryUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.CrmTaskCategoryUpdated, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.CrmTaskCategoryUpdatedIcon, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.CrmTaskCategoryUpdatedIcon, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.CrmTaskCategoriesUpdatedOrder, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.CrmTaskCategoriesUpdatedOrder, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.CrmTaskCategoryDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.CrmTaskCategoryDeleted, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - - #endregion - - #region opportunity stages settings - - { - MessageAction.OpportunityStageCreated, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.OpportunityStageCreated, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.OpportunityStageUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.OpportunityStageUpdated, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.OpportunityStageUpdatedColor, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.OpportunityStageUpdatedColor, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.OpportunityStagesUpdatedOrder, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.OpportunityStagesUpdatedOrder, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.OpportunityStageDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.OpportunityStageDeleted, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - - #endregion - - #region tags settings - - { - MessageAction.ContactsCreatedTag, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.ContactsCreatedTag, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.ContactsDeletedTag, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.ContactsDeletedTag, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.OpportunitiesCreatedTag, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.OpportunitiesCreatedTag, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.OpportunitiesDeletedTag, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.OpportunitiesDeletedTag, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.CasesCreatedTag, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.CasesCreatedTag, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.CasesDeletedTag, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.CasesDeletedTag, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - - #endregion - - { - MessageAction.WebsiteContactFormUpdatedKey, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.WebsiteContactFormUpdatedKey, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.OtherCrmSettingsModule - } - }, - { - MessageAction.CrmEntityDetachedFile, new MessageMaps - { - ActionTypeText = AuditReportResource.DetachActionType, - ActionText = AuditReportResource.CrmEntityDetachedFile, - Product = AuditReportResource.CrmProduct, - Module = AuditReportResource.CrmFilesModule - } - }, - - #endregion - - #region people - - { - MessageAction.UserCreated, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.UserCreated, - Product = AuditReportResource.PeopleProduct, - Module = AuditReportResource.UsersModule - } - }, - { - MessageAction.GuestCreated, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.GuestCreated, - Product = AuditReportResource.PeopleProduct, - Module = AuditReportResource.UsersModule - } - }, - { - MessageAction.UserUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.UserUpdated, - Product = AuditReportResource.PeopleProduct, - Module = AuditReportResource.UsersModule - } - }, - { - MessageAction.UserUpdatedAvatar, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.UserUpdatedAvatar, - Product = AuditReportResource.PeopleProduct, - Module = AuditReportResource.UsersModule - } - }, - { - MessageAction.UserSentActivationInstructions, new MessageMaps - { - ActionTypeText = AuditReportResource.SendActionType, - ActionText = AuditReportResource.UserSentActivationInstructions, - Product = AuditReportResource.PeopleProduct, - Module = AuditReportResource.UsersModule - } - }, - { - MessageAction.UserSentPasswordInstructions, new MessageMaps - { - ActionTypeText = AuditReportResource.SendActionType, - ActionText = AuditReportResource.UserSentPasswordInstructions, - Product = AuditReportResource.PeopleProduct, - Module = AuditReportResource.UsersModule - } - }, - { - MessageAction.UserSentEmailInstructions, new MessageMaps - { - ActionTypeText = AuditReportResource.SendActionType, - ActionText = AuditReportResource.UserSentEmailInstructions, - Product = AuditReportResource.PeopleProduct, - Module = AuditReportResource.UsersModule - } - }, - { - MessageAction.UserDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.UserDeleted, - Product = AuditReportResource.PeopleProduct, - Module = AuditReportResource.UsersModule - } - }, - { - MessageAction.UsersUpdatedType, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.UsersUpdatedType, - Product = AuditReportResource.PeopleProduct, - Module = AuditReportResource.UsersModule - } - }, - { - MessageAction.UsersUpdatedStatus, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.UsersUpdatedStatus, - Product = AuditReportResource.PeopleProduct, - Module = AuditReportResource.UsersModule - } - }, - { - MessageAction.UsersSentActivationInstructions, new MessageMaps - { - ActionTypeText = AuditReportResource.SendActionType, - ActionText = AuditReportResource.UsersSentActivationInstructions, - Product = AuditReportResource.PeopleProduct, - Module = AuditReportResource.UsersModule - } - }, - { - MessageAction.UsersDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.UsersDeleted, - Product = AuditReportResource.PeopleProduct, - Module = AuditReportResource.UsersModule - } - }, - { - MessageAction.GroupCreated, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.GroupCreated, - Product = AuditReportResource.PeopleProduct, - Module = AuditReportResource.GroupsModule - } - }, - { - MessageAction.GroupUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.GroupUpdated, - Product = AuditReportResource.PeopleProduct, - Module = AuditReportResource.GroupsModule - } - }, - { - MessageAction.GroupDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.GroupDeleted, - Product = AuditReportResource.PeopleProduct, - Module = AuditReportResource.GroupsModule - } - }, - { - MessageAction.UserUpdatedPassword, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.UserUpdatedPassword, - Product = AuditReportResource.PeopleProduct, - Module = AuditReportResource.UsersModule - } - }, - { - MessageAction.UserLinkedSocialAccount, new MessageMaps - { - ActionTypeText = AuditReportResource.LinkActionType, - ActionText = AuditReportResource.UserLinkedSocialAccount, - Product = AuditReportResource.PeopleProduct, - Module = AuditReportResource.UsersModule - } - }, - { - MessageAction.UserUnlinkedSocialAccount, new MessageMaps - { - ActionTypeText = AuditReportResource.UnlinkActionType, - ActionText = AuditReportResource.UserUnlinkedSocialAccount, - Product = AuditReportResource.PeopleProduct, - Module = AuditReportResource.UsersModule - } - }, - { - MessageAction.UserUpdatedLanguage, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.UserUpdatedLanguage, - Product = AuditReportResource.PeopleProduct, - Module = AuditReportResource.UsersModule - } - }, - { - MessageAction.UserActivatedEmail, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.UserActivatedEmail, - Product = AuditReportResource.PeopleProduct, - Module = AuditReportResource.UsersModule - } - }, - { - MessageAction.UserUpdatedAvatarThumbnails, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.UserUpdatedAvatarThumbnails, - Product = AuditReportResource.PeopleProduct, - Module = AuditReportResource.UsersModule - } - }, - { - MessageAction.UserSentDeleteInstructions, new MessageMaps - { - ActionTypeText = AuditReportResource.SendActionType, - ActionText = AuditReportResource.UserSentDeleteInstructions, - Product = AuditReportResource.PeopleProduct, - Module = AuditReportResource.UsersModule - } - }, - - #endregion - - #region documents - - { - MessageAction.FolderCreated, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.FolderCreated, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FoldersModule - } - }, - { - MessageAction.FolderRenamed, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.FolderRenamed, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FoldersModule - } - }, - { - MessageAction.FolderUpdatedAccess, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateAccessActionType, - ActionText = AuditReportResource.FolderUpdatedAccess, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FoldersModule - } - }, - { - MessageAction.FileCreated, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.FileCreated, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FilesModule - } - }, - { - MessageAction.FileRenamed, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.FileRenamed, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FilesModule - } - }, - { - MessageAction.FileUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.FileUpdated, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FilesModule - } - }, - { - MessageAction.FileCreatedVersion, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.FileCreatedVersion, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FilesModule - } - }, - { - MessageAction.FileUpdatedToVersion, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.FileUpdatedToVersion, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FilesModule - } - }, - { - MessageAction.FileDeletedVersion, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.FileDeletedVersion, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FilesModule - } - }, - { - MessageAction.FileUpdatedRevisionComment, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.FileUpdatedRevisionComment, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FilesModule - } - }, - { - MessageAction.FileLocked, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.FileLocked, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FilesModule - } - }, - { - MessageAction.FileUnlocked, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.FileUnlocked, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FilesModule - } - }, - { - MessageAction.FileUpdatedAccess, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateAccessActionType, - ActionText = AuditReportResource.FileUpdatedAccess, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FilesModule - } - }, - { - MessageAction.FileDownloaded, new MessageMaps - { - ActionTypeText = AuditReportResource.DownloadActionType, - ActionText = AuditReportResource.FileDownloaded, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FilesModule - } - }, - { - MessageAction.FileDownloadedAs, new MessageMaps - { - ActionTypeText = AuditReportResource.DownloadActionType, - ActionText = AuditReportResource.FileDownloadedAs, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FilesModule - } - }, - { - MessageAction.DocumentsDownloaded, new MessageMaps - { - ActionTypeText = AuditReportResource.DownloadActionType, - ActionText = AuditReportResource.DocumentsDownloaded, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.DocumentsModule - } - }, - { - MessageAction.DocumentsCopied, new MessageMaps - { - ActionTypeText = AuditReportResource.CopyActionType, - ActionText = AuditReportResource.DocumentsCopied, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.DocumentsModule - } - }, - { - MessageAction.DocumentsMoved, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.DocumentsMoved, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.DocumentsModule - } - }, - { - MessageAction.DocumentsDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.DocumentsDeleted, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.DocumentsModule - } - }, - { - MessageAction.TrashCleaned, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.TrashCleaned, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.DocumentsModule - } - }, - { - MessageAction.FileMovedToTrash, new MessageMaps - { - ActionTypeText = AuditReportResource.MoveActionType, - ActionText = AuditReportResource.FileMovedToTrash, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FilesModule - } - }, - { - MessageAction.FileDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.FileDeleted, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FilesModule - } - }, - { - MessageAction.FolderMovedToTrash, new MessageMaps - { - ActionTypeText = AuditReportResource.MoveActionType, - ActionText = AuditReportResource.FolderMovedToTrash, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FoldersModule - } - }, - { - MessageAction.FolderDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.FolderDeleted, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FoldersModule - } - }, - { - MessageAction.FileCopied, new MessageMaps - { - ActionTypeText = AuditReportResource.CopyActionType, - ActionText = AuditReportResource.FileCopied, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FilesModule - } - }, - { - MessageAction.FileCopiedWithOverwriting, new MessageMaps - { - ActionTypeText = AuditReportResource.CopyActionType, - ActionText = AuditReportResource.FileCopiedWithOverwriting, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FilesModule - } - }, - { - MessageAction.FileMoved, new MessageMaps - { - ActionTypeText = AuditReportResource.MoveActionType, - ActionText = AuditReportResource.FileMoved, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FilesModule - } - }, - { - MessageAction.FileMovedWithOverwriting, new MessageMaps - { - ActionTypeText = AuditReportResource.MoveActionType, - ActionText = AuditReportResource.FileMovedWithOverwriting, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FilesModule - } - }, - { - MessageAction.FolderCopied, new MessageMaps - { - ActionTypeText = AuditReportResource.CopyActionType, - ActionText = AuditReportResource.FolderCopied, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FoldersModule - } - }, - { - MessageAction.FolderCopiedWithOverwriting, new MessageMaps - { - ActionTypeText = AuditReportResource.CopyActionType, - ActionText = AuditReportResource.FolderCopiedWithOverwriting, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FoldersModule - } - }, - { - MessageAction.FolderMoved, new MessageMaps - { - ActionTypeText = AuditReportResource.MoveActionType, - ActionText = AuditReportResource.FolderMoved, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FoldersModule - } - }, - { - MessageAction.FolderMovedWithOverwriting, new MessageMaps - { - ActionTypeText = AuditReportResource.MoveActionType, - ActionText = AuditReportResource.FolderMovedWithOverwriting, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FoldersModule - } - }, - { - MessageAction.FilesImportedFromBoxNet, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.FilesImportedFromBoxNet, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FilesModule - } - }, - { - MessageAction.FilesImportedFromGoogleDocs, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.FilesImportedFromGoogleDocs, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FilesModule - } - }, - { - MessageAction.FilesImportedFromZoho, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.FilesImportedFromZoho, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FilesModule - } - }, - { - MessageAction.FileImported, new MessageMaps - { - ActionTypeText = AuditReportResource.ImportActionType, - ActionText = AuditReportResource.FileImported, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FilesModule - } - }, - { - MessageAction.DocumentsThirdPartySettingsUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.DocumentsThirdPartySettingsUpdated, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.DocumentsSettingsModule - } - }, - { - MessageAction.DocumentsOverwritingSettingsUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.DocumentsOverwritingSettingsUpdated, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.DocumentsSettingsModule - } - }, - { - MessageAction.DocumentsUploadingFormatsSettingsUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.DocumentsUploadingFormatsSettingsUpdated, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.DocumentsSettingsModule - } - }, - { - MessageAction.FileUploaded, new MessageMaps - { - ActionTypeText = AuditReportResource.UploadActionType, - ActionText = AuditReportResource.FileUploaded, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.FilesModule - } - }, - { - MessageAction.ThirdPartyCreated, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.ThirdPartyCreated, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.DocumentsSettingsModule - } - }, - { - MessageAction.ThirdPartyUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.ThirdPartyUpdated, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.DocumentsSettingsModule - } - }, - { - MessageAction.ThirdPartyDeleted, new MessageMaps - { - ActionTypeText = AuditReportResource.DeleteActionType, - ActionText = AuditReportResource.ThirdPartyDeleted, - Product = AuditReportResource.DocumentsProduct, - Module = AuditReportResource.DocumentsSettingsModule - } - }, - - #endregion - - #region settings - - { - MessageAction.LanguageSettingsUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.LanguageSettingsUpdated, - Product = AuditReportResource.SettingsProduct, - Module = AuditReportResource.GeneralModule - } - }, - { - MessageAction.TimeZoneSettingsUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.TimeZoneSettingsUpdated, - Product = AuditReportResource.SettingsProduct, - Module = AuditReportResource.GeneralModule - } - }, - { - MessageAction.DnsSettingsUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.DnsSettingsUpdated, - Product = AuditReportResource.SettingsProduct, - Module = AuditReportResource.GeneralModule - } - }, - { - MessageAction.TrustedMailDomainSettingsUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.TrustedMailDomainSettingsUpdated, - Product = AuditReportResource.SettingsProduct, - Module = AuditReportResource.GeneralModule - } - }, - { - MessageAction.PasswordStrengthSettingsUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.PasswordStrengthSettingsUpdated, - Product = AuditReportResource.SettingsProduct, - Module = AuditReportResource.GeneralModule - } - }, - { - MessageAction.TwoFactorAuthenticationSettingsUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.TwoFactorAuthenticationSettingsUpdated, - Product = AuditReportResource.SettingsProduct, - Module = AuditReportResource.GeneralModule - } - }, - { - MessageAction.AdministratorMessageSettingsUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.AdministratorMessageSettingsUpdated, - Product = AuditReportResource.SettingsProduct, - Module = AuditReportResource.GeneralModule - } - }, - { - MessageAction.DefaultStartPageSettingsUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.DefaultStartPageSettingsUpdated, - Product = AuditReportResource.SettingsProduct, - Module = AuditReportResource.GeneralModule - } - }, - { - MessageAction.ProductsListUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.ProductsListUpdated, - Product = AuditReportResource.SettingsProduct, - Module = AuditReportResource.ProductsModule - } - }, - { - MessageAction.OwnerUpdated, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.OwnerUpdated, - Product = AuditReportResource.SettingsProduct, - Module = AuditReportResource.ProductsModule - } - }, - { - MessageAction.AdministratorUpdatedProductAccess, new MessageMaps - { - ActionTypeText = AuditReportResource.UpdateActionType, - ActionText = AuditReportResource.AdministratorUpdatedProductAccess, - Product = AuditReportResource.SettingsProduct, - Module = AuditReportResource.ProductsModule - } - }, - { - MessageAction.AdministratorAdded, new MessageMaps - { - ActionTypeText = AuditReportResource.CreateActionType, - ActionText = AuditReportResource.AdministratorAdded, - Product = AuditReportResource.SettingsProduct, - Module = AuditReportResource.ProductsModule - } - }, - - #endregion - }; - - public static string GetActionText(AuditEvent evt) - { - var action = (MessageAction)evt.Action; - if (!actions.ContainsKey(action)) - throw new ArgumentException(string.Format("There is no action text for \"{0}\" type of event", action)); - - var text = actions[(MessageAction)evt.Action].ActionText; - - return evt.Ids == null || !evt.Ids.Any() - ? text - : string.Format(text, evt.Ids.Select(GetLimitedText).ToArray()); - } - - public static string GetActionText(LoginEvent evt) - { - var action = (MessageAction)evt.Action; - if (!actions.ContainsKey(action)) - throw new ArgumentException(string.Format("There is no action text for \"{0}\" type of event", action)); - - var text = actions[(MessageAction)evt.Action].ActionText; - - return evt.Ids == null || !evt.Ids.Any() - ? text - : string.Format(text, evt.Ids.Select(GetLimitedText).ToArray()); - } - - public static string GetActionTypeText(AuditEvent evt) - { - var action = (MessageAction)evt.Action; - return !actions.ContainsKey(action) - ? string.Empty - : actions[(MessageAction)evt.Action].ActionTypeText; - } - - public static string GetProductText(AuditEvent evt) - { - var action = (MessageAction)evt.Action; - return !actions.ContainsKey(action) - ? string.Empty - : actions[(MessageAction)evt.Action].Product; - } - - public static string GetModuleText(AuditEvent evt) - { - var action = (MessageAction)evt.Action; - return !actions.ContainsKey(action) - ? string.Empty - : actions[(MessageAction)evt.Action].Module; - } - - private static string GetLimitedText(string text) - { - if (text == null) throw new ArgumentException("text"); - return text.Length < 50 ? text : string.Format("[{0}...]", text.Substring(0, 47)); - } - - private class MessageMaps - { - public string ActionTypeText { get; set; } - public string ActionText { get; set; } - public string Product { get; set; } - public string Module { get; set; } - } - } -} \ No newline at end of file diff --git a/module/ASC.AuditTrail/AuditEvent.cs b/module/ASC.AuditTrail/AuditEvent.cs index 6e0a2cb6e..1aab3da52 100644 --- a/module/ASC.AuditTrail/AuditEvent.cs +++ b/module/ASC.AuditTrail/AuditEvent.cs @@ -1,41 +1,41 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using ASC.MessagingSystem; - -namespace ASC.AuditTrail -{ - public class AuditEvent : BaseEvent - { - public string Initiator { get; set; } - - [Event("ActionIdCol", 33)] - public int Action { get; set; } - - [Event("ActionTypeCol", 30)] - public string ActionTypeText { get; set; } - - [Event("ProductCol", 31)] - public string Product { get; set; } - - [Event("ModuleCol", 32)] - public string Module { get; set; } - - [Event("TargetIdCol", 34)] - public MessageTarget Target { get; set; } - } +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using ASC.MessagingSystem; + +namespace ASC.AuditTrail +{ + public class AuditEvent : BaseEvent + { + public string Initiator { get; set; } + + [Event("ActionIdCol", 33)] + public int Action { get; set; } + + [Event("ActionTypeCol", 30)] + public string ActionTypeText { get; set; } + + [Event("ProductCol", 31)] + public string Product { get; set; } + + [Event("ModuleCol", 32)] + public string Module { get; set; } + + [Event("TargetIdCol", 34)] + public MessageTarget Target { get; set; } + } } \ No newline at end of file diff --git a/module/ASC.AuditTrail/AuditEventsRepository.cs b/module/ASC.AuditTrail/AuditEventsRepository.cs index eff0081db..4d46782cd 100644 --- a/module/ASC.AuditTrail/AuditEventsRepository.cs +++ b/module/ASC.AuditTrail/AuditEventsRepository.cs @@ -18,14 +18,17 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text; using ASC.AuditTrail.Mappers; +using ASC.AuditTrail.Types; using ASC.Common.Data; using ASC.Common.Data.Sql; using ASC.Common.Data.Sql.Expressions; using ASC.Core.Tenants; using ASC.Core.Users; using ASC.MessagingSystem; +using ASC.Web.Studio.Utility; using Newtonsoft.Json; @@ -50,50 +53,155 @@ namespace ASC.AuditTrail "target" }; - - public static IEnumerable GetLast(int tenant, int chunk) - { - return Get(tenant, null, null, chunk); - } - - public static IEnumerable Get(int tenant, DateTime from, DateTime to) - { - return Get(tenant, from, to, null); - } - - private static IEnumerable Get(int tenant, DateTime? from, DateTime? to, int? limit) + public static IEnumerable GetByFilter( + Guid? userId = null, + ProductType? productType = null, + ModuleType? moduleType = null, + ActionType? actionType = null, + MessageAction? action = null, + EntryType? entry = null, + string target = null, + DateTime? from = null, + DateTime? to = null, + int startIndex = 0, + int limit = 0) { var q = new SqlQuery("audit_events a") .Select(auditColumns.Select(x => "a." + x).ToArray()) .LeftOuterJoin("core_user u", Exp.EqColumns("a.user_id", "u.id")) .Select("u.firstname", "u.lastname") - .Where("a.tenant_id", tenant) + .Where("a.tenant_id", TenantProvider.CurrentTenantID) .OrderBy("a.date", false); - if (from.HasValue && to.HasValue) + if (startIndex > 0) { - q.Where(Exp.Between("a.date", from.Value, to.Value)); + q.SetFirstResult(startIndex); } - if (limit.HasValue) + if (limit > 0) { - q.SetMaxResults(limit.Value); + q.SetMaxResults(limit); + } + + if (userId.HasValue && userId.Value != Guid.Empty) + { + q.Where("a.user_id", userId.Value.ToString()); + } + + var isNeedFindEntry = entry.HasValue && entry.Value != EntryType.None && target != null; + + + if (action.HasValue && action.Value != MessageAction.None) + { + q.Where("a.action", (int)action); + } + else + { + IEnumerable> actions = new List>(); + + var isFindActionType = actionType.HasValue && actionType.Value != ActionType.None; + + if (productType.HasValue && productType.Value != ProductType.None) + { + var productMapper = AuditActionMapper.Mappers.FirstOrDefault(m => m.Product == productType.Value); + + if (productMapper != null) + { + if (moduleType.HasValue && moduleType.Value != ModuleType.None) + { + var moduleMapper = productMapper.Mappers.FirstOrDefault(m => m.Module == moduleType.Value); + if (moduleMapper != null) + { + actions = moduleMapper.Actions; + } + } + else + { + actions = productMapper.Mappers.SelectMany(r => r.Actions); + } + } + } + else + { + actions = AuditActionMapper.Mappers + .SelectMany(r => r.Mappers) + .SelectMany(r => r.Actions); + } + + if (isFindActionType || isNeedFindEntry) + { + actions = actions + .Where(a => (!isFindActionType || a.Value.ActionType == actionType.Value) && (!isNeedFindEntry || (entry.Value == a.Value.EntryType1) || entry.Value == a.Value.EntryType2)) + .ToList(); + } + + if (isNeedFindEntry) + { + FindByEntry(q, entry.Value, target, actions); + } + else + { + var keys = actions.Select(x => (int)x.Key).ToList(); + q.Where(Exp.In("a.action", keys)); + } + } + + + var hasFromFilter = (from.HasValue && from.Value != DateTime.MinValue); + var hasToFilter = (to.HasValue && to.Value != DateTime.MinValue); + + if (hasFromFilter || hasToFilter) + { + if (hasFromFilter) + { + if (hasToFilter) + { + q.Where(Exp.Between("a.date", from, to)); + } + else + { + q.Where(Exp.Ge("a.date", from)); + } + } + else if (hasToFilter) + { + q.Where(Exp.Le("a.date", to)); + } } using (var db = DbManager.FromHttpContext(dbid)) { - return db.ExecuteList(q) + return db + .ExecuteList(q) .Select(ToAuditEvent) - .Where(x => x != null) - .ToList(); + .Where(x => x != null); } } + private static void FindByEntry(SqlQuery q, EntryType entry, string target, IEnumerable> actions) + { + var sb = new StringBuilder(); + + foreach (var action in actions) + { + if (action.Value.EntryType1 == entry) + { + sb.Append(string.Format("(a.action = {0} AND SUBSTRING_INDEX(SUBSTRING_INDEX(a.target,',',{1}),',',1) = {2}) OR ", (int)action.Key, -2, target)); + } + if (action.Value.EntryType2 == entry) + { + sb.Append(string.Format("(a.action = {0} AND SUBSTRING_INDEX(SUBSTRING_INDEX(a.target,',',{1}),',',1) = {2}) OR ", (int)action.Key, -1, target)); + } + } + + sb.Remove(sb.Length - 3, 3); + q.Where(sb.ToString()); + } + public static int GetCount(int tenant, DateTime? from = null, DateTime? to = null) { var q = new SqlQuery("audit_events a") .SelectCount() - .Where("a.tenant_id", tenant) - .OrderBy("a.date", false); + .Where("a.tenant_id", tenant); if (from.HasValue && to.HasValue) { @@ -108,47 +216,66 @@ namespace ASC.AuditTrail private static AuditEvent ToAuditEvent(object[] row) { - try + var evt = new AuditEvent { - var evt = new AuditEvent - { - Id = Convert.ToInt32(row[0]), - IP = Convert.ToString(row[1]), - Initiator = Convert.ToString(row[2]), - Browser = Convert.ToString(row[3]), - Platform = Convert.ToString(row[4]), - Date = TenantUtil.DateTimeFromUtc(Convert.ToDateTime(row[5])), - TenantId = Convert.ToInt32(row[6]), - UserId = Guid.Parse(Convert.ToString(row[7])), - Page = Convert.ToString(row[8]), - Action = Convert.ToInt32(row[9]) - }; + Id = Convert.ToInt32(row[0]), + IP = Convert.ToString(row[1]), + Initiator = Convert.ToString(row[2]), + Browser = Convert.ToString(row[3]), + Platform = Convert.ToString(row[4]), + Date = TenantUtil.DateTimeFromUtc(Convert.ToDateTime(row[5])), + TenantId = Convert.ToInt32(row[6]), + UserId = Guid.Parse(Convert.ToString(row[7])), + Page = Convert.ToString(row[8]), + Action = Convert.ToInt32(row[9]) + }; - if (row[10] != null) - { - evt.Description = JsonConvert.DeserializeObject>( - Convert.ToString(row[10]), - new JsonSerializerSettings { DateTimeZoneHandling = DateTimeZoneHandling.Utc }); - } - - evt.Target = MessageTarget.Parse(Convert.ToString(row[11])); - - evt.UserName = (row[12] != null && row[13] != null) ? UserFormatter.GetUserName(Convert.ToString(row[12]), Convert.ToString(row[13])) : - evt.UserId == Core.Configuration.Constants.CoreSystem.ID ? AuditReportResource.SystemAccount : - evt.UserId == Core.Configuration.Constants.Guest.ID ? AuditReportResource.GuestAccount : - evt.Initiator ?? AuditReportResource.UnknownAccount; - - evt.ActionText = AuditActionMapper.GetActionText(evt); - evt.ActionTypeText = AuditActionMapper.GetActionTypeText(evt); - evt.Product = AuditActionMapper.GetProductText(evt); - evt.Module = AuditActionMapper.GetModuleText(evt); - - return evt; - } - catch (Exception) + if (row[10] != null) { - return null; + evt.Description = JsonConvert.DeserializeObject>( + Convert.ToString(row[10]), + new JsonSerializerSettings { DateTimeZoneHandling = DateTimeZoneHandling.Utc }); } + + evt.Target = MessageTarget.Parse(Convert.ToString(row[11])); + var firstName = Convert.ToString(row[12]); + var lastName = Convert.ToString(row[13]); + + if (evt.UserId == Core.Configuration.Constants.CoreSystem.ID) + { + evt.UserName = AuditReportResource.SystemAccount; + } + else if (evt.UserId == Core.Configuration.Constants.Guest.ID) + { + evt.UserName = AuditReportResource.GuestAccount; + } + else if (!(string.IsNullOrEmpty(firstName) || string.IsNullOrEmpty(lastName))) + { + evt.UserName = UserFormatter.GetUserName(firstName, lastName); + } + else if (!string.IsNullOrEmpty(firstName)) + { + evt.UserName = firstName; + } + else if (!string.IsNullOrEmpty(lastName)) + { + evt.UserName = lastName; + } + else + { + evt.UserName = evt.Initiator ?? AuditReportResource.UnknownAccount; + } + + var map = AuditActionMapper.GetMessageMaps(evt.Action); + if (map != null) + { + evt.ActionText = map.GetActionText(evt); + evt.ActionTypeText = map.GetActionTypeText(); + evt.Product = map.GetProductText(); + evt.Module = map.GetModuleText(); + } + + return evt; } } } \ No newline at end of file diff --git a/module/ASC.AuditTrail/AuditReportCreator.cs b/module/ASC.AuditTrail/AuditReportCreator.cs deleted file mode 100644 index adbfe51c8..000000000 --- a/module/ASC.AuditTrail/AuditReportCreator.cs +++ /dev/null @@ -1,67 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Text; - -using ASC.Common.Logging; -using ASC.Web.Core.Files; -using ASC.Web.Files.Classes; -using ASC.Web.Files.Utils; -using ASC.Web.Studio.Utility; - -using CsvHelper; - -namespace ASC.AuditTrail -{ - public static class AuditReportCreator - { - private static readonly ILog Log = LogManager.GetLogger("ASC.Messaging"); - - public static string CreateCsvReport(IEnumerable events, string reportName) where TEvent : BaseEvent - { - try - { - using (var stream = new MemoryStream()) - using (var writer = new StreamWriter(stream, Encoding.UTF8)) - using (var csv = new CsvWriter(writer, CultureInfo.CurrentCulture)) - { - csv.Configuration.RegisterClassMap(new BaseEventMap()); - - csv.WriteHeader(); - csv.NextRecord(); - csv.WriteRecords(events); - writer.Flush(); - - var file = FileUploader.Exec(Global.FolderMy.ToString(), reportName, stream.Length, stream, true); - var fileUrl = CommonLinkUtility.GetFullAbsolutePath(FilesLinkUtility.GetFileWebEditorUrl((int)file.ID)); - - fileUrl += string.Format("&options={{\"codePage\":{0}}}", Encoding.UTF8.CodePage); - return fileUrl; - } - } - catch (Exception ex) - { - Log.Error("Error while generating login report: " + ex); - throw; - } - } - } -} \ No newline at end of file diff --git a/module/ASC.AuditTrail/AuditReportResource.Designer.cs b/module/ASC.AuditTrail/AuditReportResource.Designer.cs index a643e0f29..d610ddeca 100644 --- a/module/ASC.AuditTrail/AuditReportResource.Designer.cs +++ b/module/ASC.AuditTrail/AuditReportResource.Designer.cs @@ -19,7 +19,7 @@ namespace ASC.AuditTrail { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class AuditReportResource { @@ -1293,6 +1293,15 @@ namespace ASC.AuditTrail { } } + /// + /// Looks up a localized string similar to External share Settings Updated. + /// + public static string DocumentsExternalShareSettingsUpdated { + get { + return ResourceManager.GetString("DocumentsExternalShareSettingsUpdated", resourceCulture); + } + } + /// /// Looks up a localized string similar to Forcesave settings Updated. /// @@ -1473,6 +1482,15 @@ namespace ASC.AuditTrail { } } + /// + /// Looks up a localized string similar to File {0}. External Link Access Updated: {1}. + /// + public static string FileExternalLinkAccessUpdated { + get { + return ResourceManager.GetString("FileExternalLinkAccessUpdated", resourceCulture); + } + } + /// /// Looks up a localized string similar to Folders [{0}]. File Imported: {1}. Provider: {2}. /// @@ -1491,6 +1509,24 @@ namespace ASC.AuditTrail { } } + /// + /// Looks up a localized string similar to File Marked As Favorite: {0}. + /// + public static string FileMarkedAsFavorite { + get { + return ResourceManager.GetString("FileMarkedAsFavorite", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to File {0}: Marked As Read. + /// + public static string FileMarkedAsRead { + get { + return ResourceManager.GetString("FileMarkedAsRead", resourceCulture); + } + } + /// /// Looks up a localized string similar to Files [{0}]. Moved From Folder "{1}" To Folder: "{2}". /// @@ -1518,6 +1554,42 @@ namespace ASC.AuditTrail { } } + /// + /// Looks up a localized string similar to File {0}: Opened For Change. + /// + public static string FileOpenedForChange { + get { + return ResourceManager.GetString("FileOpenedForChange", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to File {0}: Readed. + /// + public static string FileReaded { + get { + return ResourceManager.GetString("FileReaded", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to File Removed From Favorite: {0}. + /// + public static string FileRemovedFromFavorite { + get { + return ResourceManager.GetString("FileRemovedFromFavorite", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to File Removed From List: {0}. + /// + public static string FileRemovedFromList { + get { + return ResourceManager.GetString("FileRemovedFromList", resourceCulture); + } + } + /// /// Looks up a localized string similar to File Renamed: {0}. /// @@ -1536,6 +1608,15 @@ namespace ASC.AuditTrail { } } + /// + /// Looks up a localized string similar to File {0}. Revision {1} Downloaded. + /// + public static string FileRevisionDownloaded { + get { + return ResourceManager.GetString("FileRevisionDownloaded", resourceCulture); + } + } + /// /// Looks up a localized string similar to Document {1} signed via {0}. /// @@ -1599,6 +1680,15 @@ namespace ASC.AuditTrail { } } + /// + /// Looks up a localized string similar to File {0}. Access Updated for {1}: {2}. + /// + public static string FileUpdatedAccessFor { + get { + return ResourceManager.GetString("FileUpdatedAccessFor", resourceCulture); + } + } + /// /// Looks up a localized string similar to Files [{0}]. Revision {1}. Comment Updated . /// @@ -1653,6 +1743,33 @@ namespace ASC.AuditTrail { } } + /// + /// Looks up a localized string similar to Folder Downloaded: {0}. + /// + public static string FolderDownloaded { + get { + return ResourceManager.GetString("FolderDownloaded", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Folder Marked As Favorite: {0}. + /// + public static string FolderMarkedAsFavorite { + get { + return ResourceManager.GetString("FolderMarkedAsFavorite", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Folder {0}: Marked As Read. + /// + public static string FolderMarkedAsRead { + get { + return ResourceManager.GetString("FolderMarkedAsRead", resourceCulture); + } + } + /// /// Looks up a localized string similar to Folders [{0}]. Moved To Folder: {1}. /// @@ -1662,6 +1779,15 @@ namespace ASC.AuditTrail { } } + /// + /// Looks up a localized string similar to Folders [{0}]. Moved From Folder "{1}" To Folder: "{2}". + /// + public static string FolderMovedFrom { + get { + return ResourceManager.GetString("FolderMovedFrom", resourceCulture); + } + } + /// /// Looks up a localized string similar to Folders [{0}]. Moved To Trash Folder. /// @@ -1680,6 +1806,24 @@ namespace ASC.AuditTrail { } } + /// + /// Looks up a localized string similar to Folder Removed From Favorite: {0}. + /// + public static string FolderRemovedFromFavorite { + get { + return ResourceManager.GetString("FolderRemovedFromFavorite", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Folder Removed From List: {0}. + /// + public static string FolderRemovedFromList { + get { + return ResourceManager.GetString("FolderRemovedFromList", resourceCulture); + } + } + /// /// Looks up a localized string similar to Folder Renamed: {0}. /// @@ -1707,6 +1851,15 @@ namespace ASC.AuditTrail { } } + /// + /// Looks up a localized string similar to Folder {0}. Access Updated for {1}: {2}. + /// + public static string FolderUpdatedAccessFor { + get { + return ResourceManager.GetString("FolderUpdatedAccessFor", resourceCulture); + } + } + /// /// Looks up a localized string similar to Follow. /// @@ -2382,6 +2535,15 @@ namespace ASC.AuditTrail { } } + /// + /// Looks up a localized string similar to Open. + /// + public static string OpenActionType { + get { + return ResourceManager.GetString("OpenActionType", resourceCulture); + } + } + /// /// Looks up a localized string similar to Opportunities. Tag Created: {0}. /// @@ -3489,6 +3651,15 @@ namespace ASC.AuditTrail { } } + /// + /// Looks up a localized string similar to Projects [{0}]. Tasks [{1}]. Subtask Moved: {2}. + /// + public static string SubtaskMoved { + get { + return ResourceManager.GetString("SubtaskMoved", resourceCulture); + } + } + /// /// Looks up a localized string similar to Projects [{0}]. Tasks [{1}]. Subtask Updated: {2}. /// @@ -3759,6 +3930,15 @@ namespace ASC.AuditTrail { } } + /// + /// Looks up a localized string similar to Trash Emptied. + /// + public static string TrashEmptied { + get { + return ResourceManager.GetString("TrashEmptied", resourceCulture); + } + } + /// /// Looks up a localized string similar to Trusted mail domain settings updated. /// @@ -3966,6 +4146,33 @@ namespace ASC.AuditTrail { } } + /// + /// Looks up a localized string similar to User {0} has logged out active connection. + /// + public static string UserLogoutActiveConnection { + get { + return ResourceManager.GetString("UserLogoutActiveConnection", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to User {0} has logged out their active connections. + /// + public static string UserLogoutActiveConnections { + get { + return ResourceManager.GetString("UserLogoutActiveConnections", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Logged out active connections for user: {0} . + /// + public static string UserLogoutActiveConnectionsForUser { + get { + return ResourceManager.GetString("UserLogoutActiveConnectionsForUser", resourceCulture); + } + } + /// /// Looks up a localized string similar to Users Deleted: {0}. /// diff --git a/module/ASC.AuditTrail/AuditReportResource.az-Latn-AZ.resx b/module/ASC.AuditTrail/AuditReportResource.az-Latn-AZ.resx new file mode 100644 index 000000000..11a53fb5b --- /dev/null +++ b/module/ASC.AuditTrail/AuditReportResource.az-Latn-AZ.resx @@ -0,0 +1,1423 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Fəaliyyət + + + Fəaliyyət İD + + + Fəaliyyət Növü + + + Administrator əlavə edildi: {0} + + + Administrator Silindi: {0} + + + Administrator Mesaj Parametrləri Yeniləndi + + + Administratorlar [{0}]. Tam Giriş Açıldı + + + Əlavə edin + + + Audit İstifadə müddəti Parametrləri Yeniləndi + + + Audit İzi Hesabatı Yükləndi + + + Audit İzi Hesabatı ({0}-{1}) + + + Üçüncü tərəfin icazə açarları yeniləndi + + + Brauzer + + + Tədbirlər[{0}]. Fayl əlavə edildi: {1} + + + İş Bağlanıb: {0} + + + İş Yaradıldı: {0} + + + Tədbirlər[{0}]. Tarix Hadisəsi Yaradıldı: {1} + + + Tədbirlər[{0}]. Yaradılan Teq: {1} + + + İş Silindi: {0} + + + Tədbirlər[{0}]. Tarix Hadisəsi Silindi: {1} + + + Tədbirlər[{0}]. Silinmiş Teq: {1} + + + Tədbirlər[{0}]. Fayl ayrıldı: {1} + + + Tədbirlər[{0}]. Əlaqədar şirkət: {1} + + + Tədbirlər[{0}]. Əlaqədar şəxs: {1} + + + İş Açıldı: {0} + + + Tədbirlər[{0}]. Giriş Açıldı + + + Tədbirlər[{0}]. Giriş Məhdudlaşdırıldı: {1} + + + Tədbirlər. Yaradılan Teq: {0} + + + Silinmiş Tədbirlər: {0} + + + Tədbirlər. Teq silindi: {0} + + + CSV Faylına Eksport Edilən Tədbirlər: {0} + + + CSV faylından idxal edilən tədbirlər + + + Tədbirlər + + + Tədbirlər[{0}]. Şirkətlə əlaqə kəsildi: {1} + + + Tədbirlər[{0}]. Əlaqəsi kəsilmiş şəxs: {1} + + + İş Yeniləndi: {0} + + + İş İstifadəçi Sahəsi Yaradıldı: {0} + + + İş İstifadəçi Sahəsi Silindi: {0} + + + İş İstifadəçi Sahələri Sifarişi Yenilənib: {0} + + + İş İstifadəçi Sahəsi Yeniləndi: {0} + + + Rəng teması dəyişdirildi + + + Ümumi parametrlər + + + Birləşdirilmiş şirkətlər: {0} - {1} + + + Şirkətlər + + + Şirkətlər [{0}]. Əlavə edilmiş fayllar: {1} + + + Yaradılmış Şirkət: {0} + + + Şirkətlər [{0}]. Hadisə Tarixçəsi Yaradıldı: {1} + + + Şirkətlər [{0}]. Yaradılan Şəxs Teqi: {1} + + + Şirkətlər [{0}]. Yaradılan Teq: {1} + + + Veb Forma ilə Yaradılan Şirkət: {0} + + + Şirkət Silindi: {0} + + + Şirkətlər [{0}]. Hadisə Tarixçəsi Silindi: {1} + + + Şirkətlər [{0}]. Teq silindi: {1} + + + Şirkətlər [{0}]. Ayrılmış fayl: {1} + + + Şirkətlər [{0}]. Əlaqədar şəxs: {1} + + + Şirkətlər [{0}]. Əlaqələndirilmiş Layihə: {1} + + + Şirkətlər [{0}]. Əlaqəsi kəsilmiş şəxs: {1} + + + Şirkətlər [{0}]. Əlaqəsi kəsilmiş layihə: {1} + + + Şirkət Yenilənib: {0} + + + Şirkətlər [{0}]. Şəxsin Temperatur Səviyyəsi Yenilənib + + + Şirkətlər [{0}]. Foto Yenilənib + + + Şirkətlər [{0}]. Əsas Məlumat Yenilənib + + + Şirkətlər [{0}]. Temperatur Səviyyəsi Yenilənib + + + Şirkətlər [{0}]. Yüklənmiş fayllar: {1} + + + Şirkət İstifadəçi Sahəsi Yaradıldı: {0} + + + Şirkət İstifadəçi Sahəsi Silindi: {0} + + + Şirkət İstifadəçi Sahələri Əmri Yenilənib: {0} + + + Şirkət İstifadəçi Sahəsi Yenilənib: {0} + + + Əlaqə Admini Poçtu göndərildi + + + Əlaqələr[{0}]. Əlaqələndirilmiş Layihə: {1} + + + Əlaqələr[{0}]. Tapşırıq Yaradıldı: {1} + + + Əlaqələr. Yaradılan Teq: {0} + + + Əlaqələr silindi: {0} + + + Əlaqələr. Silinən teq: {0} + + + CSV Faylına Eksport edilmiş əlaqələr: {0} + + + CSV faylından idxal edilən əlaqələr + + + Əlaqələr + + + Parametrlər + + + Teq parametrləri əlaqələri yeniləndi + + + Temperatur səviyyəsi ilə əlaqə yenilənib: {0} + + + Temperatur səviyyəsi ilə əlaqə silinib: {0} + + + Temperatur səviyyəsi ilə əlaqə parametrləri yenilənib + + + Temperatur səviyyələri ilə əlaqə əmri yenilənib: {0} + + + Temperatur səviyyəsi ilə əlaqə yenilənib: {0} + + + Temperatur səviyyəsi ilə əlaqə [{0}]. Rəng Yenilənib + + + Əlaqə növü yaradılıb: {0} + + + Əlaqə növü silinib: {0} + + + Əlaqə növləri + + + Əlaqə növləri əmri yenilənib: {0} + + + Əlaqə növü yenilənib: {0} + + + Əlaqələr[{0}]. Əlaqəsi kəsilmiş layihə: {1} + + + İstifadəçi Sahəsi ilə əlaqə Yaradıldı: {0} + + + İstifadəçi Sahəsi ilə əlaqə silindi: {0} + + + İstifadəçi sahələri əmri ilə əlaqə yenilənib: {0} + + + İstifadəçi Sahəsi ilə əlaqə yenilənib: {0} + + + Kuki Parametrləri Yeniləndi + + + Kopyala + + + Yaradın + + + Bütün Məlumatlar İxrac edilmişdir + + + Defolt Valyuta Yenilənib + + + CRM Müəssisəsi. Ayrılmış Fayl: {0} + + + CRM Faylları + + + CRM + + + SMTP Poçtu Kontaktlara Göndərildi: {0} + + + SMTP Parametrləri Yeniləndi + + + CRM Tapşırıq Kateqoriyaları Əmri Yenilənib: {0} + + + CRM Tapşırıq Kateqoriyası Yaradıldı: {0} + + + CRM Tapşırıq Kateqoriyası Silindi: {0} + + + CRM Tapşırıq Kateqoriyası Yeniləndi: {0} + + + CRM Tapşırıq Kateqoriyası İkonu Yeniləndi: {0} + + + CRM Tapşırığı Bağlandı: {0} + + + CRM Tapşırığı Yaradıldı: {0} + + + CRM Tapşırığı Silindi: {0} + + + CRM Tapşırığı Açıldı: {0} + + + CSV Faylına Eksport edilmiş CRM Tapşırıqları: {0} + + + CSV faylından İdxal edilmiş CRM tapşırıqları + + + CRM Tapşırıqları + + + CRM Tapşırıqları Yeniləndi: {0} + + + Test Poçtu Göndərildi + + + Valyuta məzənnəsi yeniləndi: {0} - {1} + + + Fərdi Naviqasiya Parametrləri Yeniləndi + + + Tarix + + + Defolt Başlanğıc Səhifəsi Parametrləri Yeniləndi + + + Silin + + + Ayır + + + Layihələr [{0}]. Müzakirələr [{1}]. Fayllar Əlavə edildi: {2} + + + Layihələr [{0}]. Müzakirələr [{1}]. Şərh Yaradıldı + + + Layihələr [{0}]. Müzakirələr [{1}]. Şərh Silindi + + + Layihələr [{0}]. Müzakirələr [{1}]. Şərh Yeniləndi + + + Layihələr [{0}]. Müzakirə Yaradıldı: {1} + + + Layihələr [{0}]. Müzakirə Silindi: {1} + + + Layihələr [{0}]. Müzakirələr [{1}]. Fayl Ayrıldı: {2} + + + Müzakirələr + + + Layihələr [{0}]. Müzakirə Yeniləndi: {1} + + + Layihələr [{0}]. Müzakirənin İzlənməsi Yeniləndi: {1} + + + DNS Parametrləri Yeniləndi + + + Sənəd Xidmətinin Yeri Yeniləndi + + + Məcbur yaddaşda saxlama parametrləri Yeniləndi + + + Sənədlər + + + Üstündən Yazma Parametrləri Yeniləndi + + + Sənədlər + + + Parametrlər + + + Forcesave saxlama parametrləri Yeniləndi + + + Üçüncü Tərəf Parametrləri Yeniləndi + + + Yükləmə Formatları Parametrləri Yeniləndi + + + Endir + + + Eksport + + + [{0}] sahibini [{1}] olaraq təyin edin + + + Fayl Daxili Formaya Çevrildi: {0} + + + Fayllar [{0}]. "{1}" Qovluğundan Qovluğa kopyalandı: "{2}" + + + Fayllar [{0}]. "{1}" Qovluğunun Üstünə Yazmaqla Qovluğa: "{2}" Kopyalandı + + + Fayl Yaradıldı: {0} + + + Fayllar [{0}]. Təftiş: {1}. Versiya Yaradıldı + + + Fayl Silindi: {0} + + + Fayllar [{0}]. Təftiş: {1}. Versiya Silindi + + + Fayl Endirildi: {0} + + + Fayllar [{0}]. : {1} Kimi Endirildi + + + Qovluqlar [{0}]. Fayl İdxal edildi: {1}. Provayder: {2} + + + Fayl Kilidləndi: {0} + + + Fayllar [{0}]. "{1}" Qovluğundan Qovluğa: "{2}" köçürüldü + + + Fayllar [{0}]. Zibil qutusuna köçürüldü + + + Fayllar [{0}]. "{1}" Qovluğunun Üzərinə Yazmaqla Qovluğa: "{2}" köçürüldü + + + Faylın adı dəyişdirildi: {0} + + + Fayllar [{0}]. Təftiş: {1}. Versiya Bərpa edildi + + + {1} sənədi {0} vasitəsilə imzalanıb + + + [{0}] faylına paylaşılan keçid göndərildi. Qəbul edənlər: [{1}] + + + Fayllar + + + Sənəd {1} {0} vasitəsilə imzalanmaq üçün göndərildi + + + Faylın Kilidi Açıldı: {0} + + + Fayl Yeniləndi: {0} + + + Fayllar [{0}]. Giriş Yeniləndi + + + Fayllar [{0}]. Təftiş{1}. Şərh Yeniləndi + + + Fayl Endirildi: {0} + + + Qovluqlar [{0}]. Qovluğa: {1} kopyalandı + + + Qovluqlar [{0}]. Qovluğun: {1} Üstünə Yazmaqla Kopyalandı + + + Qovluq Yaradıldı: {0} + + + Qovluq Silindi: {0} + + + Qovluqlar [{0}]. Qovluğa: {1} köçürüldü + + + Qovluqlar [{0}]. Zibil qutusuna köçürüldü + + + Qovluqlar [{0}]. Qovluğun: {1} Üstünə yazmaqla Qovluğa köçürüldü + + + Qovluğun adı dəyişdirildi: {0} + + + Qovluqlar + + + Qovluqlar [{0}]. Giriş Yeniləndi + + + İzlə + + + Sfenks parametrləri yeniləndi + + + Ümumi + + + Salamalama Səhifə Parametrləri Yeniləndi + + + Qrup Yaradıldı: {0} + + + Qrup Silindi: {0} + + + Qruplar + + + Qrup Yeniləndi: {0} + + + Qonaq Hesabı + + + Qonaq Aktivləşdirildi: {0} + + + Qonaq Yaradıldı: {0} + + + Qonaq Dəvətlə Yaradıldı: {0} + + + Qonaq İdxal edildi: {0} + + + Hadisə Tarixçəsi Kateqoriyaları. Əmr Yeniləndi: {0} + + + Hadisə Tarixçəsi Kateqoriyası Yaradıldı: {0} + + + Hadisə Tarixçəsi Kateqoriyası Silindi: {0} + + + Hadisə Tarixçəsi Kateqoriyası Yeniləndi: {0} + + + Hadisə Tarixçəsi Kateqoriyaları [{0}]. İşarə Yeniləndi + + + İdxal edin + + + Faktura Yaradıldı: {0} + + + Faktura Defolt Şərtləri Yeniləndi + + + Faktura Silindi: {0} + + + Faktura Endirildi: {0} + + + Faktura Elementi Yaradıldı: {0} + + + Faktura Elementi Silindi: {0} + + + Faktura Elementləri Silindi: {0} + + + Faktura Elementi Yeniləndi: {0} + + + Faktura Nömrəsi Formatı Yeniləndi + + + Hesab-fakturalar Silindi: {0} + + + Faktura parametrləri + + + Fakturalar + + + Fakturalar [{0}]. Status Yeniləndi: {1} + + + Faktura Vergisi Yaradıldı: {0} + + + Faktura Vergisi Silindi: {0} + + + Faktura Vergisi Yeniləndi: {0} + + + Faktura Yeniləndi: {0} + + + İP + + + Dil Parametrləri Yeniləndi + + + Lisenziya açarı yükləndi + + + Link + + + Giriş uğursuz oldu + + + Giriş Uğursuz oldu. Həddindən artıq cəhd + + + Giriş Uğursuz oldu. Profil Qeyri-aktivdir + + + Giriş Uğursuz oldu. Yanlış istifadəçi adı-parol birləşməsi + + + IP Təhlükəsizliyi: Giriş uğursuz oldu + + + Giriş Uğursuz oldu. Recaptcha yanlışdır + + + Giriş Uğursuz oldu. Əlaqədar Sosial Hesab Tapılmadı + + + API vasitəsilə giriş uğursuz oldu + + + API və SMS kodu ilə giriş uğursuz oldu + + + API və Sosial Media vasitəsilə Giriş Uğursuz oldu + + + API və autentifikator tətbiqi ilə daxil olmaq uğursuz oldu + + + SMS vasitəsilə Giriş uğursuz oldu + + + SSO vasitəsilə Giriş Uğursuz oldu + + + Autentifikator proqramı ilə Giriş Uğursuz oldu + + + Giriş Tarixçəsi Hesabatı Endirildi + + + Giriş Tarixçəsi Hesabatı ({0}-{1}) + + + Uğurlu Giriş + + + Sosial Hesab vasitəsilə Uğurlu Giriş + + + Sosial proqram vasitəsilə uğurlu giriş + + + API vasitəsilə Uğurlu Giriş + + + API və SMS kodu vasitəsilə uğurlu giriş + + + API və autentifikator proqramı vasitəsilə uğurlu giriş + + + SMS vasitəsilə giriş uğurlu oldu + + + API və Sosial Media vasitəsilə Giriş Uğurlu oldu + + + SSO vasitəsilə Uğurlu Giriş + + + Autentifikator tətbiqi ilə Uğurlu Giriş + + + Çıxış + + + Poçt Server Parametrləri Yeniləndi + + + Layihələr [{0}]. Mərhələ Yaradıldı: {1} + + + Layihələr [{0}]. Mərhələ Silindi: {1} + + + Mərhələlər + + + Layihələr [{0}]. Mərhələ Yeniləndi: {1} + + + Layihələr [{0}]. Mərhələlər [{1}]. Status Yeniləndi: {2} + + + Modul + + + Daşıyın + + + İmkanlar. Teq Yaradıldı: {0} + + + İmkanlar silindi: {0} + + + İmkanlar. Teq silindi: {0} + + + CSV Faylına İxrac edilən İmkanlar: {0} + + + CSV Faylından İdxal Edilən İmkanlar + + + İmkanlar + + + İmkanlar [{0}]. Fayllar Əlavə edildi: {1} + + + İmkan Yaradıldı: {0} + + + İmkanlar [{0}]. Hadisə Tarixçəsi Yaradıldı: {1} + + + İmkanlar [{0}]. Teq Yaradıldı: {1} + + + İmkan Silindi: {0} + + + İmkanlar [{0}]. Hadisə Tarixçəsi Silindi: {1} + + + İmkanlar [{0}]. Teq silindi: {1} + + + İmkanlar [{0}]. Fayl ayrıldı: {1} + + + İmkanlar [{0}]. Şirkət Əlaqələndirildi: {1} + + + İmkanlar [{0}]. Şəxs Əlaqələndirildi: {1} + + + İmkanlar [{0}]. Giriş Açıldı + + + İmkanlar [{0}]. Giriş Məhdudlaşdırıldı: {1} + + + İmkan Mərhələsi Yaradıldı: {0} + + + İmkan Mərhələsi Silindi: {0} + + + İmkan Mərhələləri Əmri Yeniləndi: {0} + + + İmkan Mərhələsi Yeniləndi: {0} + + + İmkan Mərhələ Rəngi Yeniləndi: {0} + + + Fürsətlər[{0}]. Şirkət Ayrıldı: {1} + + + İmkanlar [{0}]. Şəxsin Əlaqəsi kəsildi: {1} + + + Yenilənmiş İmkan: {0} + + + İmkan Mərhələsi Yeniləndi: {0} + + + İmkan İstifadəçi Sahəsi Yaradıldı: {0} + + + İmkan İstifadəçi Sahəsi Silindi: {0} + + + İmkan İstifadəçi Sahələri Əmri Yeniləndi: {0} + + + İmkan İstifadəçi Sahəsi Yeniləndi: {0} + + + Təşkilat Profili. Ünvan Yeniləndi + + + Təşkilat Profili. Şirkət Adı Yeniləndi: {0} + + + Təşkilat Profili. Faktura Loqosu Yeniləndi + + + Digər parametrlər + + + Digərləri + + + Sahib dəyişdirildi: {0} + + + Sahib [{0}]. Sahibi Dəyiş Təlimatları Göndərildi + + + Sahib [{0}]. Portalın Deaktivasiyası Təlimatları Göndərildi + + + Sahib [{0}]. Portal Silinmə Təlimatları Göndərildi + + + Səhifə + + + Parol Gücü Parametrləri Yeniləndi + + + Adamlar + + + Şəxslər [{0}]. Fayllar Əlavə edildi: {1} + + + Şəxs Yaradıldı: {0} + + + Şəxslər [{0}]. Şirkət Teqi Yaradıldı: {1} + + + Şəxslər [{0}]. Hadisə Tarixçəsi Yaradıldı: {1} + + + Şəxslər [{0}]. Teq Yaradıldı: {1} + + + Veb Forması ilə Yaradılmış Şəxs: {0} + + + Şəxs Silindi: {0} + + + Şəxslər [{0}]. Hadisə Tarixçəsi Silindi: {1} + + + Şəxslər [{0}]. Teq Silindi: {1} + + + Şəxslər [{0}]. Fayl Ayrıldı: {1} + + + Şəxslər [{0}]. Layihə Əlaqələndirildi: {1} + + + Şəxslər Yaradıldı: {0} + + + Şəxslər Birləşdirildi: {0} - {1} + + + Şəxslər + + + Şəxslər [{0}]. Layihə Əlaqəsi kəsildi: {1} + + + Şəxs Yeniləndi: {0} + + + Şəxslər [{0}]. Şirkət Temperatur Səviyyəsi Yeniləndi + + + Şəxslər [{0}]. Foto Yeniləndi + + + Şəxslər [{0}]. Əsas Məlumat Yeniləndi + + + Şəxslər [{0}]. Temperatur Səviyyəsi Yeniləndi + + + Şəxslər [{0}]. Fayllar Yükləndi: {1} + + + Şəxs İstifadəçi Sahəsi Yaradıldı: {0} + + + Şəxs İstifadəçi Sahəsi Silindi: {0} + + + Şəxsi İstifadəçi Sahələri Əmri Yeniləndi: {0} + + + Şəxs İstifadəçi Sahəsi Yeniləndi: {0} + + + Platforma + + + Portala Giriş Parametrləri Yeniləndi + + + Portal Deaktiv edilib + + + Portal Silindi + + + Şəxsi Otaq deaktivdir + + + Şəxsi Otaq aktivdir + + + Məhsullar [{0}]. Giriş Açıldı + + + Məhsullar: [{0}]. Qruplar [{1}]. Giriş Açıldı + + + Məhsullar: [{0}]. İstifadəçilər [{1}]. Giriş Açıldı + + + Məhsullar [{0}]. Giriş Məhdudlaşdırıldı + + + Məhsullar [{0}]. Administrator Əlavə edildi: {1} + + + Məhsul + + + Məhsullar [{0}]. Administrator Silindi: {1} + + + Məhsulların Siyahısı Yeniləndi + + + Məhsullar + + + Layihə Yaradıldı: {0} + + + Şablonlar [{0}]. Layihə Yaradıldı: {1} + + + Layihə Silindi: {0} + + + Layihələr [{0}]. Üzv silindi: {1} + + + Layihə İzləndi: {0} + + + Layihələr [{0}]. Şirkət Əlaqələndirildi: {1} + + + Layihələr [{0}]. Kontaktlar Əlaqələndirildi: {1} + + + Layihələr [{0}]. Şəxs Əlaqələndirildi: {1} + + + Basecamp-dan İdxal edilən Layihələr: {0} + + + Layihələr + + + Layihələr + + + Parametrlər + + + Layihə Şablonu Yaradıldı: {0} + + + Layihə Şablonu Silindi: {0} + + + Layihə Şablonu Yeniləndi: {0} + + + Layihə İzlənilmədi: {0} + + + Layihələr [{0}]. Şirkətin Əlaqəsi kəsildi: {1} + + + Layihələr [{0}]. Şəxsin Əlaqəsi kəsildi: {1} + + + Layihə Yeniləndi: {0} + + + Layihələr [{0}]. Üzvlər [{1}]. Giriş Hüquqları Yeniləndi + + + Layihələr [{0}]. Status Yeniləndi: {1} + + + Layihələr [{0}]. Komanda Yeniləndi: {1} + + + Yenidən təyin edir + + + Hesabatlar + + + Layihə Hesabatı Şablonu Yaradıldı: {0} + + + Layihə Hesabatı Şablonu Silindi: {0} + + + Layihə Hesabatı Şablonu Yeniləndi: {0} + + + Göndər + + + Dəvət Təlimatları Göndərildi: {0} + + + Sessiya Tamamlandı + + + Sessiya Başladı + + + Parametrlər + + + SSO Deaktivdir + + + SSO Aktivdir + + + Ehtiyat nüsxəni başlat + + + Yaddaşın şifrəsini açmağa başla + + + Yaddaş şifrələməsinə başla + + + Portal bölgəsinin köçürülməsinə başla + + + Layihələr [{0}]. Tapşırıqlar [{1}]. Alt tapşırıq Yaradıldı: {2} + + + Layihələr [{0}]. Tapşırıqlar [{1}]. Alt tapşırıq Silindi: {2} + + + Layihələr [{0}]. Tapşırıqlar [{1}]. Alt tapşırıq Köçürüldü: {2} + + + Layihələr [{0}]. Tapşırıqlar [{1}]. Alt tapşırıq Yeniləndi: {2} + + + Layihələr [{0}]. Tapşırıqlar [{1}]. Alt tapşırıqlar [{2}]. Status Yeniləndi: {3} + + + Sistem Hesabı + + + Hədəf İD + + + Layihələr [{0}]. Tapşırıqlar [{1}]. Fayllar Əlavə edildi: {2} + + + Layihələr [{0}]. Tapşırıqlar [{1}]. Şərh Yaradıldı + + + Layihələr [{0}]. Tapşırıqlar [{1}]. Şərh Silindi + + + Layihələr [{0}]. Tapşırıqlar [{1}]. Şərh Yeniləndi + + + Layihələr [{0}]. Tapşırıq Yaradıldı: {1} + + + Layihələr [{0}]. Müzakirələr [{1}]. Tapşırıq Yaradıldı: {2} + + + Layihələr [{0}]. Tapşırıq Silindi: {1} + + + Layihələr [{0}]. Tapşırıq: {1}. Fayl Ayrıldı: {2} + + + Layihələr [{0}]. Mərhələlər [{1}]. Tapşırıq Köçürüldü: {2} + + + Layihələr [{0}]. Tapşırıqlar Əlaqələndirildi: {1} - {2} + + + Tapşırıqlar + + + Layihələr [{0}]. Tapşırıqların Əlaqəsi kəsildi: {1} - {2} + + + Layihələr [{0}]. Tapşırıqlar [{1}]. Tapşırıq Vaxtı Yaradıldı: {2} + + + Tapşırıq Vaxtları Silindi: {0} + + + Tapşırıq vaxtları [{0}]. Status Yeniləndi: {1} + + + Layihələr [{0}]. Tapşırıqlar [{1}]. Tapşırıq Vaxtı Yeniləndi: {2} + + + Layihələr [{0}]. Tapşırıqlar [{1}]. Mərhələlər ilə Əlaqə kəsildi. + + + Layihələr [{0}]. Tapşırıq Yeniləndi: {1} + + + Layihələr [{0}]. Tapşırığın İzlənməsi Yeniləndi: {1} + + + Layihələr [{0}]. Tapşırıqlar [{1}]. Status Yeniləndi: {2} + + + Komanda Şablonu Dəyişildi + + + Üçüncü Tərəf Yaradıldı: {0}. Provayder: {1} + + + Üçüncü Tərəf Silindi: {0}. Provayder: {1} + + + Üçüncü Tərəf Yeniləndi: {0}. Provayder: {1} + + + Vaxt İzləmə + + + Saat Qurşağının Parametrləri Yeniləndi + + + Etibarlı poçt domeni parametrləri yeniləndi + + + İki faktorlu Təsdiqləmə Parametrləri Yeniləndi: İki faktorlu autentifikasiya deaktiv edilib + + + İki faktorlu Təsdiqləmə Parametrləri Yeniləndi: SMS təsdiqi aktiv edildi + + + İki faktorlu Əslliyin Yoxlanması Parametrləri Yeniləndi: Əslliyin Yoxlanması proqramı aktiv edildi + + + İki faktorlu Təsdiqləmə Parametrləri Yeniləndi + + + İzləmədən imtina et + + + Naməlum Hesab + + + Əlaqəni kəsin + + + Yenilənmiş Giriş + + + Güncəllə + + + Yüklə + + + İstifadəçi Aktivləşdirildi: {0} + + + İstifadəçilər [{0}]. Avatar əlavə edildi + + + İstifadəçi + + + İstifadəçi Yaradıldı: {0} + + + İstifadəçi Dəvətlə Yaradılıb: {0} + + + Məlumat yenidən təyin edildi: {0} istifadəçidən {1} istifadəçisinə + + + {0} üçün istifadəçi veriləni silindi + + + İstifadəçi silindi: {0} + + + İstifadəçilər [{0}]. Avatar Silindi + + + İstifadəçilər [{0}]. Fayl Yeniləndi: {1} + + + İstifadəçi İdxal edildi: {0} + + + Sosial Hesab Əlaqələndirildi. Provayder: {0} + + + İstifadəçilər silindi: {0} + + + İstifadəçilər [{0}]. Aktivləşdirmə Təlimatları Göndərildi + + + Profil Silmə Təlimatlarını Göndər + + + İstifadəçilər [{0}]. E-poçt Dəyişikliyi Təlimatları Göndərildi + + + İstifadəçilər [{0}]. Parol Dəyişmə Təlimatları Göndərildi + + + İstifadəçilər + + + İstifadəçilər [{0}]. Aktivləşdirmə Təlimatları Göndərilib + + + İstifadəçilər [{0}]. Status Yeniləndi + + + İstifadəçilər [{0}]. Növ Yeniləndi + + + Autentifikator tətbiqi bağlantısı kəsildi: {0} + + + Yeni ehtiyat kodları yaradılır + + + Sosial Hesabın Əlaqəsi kəsildi. Provayder: {0} + + + İstifadəçi Yeniləndi: {0} + + + İstifadəçilər [{0}]. Avatar Rəsmcikləri Yeniləndi + + + E-poçt Yenilənib + + + Dil Yeniləndi + + + İstifadəçilər [{0}]. Mobil Nömrə Yeniləndi: {1} + + + Parol Yeniləndi + + + Veb-sayt Əlaqə Forması Açarı Yeniləndi + + diff --git a/module/ASC.AuditTrail/AuditReportResource.resx b/module/ASC.AuditTrail/AuditReportResource.resx index 6f85ddb67..716436fa9 100644 --- a/module/ASC.AuditTrail/AuditReportResource.resx +++ b/module/ASC.AuditTrail/AuditReportResource.resx @@ -1,5 +1,64 @@  + @@ -53,10 +112,10 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Action @@ -490,6 +549,9 @@ Third-Party Settings Updated + + External share Settings Updated + Uploading Formats Settings Updated @@ -529,12 +591,21 @@ Files [{0}]. Downloaded As: {1} + + File {0}. External Link Access Updated: {1} + Folders [{0}]. File Imported: {1}. Provider: {2} File Locked: {0} + + File Marked As Favorite: {0} + + + File {0}: Marked As Read + Files [{0}]. Moved From Folder "{1}" To Folder: "{2}" @@ -544,12 +615,27 @@ Files [{0}]. Moved With Overwriting From Folder "{1}" To Folder: "{2}" + + File {0}: Opened For Change + + + File {0}: Readed + + + File Removed From Favorite: {0} + + + File Removed From List: {0} + File Renamed: {0} Files [{0}]. Revision: {1}. Version Restored + + File {0}. Revision {1} Downloaded + Document {1} signed via {0} @@ -571,6 +657,9 @@ Files [{0}]. Access Updated + + File {0}. Access Updated for {1}: {2} + Files [{0}]. Revision {1}. Comment Updated @@ -589,15 +678,33 @@ Folder Deleted: {0} + + Folder Downloaded: {0} + + + Folder Marked As Favorite: {0} + + + Folder {0}: Marked As Read + Folders [{0}]. Moved To Folder: {1} + + Folders [{0}]. Moved From Folder "{1}" To Folder: "{2}" + Folders [{0}]. Moved To Trash Folder Folders [{0}]. Moved With Overwriting To Folder: {1} + + Folder Removed From Favorite: {0} + + + Folder Removed From List: {0} + Folder Renamed: {0} @@ -607,6 +714,9 @@ Folders [{0}]. Access Updated + + Folder {0}. Access Updated for {1}: {2} + Follow @@ -832,6 +942,9 @@ Move + + Open + Opportunities. Tag Created: {0} @@ -1201,6 +1314,9 @@ Projects [{0}]. Tasks [{1}]. Subtask Deleted: {2} + + Projects [{0}]. Tasks [{1}]. Subtask Moved: {2} + Projects [{0}]. Tasks [{1}]. Subtask Updated: {2} @@ -1291,6 +1407,9 @@ Time Zone Settings Updated + + Trash Emptied + Trusted mail domain settings updated @@ -1360,6 +1479,15 @@ Social Account Linked. Provider: {0} + + User {0} has logged out active connection + + + User {0} has logged out their active connections + + + Logged out active connections for user: {0} + Users Deleted: {0} diff --git a/module/ASC.AuditTrail/BaseEvent.cs b/module/ASC.AuditTrail/BaseEvent.cs deleted file mode 100644 index 960bd4b24..000000000 --- a/module/ASC.AuditTrail/BaseEvent.cs +++ /dev/null @@ -1,92 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; - -using CsvHelper.Configuration; - -namespace ASC.AuditTrail -{ - public class BaseEvent - { - public int Id { get; set; } - - public int TenantId { get; set; } - - public Guid UserId { get; set; } - - public bool Mobile { get; set; } - - public IList Description { get; set; } - - [Event("IpCol")] - public string IP { get; set; } - - [Event("BrowserCol")] - public string Browser { get; set; } - - [Event("PlatformCol")] - public string Platform { get; set; } - - [Event("DateCol")] - public DateTime Date { get; set; } - - [Event("UserCol")] - public string UserName { get; set; } - - [Event("PageCol")] - public string Page { get; set; } - - [Event("ActionCol")] - public string ActionText { get; set; } - } - - internal class BaseEventMap : ClassMap where T : BaseEvent - { - public BaseEventMap() - { - var eventType = typeof(T); - var eventProps = eventType - .GetProperties() - .Where(r => r.GetCustomAttribute() != null) - .OrderBy(r => r.GetCustomAttribute().Order); - - foreach (var prop in eventProps) - { - var attr = prop.GetCustomAttribute().Resource; - Map(eventType, prop).Name(AuditReportResource.ResourceManager.GetString(attr)); - } - } - } - - [AttributeUsage(AttributeTargets.Property)] - internal class EventAttribute : Attribute - { - public string Resource { get; private set; } - - public int Order { get; private set; } - - public EventAttribute(string resource, int order = 0) - { - Resource = resource; - Order = order; - } - } -} diff --git a/module/ASC.AuditTrail/LoginEvent.cs b/module/ASC.AuditTrail/LoginEvent.cs index c940d0b74..e6d091841 100644 --- a/module/ASC.AuditTrail/LoginEvent.cs +++ b/module/ASC.AuditTrail/LoginEvent.cs @@ -1,26 +1,26 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.AuditTrail -{ - public class LoginEvent : BaseEvent - { - public string Login { get; set; } - - public int Action { get; set; } - } +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +namespace ASC.AuditTrail +{ + public class LoginEvent : BaseEvent + { + public string Login { get; set; } + + public int Action { get; set; } + } } \ No newline at end of file diff --git a/module/ASC.AuditTrail/LoginEventsRepository.cs b/module/ASC.AuditTrail/LoginEventsRepository.cs index 822afad80..59df6407e 100644 --- a/module/ASC.AuditTrail/LoginEventsRepository.cs +++ b/module/ASC.AuditTrail/LoginEventsRepository.cs @@ -25,6 +25,8 @@ using ASC.Common.Data.Sql; using ASC.Common.Data.Sql.Expressions; using ASC.Core.Tenants; using ASC.Core.Users; +using ASC.MessagingSystem; +using ASC.Web.Studio.Utility; using Newtonsoft.Json; @@ -34,52 +36,24 @@ namespace ASC.AuditTrail.Data { private const string auditDbId = "core"; - private static readonly List auditColumns = - new List - { - "id", - "ip", - "login", - "browser", - "platform", - "date", - "tenant_id", - "user_id", - "page", - "action", - "description" - }; - - public static IEnumerable GetLast(int tenant, int chunk) + private static readonly List auditColumns = new List { - var q = new SqlQuery("login_events au") - .Select(auditColumns.Select(x => "au." + x).ToArray()) - .LeftOuterJoin("core_user u", Exp.EqColumns("au.user_id", "u.id")) - .Select("u.firstname", "u.lastname") - .Where("au.tenant_id", tenant) - .OrderBy("au.date", false) - .SetMaxResults(chunk); + "id", + "ip", + "login", + "browser", + "platform", + "date", + "tenant_id", + "user_id", + "page", + "action", + "description" + }; - using (var db = DbManager.FromHttpContext(auditDbId)) - { - return db.ExecuteList(q).Select(ToLoginEvent).Where(x => x != null); - } - } - - public static IEnumerable Get(int tenant, DateTime from, DateTime to) + private static IDbManager GetDbManager() { - var q = new SqlQuery("login_events au") - .Select(auditColumns.Select(x => "au." + x).ToArray()) - .LeftOuterJoin("core_user u", Exp.EqColumns("au.user_id", "u.id")) - .Select("u.firstname", "u.lastname") - .Where("au.tenant_id", tenant) - .Where(Exp.Between("au.date", from, to)) - .OrderBy("au.date", false); - - using (var db = DbManager.FromHttpContext(auditDbId)) - { - return db.ExecuteList(q).Select(ToLoginEvent).Where(x => x != null); - } + return DbManager.FromHttpContext(auditDbId); } public static int GetCount(int tenant, DateTime? from = null, DateTime? to = null) @@ -93,12 +67,77 @@ namespace ASC.AuditTrail.Data q.Where(Exp.Between("date", from.Value, to.Value)); } - using (var db = DbManager.FromHttpContext(auditDbId)) + using (var db = GetDbManager()) { return db.ExecuteScalar(q); } } + public static IEnumerable GetByFilter( + Guid? login = null, + MessageAction? action = null, + DateTime? from = null, + DateTime? to = null, + int startIndex = 0, + int limit = 0) + { + var q = new SqlQuery("login_events l") + .Select(auditColumns.Select(x => "l." + x).ToArray()) + .LeftOuterJoin("core_user u", Exp.EqColumns("l.user_id", "u.id")) + .Select("u.firstname", "u.lastname") + .Where("l.tenant_id", TenantProvider.CurrentTenantID) + .OrderBy("l.date", false); + + if (startIndex > 0) + { + q.SetFirstResult(startIndex); + } + if (limit > 0) + { + q.SetMaxResults(limit); + } + + if (login.HasValue && login.Value != Guid.Empty) + { + q.Where("l.user_id", login.Value.ToString()); + } + + if (action.HasValue && action.Value != MessageAction.None) + { + q.Where("l.action", (int)action); + } + + var hasFromFilter = (from.HasValue && from.Value != DateTime.MinValue); + var hasToFilter = (to.HasValue && to.Value != DateTime.MinValue); + + if (hasFromFilter || hasToFilter) + { + if (hasFromFilter) + { + if (hasToFilter) + { + q.Where(Exp.Between("l.date", from, to)); + } + else + { + q.Where(Exp.Ge("l.date", from)); + } + } + else if (hasToFilter) + { + q.Where(Exp.Le("l.date", to)); + } + } + + using (var db = GetDbManager()) + { + return db + .ExecuteList(q) + .Select(ToLoginEvent) + .Where(x => x != null); + } + } + private static LoginEvent ToLoginEvent(object[] row) { try @@ -126,15 +165,36 @@ namespace ASC.AuditTrail.Data DateTimeZoneHandling = DateTimeZoneHandling.Utc }); } - evt.UserName = (row[11] != null && row[12] != null) - ? UserFormatter.GetUserName(Convert.ToString(row[11]), Convert.ToString(row[12])) - : !string.IsNullOrWhiteSpace(evt.Login) - ? evt.Login - : evt.UserId == Core.Configuration.Constants.Guest.ID - ? AuditReportResource.GuestAccount - : AuditReportResource.UnknownAccount; - evt.ActionText = AuditActionMapper.GetActionText(evt); + var firstName = Convert.ToString(row[11]); + var lastName = Convert.ToString(row[12]); + + if (!(string.IsNullOrEmpty(firstName) || string.IsNullOrEmpty(lastName))) + { + evt.UserName = UserFormatter.GetUserName(firstName, lastName); + } + else if (!string.IsNullOrEmpty(firstName)) + { + evt.UserName = firstName; + } + else if (!string.IsNullOrEmpty(lastName)) + { + evt.UserName = lastName; + } + else if (!string.IsNullOrWhiteSpace(evt.Login)) + { + evt.UserName = evt.Login; + } + else if (evt.UserId == Core.Configuration.Constants.Guest.ID) + { + evt.UserName = AuditReportResource.GuestAccount; + } + else + { + evt.UserName = AuditReportResource.UnknownAccount; + } + + evt.ActionText = AuditActionMapper.GetMessageMaps(evt.Action).GetActionText(evt); return evt; } diff --git a/module/ASC.AuditTrail/Mappers/AuditActionMapper.cs b/module/ASC.AuditTrail/Mappers/AuditActionMapper.cs index e3a29b593..0c90ffe3f 100644 --- a/module/ASC.AuditTrail/Mappers/AuditActionMapper.cs +++ b/module/ASC.AuditTrail/Mappers/AuditActionMapper.cs @@ -23,29 +23,27 @@ using ASC.MessagingSystem; namespace ASC.AuditTrail.Mappers { - public class AuditActionMapper + public static class AuditActionMapper { - private static readonly Dictionary actions; + public static List Mappers { get; } static AuditActionMapper() { - actions = new Dictionary(); - - actions = actions - .Union(LoginActionsMapper.GetMaps()) - .Union(ProjectsActionsMapper.GetMaps()) - .Union(CrmActionMapper.GetMaps()) - .Union(PeopleActionMapper.GetMaps()) - .Union(DocumentsActionMapper.GetMaps()) - .Union(SettingsActionsMapper.GetMaps()) - .Union(OthersActionsMapper.GetMaps()) - .ToDictionary(x => x.Key, x => x.Value); + Mappers = new List() + { + new CrmActionMapper(), + new DocumentsActionMapper(), + new LoginActionsMapper(), + new OthersActionsMapper(), + new PeopleActionMapper(), + new ProjectsActionsMapper(), + new SettingsActionsMapper() + }; } - public static string GetActionText(AuditEvent evt) + public static string GetActionText(this MessageMaps action, AuditEvent evt) { - var action = (MessageAction)evt.Action; - if (!actions.ContainsKey(action)) + if (action == null) { //log.Error(string.Format("There is no action text for \"{0}\" type of event", action)); return string.Empty; @@ -53,7 +51,7 @@ namespace ASC.AuditTrail.Mappers try { - var actionText = actions[(MessageAction)evt.Action].GetActionText(); + var actionText = action.GetActionText(); if (evt.Description == null || !evt.Description.Any()) return actionText; @@ -71,10 +69,9 @@ namespace ASC.AuditTrail.Mappers } } - public static string GetActionText(LoginEvent evt) + public static string GetActionText(this MessageMaps action, LoginEvent evt) { - var action = (MessageAction)evt.Action; - if (!actions.ContainsKey(action)) + if (action == null) { //log.Error(string.Format("There is no action text for \"{0}\" type of event", action)); return string.Empty; @@ -82,7 +79,7 @@ namespace ASC.AuditTrail.Mappers try { - var actionText = actions[(MessageAction)evt.Action].GetActionText(); + var actionText = action.GetActionText(); if (evt.Description == null || !evt.Description.Any()) return actionText; @@ -99,28 +96,25 @@ namespace ASC.AuditTrail.Mappers } } - public static string GetActionTypeText(AuditEvent evt) + public static string GetActionTypeText(this MessageMaps action) { - var action = (MessageAction)evt.Action; - return !actions.ContainsKey(action) + return action == null ? string.Empty - : actions[(MessageAction)evt.Action].GetActionTypeText(); + : action.GetActionTypeText(); } - public static string GetProductText(AuditEvent evt) + public static string GetProductText(this MessageMaps action) { - var action = (MessageAction)evt.Action; - return !actions.ContainsKey(action) + return action == null ? string.Empty - : actions[(MessageAction)evt.Action].GetProduct(); + : action.GetProductText(); } - public static string GetModuleText(AuditEvent evt) + public static string GetModuleText(this MessageMaps action) { - var action = (MessageAction)evt.Action; - return !actions.ContainsKey(action) + return action == null ? string.Empty - : actions[(MessageAction)evt.Action].GetModule(); + : action.GetModuleText(); } private static string ToLimitedText(string text) @@ -128,5 +122,16 @@ namespace ASC.AuditTrail.Mappers if (text == null) return null; return text.Length < 50 ? text : string.Format("{0}...", text.Substring(0, 47)); } + + public static MessageMaps GetMessageMaps(int actionInt) + { + var action = (MessageAction)actionInt; + var mapper = Mappers.SelectMany(m => m.Mappers).FirstOrDefault(m => m.Actions.ContainsKey(action)); + if (mapper != null) + { + return mapper.Actions[action]; + } + return null; + } } } \ No newline at end of file diff --git a/module/ASC.AuditTrail/Mappers/CrmActionMapper.cs b/module/ASC.AuditTrail/Mappers/CrmActionMapper.cs index 67447d397..d7f87694b 100644 --- a/module/ASC.AuditTrail/Mappers/CrmActionMapper.cs +++ b/module/ASC.AuditTrail/Mappers/CrmActionMapper.cs @@ -17,1601 +17,539 @@ using System.Collections.Generic; +using ASC.AuditTrail.Types; using ASC.MessagingSystem; namespace ASC.AuditTrail.Mappers { - internal class CrmActionMapper + public class CrmActionMapper : IProductActionMapper { - public static Dictionary GetMaps() + public List Mappers { get; } + public ProductType Product { get; } + public CrmActionMapper() { - return new Dictionary + Product = ProductType.CRM; + + Mappers = new List() + { + new CompaniesActionMapper(), + new PersonsActionMapper(), + new ContactsActionMapper(), + new CrmTasksActionMapper(), + new OpportunitiesActionMapper(), + new InvoicesActionMapper(), + new CasesActionMapper(), + new CommonCrmSettingsActionMapper(), + new ContactsSettingsActionMapper(), + new ContactTypesActionMapper(), + new InvoiceSettingsActionMapper(), + new OtherCrmSettingsActionMapper() + }; + } + } + + public class CompaniesActionMapper : IModuleActionMapper + { + public ModuleType Module { get; } + public IDictionary Actions { get; } + + public CompaniesActionMapper() + { + Module = ModuleType.Companies; + + Actions = new MessageMapsDictionary(ProductType.CRM, Module) + { { - #region companies + EntryType.Contact, new Dictionary() + { + { + ActionType.Create, new [] + { + MessageAction.CompanyCreated, MessageAction.CompanyCreatedWithWebForm, MessageAction.CompanyCreatedTag, MessageAction.CompanyCreatedPersonsTag + } + }, + { + ActionType.Update, new [] + { + MessageAction.CompanyUpdated, MessageAction.CompanyUpdatedPrincipalInfo, MessageAction.CompanyUpdatedPhoto, MessageAction.CompanyUpdatedTemperatureLevel, MessageAction.CompanyUpdatedPersonsTemperatureLevel + } + }, + { + ActionType.Delete, new [] + { + MessageAction.CompanyDeleted, MessageAction.CompanyDeletedTag + } + } + }, + new Dictionary() + { + { ActionType.Attach, MessageAction.CompanyAttachedFiles }, + { ActionType.Detach, MessageAction.CompanyDetachedFile } + } + }, + { + EntryType.Relationship, new Dictionary() + { + { ActionType.Create, MessageAction.CompanyCreatedHistoryEvent }, + { ActionType.Delete, MessageAction.CompanyDeletedHistoryEvent }, + } + }, + { + EntryType.Project, new Dictionary() + { + { ActionType.Link, MessageAction.CompanyLinkedProject }, + { ActionType.Unlink, MessageAction.CompanyUnlinkedProject }, + } + }, + { + EntryType.Contact, EntryType.Contact, new Dictionary() + { + { ActionType.Link, MessageAction.CompanyLinkedPerson }, + { ActionType.Unlink, MessageAction.CompanyUnlinkedPerson }, + { ActionType.Update, MessageAction.CompaniesMerged } + } + } + }; + } + } - { - MessageAction.CompanyCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "CompanyCreated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CompaniesModule" - } - }, - { - MessageAction.CompanyCreatedWithWebForm, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "CompanyCreatedWithWebForm", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CompaniesModule" - } - }, - { - MessageAction.CompanyUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "CompanyUpdated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CompaniesModule" - } - }, - { - MessageAction.CompanyUpdatedPrincipalInfo, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "CompanyUpdatedPrincipalInfo", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CompaniesModule" - } - }, - { - MessageAction.CompanyUpdatedPhoto, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "CompanyUpdatedPhoto", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CompaniesModule" - } - }, - { - MessageAction.CompanyUpdatedTemperatureLevel, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "CompanyUpdatedTemperatureLevel", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CompaniesModule" - } - }, - { - MessageAction.CompanyUpdatedPersonsTemperatureLevel, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "CompanyUpdatedPersonsTemperatureLevel", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CompaniesModule" - } - }, - { - MessageAction.CompanyCreatedTag, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "CompanyCreatedTag", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CompaniesModule" - } - }, - { - MessageAction.CompanyCreatedPersonsTag, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "CompanyCreatedPersonsTag", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CompaniesModule" - } - }, - { - MessageAction.CompanyDeletedTag, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "CompanyDeletedTag", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CompaniesModule" - } - }, - { - MessageAction.CompanyCreatedHistoryEvent, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "CompanyCreatedHistoryEvent", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CompaniesModule" - } - }, - { - MessageAction.CompanyDeletedHistoryEvent, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "CompanyDeletedHistoryEvent", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CompaniesModule" - } - }, - { - MessageAction.CompanyLinkedPerson, new MessageMaps - { - ActionTypeTextResourceName = "LinkActionType", - ActionTextResourceName = "CompanyLinkedPerson", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CompaniesModule" - } - }, - { - MessageAction.CompanyUnlinkedPerson, new MessageMaps - { - ActionTypeTextResourceName = "UnlinkActionType", - ActionTextResourceName = "CompanyUnlinkedPerson", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CompaniesModule" - } - }, - { - MessageAction.CompanyLinkedProject, new MessageMaps - { - ActionTypeTextResourceName = "LinkActionType", - ActionTextResourceName = "CompanyLinkedProject", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CompaniesModule" - } - }, - { - MessageAction.CompanyUnlinkedProject, new MessageMaps - { - ActionTypeTextResourceName = "UnlinkActionType", - ActionTextResourceName = "CompanyUnlinkedProject", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CompaniesModule" - } - }, - { - MessageAction.CompanyAttachedFiles, new MessageMaps - { - ActionTypeTextResourceName = "AttachActionType", - ActionTextResourceName = "CompanyAttachedFiles", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CompaniesModule" - } - }, - { - MessageAction.CompanyDetachedFile, new MessageMaps - { - ActionTypeTextResourceName = "DetachActionType", - ActionTextResourceName = "CompanyDetachedFile", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CompaniesModule" - } - }, - { - MessageAction.CompaniesMerged, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "CompaniesMerged", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CompaniesModule" - } - }, - { - MessageAction.CompanyDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "CompanyDeleted", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CompaniesModule" - } - }, + public class PersonsActionMapper : IModuleActionMapper + { + public ModuleType Module { get; } + public IDictionary Actions { get; } - #endregion + public PersonsActionMapper() + { + Module = ModuleType.Persons; - #region persons - + Actions = new MessageMapsDictionary(ProductType.CRM, Module) + { + { + EntryType.Contact, new Dictionary() { - MessageAction.PersonCreated, new MessageMaps + { + ActionType.Create, new [] { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "PersonCreated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "PersonsModule" + MessageAction.PersonCreated, MessageAction.PersonCreatedWithWebForm, MessageAction.PersonsCreated, MessageAction.PersonCreatedTag, MessageAction.PersonCreatedCompanyTag } - }, - { - MessageAction.PersonCreatedWithWebForm, new MessageMaps + }, + { + ActionType.Update, new [] { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "PersonCreatedWithWebForm", - ProductResourceName = "CrmProduct", - ModuleResourceName = "PersonsModule" + MessageAction.CompanyUpdated, MessageAction.PersonUpdated, MessageAction.PersonUpdatedPrincipalInfo, MessageAction.PersonUpdatedPhoto, MessageAction.PersonUpdatedTemperatureLevel, MessageAction.PersonUpdatedCompanyTemperatureLevel } - }, - { - MessageAction.PersonsCreated, new MessageMaps + }, + { + ActionType.Delete, new [] { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "PersonsCreated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "PersonsModule" + MessageAction.PersonDeleted, MessageAction.PersonDeletedTag } - }, - { - MessageAction.PersonUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "PersonUpdated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "PersonsModule" - } - }, - { - MessageAction.PersonUpdatedPrincipalInfo, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "PersonUpdatedPrincipalInfo", - ProductResourceName = "CrmProduct", - ModuleResourceName = "PersonsModule" - } - }, - { - MessageAction.PersonUpdatedPhoto, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "PersonUpdatedPhoto", - ProductResourceName = "CrmProduct", - ModuleResourceName = "PersonsModule" - } - }, - { - MessageAction.PersonUpdatedTemperatureLevel, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "PersonUpdatedTemperatureLevel", - ProductResourceName = "CrmProduct", - ModuleResourceName = "PersonsModule" - } - }, - { - MessageAction.PersonUpdatedCompanyTemperatureLevel, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "PersonUpdatedCompanyTemperatureLevel", - ProductResourceName = "CrmProduct", - ModuleResourceName = "PersonsModule" - } - }, - { - MessageAction.PersonCreatedTag, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "PersonCreatedTag", - ProductResourceName = "CrmProduct", - ModuleResourceName = "PersonsModule" - } - }, - { - MessageAction.PersonCreatedCompanyTag, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "PersonCreatedCompanyTag", - ProductResourceName = "CrmProduct", - ModuleResourceName = "PersonsModule" - } - }, - { - MessageAction.PersonDeletedTag, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "PersonDeletedTag", - ProductResourceName = "CrmProduct", - ModuleResourceName = "PersonsModule" - } - }, - { - MessageAction.PersonCreatedHistoryEvent, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "PersonCreatedHistoryEvent", - ProductResourceName = "CrmProduct", - ModuleResourceName = "PersonsModule" - } - }, - { - MessageAction.PersonDeletedHistoryEvent, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "PersonDeletedHistoryEvent", - ProductResourceName = "CrmProduct", - ModuleResourceName = "PersonsModule" - } - }, - { - MessageAction.PersonLinkedProject, new MessageMaps - { - ActionTypeTextResourceName = "LinkActionType", - ActionTextResourceName = "PersonLinkedProject", - ProductResourceName = "CrmProduct", - ModuleResourceName = "PersonsModule" - } - }, - { - MessageAction.PersonUnlinkedProject, new MessageMaps - { - ActionTypeTextResourceName = "UnlinkActionType", - ActionTextResourceName = "PersonUnlinkedProject", - ProductResourceName = "CrmProduct", - ModuleResourceName = "PersonsModule" - } - }, - { - MessageAction.PersonAttachedFiles, new MessageMaps - { - ActionTypeTextResourceName = "AttachActionType", - ActionTextResourceName = "PersonAttachedFiles", - ProductResourceName = "CrmProduct", - ModuleResourceName = "PersonsModule" - } - }, - { - MessageAction.PersonDetachedFile, new MessageMaps - { - ActionTypeTextResourceName = "DetachActionType", - ActionTextResourceName = "PersonDetachedFile", - ProductResourceName = "CrmProduct", - ModuleResourceName = "PersonsModule" - } - }, - { - MessageAction.PersonsMerged, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "PersonsMerged", - ProductResourceName = "CrmProduct", - ModuleResourceName = "PersonsModule" - } - }, - { - MessageAction.PersonDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "PersonDeleted", - ProductResourceName = "CrmProduct", - ModuleResourceName = "PersonsModule" - } - }, + }, - #endregion + }, new Dictionary() + { + { ActionType.Attach, MessageAction.PersonAttachedFiles }, + { ActionType.Detach, MessageAction.PersonDetachedFile }, + } + }, + { + EntryType.Relationship, new Dictionary() + { + { ActionType.Create, MessageAction.PersonCreatedHistoryEvent }, + { ActionType.Delete, MessageAction.PersonDeletedHistoryEvent }, + } + }, + { + EntryType.Project, new Dictionary() + { + { ActionType.Link, MessageAction.PersonLinkedProject }, + { ActionType.Unlink, MessageAction.PersonUnlinkedProject }, + } + }, + { + EntryType.Contact, EntryType.Contact, new Dictionary() + { + { ActionType.Update, MessageAction.PersonsMerged } + } + } + }; + } + } - #region contacts - - { - MessageAction.ContactsDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "ContactsDeleted", - ProductResourceName = "CrmProduct", - ModuleResourceName = "ContactsModule" - } - }, - { - MessageAction.CrmSmtpMailSent, new MessageMaps - { - ActionTypeTextResourceName = "SendActionType", - ActionTextResourceName = "CrmSmtpMailSent", - ProductResourceName = "CrmProduct", - ModuleResourceName = "ContactsModule" - } - }, + public class ContactsActionMapper : IModuleActionMapper + { + public ModuleType Module { get; } + public IDictionary Actions { get; } - #endregion + public ContactsActionMapper() + { + Module = ModuleType.Contacts; - #region tasks + Actions = new MessageMapsDictionary(ProductType.CRM, Module) + { + { + EntryType.Contact, new Dictionary() + { + { ActionType.Delete, MessageAction.ContactsDeleted }, + { ActionType.Send, MessageAction.CrmSmtpMailSent } + } + }, + { MessageAction.ContactsExportedToCsv, ActionType.Export }, + { MessageAction.ContactsImportedFromCSV, ActionType.Import } + }; + } + } - { - MessageAction.CrmTaskCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "CrmTaskCreated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CrmTasksModule" - } - }, - { - MessageAction.ContactsCreatedCrmTasks, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "ContactsCreatedCrmTasks", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CrmTasksModule" - } - }, - { - MessageAction.CrmTaskUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "CrmTaskUpdated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CrmTasksModule" - } - }, - { - MessageAction.CrmTaskOpened, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "CrmTaskOpened", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CrmTasksModule" - } - }, - { - MessageAction.CrmTaskClosed, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "CrmTaskClosed", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CrmTasksModule" - } - }, - { - MessageAction.CrmTaskDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "CrmTaskDeleted", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CrmTasksModule" - } - }, + public class CrmTasksActionMapper : IModuleActionMapper + { + public ModuleType Module { get; } + public IDictionary Actions { get; } - #endregion + public CrmTasksActionMapper() + { + Module = ModuleType.CrmTasks; - #region opportunities + Actions = new MessageMapsDictionary(ProductType.CRM, Module) + { + { + EntryType.CRMTask, new Dictionary() + { + { + ActionType.Create, new[] + { + MessageAction.CrmTaskCreated, MessageAction.ContactsCreatedCrmTasks + } + }, + { + ActionType.Update, new[] + { + MessageAction.CrmTaskUpdated, MessageAction.CrmTaskOpened, MessageAction.CrmTaskClosed + } + } + }, + new Dictionary() + { + { ActionType.Delete, MessageAction.CrmTaskDeleted } + } + }, + { MessageAction.CrmTasksImportedFromCSV, ActionType.Import }, + { MessageAction.CrmTasksExportedToCsv, ActionType.Export } + }; + } + } - { - MessageAction.OpportunityCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "OpportunityCreated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OpportunitiesModule" - } - }, - { - MessageAction.OpportunityUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "OpportunityUpdated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OpportunitiesModule" - } - }, - { - MessageAction.OpportunityUpdatedStage, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "OpportunityUpdatedStage", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OpportunitiesModule" - } - }, - { - MessageAction.OpportunityCreatedTag, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "OpportunityCreatedTag", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OpportunitiesModule" - } - }, - { - MessageAction.OpportunityDeletedTag, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "OpportunityDeletedTag", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OpportunitiesModule" - } - }, - { - MessageAction.OpportunityCreatedHistoryEvent, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "OpportunityCreatedHistoryEvent", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OpportunitiesModule" - } - }, - { - MessageAction.OpportunityDeletedHistoryEvent, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "OpportunityDeletedHistoryEvent", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OpportunitiesModule" - } - }, - { - MessageAction.OpportunityLinkedCompany, new MessageMaps - { - ActionTypeTextResourceName = "LinkActionType", - ActionTextResourceName = "OpportunityLinkedCompany", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OpportunitiesModule" - } - }, - { - MessageAction.OpportunityUnlinkedCompany, new MessageMaps - { - ActionTypeTextResourceName = "UnlinkActionType", - ActionTextResourceName = "OpportunityUnlinkedCompany", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OpportunitiesModule" - } - }, - { - MessageAction.OpportunityLinkedPerson, new MessageMaps - { - ActionTypeTextResourceName = "LinkActionType", - ActionTextResourceName = "OpportunityLinkedPerson", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OpportunitiesModule" - } - }, - { - MessageAction.OpportunityUnlinkedPerson, new MessageMaps - { - ActionTypeTextResourceName = "UnlinkActionType", - ActionTextResourceName = "OpportunityUnlinkedPerson", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OpportunitiesModule" - } - }, - { - MessageAction.OpportunityAttachedFiles, new MessageMaps - { - ActionTypeTextResourceName = "AttachActionType", - ActionTextResourceName = "OpportunityAttachedFiles", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OpportunitiesModule" - } - }, - { - MessageAction.OpportunityDetachedFile, new MessageMaps - { - ActionTypeTextResourceName = "DetachActionType", - ActionTextResourceName = "OpportunityDetachedFile", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OpportunitiesModule" - } - }, - { - MessageAction.OpportunityOpenedAccess, new MessageMaps - { - ActionTypeTextResourceName = "UpdateAccessActionType", - ActionTextResourceName = "OpportunityOpenedAccess", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OpportunitiesModule" - } - }, - { - MessageAction.OpportunityRestrictedAccess, new MessageMaps - { - ActionTypeTextResourceName = "UpdateAccessActionType", - ActionTextResourceName = "OpportunityRestrictedAccess", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OpportunitiesModule" - } - }, - { - MessageAction.OpportunityDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "OpportunityDeleted", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OpportunitiesModule" - } - }, - { - MessageAction.OpportunitiesDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "OpportunitiesDeleted", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OpportunitiesModule" - } - }, + public class OpportunitiesActionMapper : IModuleActionMapper + { + public ModuleType Module { get; } + public IDictionary Actions { get; } - #endregion + public OpportunitiesActionMapper() + { + Module = ModuleType.Opportunities; - #region invoices + Actions = new MessageMapsDictionary(ProductType.CRM, Module) + { + { + EntryType.Opportunity, new Dictionary() + { + { ActionType.Create, new[] { MessageAction.OpportunityCreated, MessageAction.OpportunityCreatedTag } }, + { ActionType.Update, new[] { MessageAction.OpportunityUpdated, MessageAction.OpportunityUpdatedStage } }, + { ActionType.Delete, new[] { MessageAction.OpportunityDeleted, MessageAction.OpportunitiesDeleted, MessageAction.OpportunityDeletedTag } }, + { ActionType.Link, new[] { MessageAction.OpportunityLinkedCompany, MessageAction.OpportunityLinkedPerson } }, + { ActionType.Unlink, new[] { MessageAction.OpportunityUnlinkedCompany, MessageAction.OpportunityUnlinkedPerson } }, + { ActionType.UpdateAccess, new[] { MessageAction.OpportunityOpenedAccess, MessageAction.OpportunityRestrictedAccess } } + }, + new Dictionary() + { + { ActionType.Attach, MessageAction.OpportunityAttachedFiles }, + { ActionType.Detach, MessageAction.OpportunityDetachedFile } + } + }, + { + EntryType.Relationship, new Dictionary + { + { ActionType.Create, MessageAction.OpportunityCreatedHistoryEvent }, + { ActionType.Delete, MessageAction.OpportunityDeletedHistoryEvent } + } + }, + { MessageAction.OpportunitiesImportedFromCSV, ActionType.Import }, + { MessageAction.OpportunitiesExportedToCsv, ActionType.Export } + }; + } + } - { - MessageAction.InvoiceCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "InvoiceCreated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "InvoicesModule" - } - }, - { - MessageAction.InvoiceUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "InvoiceUpdated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "InvoicesModule" - } - }, - { - MessageAction.InvoicesUpdatedStatus, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "InvoicesUpdatedStatus", - ProductResourceName = "CrmProduct", - ModuleResourceName = "InvoicesModule" - } - }, - { - MessageAction.InvoiceDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "InvoiceDeleted", - ProductResourceName = "CrmProduct", - ModuleResourceName = "InvoicesModule" - } - }, - { - MessageAction.InvoicesDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "InvoicesDeleted", - ProductResourceName = "CrmProduct", - ModuleResourceName = "InvoicesModule" - } - }, + public class InvoicesActionMapper : IModuleActionMapper + { + public ModuleType Module { get; } + public IDictionary Actions { get; } - { - MessageAction.CurrencyRateUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "CurrencyRateUpdated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "InvoicesModule" - } - }, - { - MessageAction.InvoiceDefaultTermsUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "InvoiceDefaultTermsUpdated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "InvoicesModule" - } - }, - { - MessageAction.InvoiceDownloaded, new MessageMaps - { - ActionTypeTextResourceName = "DownloadActionType", - ActionTextResourceName = "InvoiceDownloaded", - ProductResourceName = "CrmProduct", - ModuleResourceName = "InvoicesModule" - } - }, + public InvoicesActionMapper() + { + Module = ModuleType.Invoices; - #endregion + Actions = new MessageMapsDictionary(ProductType.CRM, Module) + { + { + EntryType.Invoice, new Dictionary + { + { ActionType.Update, new [] { MessageAction.InvoiceUpdated, MessageAction.InvoicesUpdatedStatus } }, + { ActionType.Delete, new [] { MessageAction.InvoiceDeleted, MessageAction.InvoicesDeleted } } + }, + new Dictionary + { + { ActionType.Create, MessageAction.InvoiceCreated }, + { ActionType.Download, MessageAction.InvoiceDownloaded } + } + }, + { MessageAction.CurrencyRateUpdated, ActionType.Update }, + { MessageAction.InvoiceDefaultTermsUpdated, ActionType.Update }, + }; + } + } - #region cases + public class CasesActionMapper : IModuleActionMapper + { + public ModuleType Module { get; } + public IDictionary Actions { get; } - { - MessageAction.CaseCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "CaseCreated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CasesModule" - } - }, - { - MessageAction.CaseUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "CaseUpdated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CasesModule" - } - }, - { - MessageAction.CaseOpened, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "CaseOpened", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CasesModule" - } - }, - { - MessageAction.CaseClosed, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "CaseClosed", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CasesModule" - } - }, - { - MessageAction.CaseCreatedTag, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "CaseCreatedTag", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CasesModule" - } - }, - { - MessageAction.CaseDeletedTag, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "CaseDeletedTag", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CasesModule" - } - }, - { - MessageAction.CaseCreatedHistoryEvent, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "CaseCreatedHistoryEvent", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CasesModule" - } - }, - { - MessageAction.CaseDeletedHistoryEvent, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "CaseDeletedHistoryEvent", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CasesModule" - } - }, - { - MessageAction.CaseLinkedCompany, new MessageMaps - { - ActionTypeTextResourceName = "LinkActionType", - ActionTextResourceName = "CaseLinkedCompany", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CasesModule" - } - }, - { - MessageAction.CaseUnlinkedCompany, new MessageMaps - { - ActionTypeTextResourceName = "UnlinkActionType", - ActionTextResourceName = "CaseUnlinkedCompany", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CasesModule" - } - }, - { - MessageAction.CaseLinkedPerson, new MessageMaps - { - ActionTypeTextResourceName = "LinkActionType", - ActionTextResourceName = "CaseLinkedPerson", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CasesModule" - } - }, - { - MessageAction.CaseUnlinkedPerson, new MessageMaps - { - ActionTypeTextResourceName = "UnlinkActionType", - ActionTextResourceName = "CaseUnlinkedPerson", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CasesModule" - } - }, - { - MessageAction.CaseAttachedFiles, new MessageMaps - { - ActionTypeTextResourceName = "AttachActionType", - ActionTextResourceName = "CaseAttachedFiles", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CasesModule" - } - }, - { - MessageAction.CaseDetachedFile, new MessageMaps - { - ActionTypeTextResourceName = "DetachActionType", - ActionTextResourceName = "CaseDetachedFile", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CasesModule" - } - }, - { - MessageAction.CaseOpenedAccess, new MessageMaps - { - ActionTypeTextResourceName = "UpdateAccessActionType", - ActionTextResourceName = "CaseOpenedAccess", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CasesModule" - } - }, - { - MessageAction.CaseRestrictedAccess, new MessageMaps - { - ActionTypeTextResourceName = "UpdateAccessActionType", - ActionTextResourceName = "CaseRestrictedAccess", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CasesModule" - } - }, - { - MessageAction.CaseDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "CaseDeleted", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CasesModule" - } - }, - { - MessageAction.CasesDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "CasesDeleted", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CasesModule" - } - }, + public CasesActionMapper() + { + Module = ModuleType.Cases; - #endregion + Actions = new MessageMapsDictionary(ProductType.CRM, Module) + { + { + EntryType.Case, new Dictionary + { + { ActionType.Create, new [] { MessageAction.CaseCreated, MessageAction.CaseCreatedTag } }, + { ActionType.Update, new [] { MessageAction.CaseUpdated, MessageAction.CaseOpened, MessageAction.CaseClosed } }, + { ActionType.Delete, new [] { MessageAction.CaseDeleted, MessageAction.CasesDeleted, MessageAction.CaseDeletedTag } }, + { ActionType.Link, new [] { MessageAction.CaseLinkedCompany, MessageAction.CaseLinkedPerson } }, + { ActionType.Unlink, new [] { MessageAction.CaseUnlinkedCompany, MessageAction.CaseUnlinkedPerson } }, + { ActionType.UpdateAccess, new [] { MessageAction.CaseOpenedAccess, MessageAction.CaseRestrictedAccess } }, + }, + new Dictionary + { + { ActionType.Attach, MessageAction.CaseAttachedFiles }, + { ActionType.Detach, MessageAction.CaseDetachedFile } + } + }, + { + EntryType.Relationship, new Dictionary + { + { ActionType.Create, MessageAction.CaseCreatedHistoryEvent }, + { ActionType.Delete, MessageAction.CaseDeletedHistoryEvent }, + } + }, + { MessageAction.CasesImportedFromCSV, ActionType.Import }, + { MessageAction.CasesExportedToCsv, ActionType.Export }, + }; + } + } - #region common settings + public class CommonCrmSettingsActionMapper : IModuleActionMapper + { + public ModuleType Module { get; } + public IDictionary Actions { get; } - { - MessageAction.CrmSmtpSettingsUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "CrmSmtpSettingsUpdated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CommonCrmSettingsModule" - } - }, - { - MessageAction.CrmTestMailSent, new MessageMaps - { - ActionTypeTextResourceName = "SendActionType", - ActionTextResourceName = "CrmTestMailSent", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CommonCrmSettingsModule" - } - }, - { - MessageAction.CrmDefaultCurrencyUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "CrmDefaultCurrencyUpdated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CommonCrmSettingsModule" - } - }, - { - MessageAction.CrmAllDataExported, new MessageMaps - { - ActionTypeTextResourceName = "ExportActionType", - ActionTextResourceName = "CrmAllDataExported", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CommonCrmSettingsModule" - } - }, + public CommonCrmSettingsActionMapper() + { + Module = ModuleType.CommonCrmSettings; - #endregion + Actions = new MessageMapsDictionary(ProductType.CRM, Module) + { + { MessageAction.CrmSmtpSettingsUpdated, ActionType.Update }, + { MessageAction.CrmTestMailSent, ActionType.Send }, + { MessageAction.CrmDefaultCurrencyUpdated, ActionType.Update }, + { MessageAction.CrmAllDataExported, ActionType.Export } + }; + } + } - #region contact settings + public class ContactsSettingsActionMapper : IModuleActionMapper + { + public ModuleType Module { get; } + public IDictionary Actions { get; } - { - MessageAction.ContactTemperatureLevelCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "ContactTemperatureLevelCreated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "ContactsSettingsModule" - } - }, - { - MessageAction.ContactTemperatureLevelUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "ContactTemperatureLevelUpdated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "ContactsSettingsModule" - } - }, - { - MessageAction.ContactTemperatureLevelUpdatedColor, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "ContactTemperatureLevelUpdatedColor", - ProductResourceName = "CrmProduct", - ModuleResourceName = "ContactsSettingsModule" - } - }, - { - MessageAction.ContactTemperatureLevelsUpdatedOrder, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "ContactTemperatureLevelsUpdatedOrder", - ProductResourceName = "CrmProduct", - ModuleResourceName = "ContactsSettingsModule" - } - }, - { - MessageAction.ContactTemperatureLevelDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "ContactTemperatureLevelDeleted", - ProductResourceName = "CrmProduct", - ModuleResourceName = "ContactsSettingsModule" - } - }, - { - MessageAction.ContactTemperatureLevelSettingsUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "ContactTemperatureLevelSettingsUpdated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CommonCrmSettingsModule" - } - }, - { - MessageAction.ContactTypeCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "ContactTypeCreated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "ContactTypesModule" - } - }, - { - MessageAction.ContactTypeUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "ContactTypeUpdated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "ContactTypesModule" - } - }, - { - MessageAction.ContactTypesUpdatedOrder, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "ContactTypesUpdatedOrder", - ProductResourceName = "CrmProduct", - ModuleResourceName = "ContactTypesModule" - } - }, - { - MessageAction.ContactTypeDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "ContactTypeDeleted", - ProductResourceName = "CrmProduct", - ModuleResourceName = "ContactTypesModule" - } - }, + public ContactsSettingsActionMapper() + { + Module = ModuleType.ContactsSettings; - #endregion + Actions = new MessageMapsDictionary(ProductType.CRM, Module) + { + { + EntryType.ListItem, + new Dictionary() + { + { ActionType.Update, new[] + { + MessageAction.ContactTemperatureLevelUpdated, MessageAction.ContactTemperatureLevelUpdatedColor, + MessageAction.ContactTemperatureLevelsUpdatedOrder, MessageAction.ContactTemperatureLevelSettingsUpdated + } + } + }, + new Dictionary() + { + { ActionType.Create, MessageAction.ContactTemperatureLevelCreated }, + { ActionType.Delete, MessageAction.ContactTemperatureLevelDeleted } + } + } + }; + } + } - #region invoice settings + public class ContactTypesActionMapper : IModuleActionMapper + { + public ModuleType Module { get; } + public IDictionary Actions { get; } - { - MessageAction.InvoiceItemCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "InvoiceItemCreated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "InvoiceSettingsModule" - } - }, - { - MessageAction.InvoiceItemUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "InvoiceItemUpdated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "InvoiceSettingsModule" - } - }, - { - MessageAction.InvoiceItemDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "InvoiceItemDeleted", - ProductResourceName = "CrmProduct", - ModuleResourceName = "InvoiceSettingsModule" - } - }, - { - MessageAction.InvoiceItemsDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "InvoiceItemsDeleted", - ProductResourceName = "CrmProduct", - ModuleResourceName = "InvoiceSettingsModule" - } - }, - { - MessageAction.InvoiceTaxCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "InvoiceTaxCreated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "InvoiceSettingsModule" - } - }, - { - MessageAction.InvoiceTaxUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "InvoiceTaxUpdated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "InvoiceSettingsModule" - } - }, - { - MessageAction.InvoiceTaxDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "InvoiceTaxDeleted", - ProductResourceName = "CrmProduct", - ModuleResourceName = "InvoiceSettingsModule" - } - }, - { - MessageAction.OrganizationProfileUpdatedCompanyName, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "OrganizationProfileUpdatedCompanyName", - ProductResourceName = "CrmProduct", - ModuleResourceName = "InvoiceSettingsModule" - } - }, - { - MessageAction.OrganizationProfileUpdatedInvoiceLogo, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "OrganizationProfileUpdatedInvoiceLogo", - ProductResourceName = "CrmProduct", - ModuleResourceName = "InvoiceSettingsModule" - } - }, - { - MessageAction.OrganizationProfileUpdatedAddress, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "OrganizationProfileUpdatedAddress", - ProductResourceName = "CrmProduct", - ModuleResourceName = "InvoiceSettingsModule" - } - }, - { - MessageAction.InvoiceNumberFormatUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "InvoiceNumberFormatUpdated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "InvoiceSettingsModule" - } - }, + public ContactTypesActionMapper() + { + Module = ModuleType.ContactTypes; - #endregion + Actions = new MessageMapsDictionary(ProductType.CRM, Module) + { + { + EntryType.ListItem, new Dictionary() + { + { ActionType.Update, new [] { MessageAction.ContactTypeUpdated, MessageAction.ContactTypesUpdatedOrder } } + }, + new Dictionary() + { + { ActionType.Create, MessageAction.ContactTypeCreated }, + { ActionType.Delete, MessageAction.ContactTypeDeleted }, + } + } + }; + } + } - #region user fields settings + public class InvoiceSettingsActionMapper : IModuleActionMapper + { + public ModuleType Module { get; } + public IDictionary Actions { get; } - { - MessageAction.ContactUserFieldCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "ContactUserFieldCreated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.ContactUserFieldUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "ContactUserFieldUpdated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.ContactUserFieldsUpdatedOrder, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "ContactUserFieldsUpdatedOrder", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.ContactUserFieldDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "ContactUserFieldDeleted", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.CompanyUserFieldCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "CompanyUserFieldCreated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.CompanyUserFieldUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "CompanyUserFieldUpdated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.CompanyUserFieldsUpdatedOrder, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "CompanyUserFieldsUpdatedOrder", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.CompanyUserFieldDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "CompanyUserFieldDeleted", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.PersonUserFieldCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "PersonUserFieldCreated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.PersonUserFieldUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "PersonUserFieldUpdated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.PersonUserFieldsUpdatedOrder, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "PersonUserFieldsUpdatedOrder", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.PersonUserFieldDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "PersonUserFieldDeleted", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.OpportunityUserFieldCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "OpportunityUserFieldCreated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.OpportunityUserFieldUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "OpportunityUserFieldUpdated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.OpportunityUserFieldsUpdatedOrder, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "OpportunityUserFieldsUpdatedOrder", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.OpportunityUserFieldDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "OpportunityUserFieldDeleted", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.CaseUserFieldCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "CaseUserFieldCreated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.CaseUserFieldUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "CaseUserFieldUpdated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.CaseUserFieldsUpdatedOrder, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "CaseUserFieldsUpdatedOrder", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.CaseUserFieldDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "CaseUserFieldDeleted", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, + public InvoiceSettingsActionMapper() + { + Module = ModuleType.InvoiceSettings; - #endregion + Actions = new MessageMapsDictionary(ProductType.CRM, Module) + { + { + EntryType.InvoiceItem, new Dictionary() + { + { ActionType.Delete, new [] { MessageAction.InvoiceItemDeleted, MessageAction.InvoiceItemsDeleted } } + }, new Dictionary() + { + { ActionType.Create, MessageAction.InvoiceItemCreated }, + { ActionType.Update, MessageAction.InvoiceItemUpdated } + } + }, + { + EntryType.InvoiceTax, new Dictionary() + { + { ActionType.Create, MessageAction.InvoiceTaxCreated }, + { ActionType.Update, MessageAction.InvoiceTaxUpdated }, + { ActionType.Delete, MessageAction.InvoiceTaxDeleted } + } + }, + { + ActionType.Update, + new[] + { + MessageAction.OrganizationProfileUpdatedCompanyName, MessageAction.OrganizationProfileUpdatedInvoiceLogo, + MessageAction.OrganizationProfileUpdatedAddress, MessageAction.InvoiceNumberFormatUpdated + } + } + }; + } + } - #region history events categories settings + public class OtherCrmSettingsActionMapper : IModuleActionMapper + { + public ModuleType Module { get; } + public IDictionary Actions { get; } - { - MessageAction.HistoryEventCategoryCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "HistoryEventCategoryCreated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.HistoryEventCategoryUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "HistoryEventCategoryUpdated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.HistoryEventCategoryUpdatedIcon, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "HistoryEventCategoryUpdatedIcon", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.HistoryEventCategoriesUpdatedOrder, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "HistoryEventCategoriesUpdatedOrder", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.HistoryEventCategoryDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "HistoryEventCategoryDeleted", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - - #endregion - - #region task actegories settings + public OtherCrmSettingsActionMapper() + { + Module = ModuleType.OtherCrmSettings; + Actions = new MessageMapsDictionary(ProductType.CRM, Module) + { + { + EntryType.FieldDescription, new Dictionary() { - MessageAction.CrmTaskCategoryCreated, new MessageMaps + { + ActionType.Create, new[] { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "CrmTaskCategoryCreated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" + MessageAction.ContactUserFieldCreated, MessageAction.CompanyUserFieldCreated, + MessageAction.PersonUserFieldCreated, MessageAction.OpportunityUserFieldCreated, + MessageAction.CaseUserFieldCreated } - }, + }, + { + ActionType.Update, new[] + { + MessageAction.ContactUserFieldUpdated, MessageAction.ContactUserFieldsUpdatedOrder, + MessageAction.CompanyUserFieldUpdated, MessageAction.CompanyUserFieldsUpdatedOrder, + MessageAction.PersonUserFieldUpdated, MessageAction.PersonUserFieldsUpdatedOrder, + MessageAction.OpportunityUserFieldUpdated, MessageAction.OpportunityUserFieldsUpdatedOrder, + MessageAction.CaseUserFieldUpdated, MessageAction.CaseUserFieldsUpdatedOrder + } + }, + { + ActionType.Delete, new[] + { + MessageAction.ContactUserFieldDeleted, MessageAction.CompanyUserFieldDeleted, + MessageAction.PersonUserFieldDeleted, MessageAction.OpportunityUserFieldDeleted, + MessageAction.CaseUserFieldDeleted + } + }, + } + }, + { + EntryType.ListItem, new Dictionary() { - MessageAction.CrmTaskCategoryUpdated, new MessageMaps + { ActionType.Create, new[] { MessageAction.HistoryEventCategoryCreated, MessageAction.CrmTaskCategoryCreated } }, + { ActionType.Update, new[] { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "CrmTaskCategoryUpdated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" + MessageAction.HistoryEventCategoryUpdated, MessageAction.HistoryEventCategoryUpdatedIcon, MessageAction.HistoryEventCategoriesUpdatedOrder, + MessageAction.CrmTaskCategoryUpdated, MessageAction.CrmTaskCategoryUpdatedIcon, MessageAction.CrmTaskCategoriesUpdatedOrder } - }, + }, + { ActionType.Delete, new[] { MessageAction.HistoryEventCategoryDeleted, MessageAction.CrmTaskCategoryDeleted } } + } + }, + { + EntryType.OpportunityMilestone, new Dictionary() { - MessageAction.CrmTaskCategoryUpdatedIcon, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "CrmTaskCategoryUpdatedIcon", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } + { ActionType.Update, new[] { MessageAction.OpportunityStageUpdated, MessageAction.OpportunityStageUpdatedColor, MessageAction.OpportunityStagesUpdatedOrder } } }, + new Dictionary() { - MessageAction.CrmTaskCategoriesUpdatedOrder, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "CrmTaskCategoriesUpdatedOrder", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, + { ActionType.Create, MessageAction.OpportunityStageCreated }, + { ActionType.Delete, MessageAction.OpportunityStageDeleted }, + } + }, + { + ActionType.Create, new[] { - MessageAction.CrmTaskCategoryDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "CrmTaskCategoryDeleted", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - - #endregion - - #region opportunity stages settings - + MessageAction.ContactsCreatedTag, MessageAction.OpportunitiesCreatedTag, MessageAction.CasesCreatedTag + } + }, + { + ActionType.Update, new[] { - MessageAction.OpportunityStageCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "OpportunityStageCreated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, + MessageAction.ContactsTagSettingsUpdated, MessageAction.WebsiteContactFormUpdatedKey + } + }, + { + ActionType.Delete, new[] { - MessageAction.OpportunityStageUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "OpportunityStageUpdated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.OpportunityStageUpdatedColor, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "OpportunityStageUpdatedColor", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.OpportunityStagesUpdatedOrder, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "OpportunityStagesUpdatedOrder", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.OpportunityStageDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "OpportunityStageDeleted", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - - #endregion - - #region tags settings - - { - MessageAction.ContactsCreatedTag, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "ContactsCreatedTag", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.ContactsDeletedTag, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "ContactsDeletedTag", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.OpportunitiesCreatedTag, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "OpportunitiesCreatedTag", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.OpportunitiesDeletedTag, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "OpportunitiesDeletedTag", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.CasesCreatedTag, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "CasesCreatedTag", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.CasesDeletedTag, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "CasesDeletedTag", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - { - MessageAction.ContactsTagSettingsUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "ContactsTagSettingsUpdated", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CommonCrmSettingsModule" - } - }, - - #endregion - - #region other settings - - { - MessageAction.WebsiteContactFormUpdatedKey, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "WebsiteContactFormUpdatedKey", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OtherCrmSettingsModule" - } - }, - - #endregion - - #region import - - { - MessageAction.ContactsImportedFromCSV, new MessageMaps - { - ActionTypeTextResourceName = "ImportActionType", - ActionTextResourceName = "ContactsImportedFromCSV", - ProductResourceName = "CrmProduct", - ModuleResourceName = "ContactsModule" - } - }, - { - MessageAction.CrmTasksImportedFromCSV, new MessageMaps - { - ActionTypeTextResourceName = "ImportActionType", - ActionTextResourceName = "CrmTasksImportedFromCSV", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CrmTasksModule" - } - }, - { - MessageAction.OpportunitiesImportedFromCSV, new MessageMaps - { - ActionTypeTextResourceName = "ImportActionType", - ActionTextResourceName = "OpportunitiesImportedFromCSV", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OpportunitiesModule" - } - }, - { - MessageAction.CasesImportedFromCSV, new MessageMaps - { - ActionTypeTextResourceName = "ImportActionType", - ActionTextResourceName = "CasesImportedFromCSV", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CasesModule" - } - }, - - #endregion - - #region export - - { - MessageAction.ContactsExportedToCsv, new MessageMaps - { - ActionTypeTextResourceName = "ExportActionType", - ActionTextResourceName = "ContactsExportedToCsv", - ProductResourceName = "CrmProduct", - ModuleResourceName = "ContactsModule" - } - }, - { - MessageAction.CrmTasksExportedToCsv, new MessageMaps - { - ActionTypeTextResourceName = "ExportActionType", - ActionTextResourceName = "CrmTasksExportedToCsv", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CrmTasksModule" - } - }, - { - MessageAction.OpportunitiesExportedToCsv, new MessageMaps - { - ActionTypeTextResourceName = "ExportActionType", - ActionTextResourceName = "OpportunitiesExportedToCsv", - ProductResourceName = "CrmProduct", - ModuleResourceName = "OpportunitiesModule" - } - }, - { - MessageAction.CasesExportedToCsv, new MessageMaps - { - ActionTypeTextResourceName = "ExportActionType", - ActionTextResourceName = "CasesExportedToCsv", - ProductResourceName = "CrmProduct", - ModuleResourceName = "CasesModule" - } - }, - - #endregion - }; + MessageAction.ContactsDeletedTag, MessageAction.OpportunitiesDeletedTag, MessageAction.CasesDeletedTag + } + }, + }; } } } \ No newline at end of file diff --git a/module/ASC.AuditTrail/Mappers/DocumentsActionMapper.cs b/module/ASC.AuditTrail/Mappers/DocumentsActionMapper.cs index 335f546a2..47b77f679 100644 --- a/module/ASC.AuditTrail/Mappers/DocumentsActionMapper.cs +++ b/module/ASC.AuditTrail/Mappers/DocumentsActionMapper.cs @@ -17,404 +17,137 @@ using System.Collections.Generic; +using ASC.AuditTrail.Types; using ASC.MessagingSystem; namespace ASC.AuditTrail.Mappers { - internal class DocumentsActionMapper + public class DocumentsActionMapper : IProductActionMapper { - public static Dictionary GetMaps() + public List Mappers { get; } + public ProductType Product { get; } + public DocumentsActionMapper() { - return new Dictionary + Product = ProductType.Documents; + + Mappers = new List() + { + new FilesActionMapper(), + new FoldersActionMapper(), + new SettingsActionMapper() + }; + } + } + public class FilesActionMapper : IModuleActionMapper + { + public ModuleType Module { get; } + public IDictionary Actions { get; } + + public FilesActionMapper() + { + Module = ModuleType.Files; + Actions = new MessageMapsDictionary(ProductType.Documents, Module) + { { + EntryType.File, new Dictionary() { - MessageAction.FileCreated, new MessageMaps + { ActionType.Create, new[] { MessageAction.FileCreated, MessageAction.FileCreatedVersion, MessageAction.FileRestoreVersion, MessageAction.FileConverted } }, + { + ActionType.Update, new[] { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "FileCreated", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FilesModule" + MessageAction.FileRenamed, MessageAction.FileUpdated, MessageAction.UserFileUpdated, MessageAction.FileUpdatedRevisionComment, + MessageAction.FileLocked, MessageAction.FileUnlocked, MessageAction.FileOpenedForChange, MessageAction.FileMarkedAsFavorite, + MessageAction.FileRemovedFromFavorite, MessageAction.FileMarkedAsRead, MessageAction.FileReaded } + }, + { ActionType.Delete, new[] { MessageAction.FileDeletedVersion, MessageAction.FileDeleted, MessageAction.TrashEmptied } }, + { ActionType.UpdateAccess, new[] { MessageAction.FileUpdatedAccess, MessageAction.FileUpdatedAccessFor, MessageAction.FileRemovedFromList, MessageAction.FileExternalLinkAccessUpdated } }, + { ActionType.Download, new[] { MessageAction.FileDownloaded, MessageAction.FileDownloadedAs, MessageAction.FileRevisionDownloaded } }, + { ActionType.Send, new[] { MessageAction.FileSendAccessLink, MessageAction.FileChangeOwner } }, }, + new Dictionary() { - MessageAction.FileRenamed, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "FileRenamed", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FilesModule" - } - }, + { ActionType.Upload, MessageAction.FileUploaded }, + { ActionType.Import, MessageAction.FileImported }, + { ActionType.Move, MessageAction.FileMovedToTrash } + } + }, + { + EntryType.File, EntryType.Folder, new Dictionary() { - MessageAction.FileUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "FileUpdated", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FilesModule" - } - }, + { ActionType.Copy, new[] { MessageAction.FileCopied, MessageAction.FileCopiedWithOverwriting } }, + { ActionType.Move, new[] { MessageAction.FileMoved, MessageAction.FileMovedWithOverwriting } }, + } + }, + }; + + Actions.Add(MessageAction.DocumentSignComplete, new MessageMaps("FilesDocumentSigned", ActionType.Send, ProductType.Documents, Module, EntryType.File)); + Actions.Add(MessageAction.DocumentSendToSign, new MessageMaps("FilesRequestSign", ActionType.Send, ProductType.Documents, Module, EntryType.File)); + } + } + + public class FoldersActionMapper : IModuleActionMapper + { + public ModuleType Module { get; } + public IDictionary Actions { get; } + + public FoldersActionMapper() + { + Module = ModuleType.Folders; + Actions = new MessageMapsDictionary(ProductType.Documents, Module) + { + { + EntryType.Folder, new Dictionary() { - MessageAction.UserFileUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "UserFileUpdated", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FilesModule" - } + { ActionType.Update, new[] { MessageAction.FolderRenamed, MessageAction.FolderMarkedAsRead } }, + { ActionType.UpdateAccess, new[] { MessageAction.FolderUpdatedAccess, MessageAction.FolderUpdatedAccessFor, MessageAction.FolderRemovedFromList } } }, + new Dictionary() { - MessageAction.FileCreatedVersion, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "FileCreatedVersion", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FilesModule" - } - }, + { ActionType.Create, MessageAction.FolderCreated }, + { ActionType.Move, MessageAction.FolderMovedToTrash }, + { ActionType.Delete, MessageAction.FolderDeleted }, + { ActionType.Download, MessageAction.FolderDownloaded }, + } + }, + { + EntryType.Folder, EntryType.Folder, new Dictionary() { - MessageAction.FileDeletedVersion, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "FileDeletedVersion", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FilesModule" - } - }, + { ActionType.Copy, new[] { MessageAction.FolderCopied, MessageAction.FolderCopiedWithOverwriting } }, + { ActionType.Move, new[] { MessageAction.FolderMoved, MessageAction.FolderMovedFrom, MessageAction.FolderMovedWithOverwriting } }, + } + }, + }; + } + } + + public class SettingsActionMapper : IModuleActionMapper + { + public ModuleType Module { get; } + public IDictionary Actions { get; } + + public SettingsActionMapper() + { + Module = ModuleType.DocumentsSettings; + Actions = new MessageMapsDictionary(ProductType.Documents, Module) + { + { + EntryType.Folder, new Dictionary() { - MessageAction.FileRestoreVersion, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "FileRestoreVersion", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FilesModule" - } - }, + { ActionType.Create, MessageAction.ThirdPartyCreated }, + { ActionType.Update, MessageAction.ThirdPartyUpdated }, + { ActionType.Delete, MessageAction.ThirdPartyDeleted }, + } + }, + { + ActionType.Update, new [] { - MessageAction.FileUpdatedRevisionComment, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "FileUpdatedRevisionComment", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FilesModule" - } - }, - { - MessageAction.FileLocked, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "FileLocked", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FilesModule" - } - }, - { - MessageAction.FileUnlocked, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "FileUnlocked", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FilesModule" - } - }, - { - MessageAction.FileUpdatedAccess, new MessageMaps - { - ActionTypeTextResourceName = "UpdateAccessActionType", - ActionTextResourceName = "FileUpdatedAccess", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FilesModule" - } - }, - { - MessageAction.FileDownloaded, new MessageMaps - { - ActionTypeTextResourceName = "DownloadActionType", - ActionTextResourceName = "FileDownloaded", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FilesModule" - } - }, - { - MessageAction.FileDownloadedAs, new MessageMaps - { - ActionTypeTextResourceName = "DownloadActionType", - ActionTextResourceName = "FileDownloadedAs", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FilesModule" - } - }, - { - MessageAction.FileUploaded, new MessageMaps - { - ActionTypeTextResourceName = "UploadActionType", - ActionTextResourceName = "FileUploaded", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FilesModule" - } - }, - { - MessageAction.FileImported, new MessageMaps - { - ActionTypeTextResourceName = "ImportActionType", - ActionTextResourceName = "FileImported", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FilesModule" - } - }, - { - MessageAction.FileCopied, new MessageMaps - { - ActionTypeTextResourceName = "CopyActionType", - ActionTextResourceName = "FileCopied", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FilesModule" - } - }, - { - MessageAction.FileCopiedWithOverwriting, new MessageMaps - { - ActionTypeTextResourceName = "CopyActionType", - ActionTextResourceName = "FileCopiedWithOverwriting", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FilesModule" - } - }, - { - MessageAction.FileMoved, new MessageMaps - { - ActionTypeTextResourceName = "MoveActionType", - ActionTextResourceName = "FileMoved", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FilesModule" - } - }, - { - MessageAction.FileMovedWithOverwriting, new MessageMaps - { - ActionTypeTextResourceName = "MoveActionType", - ActionTextResourceName = "FileMovedWithOverwriting", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FilesModule" - } - }, - { - MessageAction.FileMovedToTrash, new MessageMaps - { - ActionTypeTextResourceName = "MoveActionType", - ActionTextResourceName = "FileMovedToTrash", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FilesModule" - } - }, - { - MessageAction.FileDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "FileDeleted", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FilesModule" - } - }, - { - MessageAction.FolderCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "FolderCreated", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FoldersModule" - } - }, - { - MessageAction.FolderRenamed, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "FolderRenamed", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FoldersModule" - } - }, - { - MessageAction.FolderUpdatedAccess, new MessageMaps - { - ActionTypeTextResourceName = "UpdateAccessActionType", - ActionTextResourceName = "FolderUpdatedAccess", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FoldersModule" - } - }, - { - MessageAction.FolderCopied, new MessageMaps - { - ActionTypeTextResourceName = "CopyActionType", - ActionTextResourceName = "FolderCopied", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FoldersModule" - } - }, - { - MessageAction.FolderCopiedWithOverwriting, new MessageMaps - { - ActionTypeTextResourceName = "CopyActionType", - ActionTextResourceName = "FolderCopiedWithOverwriting", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FoldersModule" - } - }, - { - MessageAction.FolderMoved, new MessageMaps - { - ActionTypeTextResourceName = "MoveActionType", - ActionTextResourceName = "FolderMoved", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FoldersModule" - } - }, - { - MessageAction.FolderMovedWithOverwriting, new MessageMaps - { - ActionTypeTextResourceName = "MoveActionType", - ActionTextResourceName = "FolderMovedWithOverwriting", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FoldersModule" - } - }, - { - MessageAction.FolderMovedToTrash, new MessageMaps - { - ActionTypeTextResourceName = "MoveActionType", - ActionTextResourceName = "FolderMovedToTrash", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FoldersModule" - } - }, - { - MessageAction.FolderDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "FolderDeleted", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FoldersModule" - } - }, - { - MessageAction.ThirdPartyCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "ThirdPartyCreated", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "DocumentsSettingsModule" - } - }, - { - MessageAction.ThirdPartyUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "ThirdPartyUpdated", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "DocumentsSettingsModule" - } - }, - { - MessageAction.ThirdPartyDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "ThirdPartyDeleted", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "DocumentsSettingsModule" - } - }, - { - MessageAction.DocumentsThirdPartySettingsUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "DocumentsThirdPartySettingsUpdated", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "DocumentsSettingsModule" - } - }, - { - MessageAction.DocumentsOverwritingSettingsUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "DocumentsOverwritingSettingsUpdated", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "DocumentsSettingsModule" - } - }, - { - MessageAction.DocumentsForcesave, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "DocumentsForcesave", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "DocumentsSettingsModule" - } - }, - { - MessageAction.DocumentsStoreForcesave, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "DocumentsStoreForcesave", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "DocumentsSettingsModule" - } - }, - { - MessageAction.DocumentsUploadingFormatsSettingsUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "DocumentsUploadingFormatsSettingsUpdated", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "DocumentsSettingsModule" - } - }, - { - MessageAction.FileConverted, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "FileConverted", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FilesModule" - } - }, - { - MessageAction.FileSendAccessLink, new MessageMaps - { - ActionTypeTextResourceName = "SendActionType", - ActionTextResourceName = "FileSendAccessLink", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FilesModule" - } - }, - { - MessageAction.FileChangeOwner, new MessageMaps - { - ActionTypeTextResourceName = "SendActionType", - ActionTextResourceName = "FileChangeOwner", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FilesModule" - } - }, - { - MessageAction.DocumentSignComplete, new MessageMaps - { - ActionTypeTextResourceName = "SendActionType", - ActionTextResourceName = "FilesDocumentSigned", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FilesModule" - } - }, - { - MessageAction.DocumentSendToSign, new MessageMaps - { - ActionTypeTextResourceName = "SendActionType", - ActionTextResourceName = "FilesRequestSign", - ProductResourceName = "DocumentsProduct", - ModuleResourceName = "FilesModule" - } - }, - }; + MessageAction.DocumentsThirdPartySettingsUpdated, MessageAction.DocumentsOverwritingSettingsUpdated, + MessageAction.DocumentsForcesave, MessageAction.DocumentsStoreForcesave, MessageAction.DocumentsUploadingFormatsSettingsUpdated, + MessageAction.DocumentsExternalShareSettingsUpdated + } + }, + }; } } } \ No newline at end of file diff --git a/module/ASC.AuditTrail/Mappers/IModuleActionMapper.cs b/module/ASC.AuditTrail/Mappers/IModuleActionMapper.cs new file mode 100644 index 000000000..15bced03b --- /dev/null +++ b/module/ASC.AuditTrail/Mappers/IModuleActionMapper.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +using ASC.AuditTrail.Types; +using ASC.MessagingSystem; + +namespace ASC.AuditTrail.Mappers +{ + public interface IModuleActionMapper + { + ModuleType Module { get; } + IDictionary Actions { get; } + } +} diff --git a/module/ASC.AuditTrail/Mappers/IProductActionMapper.cs b/module/ASC.AuditTrail/Mappers/IProductActionMapper.cs new file mode 100644 index 000000000..55c89628a --- /dev/null +++ b/module/ASC.AuditTrail/Mappers/IProductActionMapper.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; + +using ASC.AuditTrail.Types; + +namespace ASC.AuditTrail.Mappers +{ + public interface IProductActionMapper + { + ProductType Product { get; } + List Mappers { get; } + } +} diff --git a/module/ASC.AuditTrail/Mappers/LoginActionMapper.cs b/module/ASC.AuditTrail/Mappers/LoginActionMapper.cs index e6a99485c..cc8b262f0 100644 --- a/module/ASC.AuditTrail/Mappers/LoginActionMapper.cs +++ b/module/ASC.AuditTrail/Mappers/LoginActionMapper.cs @@ -17,44 +17,50 @@ using System.Collections.Generic; +using ASC.AuditTrail.Types; using ASC.MessagingSystem; namespace ASC.AuditTrail.Mappers { - internal class LoginActionsMapper + public class LoginActionsMapper : IProductActionMapper { - public static Dictionary GetMaps() + public ProductType Product { get; } + public List Mappers { get; } + + public LoginActionsMapper() { - return new Dictionary - { - {MessageAction.LoginSuccess, new MessageMaps {ActionTextResourceName = "LoginSuccess"}}, - {MessageAction.LoginSuccessViaSocialAccount, new MessageMaps {ActionTextResourceName = "LoginSuccessSocialAccount"}}, - {MessageAction.LoginSuccessViaSocialApp, new MessageMaps {ActionTextResourceName = "LoginSuccessSocialApp"}}, - {MessageAction.LoginSuccessViaSms, new MessageMaps {ActionTextResourceName = "LoginSuccessViaSms"}}, - {MessageAction.LoginSuccessViaApi, new MessageMaps {ActionTextResourceName = "LoginSuccessViaApi"}}, - {MessageAction.LoginSuccessViaApiSms, new MessageMaps {ActionTextResourceName = "LoginSuccessViaApiSms"}}, - {MessageAction.LoginSuccessViaApiTfa, new MessageMaps {ActionTextResourceName = "LoginSuccessViaApiTfa"}}, - {MessageAction.LoginSuccessViaApiSocialAccount, new MessageMaps {ActionTextResourceName = "LoginSuccessViaSocialAccount"}}, - {MessageAction.LoginSuccessViaSSO, new MessageMaps {ActionTextResourceName = "LoginSuccessViaSSO"}}, - {MessageAction.LoginSuccesViaTfaApp, new MessageMaps {ActionTextResourceName = "LoginSuccesViaTfaApp"}}, - {MessageAction.LoginFailInvalidCombination, new MessageMaps {ActionTextResourceName = "LoginFailInvalidCombination"}}, - {MessageAction.LoginFailSocialAccountNotFound, new MessageMaps {ActionTextResourceName = "LoginFailSocialAccountNotFound"}}, - {MessageAction.LoginFailDisabledProfile, new MessageMaps {ActionTextResourceName = "LoginFailDisabledProfile"}}, - {MessageAction.LoginFail, new MessageMaps {ActionTextResourceName = "LoginFail"}}, - {MessageAction.LoginFailViaSms, new MessageMaps {ActionTextResourceName = "LoginFailViaSms"}}, - {MessageAction.LoginFailViaApi, new MessageMaps {ActionTextResourceName = "LoginFailViaApi"}}, - {MessageAction.LoginFailViaApiSms, new MessageMaps {ActionTextResourceName = "LoginFailViaApiSms"}}, - {MessageAction.LoginFailViaApiTfa, new MessageMaps {ActionTextResourceName = "LoginFailViaApiTfa"}}, - {MessageAction.LoginFailViaApiSocialAccount, new MessageMaps {ActionTextResourceName = "LoginFailViaApiSocialAccount"}}, - {MessageAction.LoginFailViaTfaApp, new MessageMaps {ActionTextResourceName = "LoginFailViaTfaApp"}}, - {MessageAction.LoginFailIpSecurity, new MessageMaps {ActionTextResourceName = "LoginFailIpSecurity"}}, - {MessageAction.LoginFailViaSSO, new MessageMaps {ActionTextResourceName = "LoginFailViaSSO"}}, - {MessageAction.LoginFailBruteForce, new MessageMaps {ActionTextResourceName = "LoginFailBruteForce"}}, - {MessageAction.LoginFailRecaptcha, new MessageMaps {ActionTextResourceName = "LoginFailRecaptcha"}}, - {MessageAction.Logout, new MessageMaps {ActionTextResourceName = "Logout"}}, - {MessageAction.SessionStarted, new MessageMaps {ActionTextResourceName = "SessionStarted"}}, - {MessageAction.SessionCompleted, new MessageMaps {ActionTextResourceName = "SessionCompleted"}} - }; + Product = ProductType.Login; + + Mappers = new List() + { + new LoginNoneModuleActionMapper() + }; + } + } + + public class LoginNoneModuleActionMapper : IModuleActionMapper + { + public ModuleType Module { get; } + public IDictionary Actions { get; } + + public LoginNoneModuleActionMapper() + { + Module = ModuleType.None; + + Actions = new MessageMapsDictionary() + { + MessageAction.LoginSuccess, + MessageAction.LoginSuccessViaSms,MessageAction.LoginSuccessViaApi,MessageAction.LoginSuccessViaApiSms, + MessageAction.LoginSuccessViaApiTfa,MessageAction.LoginSuccessViaApiSocialAccount,MessageAction.LoginSuccessViaSSO, + MessageAction.LoginSuccesViaTfaApp,MessageAction.LoginFailInvalidCombination,MessageAction.LoginFailSocialAccountNotFound, + MessageAction.LoginFailDisabledProfile, MessageAction.LoginFail,MessageAction.LoginFailViaSms,MessageAction.LoginFailViaApi, + MessageAction.LoginFailViaApiSms,MessageAction.LoginFailViaApiTfa,MessageAction.LoginFailViaApiSocialAccount, + MessageAction.LoginFailViaTfaApp,MessageAction.LoginFailIpSecurity,MessageAction.LoginFailViaSSO,MessageAction.LoginFailBruteForce, + MessageAction.LoginFailRecaptcha,MessageAction.Logout,MessageAction.SessionStarted,MessageAction.SessionCompleted + }; + + Actions.Add(MessageAction.LoginSuccessViaSocialAccount, new MessageMaps("LoginSuccessSocialAccount")); + Actions.Add(MessageAction.LoginSuccessViaSocialApp, new MessageMaps("LoginSuccessSocialApp")); } } } \ No newline at end of file diff --git a/module/ASC.AuditTrail/Mappers/MessageMaps.cs b/module/ASC.AuditTrail/Mappers/MessageMaps.cs index 1bda24281..ad175e4f8 100644 --- a/module/ASC.AuditTrail/Mappers/MessageMaps.cs +++ b/module/ASC.AuditTrail/Mappers/MessageMaps.cs @@ -15,20 +15,65 @@ */ +using System.Collections; +using System.Collections.Generic; + +using ASC.AuditTrail.Types; +using ASC.MessagingSystem; + namespace ASC.AuditTrail.Mappers { - internal class MessageMaps + public class MessageMaps { - public string ActionTypeTextResourceName { get; set; } - public string ActionTextResourceName { get; set; } - public string ProductResourceName { get; set; } - public string ModuleResourceName { get; set; } + public ActionType ActionType { get; } + public ProductType ProductType { get; protected set; } + public ModuleType ModuleType { get; } + public EntryType EntryType1 { get; } + public EntryType EntryType2 { get; } + + public string ActionTextResourceName { get; } + + + public MessageMaps(string actionTextResourceName, + ActionType? actionType = null, + ProductType? productType = null, + ModuleType? moduleType = null, + EntryType? entryType1 = null, + EntryType? entryType2 = null) + { + ActionTextResourceName = actionTextResourceName; + + if (actionType.HasValue) + { + ActionType = actionType.Value; + } + + if (productType.HasValue) + { + ProductType = productType.Value; + } + + if (moduleType.HasValue) + { + ModuleType = moduleType.Value; + } + + if (entryType1.HasValue) + { + EntryType1 = entryType1.Value; + } + + if (entryType2.HasValue) + { + EntryType2 = entryType2.Value; + } + } public string GetActionTypeText() { try { - return AuditReportResource.ResourceManager.GetString(ActionTypeTextResourceName); + return AuditReportResource.ResourceManager.GetString(ActionType.ToString() + "ActionType"); } catch { @@ -48,11 +93,11 @@ namespace ASC.AuditTrail.Mappers } } - public string GetProduct() + public string GetProductText() { try { - return AuditReportResource.ResourceManager.GetString(ProductResourceName); + return AuditReportResource.ResourceManager.GetString(ProductType == ProductType.CRM ? "CrmProduct" : ProductType.ToString() + "Product"); } catch { @@ -60,11 +105,11 @@ namespace ASC.AuditTrail.Mappers } } - public string GetModule() + public string GetModuleText() { try { - return AuditReportResource.ResourceManager.GetString(ModuleResourceName); + return AuditReportResource.ResourceManager.GetString(ModuleType.ToString() + "Module"); } catch { @@ -72,4 +117,200 @@ namespace ASC.AuditTrail.Mappers } } } + + public class MessageMapsDictionary : IDictionary + { + private readonly ProductType _productType; + private readonly ModuleType _moduleType; + private IDictionary Actions { get; } + + public ICollection Keys + { + get + { + return Actions.Keys; + } + } + + public ICollection Values + { + get + { + return Actions.Values; + } + } + + public int Count + { + get + { + return Actions.Count; + } + } + + public bool IsReadOnly + { + get + { + return Actions.IsReadOnly; + } + } + + public MessageMaps this[MessageAction key] + { + get + { + return Actions[key]; + } + set + { + Actions[key] = value; + } + } + + public MessageMapsDictionary() + { + Actions = new Dictionary(); + } + + public MessageMapsDictionary(ProductType productType, ModuleType moduleType) : this() + { + _productType = productType; + _moduleType = moduleType; + } + + public void Add(MessageAction[] value) + { + foreach (var item in value) + { + Add(item); + } + } + + public void Add(EntryType key, Dictionary value) + { + foreach (var item in value) + { + foreach (var messageAction in item.Value) + { + Add(messageAction, item.Key, key); + } + } + } + + public void Add(EntryType key, Dictionary value, Dictionary value1) + { + foreach (var item in value) + { + foreach (var messageAction in item.Value) + { + Add(messageAction, item.Key, key); + } + } + + Add(key, value1); + } + + public void Add(EntryType key, Dictionary value) + { + foreach (var item in value) + { + Add(item.Value, item.Key, key); + } + } + + public void Add(ActionType key, MessageAction[] value) + { + foreach (var item in value) + { + Add(item, key); + } + } + + public void Add(EntryType entryType1, EntryType entryType2, Dictionary value) + { + foreach (var item in value) + { + Add(item.Value, item.Key, entryType1, entryType2); + } + } + + public void Add(EntryType entryType1, EntryType entryType2, Dictionary value) + { + foreach (var item in value) + { + foreach (var messageAction in item.Value) + { + Add(messageAction, item.Key, entryType1, entryType2); + } + } + } + + public MessageMapsDictionary Add(MessageAction action, + ActionType? actionType = null, + EntryType? entryType1 = null, + EntryType? entryType2 = null, + ProductType? productType = null, + ModuleType? moduleType = null) + { + var map = new MessageMaps(action.ToString(), actionType, productType ?? _productType, moduleType ?? _moduleType, entryType1, entryType2); + Actions.Add(new KeyValuePair(action, map)); + return this; + } + + public bool ContainsKey(MessageAction key) + { + return Actions.ContainsKey(key); + } + + public void Add(MessageAction key, MessageMaps value) + { + Actions.Add(key, value); + } + + public bool Remove(MessageAction key) + { + return Actions.Remove(key); + } + + public bool TryGetValue(MessageAction key, out MessageMaps value) + { + return Actions.TryGetValue(key, out value); + } + + public void Add(KeyValuePair item) + { + Actions.Add(item); + } + + public void Clear() + { + Actions.Clear(); + } + + public bool Contains(KeyValuePair item) + { + return Actions.Contains(item); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + Actions.CopyTo(array, arrayIndex); + } + + public bool Remove(KeyValuePair item) + { + return Actions.Remove(item); + } + + public IEnumerator> GetEnumerator() + { + return Actions.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return Actions.GetEnumerator(); + } + } } \ No newline at end of file diff --git a/module/ASC.AuditTrail/Mappers/OthersActionMapper.cs b/module/ASC.AuditTrail/Mappers/OthersActionMapper.cs index ed0cd1898..569fb1de9 100644 --- a/module/ASC.AuditTrail/Mappers/OthersActionMapper.cs +++ b/module/ASC.AuditTrail/Mappers/OthersActionMapper.cs @@ -17,25 +17,39 @@ using System.Collections.Generic; +using ASC.AuditTrail.Types; using ASC.MessagingSystem; namespace ASC.AuditTrail.Mappers { - internal class OthersActionsMapper + public class OthersActionsMapper : IProductActionMapper { - public static Dictionary GetMaps() + public ProductType Product { get; } + public List Mappers { get; } + + public OthersActionsMapper() { - return new Dictionary - { - { - MessageAction.ContactAdminMailSent, new MessageMaps - { - ActionTypeTextResourceName = "SendActionType", - ActionTextResourceName = "ContactAdminMailSent", - ProductResourceName = "OthersProduct" - } - } - }; + Product = ProductType.Others; + Mappers = new List() + { + new OthersNoneModuleActionMapper() + }; + } + } + + public class OthersNoneModuleActionMapper : IModuleActionMapper + { + public ModuleType Module { get; } + public IDictionary Actions { get; } + + public OthersNoneModuleActionMapper() + { + Module = ModuleType.None; + + Actions = new MessageMapsDictionary() + { + { ActionType.Send, new[] { MessageAction.ContactAdminMailSent } } + }; } } } \ No newline at end of file diff --git a/module/ASC.AuditTrail/Mappers/PeopleActionMapper.cs b/module/ASC.AuditTrail/Mappers/PeopleActionMapper.cs index 0261a96f6..a40d6cb24 100644 --- a/module/ASC.AuditTrail/Mappers/PeopleActionMapper.cs +++ b/module/ASC.AuditTrail/Mappers/PeopleActionMapper.cs @@ -17,332 +17,96 @@ using System.Collections.Generic; +using ASC.AuditTrail.Types; using ASC.MessagingSystem; namespace ASC.AuditTrail.Mappers { - internal class PeopleActionMapper + public class PeopleActionMapper : IProductActionMapper { - public static Dictionary GetMaps() + public List Mappers { get; } + public ProductType Product { get; } + + public PeopleActionMapper() { - return new Dictionary + Product = ProductType.People; + + Mappers = new List() + { + new UsersActionMapper(), + new GroupsActionMapper() + }; + } + } + + public class UsersActionMapper : IModuleActionMapper + { + public ModuleType Module { get; } + public IDictionary Actions { get; } + + public UsersActionMapper() + { + Module = ModuleType.Users; + + Actions = new MessageMapsDictionary(ProductType.People, Module) + { { + EntryType.User, new Dictionary() { - MessageAction.UserCreated, new MessageMaps + { ActionType.Create, new[] { MessageAction.UserCreated, MessageAction.GuestCreated, MessageAction.UserCreatedViaInvite, MessageAction.GuestCreatedViaInvite } }, + { + ActionType.Update, new[] { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "UserCreated", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" + MessageAction.UserActivated, MessageAction.GuestActivated, MessageAction.UserUpdated, + MessageAction.UserUpdatedMobileNumber, MessageAction.UserUpdatedLanguage, MessageAction.UserAddedAvatar, + MessageAction.UserUpdatedAvatarThumbnails, MessageAction.UserUpdatedEmail, MessageAction.UsersUpdatedType, + MessageAction.UsersUpdatedStatus, MessageAction.UsersSentActivationInstructions, } + }, + { ActionType.Delete, new[] { MessageAction.UserDeletedAvatar, MessageAction.UserDeleted, MessageAction.UsersDeleted, MessageAction.UserDataRemoving } }, + { ActionType.Import, new[] { MessageAction.UserImported, MessageAction.GuestImported } }, + { ActionType.Logout, new[] { MessageAction.UserLogoutActiveConnections, MessageAction.UserLogoutActiveConnection, MessageAction.UserLogoutActiveConnectionsForUser } }, }, + new Dictionary() { - MessageAction.GuestCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "GuestCreated", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.UserCreatedViaInvite, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "UserCreatedViaInvite", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.GuestCreatedViaInvite, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "GuestCreatedViaInvite", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.UserActivated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "UserActivated", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.GuestActivated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "GuestActivated", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.UserUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "UserUpdated", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.UserUpdatedMobileNumber, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "UserUpdatedMobileNumber", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.UserUpdatedLanguage, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "UserUpdatedLanguage", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.UserAddedAvatar, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "UserAddedAvatar", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.UserDeletedAvatar, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "UserDeletedAvatar", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.UserUpdatedAvatarThumbnails, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "UserUpdatedAvatarThumbnails", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.UserLinkedSocialAccount, new MessageMaps - { - ActionTypeTextResourceName = "LinkActionType", - ActionTextResourceName = "UserLinkedSocialAccount", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.UserUnlinkedSocialAccount, new MessageMaps - { - ActionTypeTextResourceName = "UnlinkActionType", - ActionTextResourceName = "UserUnlinkedSocialAccount", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.UserSentActivationInstructions, new MessageMaps - { - ActionTypeTextResourceName = "SendActionType", - ActionTextResourceName = "UserSentActivationInstructions", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.UserSentEmailChangeInstructions, new MessageMaps - { - ActionTypeTextResourceName = "SendActionType", - ActionTextResourceName = "UserSentEmailInstructions", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.UserSentPasswordChangeInstructions, new MessageMaps - { - ActionTypeTextResourceName = "SendActionType", - ActionTextResourceName = "UserSentPasswordInstructions", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.UserSentDeleteInstructions, new MessageMaps - { - ActionTypeTextResourceName = "SendActionType", - ActionTextResourceName = "UserSentDeleteInstructions", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.UserUpdatedEmail, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "UserUpdatedEmail", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.UserUpdatedPassword, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "UserUpdatedPassword", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.UserDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "UserDeleted", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.UsersUpdatedType, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "UsersUpdatedType", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.UsersUpdatedStatus, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "UsersUpdatedStatus", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.UsersSentActivationInstructions, new MessageMaps - { - ActionTypeTextResourceName = "SendActionType", - ActionTextResourceName = "UsersSentActivationInstructions", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.UsersDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "UsersDeleted", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.SentInviteInstructions, new MessageMaps - { - ActionTypeTextResourceName = "SendActionType", - ActionTextResourceName = "SentInviteInstructions", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.UserImported, new MessageMaps - { - ActionTypeTextResourceName = "ImportActionType", - ActionTextResourceName = "UserImported", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.GuestImported, new MessageMaps - { - ActionTypeTextResourceName = "ImportActionType", - ActionTextResourceName = "GuestImported", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.GroupCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "GroupCreated", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "GroupsModule" - } - }, - { - MessageAction.GroupUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "GroupUpdated", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "GroupsModule" - } - }, - { - MessageAction.GroupDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "GroupDeleted", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "GroupsModule" - } - }, - { - MessageAction.UserDataReassigns, new MessageMaps - { - ActionTypeTextResourceName = "ReassignsActionType", - ActionTextResourceName = "UserDataReassigns", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.UserDataRemoving, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "UserDataRemoving", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.UserConnectedTfaApp, new MessageMaps - { - ActionTypeTextResourceName = "LinkActionType", - ActionTextResourceName = "UserTfaGenerateCodes", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } - }, - { - MessageAction.UserDisconnectedTfaApp, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "UserTfaDisconnected", - ProductResourceName = "PeopleProduct", - ModuleResourceName = "UsersModule" - } + { ActionType.Reassigns, MessageAction.UserDataReassigns } } - }; + }, + { MessageAction.UserLinkedSocialAccount, ActionType.Link }, + { MessageAction.UserUnlinkedSocialAccount, ActionType.Unlink }, + { + ActionType.Send, new[] { MessageAction.UserSentActivationInstructions, MessageAction.UserSentDeleteInstructions, MessageAction.SentInviteInstructions } + }, + { MessageAction.UserUpdatedPassword, ActionType.Update } + }; + + Actions.Add(MessageAction.UserSentEmailChangeInstructions, new MessageMaps("UserSentEmailInstructions", ActionType.Send, ProductType.People, Module, EntryType.User)); + Actions.Add(MessageAction.UserSentPasswordChangeInstructions, new MessageMaps("UserSentPasswordInstructions", ActionType.Send, ProductType.People, Module, EntryType.User)); + Actions.Add(MessageAction.UserConnectedTfaApp, new MessageMaps("UserTfaGenerateCodes", ActionType.Link, ProductType.People, Module, EntryType.User)); + Actions.Add(MessageAction.UserDisconnectedTfaApp, new MessageMaps("UserTfaDisconnected", ActionType.Delete, ProductType.People, Module, EntryType.User)); + } + } + + public class GroupsActionMapper : IModuleActionMapper + { + public ModuleType Module { get; } + public IDictionary Actions { get; } + + public GroupsActionMapper() + { + Module = ModuleType.Groups; + + Actions = new MessageMapsDictionary(ProductType.People, Module) + { + { + EntryType.Group, new Dictionary() + { + { ActionType.Create, MessageAction.GroupCreated }, + { ActionType.Update, MessageAction.GroupUpdated }, + { ActionType.Delete, MessageAction.GroupDeleted } + } + } + }; } } } \ No newline at end of file diff --git a/module/ASC.AuditTrail/Mappers/ProjectsActionMapper.cs b/module/ASC.AuditTrail/Mappers/ProjectsActionMapper.cs index d46d47b26..1eeea5a6b 100644 --- a/module/ASC.AuditTrail/Mappers/ProjectsActionMapper.cs +++ b/module/ASC.AuditTrail/Mappers/ProjectsActionMapper.cs @@ -17,588 +17,244 @@ using System.Collections.Generic; +using ASC.AuditTrail.Types; using ASC.MessagingSystem; namespace ASC.AuditTrail.Mappers { - internal class ProjectsActionsMapper + public class ProjectsActionsMapper : IProductActionMapper { - public static Dictionary GetMaps() + public List Mappers { get; } + public ProductType Product { get; } + public ProjectsActionsMapper() { - return new Dictionary + Product = ProductType.Projects; + + Mappers = new List() + { + new ProjectsActionMapper(), + new MilestonesActionMapper(), + new TasksActionMapper(), + new DiscussionsActionMapper(), + new TimeTrackingActionMapper(), + new ReportsActionMapper(), + new ProjectsSettingsActionMapper() + }; + } + } + + public class ProjectsActionMapper : IModuleActionMapper + { + public ModuleType Module { get; } + public IDictionary Actions { get; } + + public ProjectsActionMapper() + { + Module = ModuleType.Projects; + + Actions = new MessageMapsDictionary(ProductType.Projects, Module) + { { - #region projects + EntryType.Project, new Dictionary() + { + { ActionType.Create, new[] { MessageAction.ProjectCreated, MessageAction.ProjectCreatedFromTemplate } }, + { ActionType.Update, new[] { MessageAction.ProjectUpdated, MessageAction.ProjectUpdatedStatus, MessageAction.ProjectUpdatedTeam } }, + { ActionType.Delete, new[] { MessageAction.ProjectDeleted, MessageAction.ProjectDeletedMember } }, + { ActionType.Link, new[] { MessageAction.ProjectLinkedCompany, MessageAction.ProjectLinkedPerson, MessageAction.ProjectLinkedContacts } }, + { ActionType.Unlink, new[] { MessageAction.ProjectUnlinkedCompany, MessageAction.ProjectUnlinkedPerson } } + }, + new Dictionary() + { - { - MessageAction.ProjectCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "ProjectCreated", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "ProjectsModule" - } - }, - { - MessageAction.ProjectCreatedFromTemplate, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "ProjectCreatedFromTemplate", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "ProjectsModule" - } - }, - { - MessageAction.ProjectUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "ProjectUpdated", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "ProjectsModule" - } - }, - { - MessageAction.ProjectUpdatedStatus, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "ProjectUpdatedStatus", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "ProjectsModule" - } - }, - { - MessageAction.ProjectFollowed, new MessageMaps - { - ActionTypeTextResourceName = "FollowActionType", - ActionTextResourceName = "ProjectFollowed", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "ProjectsModule" - } - }, - { - MessageAction.ProjectUnfollowed, new MessageMaps - { - ActionTypeTextResourceName = "UnfollowActionType", - ActionTextResourceName = "ProjectUnfollowed", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "ProjectsModule" - } - }, - { - MessageAction.ProjectDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "ProjectDeleted", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "ProjectsModule" - } - }, + { ActionType.Follow, MessageAction.ProjectFollowed }, + { ActionType.Unfollow, MessageAction.ProjectUnfollowed }, + { ActionType.UpdateAccess, MessageAction.ProjectUpdatedMemberRights }, + { ActionType.Import, MessageAction.ProjectsImportedFromBasecamp } + } + } + }; + } + } - #endregion - - #region team + public class MilestonesActionMapper : IModuleActionMapper + { + public ModuleType Module { get; } + public IDictionary Actions { get; } - { - MessageAction.ProjectDeletedMember, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "ProjectDeletedMember", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "ProjectsModule" - } - }, - { - MessageAction.ProjectUpdatedTeam, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "ProjectUpdatedTeam", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "ProjectsModule" - } - }, - { - MessageAction.ProjectUpdatedMemberRights, new MessageMaps - { - ActionTypeTextResourceName = "UpdateAccessActionType", - ActionTextResourceName = "ProjectUpdatedMemberRights", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "ProjectsModule" - } - }, + public MilestonesActionMapper() + { + Module = ModuleType.Milestones; - #endregion + Actions = new MessageMapsDictionary(ProductType.Projects, Module) + { + { + EntryType.Milestone, new Dictionary() + { + { ActionType.Update, new[] { MessageAction.MilestoneUpdated, MessageAction.MilestoneUpdatedStatus } } + }, new Dictionary() + { + { ActionType.Create, MessageAction.MilestoneCreated }, + { ActionType.Delete, MessageAction.MilestoneDeleted } + } + } + }; + } + } - #region contacts + public class TasksActionMapper : IModuleActionMapper + { + public ModuleType Module { get; } + public IDictionary Actions { get; } - { - MessageAction.ProjectLinkedCompany, new MessageMaps - { - ActionTypeTextResourceName = "LinkActionType", - ActionTextResourceName = "ProjectLinkedCompany", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "ProjectsModule" - } - }, - { - MessageAction.ProjectUnlinkedCompany, new MessageMaps - { - ActionTypeTextResourceName = "UnlinkActionType", - ActionTextResourceName = "ProjectUnlinkedCompany", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "ProjectsModule" - } - }, - { - MessageAction.ProjectLinkedPerson, new MessageMaps - { - ActionTypeTextResourceName = "LinkActionType", - ActionTextResourceName = "ProjectLinkedPerson", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "ProjectsModule" - } - }, - { - MessageAction.ProjectUnlinkedPerson, new MessageMaps - { - ActionTypeTextResourceName = "UnlinkActionType", - ActionTextResourceName = "ProjectUnlinkedPerson", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "ProjectsModule" - } - }, - { - MessageAction.ProjectLinkedContacts, new MessageMaps - { - ActionTypeTextResourceName = "LinkActionType", - ActionTextResourceName = "ProjectLinkedContacts", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "ProjectsModule" - } - }, + public TasksActionMapper() + { + Module = ModuleType.Tasks; - #endregion + Actions = new MessageMapsDictionary(ProductType.Projects, Module) + { + { + EntryType.Task, new Dictionary() + { + { ActionType.Create, new[] { MessageAction.TaskCreated, MessageAction.TaskCreatedFromDiscussion } }, + { ActionType.Update, new[] { MessageAction.TaskUpdated, MessageAction.TaskUpdatedStatus, MessageAction.TaskMovedToMilestone, MessageAction.TaskUnlinkedMilestone } } + }, + new Dictionary() + { + { ActionType.Delete, MessageAction.TaskDeleted }, + { ActionType.Follow, MessageAction.TaskUpdatedFollowing }, + { ActionType.Attach, MessageAction.TaskAttachedFiles }, + { ActionType.Detach, MessageAction.TaskDetachedFile }, + { ActionType.Link, MessageAction.TasksLinked }, + { ActionType.Unlink, MessageAction.TasksUnlinked }, + } + }, + { + EntryType.Comment, new Dictionary() + { + { ActionType.Create, MessageAction.TaskCommentCreated }, + { ActionType.Update, MessageAction.TaskCommentUpdated }, + { ActionType.Delete, MessageAction.TaskCommentDeleted } + } + }, + { + EntryType.SubTask, new Dictionary() + { + { ActionType.Update, new[] { MessageAction.SubtaskUpdated, MessageAction.SubtaskUpdatedStatus, MessageAction.SubtaskMoved } } + }, + new Dictionary() + { + { ActionType.Create, MessageAction.SubtaskCreated }, + { ActionType.Delete,MessageAction.SubtaskDeleted } + } + }, + }; + } + } - #region milestones + public class DiscussionsActionMapper : IModuleActionMapper + { + public ModuleType Module { get; } - { - MessageAction.MilestoneCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "MilestoneCreated", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "MilestonesModule" - } - }, - { - MessageAction.MilestoneUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "MilestoneUpdated", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "MilestonesModule" - } - }, - { - MessageAction.MilestoneUpdatedStatus, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "MilestoneUpdatedStatus", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "MilestonesModule" - } - }, - { - MessageAction.MilestoneDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "MilestoneDeleted", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "MilestonesModule" - } - }, + public IDictionary Actions { get; } - #endregion + public DiscussionsActionMapper() + { + Module = ModuleType.Discussions; - #region tasks + Actions = new MessageMapsDictionary(ProductType.Projects, Module) + { + { + EntryType.Message, new Dictionary() + { + { ActionType.Create, MessageAction.DiscussionCreated }, + { ActionType.Update, MessageAction.DiscussionUpdated }, + { ActionType.Delete, MessageAction.DiscussionDeleted }, + { ActionType.Follow, MessageAction.DiscussionUpdatedFollowing }, + { ActionType.Attach, MessageAction.DiscussionAttachedFiles }, + { ActionType.Detach, MessageAction.DiscussionDetachedFile } + } + }, + { + EntryType.Comment, new Dictionary() + { + { ActionType.Create, MessageAction.DiscussionCommentCreated }, + { ActionType.Update, MessageAction.DiscussionCommentUpdated }, + { ActionType.Delete, MessageAction.DiscussionCommentDeleted } + } + }, + }; + } + } - { - MessageAction.TaskCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "TaskCreated", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "TasksModule" - } - }, - { - MessageAction.TaskCreatedFromDiscussion, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "TaskCreatedFromDiscussion", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "TasksModule" - } - }, - { - MessageAction.TaskUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "TaskUpdated", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "TasksModule" - } - }, - { - MessageAction.TaskUpdatedStatus, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "TaskUpdatedStatus", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "TasksModule" - } - }, - { - MessageAction.TaskMovedToMilestone, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "TaskMovedToMilestone", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "TasksModule" - } - }, - { - MessageAction.TaskUnlinkedMilestone, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "TaskUnlinkedMilestone", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "TasksModule" - } - }, - { - MessageAction.TaskUpdatedFollowing, new MessageMaps - { - ActionTypeTextResourceName = "FollowActionType", - ActionTextResourceName = "TaskUpdatedFollowing", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "TasksModule" - } - }, - { - MessageAction.TaskAttachedFiles, new MessageMaps - { - ActionTypeTextResourceName = "AttachActionType", - ActionTextResourceName = "TaskAttachedFiles", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "TasksModule" - } - }, - { - MessageAction.TaskDetachedFile, new MessageMaps - { - ActionTypeTextResourceName = "DetachActionType", - ActionTextResourceName = "TaskDetachedFile", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "TasksModule" - } - }, - { - MessageAction.TasksLinked, new MessageMaps - { - ActionTypeTextResourceName = "LinkActionType", - ActionTextResourceName = "TasksLinked", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "TasksModule" - } - }, - { - MessageAction.TasksUnlinked, new MessageMaps - { - ActionTypeTextResourceName = "UnlinkActionType", - ActionTextResourceName = "TasksUnlinked", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "TasksModule" - } - }, - { - MessageAction.TaskDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "TaskDeleted", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "TasksModule" - } - }, - { - MessageAction.TaskCommentCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "TaskCommentCreated", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "TasksModule" - } - }, - { - MessageAction.TaskCommentUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "TaskCommentUpdated", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "TasksModule" - } - }, - { - MessageAction.TaskCommentDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "TaskCommentDeleted", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "TasksModule" - } - }, + public class TimeTrackingActionMapper : IModuleActionMapper + { + public ModuleType Module { get; } + public IDictionary Actions { get; } - #endregion + public TimeTrackingActionMapper() + { + Module = ModuleType.TimeTracking; - #region subtasks + Actions = new MessageMapsDictionary(ProductType.Projects, Module) + { + { + EntryType.TimeSpend, new Dictionary() + { + { ActionType.Update, new[] { MessageAction.TaskTimeUpdated, MessageAction.TaskTimesUpdatedStatus } } + }, + new Dictionary() + { + { ActionType.Create, MessageAction.TaskTimeCreated }, + { ActionType.Delete, MessageAction.TaskTimesDeleted }, + } + } + }; + } + } - { - MessageAction.SubtaskCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "SubtaskCreated", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "TasksModule" - } - }, - { - MessageAction.SubtaskUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "SubtaskUpdated", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "TasksModule" - } - }, - { - MessageAction.SubtaskUpdatedStatus, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "SubtaskUpdatedStatus", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "TasksModule" - } - }, - { - MessageAction.SubtaskDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "SubtaskDeleted", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "TasksModule" - } - }, + public class ReportsActionMapper : IModuleActionMapper + { + public ModuleType Module { get; } + public IDictionary Actions { get; } - #endregion + public ReportsActionMapper() + { + Module = ModuleType.Reports; - #region discussions + Actions = new MessageMapsDictionary(ProductType.Projects, Module) + { + { + EntryType.TimeSpend, new Dictionary() + { + { ActionType.Create, MessageAction.ReportTemplateCreated }, + { ActionType.Update, MessageAction.ReportTemplateUpdated }, + { ActionType.Delete, MessageAction.ReportTemplateDeleted }, + } + } + }; + } + } - { - MessageAction.DiscussionCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "DiscussionCreated", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "DiscussionsModule" - } - }, - { - MessageAction.DiscussionUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "DiscussionUpdated", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "DiscussionsModule" - } - }, - { - MessageAction.DiscussionUpdatedFollowing, new MessageMaps - { - ActionTypeTextResourceName = "FollowActionType", - ActionTextResourceName = "DiscussionUpdatedFollowing", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "DiscussionsModule" - } - }, - { - MessageAction.DiscussionAttachedFiles, new MessageMaps - { - ActionTypeTextResourceName = "AttachActionType", - ActionTextResourceName = "DiscussionAttachedFiles", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "DiscussionsModule" - } - }, - { - MessageAction.DiscussionDetachedFile, new MessageMaps - { - ActionTypeTextResourceName = "DetachActionType", - ActionTextResourceName = "DiscussionDetachedFile", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "DiscussionsModule" - } - }, - { - MessageAction.DiscussionDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "DiscussionDeleted", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "DiscussionsModule" - } - }, - { - MessageAction.DiscussionCommentCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "DiscussionCommentCreated", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "DiscussionsModule" - } - }, - { - MessageAction.DiscussionCommentUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "DiscussionCommentUpdated", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "DiscussionsModule" - } - }, - { - MessageAction.DiscussionCommentDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "DiscussionCommentDeleted", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "DiscussionsModule" - } - }, + public class ProjectsSettingsActionMapper : IModuleActionMapper + { + public ModuleType Module { get; } + public IDictionary Actions { get; } - #endregion - - #region time tracking + public ProjectsSettingsActionMapper() + { + Module = ModuleType.ProjectsSettings; + Actions = new MessageMapsDictionary(ProductType.Projects, Module) + { + { + EntryType.Template, new Dictionary() { - MessageAction.TaskTimeCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "TaskTimeCreated", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "TimeTrackingModule" - } - }, - { - MessageAction.TaskTimeUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "TaskTimeUpdated", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "TimeTrackingModule" - } - }, - { - MessageAction.TaskTimesUpdatedStatus, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "TaskTimesUpdatedStatus", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "TimeTrackingModule" - } - }, - { - MessageAction.TaskTimesDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "TaskTimesDeleted", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "TimeTrackingModule" - } - }, - - #endregion - - #region reports - - { - MessageAction.ReportTemplateCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "ReportTemplateCreated", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "ReportsModule" - } - }, - { - MessageAction.ReportTemplateUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "ReportTemplateUpdated", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "ReportsModule" - } - }, - { - MessageAction.ReportTemplateDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "ReportTemplateDeleted", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "ReportsModule" - } - }, - - #endregion - - #region settings - - { - MessageAction.ProjectTemplateCreated, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "ProjectTemplateCreated", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "ProjectsSettingsModule" - } - }, - { - MessageAction.ProjectTemplateUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "ProjectTemplateUpdated", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "ProjectsSettingsModule" - } - }, - { - MessageAction.ProjectTemplateDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "ProjectTemplateDeleted", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "ProjectsSettingsModule" - } - }, - { - MessageAction.ProjectsImportedFromBasecamp, new MessageMaps - { - ActionTypeTextResourceName = "ImportActionType", - ActionTextResourceName = "ProjectsImportedFromBasecamp", - ProductResourceName = "ProjectsProduct", - ModuleResourceName = "ProjectsModule" - } - }, - - #endregion - }; + { ActionType.Create, MessageAction.ProjectTemplateCreated }, + { ActionType.Update, MessageAction.ProjectTemplateUpdated }, + { ActionType.Delete, MessageAction.ProjectTemplateDeleted }, + } + } + }; } } } \ No newline at end of file diff --git a/module/ASC.AuditTrail/Mappers/SettingsActionMapper.cs b/module/ASC.AuditTrail/Mappers/SettingsActionMapper.cs index 0671a4d68..fed825d41 100644 --- a/module/ASC.AuditTrail/Mappers/SettingsActionMapper.cs +++ b/module/ASC.AuditTrail/Mappers/SettingsActionMapper.cs @@ -17,458 +17,117 @@ using System.Collections.Generic; +using ASC.AuditTrail.Types; using ASC.MessagingSystem; namespace ASC.AuditTrail.Mappers { - internal class SettingsActionsMapper + public class SettingsActionsMapper : IProductActionMapper { - public static Dictionary GetMaps() + public List Mappers { get; } + public ProductType Product { get; } + + public SettingsActionsMapper() { - return new Dictionary + Product = ProductType.Settings; + + Mappers = new List() + { + new GeneralActionMapper(), + new ProductsActionMapper() + }; + } + } + + public class GeneralActionMapper : IModuleActionMapper + { + public ModuleType Module { get; } + public IDictionary Actions { get; } + + public GeneralActionMapper() + { + Module = ModuleType.General; + var productType = ProductType.Settings; + Actions = new MessageMapsDictionary(productType, Module) + { { + ActionType.Update, new MessageAction[] { - MessageAction.LanguageSettingsUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "LanguageSettingsUpdated", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "GeneralModule" - } - }, - { - MessageAction.TimeZoneSettingsUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "TimeZoneSettingsUpdated", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "GeneralModule" - } - }, - { - MessageAction.DnsSettingsUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "DnsSettingsUpdated", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "GeneralModule" - } - }, - { - MessageAction.TrustedMailDomainSettingsUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "TrustedMailDomainSettingsUpdated", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "GeneralModule" - } - }, - { - MessageAction.PasswordStrengthSettingsUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "PasswordStrengthSettingsUpdated", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "GeneralModule" - } - }, - { - MessageAction.TwoFactorAuthenticationSettingsUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "TwoFactorAuthenticationSettingsUpdated", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "GeneralModule" - } - }, - { - MessageAction.TwoFactorAuthenticationDisabled, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "TwoFactorAuthenticationSettingsDisabled", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "GeneralModule" - } - }, - { - MessageAction.TwoFactorAuthenticationEnabledBySms, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "TwoFactorAuthenticationSettingsEnabledBySms", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "GeneralModule" - } - }, - { - MessageAction.TwoFactorAuthenticationEnabledByTfaApp, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "TwoFactorAuthenticationSettingsEnabledByTfaApp", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "GeneralModule" - } - }, - { - MessageAction.AdministratorMessageSettingsUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "AdministratorMessageSettingsUpdated", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "GeneralModule" - } - }, - { - MessageAction.DefaultStartPageSettingsUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "DefaultStartPageSettingsUpdated", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "GeneralModule" - } - }, - { - MessageAction.ProductsListUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "ProductsListUpdated", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.OwnerUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "OwnerChanged", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.AdministratorAdded, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "AdministratorAdded", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.UsersOpenedProductAccess, new MessageMaps - { - ActionTypeTextResourceName = "UpdateAccessActionType", - ActionTextResourceName = "ProductAccessOpenedForUsers", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.GroupsOpenedProductAccess, new MessageMaps - { - ActionTypeTextResourceName = "UpdateAccessActionType", - ActionTextResourceName = "ProductAccessOpenedForGroups", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.ProductAccessOpened, new MessageMaps - { - ActionTypeTextResourceName = "UpdateAccessActionType", - ActionTextResourceName = "ProductAccessOpened", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.ProductAccessRestricted, new MessageMaps - { - ActionTypeTextResourceName = "UpdateAccessActionType", - ActionTextResourceName = "ProductAccessRestricted", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.ProductAddedAdministrator, new MessageMaps - { - ActionTypeTextResourceName = "CreateActionType", - ActionTextResourceName = "ProductAddedAdministrator", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.ProductDeletedAdministrator, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "ProductDeletedAdministrator", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.AdministratorDeleted, new MessageMaps - { - ActionTypeTextResourceName = "UpdateAccessActionType", - ActionTextResourceName = "AdministratorDeleted", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.AdministratorOpenedFullAccess, new MessageMaps - { - ActionTypeTextResourceName = "UpdateAccessActionType", - ActionTextResourceName = "AdministratorOpenedFullAccess", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.GreetingSettingsUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "GreetingSettingsUpdated", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.TeamTemplateChanged, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "TeamTemplateChanged", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.ColorThemeChanged, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "ColorThemeChanged", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.OwnerSentPortalDeactivationInstructions, new MessageMaps - { - ActionTypeTextResourceName = "SendActionType", - ActionTextResourceName = "OwnerSentPortalDeactivationInstructions", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.OwnerSentPortalDeleteInstructions, new MessageMaps - { - ActionTypeTextResourceName = "SendActionType", - ActionTextResourceName = "OwnerSentPortalDeleteInstructions", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.LoginHistoryReportDownloaded, new MessageMaps - { - ActionTypeTextResourceName = "DownloadActionType", - ActionTextResourceName = "LoginHistoryReportDownloaded", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.AuditTrailReportDownloaded, new MessageMaps - { - ActionTypeTextResourceName = "DownloadActionType", - ActionTextResourceName = "AuditTrailReportDownloaded", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.PortalDeactivated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "PortalDeactivated", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.PortalDeleted, new MessageMaps - { - ActionTypeTextResourceName = "DeleteActionType", - ActionTextResourceName = "PortalDeleted", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.OwnerSentChangeOwnerInstructions, new MessageMaps - { - ActionTypeTextResourceName = "SendActionType", - ActionTextResourceName = "OwnerSentChangeOwnerInstructions", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.SSOEnabled, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "SSOEnabled", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.SSODisabled, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "SSODisabled", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.PortalAccessSettingsUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "PortalAccessSettingsUpdated", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.DocumentServiceLocationSetting, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "DocumentServiceLocationSetting", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.AuthorizationKeysSetting, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "AuthorizationKeysSetting", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.FullTextSearchSetting, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "FullTextSearchSetting", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.StartTransferSetting, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "StartTransferSetting", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.StartBackupSetting, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "StartBackupSetting", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.LicenseKeyUploaded, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "LicenseKeyUploaded", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.StartStorageEncryption, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "StartStorageEncryption", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.StartStorageDecryption, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "StartStorageDecryption", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.CookieSettingsUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "CookieSettingsUpdated", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.MailServiceSettingsUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "MailServiceSettingsUpdated", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.CustomNavigationSettingsUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "CustomNavigationSettingsUpdated", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.AuditSettingsUpdated, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "AuditSettingsUpdated", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.PrivacyRoomEnable, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "PrivacyRoomEnable", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } - }, - { - MessageAction.PrivacyRoomDisable, new MessageMaps - { - ActionTypeTextResourceName = "UpdateActionType", - ActionTextResourceName = "PrivacyRoomDisable", - ProductResourceName = "SettingsProduct", - ModuleResourceName = "ProductsModule" - } + MessageAction.LanguageSettingsUpdated, MessageAction.TimeZoneSettingsUpdated, MessageAction.DnsSettingsUpdated, + MessageAction.TrustedMailDomainSettingsUpdated,MessageAction.PasswordStrengthSettingsUpdated,MessageAction.TwoFactorAuthenticationSettingsUpdated, + MessageAction.AdministratorMessageSettingsUpdated,MessageAction.DefaultStartPageSettingsUpdated, } - }; + } + }; + + Actions.Add(MessageAction.TwoFactorAuthenticationDisabled, new MessageMaps("TwoFactorAuthenticationSettingsDisabled", ActionType.Update, productType, Module)); + Actions.Add(MessageAction.TwoFactorAuthenticationEnabledBySms, new MessageMaps("TwoFactorAuthenticationSettingsEnabledBySms", ActionType.Update, productType, Module)); + Actions.Add(MessageAction.TwoFactorAuthenticationEnabledByTfaApp, new MessageMaps("TwoFactorAuthenticationSettingsEnabledByTfaApp", ActionType.Update, productType, Module)); + } + } + + public class ProductsActionMapper : IModuleActionMapper + { + public ModuleType Module { get; } + public IDictionary Actions { get; } + + public ProductsActionMapper() + { + Module = ModuleType.Products; + var productType = ProductType.Projects; + + Actions = new MessageMapsDictionary(ProductType.Projects, Module) + { + { + ActionType.Update, new MessageAction[] + { + MessageAction.ProductsListUpdated, + MessageAction.GreetingSettingsUpdated,MessageAction.TeamTemplateChanged,MessageAction.ColorThemeChanged, + MessageAction.OwnerSentPortalDeactivationInstructions, MessageAction.PortalDeactivated, + MessageAction.SSOEnabled,MessageAction.SSODisabled,MessageAction.PortalAccessSettingsUpdated, + MessageAction.DocumentServiceLocationSetting, MessageAction.AuthorizationKeysSetting, + MessageAction.FullTextSearchSetting, MessageAction.StartTransferSetting, + MessageAction.StartBackupSetting,MessageAction.LicenseKeyUploaded, MessageAction.StartStorageEncryption, + MessageAction.StartStorageDecryption, MessageAction.CookieSettingsUpdated, MessageAction.MailServiceSettingsUpdated, + MessageAction.CustomNavigationSettingsUpdated,MessageAction.AuditSettingsUpdated,MessageAction.PrivacyRoomEnable, + MessageAction.PrivacyRoomDisable, + } + }, + { + ActionType.Create, new MessageAction[] + { + MessageAction.AdministratorAdded, MessageAction.ProductAddedAdministrator, + } + }, + { + ActionType.UpdateAccess, new MessageAction[] + { + MessageAction.ProductAccessOpened,MessageAction.ProductAccessRestricted,MessageAction.AdministratorDeleted, MessageAction.AdministratorOpenedFullAccess + } + }, + { + ActionType.Delete, new MessageAction[] + { + MessageAction.ProductDeletedAdministrator,MessageAction.PortalDeleted, + } + }, + { + ActionType.Send, new MessageAction[] + { + MessageAction.OwnerSentPortalDeleteInstructions, MessageAction.OwnerSentChangeOwnerInstructions, + } + }, + { + ActionType.Download, new MessageAction[] + { + MessageAction.LoginHistoryReportDownloaded, MessageAction.AuditTrailReportDownloaded + } + }, + }; + + Actions.Add(MessageAction.UsersOpenedProductAccess, new MessageMaps("ProductAccessOpenedForUsers", ActionType.UpdateAccess, productType, Module)); + Actions.Add(MessageAction.GroupsOpenedProductAccess, new MessageMaps("ProductAccessOpenedForGroups", ActionType.UpdateAccess, productType, Module)); + Actions.Add(MessageAction.OwnerUpdated, new MessageMaps("OwnerChanged", ActionType.Update, productType, Module)); } } } \ No newline at end of file diff --git a/module/ASC.AuditTrail/Types/ActionType.cs b/module/ASC.AuditTrail/Types/ActionType.cs new file mode 100644 index 000000000..20c21b9a3 --- /dev/null +++ b/module/ASC.AuditTrail/Types/ActionType.cs @@ -0,0 +1,26 @@ +namespace ASC.AuditTrail.Types +{ + public enum ActionType + { + None, + Create, + Update, + Delete, + Link, + Unlink, + Attach, + Detach, + Send, + Import, + Export, + UpdateAccess, + Download, + Upload, + Copy, + Move, + Reassigns, + Follow, + Unfollow, + Logout + } +} \ No newline at end of file diff --git a/module/ASC.AuditTrail/Types/EntryType.cs b/module/ASC.AuditTrail/Types/EntryType.cs new file mode 100644 index 000000000..78e97930a --- /dev/null +++ b/module/ASC.AuditTrail/Types/EntryType.cs @@ -0,0 +1,31 @@ +namespace ASC.AuditTrail.Types +{ + public enum EntryType + { + None, + File, + Folder, + Project, + Contact, + Milestone, + Task, + Comment, + SubTask, + Message, + TimeSpend, + ReportTemplate, + Template, + Relationship, + CRMTask, + Opportunity, + Invoice, + Case, + ListItem, + InvoiceItem, + InvoiceTax, + FieldDescription, + OpportunityMilestone, + User, + Group + } +} diff --git a/module/ASC.AuditTrail/Types/ModuleType.cs b/module/ASC.AuditTrail/Types/ModuleType.cs new file mode 100644 index 000000000..4316c08e2 --- /dev/null +++ b/module/ASC.AuditTrail/Types/ModuleType.cs @@ -0,0 +1,33 @@ +namespace ASC.AuditTrail.Types +{ + public enum ModuleType + { + None, + Files, + Folders, + DocumentsSettings, + Companies, + Persons, + Contacts, + CrmTasks, + Opportunities, + Invoices, + Cases, + CommonCrmSettings, + ContactsSettings, + ContactTypes, + InvoiceSettings, + OtherCrmSettings, + Users, + Groups, + Projects, + Milestones, + Tasks, + Discussions, + TimeTracking, + Reports, + ProjectsSettings, + General, + Products + } +} diff --git a/module/ASC.AuditTrail/Types/ProductType.cs b/module/ASC.AuditTrail/Types/ProductType.cs new file mode 100644 index 000000000..6104a9b90 --- /dev/null +++ b/module/ASC.AuditTrail/Types/ProductType.cs @@ -0,0 +1,14 @@ +namespace ASC.AuditTrail.Types +{ + public enum ProductType + { + None, + CRM, + Documents, + Login, + Others, + People, + Projects, + Settings + } +} diff --git a/module/ASC.Data.Reassigns/ASC.Data.Reassigns.csproj b/module/ASC.Data.Reassigns/ASC.Data.Reassigns.csproj index 48d275e27..9fa749497 100644 --- a/module/ASC.Data.Reassigns/ASC.Data.Reassigns.csproj +++ b/module/ASC.Data.Reassigns/ASC.Data.Reassigns.csproj @@ -86,7 +86,7 @@ - 5.1.2 + 6.2.0 diff --git a/module/ASC.Data.Reassigns/ReassignProgressItem.cs b/module/ASC.Data.Reassigns/ReassignProgressItem.cs index b910ddd90..1b90f5ee4 100644 --- a/module/ASC.Data.Reassigns/ReassignProgressItem.cs +++ b/module/ASC.Data.Reassigns/ReassignProgressItem.cs @@ -88,7 +88,7 @@ namespace ASC.Data.Reassigns Status = ProgressStatus.Started; CoreContext.TenantManager.SetCurrentTenant(_tenantId); - SecurityContext.AuthenticateMe(Core.Configuration.Constants.CoreSystem); + SecurityContext.CurrentAccount = Core.Configuration.Constants.CoreSystem; logger.InfoFormat("reassignment of data from {0} to {1}", _fromUserId, _toUserId); @@ -135,7 +135,7 @@ namespace ASC.Data.Reassigns { logger.Info("data reassignment is complete"); IsCompleted = true; - SecurityContext.AuthenticateMe(_currentUserId); + SecurityContext.CurrentUser = _currentUserId; } } diff --git a/module/ASC.Data.Reassigns/RemoveProgressItem.cs b/module/ASC.Data.Reassigns/RemoveProgressItem.cs index e89ad6fd9..4c6a4f6fc 100644 --- a/module/ASC.Data.Reassigns/RemoveProgressItem.cs +++ b/module/ASC.Data.Reassigns/RemoveProgressItem.cs @@ -92,7 +92,7 @@ namespace ASC.Data.Reassigns Status = ProgressStatus.Started; CoreContext.TenantManager.SetCurrentTenant(_tenantId); - SecurityContext.AuthenticateMe(Core.Configuration.Constants.CoreSystem); + SecurityContext.CurrentAccount = Core.Configuration.Constants.CoreSystem; long docsSpace, crmSpace, mailSpace, talkSpace; GetUsageSpace(out docsSpace, out mailSpace, out talkSpace); @@ -140,7 +140,7 @@ namespace ASC.Data.Reassigns { logger.Info("data deletion is complete"); IsCompleted = true; - SecurityContext.AuthenticateMe(_currentUserId); + SecurityContext.CurrentUser = _currentUserId; } } diff --git a/module/ASC.ElasticSearch/ASC.ElasticSearch.csproj b/module/ASC.ElasticSearch/ASC.ElasticSearch.csproj index a2b4aa858..4e53f290d 100644 --- a/module/ASC.ElasticSearch/ASC.ElasticSearch.csproj +++ b/module/ASC.ElasticSearch/ASC.ElasticSearch.csproj @@ -64,13 +64,13 @@ - 5.1.2 + 6.2.0 - 7.9.0 + 7.10.0 - 12.0.3 + 13.0.1 diff --git a/module/ASC.ElasticSearch/Core/SearchSettings.cs b/module/ASC.ElasticSearch/Core/SearchSettings.cs index 3c9aa1e36..86a73315a 100644 --- a/module/ASC.ElasticSearch/Core/SearchSettings.cs +++ b/module/ASC.ElasticSearch/Core/SearchSettings.cs @@ -101,12 +101,7 @@ namespace ASC.ElasticSearch.Core var tenantId = CoreContext.TenantManager.GetCurrentTenant().TenantId; if (!CanIndexByContent(t, tenantId)) return false; - if (CoreContext.Configuration.Standalone) - { - return true; - } - - return CoreContext.TenantManager.GetTenantQuota(tenantId).ContentSearch; + return CoreContext.Configuration.Standalone || CoreContext.TenantManager.GetTenantQuota(tenantId).ContentSearch; } public static List GetAllItems() diff --git a/module/ASC.ElasticSearch/Core/WrapperWithDoc.cs b/module/ASC.ElasticSearch/Core/WrapperWithDoc.cs index 288a29b1a..779766ceb 100644 --- a/module/ASC.ElasticSearch/Core/WrapperWithDoc.cs +++ b/module/ASC.ElasticSearch/Core/WrapperWithDoc.cs @@ -33,6 +33,8 @@ namespace ASC.ElasticSearch.Core { public Document Document { get; set; } + private ILog Log = LogManager.GetLogger("ASC.Indexer"); + public static readonly long MaxFileSize = Settings.Default.MaxFileSize; protected virtual Task GetDocumentDataAsync() @@ -83,11 +85,11 @@ namespace ASC.ElasticSearch.Core } catch (FileNotFoundException e) { - LogManager.GetLogger("ASC.Indexer").Error("InitDocument FileNotFoundException", e); + Log.Error("InitDocument FileNotFoundException", e); } catch (Exception e) { - LogManager.GetLogger("ASC.Indexer").Error("InitDocument", e); + Log.Error("InitDocument", e); } } @@ -123,11 +125,11 @@ namespace ASC.ElasticSearch.Core } catch (FileNotFoundException e) { - LogManager.GetLogger("ASC.Indexer").Error("InitDocument FileNotFoundException", e); + Log.Error("InitDocument FileNotFoundException", e); } catch (Exception e) { - LogManager.GetLogger("ASC.Indexer").Error("InitDocument", e); + Log.Error("InitDocument", e); } } } diff --git a/module/ASC.ElasticSearch/Engine/BaseIndexer.cs b/module/ASC.ElasticSearch/Engine/BaseIndexer.cs index 520849b39..9f177aa91 100644 --- a/module/ASC.ElasticSearch/Engine/BaseIndexer.cs +++ b/module/ASC.ElasticSearch/Engine/BaseIndexer.cs @@ -30,7 +30,6 @@ using ASC.Common.Data; using ASC.Common.Data.Sql; using ASC.Common.Data.Sql.Expressions; using ASC.Common.Logging; -using ASC.Common.Threading; using ASC.Core; using ASC.Core.Tenants; using ASC.ElasticSearch.Core; @@ -49,7 +48,6 @@ namespace ASC.ElasticSearch private static readonly ILog Logger = LogManager.GetLogger("ASC.Indexer"); private static readonly ICacheNotify Notify; private static CancellationTokenSource Cts; - private static TaskScheduler Scheduler; private const int QueryLimit = 10000; @@ -63,7 +61,6 @@ namespace ASC.ElasticSearch { Cts = new CancellationTokenSource(); Notify = AscCache.Notify; - Scheduler = new LimitedConcurrencyLevelTaskScheduler(4); Notify.Subscribe>((a, b) => { lock (Locker) @@ -81,14 +78,14 @@ namespace ASC.ElasticSearch internal void Index(T data, bool immediately = true) { - BeforeIndex(data); + if (!BeforeIndex(data)) return; Client.Instance.Index(data, idx => GetMeta(idx, data, immediately)); } internal void Index(List data, bool immediately = true) { - CreateIfNotExist(data[0]); + if (!CheckExist(data[0])) return; if (typeof(T).IsSubclassOf(typeof(WrapperWithDoc))) { @@ -124,17 +121,17 @@ namespace ASC.ElasticSearch { throw; } - LogManager.GetLogger("ASC.Indexer").Error(e); + Logger.Error(e); } catch (Exception e) { - LogManager.GetLogger("ASC.Indexer").Error(e); + Logger.Error(e); } finally { wwd.Document.Data = null; wwd.Document = null; - wwd = null; + data[i] = null; GC.Collect(); } continue; @@ -164,7 +161,7 @@ namespace ASC.ElasticSearch doc.Document.Data = null; doc.Document = null; } - doc = null; + data[j] = null; } portionStart = i; @@ -187,7 +184,7 @@ namespace ASC.ElasticSearch internal async Task IndexAsync(List data, bool immediately = true) { - CreateIfNotExist(data[0]); + if (!CheckExist(data[0])) return; if (typeof(T).IsSubclassOf(typeof(WrapperWithDoc))) { @@ -223,17 +220,17 @@ namespace ASC.ElasticSearch { throw; } - LogManager.GetLogger("ASC.Indexer").Error(e); + Logger.Error(e); } catch (Exception e) { - LogManager.GetLogger("ASC.Indexer").Error(e); + Logger.Error(e); } finally { wwd.Document.Data = null; wwd.Document = null; - wwd = null; + data[i] = null; GC.Collect(); } continue; @@ -263,7 +260,7 @@ namespace ASC.ElasticSearch doc.Document.Data = null; doc.Document = null; } - doc = null; + data[j] = null; } portionStart = i; @@ -286,25 +283,25 @@ namespace ASC.ElasticSearch internal void Update(T data, bool immediately = true, params Expression>[] fields) { - CreateIfNotExist(data); + if (!CheckExist(data)) return; Client.Instance.Update(DocumentPath.Id(data), r => GetMetaForUpdate(r, data, immediately, fields)); } internal void Update(T data, UpdateAction action, Expression> fields, bool immediately = true) { - CreateIfNotExist(data); + if (!CheckExist(data)) return; Client.Instance.Update(DocumentPath.Id(data), r => GetMetaForUpdate(r, data, action, fields, immediately)); } internal void Update(T data, Expression, Selector>> expression, int tenantId, bool immediately = true, params Expression>[] fields) { - CreateIfNotExist(data); + if (!CheckExist(data)) return; Client.Instance.UpdateByQuery(GetDescriptorForUpdate(data, expression, tenantId, immediately, fields)); } internal void Update(T data, Expression, Selector>> expression, int tenantId, UpdateAction action, Expression> fields, bool immediately = true) { - CreateIfNotExist(data); + if (!CheckExist(data)) return; Client.Instance.UpdateByQuery(GetDescriptorForUpdate(data, expression, tenantId, action, fields, immediately)); } @@ -459,22 +456,26 @@ namespace ASC.ElasticSearch ids.AddRange(Ids(lastIndexed)); ids.Add(meta.Item2); + Logger.DebugFormat("Index: {0},iterations total {1}", IndexName, ids.Count); + var j = 0; var tasks = new List(); - for (var i = 0; i < ids.Count - 1; i ++) + for (var i = 0; i < ids.Count - 1; i++) { if (Settings.Default.Threads == 1) { - IndexAllGetData(ids[i], ids[i+1], lastIndexed); + IndexAllGetData(ids[i], ids[i + 1], lastIndexed); } else { - tasks.Add(IndexAllGetDataAsync(ids[i], ids[i+1], lastIndexed)); + tasks.Add(IndexAllGetDataAsync(ids[i], ids[i + 1], lastIndexed)); j++; - if (j >= Settings.Default.Threads) + if (j >= Settings.Default.Threads || i == ids.Count - 2) { Task.WaitAll(tasks.ToArray()); + GC.Collect(); + Logger.DebugFormat("Index: {0},iteration:{1}", IndexName, i); tasks = new List(); j = 0; } @@ -520,26 +521,30 @@ namespace ASC.ElasticSearch return result.Documents; } - private void BeforeIndex(T data) + private bool BeforeIndex(T data) { - CreateIfNotExist(data); + if (!CheckExist(data)) return false; var wrapperWithDoc = data as WrapperWithDoc; if (wrapperWithDoc != null) { wrapperWithDoc.InitDocument(SearchSettings.CanIndexByContent(data.TenantId)); } + + return true; } - private async Task BeforeIndexAsync(T data) + private async Task BeforeIndexAsync(T data) { - CreateIfNotExist(data); + if (!CheckExist(data)) return false; var wrapperWithDoc = data as WrapperWithDoc; if (wrapperWithDoc != null) { await wrapperWithDoc.InitDocumentAsync(SearchSettings.CanIndexByContent(data.TenantId)).ConfigureAwait(false); } + + return true; } private void CreateIfNotExist(T data) @@ -1031,7 +1036,7 @@ namespace ASC.ElasticSearch var result = new List(); using (var db = DbManager.FromHttpContext("default", 1800000)) { - while(true) + while (true) { var dataQuery = GetBaseQuery(lastIndexed) .Select(idColumn) @@ -1065,7 +1070,8 @@ namespace ASC.ElasticSearch if (!string.IsNullOrEmpty(tenantIdColumn)) { dataQuery.InnerJoin("tenants_tenants t", Exp.EqColumns(tenantIdColumn, "t.id")) - .Where("t.status", TenantStatus.Active); + .Where("t.status", TenantStatus.Active) + .Where(!Exp.Like("t.alias", "nctautotest", SqlLike.StartWith)); } diff --git a/module/ASC.ElasticSearch/Engine/FactoryIndexer.cs b/module/ASC.ElasticSearch/Engine/FactoryIndexer.cs index 3bf13a929..62540b398 100644 --- a/module/ASC.ElasticSearch/Engine/FactoryIndexer.cs +++ b/module/ASC.ElasticSearch/Engine/FactoryIndexer.cs @@ -172,7 +172,7 @@ namespace ASC.ElasticSearch { Indexer.Index(data, immediately); } - catch(ElasticsearchClientException e) + catch (ElasticsearchClientException e) { Logger.Error(e); @@ -182,14 +182,14 @@ namespace ASC.ElasticSearch if (e.Response.HttpStatusCode == 413 || e.Response.HttpStatusCode == 403 || e.Response.HttpStatusCode == 408) { - data.ForEach(r => Index(r, immediately)); + data.Where(r => r != null).ToList().ForEach(r => Index(r, immediately)); } else if (e.Response.HttpStatusCode == 429) { Thread.Sleep(60000); - if (retry < 5) + if (retry < 10) { - Index(data, immediately, retry++); + Index(data.Where(r => r != null).ToList(), immediately, retry++); return; } @@ -211,14 +211,14 @@ namespace ASC.ElasticSearch if (inner.Response.HttpStatusCode == 413 || inner.Response.HttpStatusCode == 403) { Logger.Error(inner.Response.HttpStatusCode); - data.ForEach(r => Index(r, immediately)); + data.Where(r => r != null).ToList().ForEach(r => Index(r, immediately)); } - else if(inner.Response.HttpStatusCode == 429) + else if (inner.Response.HttpStatusCode == 429) { Thread.Sleep(60000); - if (retry < 5) + if (retry < 10) { - Index(data, immediately, retry++); + Index(data.Where(r => r != null).ToList(), immediately, retry++); return; } @@ -240,7 +240,7 @@ namespace ASC.ElasticSearch { await Indexer.IndexAsync(data, immediately).ConfigureAwait(false); } - catch(ElasticsearchClientException e) + catch (ElasticsearchClientException e) { Logger.Error(e); @@ -250,14 +250,14 @@ namespace ASC.ElasticSearch if (e.Response.HttpStatusCode == 413 || e.Response.HttpStatusCode == 403 || e.Response.HttpStatusCode == 408) { - data.ForEach(r => Index(r, immediately)); + data.Where(r => r != null).ToList().ForEach(r => Index(r, immediately)); } else if (e.Response.HttpStatusCode == 429) { await Task.Delay(60000); - if (retry < 5) + if (retry < 10) { - await IndexAsync(data, immediately, retry++); + await IndexAsync(data.Where(r => r != null).ToList(), immediately, retry++); return; } @@ -279,14 +279,14 @@ namespace ASC.ElasticSearch if (inner.Response.HttpStatusCode == 413 || inner.Response.HttpStatusCode == 403) { Logger.Error(inner.Response.HttpStatusCode); - data.ForEach(r => Index(r, immediately)); + data.Where(r => r != null).ToList().ForEach(r => Index(r, immediately)); } - else if(inner.Response.HttpStatusCode == 429) + else if (inner.Response.HttpStatusCode == 429) { await Task.Delay(60000); - if (retry < 5) + if (retry < 10) { - await IndexAsync(data, immediately, retry++); + await IndexAsync(data.Where(r => r != null).ToList(), immediately, retry++); return; } @@ -461,6 +461,7 @@ namespace ASC.ElasticSearch public class FactoryIndexer { private static readonly ICache cache = AscCache.Memory; + private static readonly ILog Logger = LogManager.GetLogger("ASC.Indexer"); internal static IContainer Builder { get; set; } internal static bool Init { get; set; } @@ -476,7 +477,7 @@ namespace ASC.ElasticSearch } else { - LogManager.GetLogger("ASC.Indexer").Fatal("FactoryIndexer container == null"); + Logger.Fatal("FactoryIndexer container == null"); return; } @@ -490,7 +491,7 @@ namespace ASC.ElasticSearch } catch (Exception e) { - LogManager.GetLogger("ASC.Indexer").Fatal("FactoryIndexer", e); + Logger.Fatal("FactoryIndexer", e); } } @@ -510,7 +511,6 @@ namespace ASC.ElasticSearch } var cacheTime = DateTime.UtcNow.AddMinutes(15); - var logger = LogManager.GetLogger("ASC.Indexer"); try { @@ -518,7 +518,7 @@ namespace ASC.ElasticSearch var isValid = result.IsValid; - logger.DebugFormat("CheckState ping {0}", result.DebugInformation); + Logger.DebugFormat("CheckState ping {0}", result.DebugInformation); if (cacheState) { @@ -534,7 +534,7 @@ namespace ASC.ElasticSearch cache.Insert(key, "false", cacheTime); } - logger.Error("Ping false", e); + Logger.Error("Ping false", e); return false; } } diff --git a/module/ASC.Feed.Aggregator/ASC.Feed.Aggregator.csproj b/module/ASC.Feed.Aggregator/ASC.Feed.Aggregator.csproj index 3516cde01..348ae5910 100644 --- a/module/ASC.Feed.Aggregator/ASC.Feed.Aggregator.csproj +++ b/module/ASC.Feed.Aggregator/ASC.Feed.Aggregator.csproj @@ -72,6 +72,8 @@ + + @@ -135,7 +137,7 @@ - 5.1.2 + 6.2.0 diff --git a/module/ASC.Feed.Aggregator/FeedAggregatorService.cs b/module/ASC.Feed.Aggregator/FeedAggregatorService.cs index c6c09d782..98a568ce5 100644 --- a/module/ASC.Feed.Aggregator/FeedAggregatorService.cs +++ b/module/ASC.Feed.Aggregator/FeedAggregatorService.cs @@ -34,6 +34,7 @@ using ASC.Feed.Aggregator.Modules; using ASC.Feed.Aggregator.Modules.Community; using ASC.Feed.Aggregator.Modules.CRM; using ASC.Feed.Aggregator.Modules.Documents; +using ASC.Feed.Aggregator.Modules.People; using ASC.Feed.Aggregator.Modules.Projects; using ASC.Feed.Data; using ASC.Projects.Core.Services.NotifyService; @@ -55,6 +56,8 @@ namespace ASC.Feed.Aggregator new ForumTopicsModule(), new ForumPostsModule(), new EventsModule(), + new BirthdaysModule(), + new NewEmployeeModule(), new ContactsModule(), new CrmTasksModule(), new DealsModule(), @@ -292,7 +295,7 @@ namespace ASC.Feed.Aggregator { try { - SecurityContext.AuthenticateMe(CoreContext.Authentication.GetAccountByID(userid)); + SecurityContext.CurrentUser = userid; return true; } catch diff --git a/module/ASC.Feed.Aggregator/Modules/Community/BlogsModule.cs b/module/ASC.Feed.Aggregator/Modules/Community/BlogsModule.cs index cac5c6199..aac4e4ba9 100644 --- a/module/ASC.Feed.Aggregator/Modules/Community/BlogsModule.cs +++ b/module/ASC.Feed.Aggregator/Modules/Community/BlogsModule.cs @@ -186,7 +186,7 @@ namespace ASC.Feed.Aggregator.Modules.Community Module = Name, Action = comments.Any() ? FeedAction.Commented : FeedAction.Created, Title = post.Title, - Description = HtmlUtility.GetFull(post.Content), + Description = HtmlUtility.GetFull(post.Content, false), HasPreview = post.Content.Contains("class=\"asccut\""), CanComment = true, CommentApiUrl = CommonLinkUtility.ToAbsolute(commentApiUrl), diff --git a/module/ASC.Feed.Aggregator/Modules/Community/EventsModule.cs b/module/ASC.Feed.Aggregator/Modules/Community/EventsModule.cs index 5fa184221..1480fe113 100644 --- a/module/ASC.Feed.Aggregator/Modules/Community/EventsModule.cs +++ b/module/ASC.Feed.Aggregator/Modules/Community/EventsModule.cs @@ -191,8 +191,8 @@ namespace ASC.Feed.Aggregator.Modules.Community Module = Name, Action = comments.Any() ? FeedAction.Commented : FeedAction.Created, Title = evt.Caption, - Description = HtmlUtility.GetFull(evt.Text), - HasPreview = false, + Description = HtmlUtility.GetFull(evt.Text, false), + HasPreview = evt.Text.Contains("class=\"asccut\""), CanComment = true, CommentApiUrl = CommonLinkUtility.ToAbsolute(commentApiUrl), Comments = comments.Select(ToFeedComment), diff --git a/module/ASC.Feed.Aggregator/Modules/Community/ForumPostsModule.cs b/module/ASC.Feed.Aggregator/Modules/Community/ForumPostsModule.cs index 022554a6d..4f8d5b413 100644 --- a/module/ASC.Feed.Aggregator/Modules/Community/ForumPostsModule.cs +++ b/module/ASC.Feed.Aggregator/Modules/Community/ForumPostsModule.cs @@ -124,9 +124,9 @@ namespace ASC.Feed.Aggregator.Modules.Community Product = Product, Module = Name, Title = post.Subject, - Description = HtmlUtility.GetFull(post.Text), + Description = HtmlUtility.GetFull(post.Text, false), Keywords = string.Format("{0} {1}", post.Subject, post.Text), - HasPreview = false, + HasPreview = post.Text.Contains("class=\"asccut\""), CanComment = false, GroupId = string.Format("{0}_{1}", item, post.ID) }; diff --git a/module/ASC.Feed.Aggregator/Modules/Community/ForumTopicsModule.cs b/module/ASC.Feed.Aggregator/Modules/Community/ForumTopicsModule.cs index 3ee84c6df..75cd3d2df 100644 --- a/module/ASC.Feed.Aggregator/Modules/Community/ForumTopicsModule.cs +++ b/module/ASC.Feed.Aggregator/Modules/Community/ForumTopicsModule.cs @@ -168,11 +168,11 @@ namespace ASC.Feed.Aggregator.Modules.Community Product = Product, Module = Name, Title = post.Topic.Title, - Description = HtmlUtility.GetFull(post.Text), + Description = HtmlUtility.GetFull(post.Text, false), ExtraLocation = post.Topic.ThreadTitle, ExtraLocationUrl = CommonLinkUtility.ToAbsolute(threadUrl), Keywords = string.Format("{0} {1}", post.Topic.Title, post.Text), - HasPreview = false, + HasPreview = post.Text.Contains("class=\"asccut\""), CanComment = false, GroupId = GetGroupId(item, post.Topic.PosterID, post.Topic.ThreadID.ToString(CultureInfo.InvariantCulture)) }; diff --git a/module/ASC.Feed.Aggregator/Modules/ModulesHelper.cs b/module/ASC.Feed.Aggregator/Modules/ModulesHelper.cs index 3f73c7c04..9e6293577 100644 --- a/module/ASC.Feed.Aggregator/Modules/ModulesHelper.cs +++ b/module/ASC.Feed.Aggregator/Modules/ModulesHelper.cs @@ -62,5 +62,15 @@ namespace ASC.Feed.Aggregator.Modules { get { return "documents"; } } + + public static Guid PeopleProductID + { + get { return WebItemManager.PeopleProductID; } + } + + public static string PeopleProductName + { + get { return "people"; } + } } } \ No newline at end of file diff --git a/module/ASC.Feed.Aggregator/Modules/People/BirthdaysModule.cs b/module/ASC.Feed.Aggregator/Modules/People/BirthdaysModule.cs new file mode 100644 index 000000000..179fd6a7d --- /dev/null +++ b/module/ASC.Feed.Aggregator/Modules/People/BirthdaysModule.cs @@ -0,0 +1,129 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Common.Data; +using ASC.Common.Data.Sql; +using ASC.Common.Data.Sql.Expressions; +using ASC.Core; +using ASC.Core.Tenants; +using ASC.Core.Users; +using ASC.Feed.Data; +using ASC.Web.Studio.Utility; + +namespace ASC.Feed.Aggregator.Modules.People +{ + internal class BirthdaysModule : FeedModule + { + + public override string Name + { + get { return Constants.BirthdaysModule; } + } + + public override string Product + { + get { return ModulesHelper.PeopleProductName; } + } + + public override Guid ProductID + { + get { return ModulesHelper.PeopleProductID; } + } + + protected override string Table + { + get { return null; } + } + + protected override string LastUpdatedColumn + { + get { return null; } + } + + protected override string TenantColumn + { + get { return null; } + } + + protected override string DbId + { + get { return Constants.PeopleDbId; } + } + + public override IEnumerable GetTenantsWithFeeds(DateTime fromTime) + { + var lastTimeAggregate = FeedAggregateDataProvider.GetLastTimeAggregate(GetType().Name); + var now = DateTime.UtcNow; + + if (lastTimeAggregate.Date == now.Date) + { + return new List(); + } + + var q = new SqlQuery("tenants_tenants t") + .Select("t.id") + .Distinct() + .InnerJoin("core_user u", Exp.EqColumns("u.tenant", "t.id")) + .Where(Exp.Eq("t.status", TenantStatus.Active)) + .Where(Exp.Eq("u.status", EmployeeStatus.Active)) + .Where(!Exp.Eq("u.bithdate", null)) + .Where(Exp.Eq("month(u.bithdate)", now.Month)) + .Where(Exp.Eq("day(u.bithdate)", now.Day)); + + using (var db = DbManager.FromHttpContext(DbId)) + { + return db.ExecuteList(q) + .ConvertAll(r => Convert.ToInt32(r[0])); + } + } + + public override IEnumerable> GetFeeds(FeedFilter filter) + { + var now = DateTime.UtcNow; + var list = (from u in CoreContext.UserManager.GetUsers(EmployeeStatus.Active, EmployeeType.User) + where u.BirthDate.HasValue && u.BirthDate.Value.Month.Equals(now.Month) && u.BirthDate.Value.Day.Equals(now.Day) + orderby u.DisplayUserName() + select u).ToList(); + return list.Select(c => new Tuple(ToFeed(c, now), c)); + } + + + private Feed ToFeed(UserInfo user, DateTime now) + { + var item = Constants.BirthdaysModule; + + return new Feed(user.ID, now.Date) + { + Item = item, + ItemId = user.ID.ToString(), + Title = Web.Studio.PublicResources.FeedResource.Birthday, + ItemUrl = CommonLinkUtility.ToAbsolute("~/Products/People/Birthdays.aspx"), + Product = Product, + Action = FeedAction.AllDayEventCreated, + Module = Name, + IsAllDayEvent = true, + GroupId = GetGroupId(item, user.ID), + HasPreview = false, + CanComment = false + }; + } + } +} diff --git a/module/ASC.Feed.Aggregator/Modules/People/NewEmployeeModule.cs b/module/ASC.Feed.Aggregator/Modules/People/NewEmployeeModule.cs new file mode 100644 index 000000000..6fe8dfc9a --- /dev/null +++ b/module/ASC.Feed.Aggregator/Modules/People/NewEmployeeModule.cs @@ -0,0 +1,115 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Common.Data; +using ASC.Common.Data.Sql; +using ASC.Common.Data.Sql.Expressions; +using ASC.Core; +using ASC.Core.Users; +using ASC.Web.Studio.Utility; + +namespace ASC.Feed.Aggregator.Modules.People +{ + internal class NewEmployeeModule : FeedModule + { + + public override string Name + { + get { return Constants.NewEmployeeModule; } + } + + public override string Product + { + get { return ModulesHelper.PeopleProductName; } + } + + public override Guid ProductID + { + get { return ModulesHelper.PeopleProductID; } + } + + protected override string Table + { + get { return "core_user"; } + } + + protected override string LastUpdatedColumn + { + get { return "last_modified"; } + } + + protected override string TenantColumn + { + get { return "tenant"; } + } + + protected override string DbId + { + get { return Constants.PeopleDbId; } + } + + public override IEnumerable GetTenantsWithFeeds(DateTime fromTime) + { + var q1 = new SqlQuery(Table) + .Select(TenantColumn) + .Where(Exp.Gt(LastUpdatedColumn, fromTime)) + .GroupBy(1) + .Having(Exp.Gt("count(*)", 0)); + + using (var db = DbManager.FromHttpContext(DbId)) + { + return db.ExecuteList(q1) + .ConvertAll(r => Convert.ToInt32(r[0])); + } + } + + public override IEnumerable> GetFeeds(FeedFilter filter) + { + var list = (from u in CoreContext.UserManager.GetUsers(EmployeeStatus.Active, EmployeeType.User) + where u.CreateDate >= filter.Time.From && u.CreateDate <= filter.Time.To + orderby u.DisplayUserName() + select u) + .ToList(); + return list.Select(c => new Tuple(ToFeed(c), c)); + } + + + private Feed ToFeed(UserInfo user) + { + var item = Constants.NewEmployeeModule; + + return new Feed(user.ID, user.CreateDate) + { + Item = item, + ItemId = user.ID.ToString(), + Title = Web.Studio.PublicResources.FeedResource.NewEmployee, + ItemUrl = CommonLinkUtility.ToAbsolute("~/Products/People/Default.aspx"), + Product = Product, + Module = Name, + IsAllDayEvent = true, + Action = FeedAction.AllDayEventCreated, + GroupId = GetGroupId(item, user.ID), + HasPreview = false, + CanComment = false + }; + } + } +} diff --git a/module/ASC.Feed.Aggregator/Modules/Projects/DiscussionsModule.cs b/module/ASC.Feed.Aggregator/Modules/Projects/DiscussionsModule.cs index 842b978fa..4f5cf334b 100644 --- a/module/ASC.Feed.Aggregator/Modules/Projects/DiscussionsModule.cs +++ b/module/ASC.Feed.Aggregator/Modules/Projects/DiscussionsModule.cs @@ -254,7 +254,7 @@ namespace ASC.Feed.Aggregator.Modules.Projects Module = Name, Action = comments.Any() ? FeedAction.Commented : FeedAction.Created, Title = discussion.Title, - Description = HtmlUtility.GetFull(discussion.Description), + Description = HtmlUtility.GetFull(discussion.Description, false), ExtraLocation = discussion.Project.Title, ExtraLocationUrl = CommonLinkUtility.ToAbsolute(projectUrl), HasPreview = discussion.Description.Contains("class=\"asccut\""), diff --git a/module/ASC.Feed/ASC.Feed.csproj b/module/ASC.Feed/ASC.Feed.csproj index b913d5311..153e7501b 100644 --- a/module/ASC.Feed/ASC.Feed.csproj +++ b/module/ASC.Feed/ASC.Feed.csproj @@ -93,7 +93,7 @@ - 12.0.3 + 13.0.1 diff --git a/module/ASC.Feed/Constants.cs b/module/ASC.Feed/Constants.cs index 888b1197f..7b770d7b9 100644 --- a/module/ASC.Feed/Constants.cs +++ b/module/ASC.Feed/Constants.cs @@ -19,88 +19,36 @@ namespace ASC.Feed { public class Constants { + #region DbId + public const string FeedDbId = "core"; public const string ProjectsDbId = "projects"; public const string FilesDbId = "files"; public const string CommunityDbId = "community"; public const string CrmDbId = "crm"; + public const string PeopleDbId = "people"; + + #endregion #region Modules - public static string BookmarksModule - { - get { return "bookmarks"; } - } - - public static string BlogsModule - { - get { return "blogs"; } - } - - public static string ForumsModule - { - get { return "forums"; } - } - - public static string EventsModule - { - get { return "events"; } - } - - public static string ProjectsModule - { - get { return "projects"; } - } - - public static string MilestonesModule - { - get { return "milestones"; } - } - - public static string DiscussionsModule - { - get { return "discussions"; } - } - - public static string TasksModule - { - get { return "tasks"; } - } - - public static string CommentsModule - { - get { return "comments"; } - } - - public static string CrmTasksModule - { - get { return "crmTasks"; } - } - - public static string ContactsModule - { - get { return "contacts"; } - } - - public static string DealsModule - { - get { return "deals"; } - } - - public static string CasesModule - { - get { return "cases"; } - } - - public static string FilesModule - { - get { return "files"; } - } - - public static string FoldersModule - { - get { return "folders"; } - } + public static string BookmarksModule = "bookmarks"; + public static string BlogsModule = "blogs"; + public static string ForumsModule = "forums"; + public static string EventsModule = "events"; + public static string ProjectsModule = "projects"; + public static string MilestonesModule = "milestones"; + public static string DiscussionsModule = "discussions"; + public static string TasksModule = "tasks"; + public static string CommentsModule = "comments"; + public static string CrmTasksModule = "crmTasks"; + public static string ContactsModule = "contacts"; + public static string DealsModule = "deals"; + public static string CasesModule = "cases"; + public static string FilesModule = "files"; + public static string FoldersModule = "folders"; + public static string BirthdaysModule = "birthday"; + public static string NewEmployeeModule = "newEmployee"; #endregion } diff --git a/module/ASC.Feed/Data/FeedAggregateDataProvider.cs b/module/ASC.Feed/Data/FeedAggregateDataProvider.cs index 1644a4ddc..2d40b1a6f 100644 --- a/module/ASC.Feed/Data/FeedAggregateDataProvider.cs +++ b/module/ASC.Feed/Data/FeedAggregateDataProvider.cs @@ -27,6 +27,7 @@ using ASC.Core; using ASC.Core.Tenants; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; namespace ASC.Feed.Data { @@ -331,11 +332,19 @@ namespace ASC.Feed.Data GroupId = groupId; - if (now.Year == createdDate.Year && now.Date == createdDate.Date) + var compareDate = JObject.Parse(Json).Value("IsAllDayEvent") + ? TenantUtil.DateTimeToUtc(createdDate).Date + : createdDate.Date; + + if (now.Date == compareDate.AddDays(-1)) + { + IsTomorrow = true; + } + else if(now.Date == compareDate) { IsToday = true; } - else if (now.Year == createdDate.Year && now.Date == createdDate.Date.AddDays(1)) + else if (now.Date == compareDate.AddDays(1)) { IsYesterday = true; } @@ -359,6 +368,8 @@ namespace ASC.Feed.Data public bool IsYesterday { get; private set; } + public bool IsTomorrow { get; private set; } + public DateTime CreatedDate { get; private set; } public DateTime ModifiedDate { get; private set; } diff --git a/module/ASC.Feed/Feed.cs b/module/ASC.Feed/Feed.cs index 53771c166..ff7139ee2 100644 --- a/module/ASC.Feed/Feed.cs +++ b/module/ASC.Feed/Feed.cs @@ -70,6 +70,9 @@ namespace ASC.Feed.Aggregator [DataMember] public string Module { get; set; } + [DataMember(EmitDefaultValue = false)] + public bool? IsAllDayEvent { get; set; } + [DataMember] public string ExtraLocation { get; set; } diff --git a/module/ASC.Feed/FeedAction.cs b/module/ASC.Feed/FeedAction.cs index 434f2321e..c007b7eca 100644 --- a/module/ASC.Feed/FeedAction.cs +++ b/module/ASC.Feed/FeedAction.cs @@ -24,6 +24,7 @@ namespace ASC.Feed { Created = 0, Updated = 1, - Commented = 2 + Commented = 2, + AllDayEventCreated = 3 } } \ No newline at end of file diff --git a/module/ASC.Files.AutoCleanUp/ASC.Files.AutoCleanUp.csproj b/module/ASC.Files.AutoCleanUp/ASC.Files.AutoCleanUp.csproj new file mode 100644 index 000000000..7b1acbdc6 --- /dev/null +++ b/module/ASC.Files.AutoCleanUp/ASC.Files.AutoCleanUp.csproj @@ -0,0 +1,69 @@ + + + + + Debug + AnyCPU + {F120112A-92BB-4F9F-AE3F-3F0459192C9E} + Library + Properties + ASC.Files.AutoCleanUp + ASC.Files.AutoCleanUp + v4.8 + 512 + true + + + true + full + false + ..\..\web\studio\ASC.Web.Studio\bin\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + {76DE7717-3D4B-4A5B-B740-15B8913DF0CB} + ASC.Common + + + {A51D0454-4AFA-46DE-89D4-B03D37E1816C} + ASC.Core.Common + + + {02C40A64-FE22-41D0-9037-69F0D6F787A9} + ASC.Web.Core + + + {8c534af7-5696-4e68-9ff4-ffc311893c10} + ASC.Web.Files + + + + + + + + + + + \ No newline at end of file diff --git a/module/ASC.Files.AutoCleanUp/ConfigSection.cs b/module/ASC.Files.AutoCleanUp/ConfigSection.cs new file mode 100644 index 000000000..21626623b --- /dev/null +++ b/module/ASC.Files.AutoCleanUp/ConfigSection.cs @@ -0,0 +1,36 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System; +using System.Configuration; + +namespace ASC.Files.AutoCleanUp +{ + public class ConfigSection : ConfigurationSection + { + [ConfigurationProperty("period", DefaultValue = "0:5:0")] + public TimeSpan Period + { + get { return (TimeSpan)this["period"]; } + } + + public static ConfigSection GetSection() + { + return (ConfigSection)ConfigurationManagerExtension.GetSection("autoCleanUp") ?? new ConfigSection(); + } + } +} \ No newline at end of file diff --git a/module/ASC.Files.AutoCleanUp/Launcher.cs b/module/ASC.Files.AutoCleanUp/Launcher.cs new file mode 100644 index 000000000..b399d2552 --- /dev/null +++ b/module/ASC.Files.AutoCleanUp/Launcher.cs @@ -0,0 +1,57 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System.Threading; + +using ASC.Common.Logging; +using ASC.Common.Module; + +namespace ASC.Files.AutoCleanUp +{ + public class Launcher : IServiceController + { + private Worker worker; + private CancellationTokenSource cancellationToken; + private ILog logger = LogManager.GetLogger("ASC.Files.AutoCleanUp"); + + public void Start() + { + logger.Info("Launcher: Starting"); + var configSection = ConfigSection.GetSection(); + cancellationToken = new CancellationTokenSource(); + worker = new Worker(configSection, logger, cancellationToken); + worker.Start(); + logger.Info("Launcher: Started"); + } + + public void Stop() + { + logger.Info("Launcher: Stopping"); + if (cancellationToken != null) + { + cancellationToken.Cancel(); + } + + if (worker != null) + { + worker.Stop(); + worker = null; + } + logger.Info("Launcher: Stopped"); + } + } +} \ No newline at end of file diff --git a/module/ASC.Files.AutoCleanUp/Properties/AssemblyInfo.cs b/module/ASC.Files.AutoCleanUp/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..06316e915 --- /dev/null +++ b/module/ASC.Files.AutoCleanUp/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ASC.Files.AutoCleanUp")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Ascensio System SIA")] +[assembly: AssemblyProduct("ASC.Files.AutoCleanUp")] +[assembly: AssemblyCopyright("(c) Ascensio System SIA. All rights reserved")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("af1000b6-a647-4a7f-b2b8-842061cc77ec")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/module/ASC.Files.AutoCleanUp/TenantUserSettings.cs b/module/ASC.Files.AutoCleanUp/TenantUserSettings.cs new file mode 100644 index 000000000..08a9813c0 --- /dev/null +++ b/module/ASC.Files.AutoCleanUp/TenantUserSettings.cs @@ -0,0 +1,29 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System; +using ASC.Files.Core; + +namespace ASC.Files.AutoCleanUp +{ + public class TenantUserSettings + { + public int TenantId { get; set; } + public Guid UserId { get; set; } + public DateToAutoCleanUp Setting { get; set; } + } +} diff --git a/module/ASC.Files.AutoCleanUp/Worker.cs b/module/ASC.Files.AutoCleanUp/Worker.cs new file mode 100644 index 000000000..27aea8b5b --- /dev/null +++ b/module/ASC.Files.AutoCleanUp/Worker.cs @@ -0,0 +1,191 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +using ASC.Common.Data; +using ASC.Common.Data.Sql; +using ASC.Common.Data.Sql.Expressions; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Configuration; +using ASC.Core.Tenants; +using ASC.Files.Core; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Services.WCFService; + +namespace ASC.Files.AutoCleanUp +{ + public class Worker + { + private readonly ILog logger; + private readonly CancellationTokenSource cancellationToken; + private bool _isStarted; + private Timer timer; + + public Worker(ConfigSection configSection, ILog log, CancellationTokenSource cancellationTokenSource) + { + logger = log; + cancellationToken = cancellationTokenSource; + Period = configSection.Period; + } + + public TimeSpan Period { get; set; } + + public void Start() + { + if (!_isStarted) + { + logger.Info("Service: Starting"); + timer = new Timer(DeleteExpiredFilesInTrash, null, TimeSpan.Zero, Period); + logger.Info("Service: Started"); + _isStarted = true; + } + } + + public void Stop() + { + if (_isStarted) + { + logger.Info("Service: Stopping"); + if (timer != null) + { + timer.Change(Timeout.Infinite, Timeout.Infinite); + timer.Dispose(); + timer = null; + } + + if (cancellationToken != null) + { + cancellationToken.Dispose(); + } + + _isStarted = false; + logger.Info("Service: Stopped"); + } + } + + + public void DeleteExpiredFilesInTrash(object _) + { + if (cancellationToken != null && cancellationToken.IsCancellationRequested) + { + Stop(); + return; + } + + timer.Change(Timeout.Infinite, Timeout.Infinite); + logger.Info("Start procedure"); + + var activeTenantsUsers = GetTenantsUsers(); + + if (!activeTenantsUsers.Any()) + { + logger.InfoFormat("Waiting for data. Sleep {0}.", Period); + timer.Change(Period, TimeSpan.FromMilliseconds(-1)); + return; + } + + logger.InfoFormat("Found {0} active users", activeTenantsUsers.Count); + + Parallel.ForEach(activeTenantsUsers, tenantUser => + { + DeleteFilesAndFolders(tenantUser); + }); + + logger.Info("Finish procedure"); + timer.Change(Period, Period); + } + + private void DeleteFilesAndFolders(TenantUserSettings tenantUser) + { + try + { + CoreContext.TenantManager.SetCurrentTenant(tenantUser.TenantId); + var userAccount = CoreContext.Authentication.GetAccountByID(tenantUser.UserId); + + if (userAccount == Constants.Guest) return; + + SecurityContext.CurrentAccount = userAccount; + + using (var fileDao = Global.DaoFactory.GetFileDao()) + using (var folderDao = Global.DaoFactory.GetFolderDao()) + { + var now = DateTime.UtcNow; + + var itemList = new ItemList(); + var trashId = folderDao.GetFolderIDTrash(false, tenantUser.UserId); + itemList.AddRange(folderDao.GetFolders(trashId) + .Where(x => FileDateTime.GetModifiedOnWithAutoCleanUp(x.ModifiedOn, tenantUser.Setting, true) < now) + .Select(f => "folder_" + f.ID)); + itemList.AddRange(fileDao.GetFiles(trashId, null, default(FilterType), false, Guid.Empty, string.Empty, false) + .Where(x => FileDateTime.GetModifiedOnWithAutoCleanUp(x.ModifiedOn, tenantUser.Setting, true) < now) + .Select(y => "file_" + y.ID)); + + logger.InfoFormat("Start clean up tenant {0}, folder {1}", tenantUser.TenantId, trashId); + Global.FileStorageService.DeleteItems("delete", itemList, true, false, true); + logger.InfoFormat("Waiting for tenant {0}, folder {1}...", tenantUser.TenantId, trashId); + while (true) + { + var statuses = Global.FileStorageService.GetTasksStatuses(); + + if (statuses.TrueForAll(r => r.Finished)) + break; + + Thread.Sleep(100); + } + logger.InfoFormat("Finish clean up tenant {0}, folder {1}", tenantUser.TenantId, trashId); + } + } + catch (Exception ex) + { + logger.Error(ex); + } + finally + { + SecurityContext.Logout(); + } + } + + private List GetTenantsUsers() + { + var query = new SqlQuery("tenants_tenants t") + .Select("t.id") + .Select("wss.userid") + .Select("JSON_EXTRACT(`Data`, '$.AutomaticallyCleanUp.Gap')") + .Select("JSON_EXTRACT(`Data`, '$.AutomaticallyCleanUp.IsAutoCleanUp') settings") + .InnerJoin("webstudio_settings wss", Exp.EqColumns("wss.TenantID", "t.id")) + .Where("wss.id", new FilesSettings().ID) + .Where("t.status", (int)TenantStatus.Active) + .Having(Exp.Sql("settings = true")); + + using (var dbManager = DbManager.FromHttpContext("default", 180000)) + { + return dbManager.ExecuteList(query).Select(r => new TenantUserSettings() + { + TenantId = Convert.ToInt32(r[0]), + UserId = new Guid(Convert.ToString(r[1])), + Setting = (DateToAutoCleanUp)Convert.ToInt32(r[2]) + }).ToList(); + } + } + } +} \ No newline at end of file diff --git a/module/ASC.Files.AutoCleanUp/app.config b/module/ASC.Files.AutoCleanUp/app.config new file mode 100644 index 000000000..58c01219d --- /dev/null +++ b/module/ASC.Files.AutoCleanUp/app.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/module/ASC.Files.Thirdparty/ASC.Files.Thirdparty.csproj b/module/ASC.Files.Thirdparty/ASC.Files.Thirdparty.csproj index 2e3cf3bcf..f260e2c6e 100644 --- a/module/ASC.Files.Thirdparty/ASC.Files.Thirdparty.csproj +++ b/module/ASC.Files.Thirdparty/ASC.Files.Thirdparty.csproj @@ -130,26 +130,26 @@ - 1.1.0.452 + 1.2.0.1 - 3.23.0 + 3.26.0 - 4.9.4 + 6.13.0 - 1.45.0.1911 + 1.54.0.2397 - 1.20.1.4 + 1.6.0 + TargetFramework=net45 - 2.0.7.5 + 2.1.0 - 16.1.20324.12000 - TargetFramework=net45 + 16.1.21610.12000 4.3.4 diff --git a/module/ASC.Files.Thirdparty/Box/BoxFileDao.cs b/module/ASC.Files.Thirdparty/Box/BoxFileDao.cs index a23de5837..c0515acef 100644 --- a/module/ASC.Files.Thirdparty/Box/BoxFileDao.cs +++ b/module/ASC.Files.Thirdparty/Box/BoxFileDao.cs @@ -515,9 +515,9 @@ namespace ASC.Files.Thirdparty.Box return false; } - public void SaveThumbnail(File file, Stream thumbnail) - { - //Do nothing + public void SaveThumbnail(File file, Stream thumbnail) + { + //Do nothing } public Stream GetThumbnail(File file) @@ -535,6 +535,15 @@ namespace ASC.Files.Thirdparty.Box return Task.FromResult(IsExistOnStorage(file)); } + public EntryProperties GetProperties(object fileId) + { + return null; + } + + public void SaveProperties(object fileId, EntryProperties entryProperties) + { + } + #endregion } } \ No newline at end of file diff --git a/module/ASC.Files.Thirdparty/CachedProviderAccountDao.cs b/module/ASC.Files.Thirdparty/CachedProviderAccountDao.cs index 82cab045c..1ad8bf760 100644 --- a/module/ASC.Files.Thirdparty/CachedProviderAccountDao.cs +++ b/module/ASC.Files.Thirdparty/CachedProviderAccountDao.cs @@ -61,6 +61,7 @@ namespace ASC.Files.Thirdparty base.RemoveProviderInfo(linkId); var key = _rootKey + linkId.ToString(CultureInfo.InvariantCulture); + RemoveFromCache(key); cacheNotify.Publish(new ProviderAccountCacheItem { Key = key }, CacheNotifyAction.Remove); } @@ -69,6 +70,7 @@ namespace ASC.Files.Thirdparty var result = base.UpdateProviderInfo(linkId, customerTitle, authData, folderType, userId); var key = _rootKey + linkId.ToString(CultureInfo.InvariantCulture); + RemoveFromCache(key); cacheNotify.Publish(new ProviderAccountCacheItem { Key = key }, CacheNotifyAction.Update); return result; } @@ -78,6 +80,7 @@ namespace ASC.Files.Thirdparty var result = base.UpdateProviderInfo(linkId, authData); var key = _rootKey + linkId.ToString(CultureInfo.InvariantCulture); + RemoveFromCache(key); cacheNotify.Publish(new ProviderAccountCacheItem { Key = key }, CacheNotifyAction.Update); return result; } diff --git a/module/ASC.Files.Thirdparty/Dropbox/DropboxDaoBase.cs b/module/ASC.Files.Thirdparty/Dropbox/DropboxDaoBase.cs index 12b58d667..239e6b82e 100644 --- a/module/ASC.Files.Thirdparty/Dropbox/DropboxDaoBase.cs +++ b/module/ASC.Files.Thirdparty/Dropbox/DropboxDaoBase.cs @@ -280,7 +280,7 @@ namespace ASC.Files.Thirdparty.Dropbox ContentLength = (long)dropboxFile.Size, CreateBy = DropboxProviderInfo.Owner, CreateOn = TenantUtil.DateTimeFromUtc(dropboxFile.ServerModified), - FileStatus = FileStatus.None, + FileStatus = ASC.Files.Core.FileStatus.None, FolderID = MakeId(GetParentFolderPath(dropboxFile)), ModifiedBy = DropboxProviderInfo.Owner, ModifiedOn = TenantUtil.DateTimeFromUtc(dropboxFile.ServerModified), diff --git a/module/ASC.Files.Thirdparty/Dropbox/DropboxFileDao.cs b/module/ASC.Files.Thirdparty/Dropbox/DropboxFileDao.cs index 1d9af51be..06f1e75ff 100644 --- a/module/ASC.Files.Thirdparty/Dropbox/DropboxFileDao.cs +++ b/module/ASC.Files.Thirdparty/Dropbox/DropboxFileDao.cs @@ -582,6 +582,15 @@ namespace ASC.Files.Thirdparty.Dropbox return Task.FromResult(IsExistOnStorage(file)); } + public EntryProperties GetProperties(object fileId) + { + return null; + } + + public void SaveProperties(object fileId, EntryProperties entryProperties) + { + } + #endregion } } \ No newline at end of file diff --git a/module/ASC.Files.Thirdparty/Dropbox/DropboxProviderInfo.cs b/module/ASC.Files.Thirdparty/Dropbox/DropboxProviderInfo.cs index 34a898c44..1a926f91f 100644 --- a/module/ASC.Files.Thirdparty/Dropbox/DropboxProviderInfo.cs +++ b/module/ASC.Files.Thirdparty/Dropbox/DropboxProviderInfo.cs @@ -25,6 +25,8 @@ using ASC.Common.Caching; using ASC.Common.Web; using ASC.Core; using ASC.FederatedLogin; +using ASC.FederatedLogin.Helpers; +using ASC.FederatedLogin.LoginProviders; using ASC.Files.Core; using Dropbox.Api.Files; @@ -34,7 +36,7 @@ namespace ASC.Files.Thirdparty.Dropbox [DebuggerDisplay("{CustomerTitle}")] public class DropboxProviderInfo : IProviderInfo, IDisposable { - private readonly OAuth20Token _token; + private OAuth20Token _token; private readonly FolderType _rootFolderType; private readonly DateTime _createOn; @@ -152,10 +154,26 @@ namespace ASC.Files.Thirdparty.Dropbox { var dropboxStorage = new DropboxStorage(); + CheckToken(); + dropboxStorage.Open(_token); return dropboxStorage; } + private void CheckToken() + { + if (_token == null) throw new UnauthorizedAccessException("Cannot create Dropbox session with given token"); + if (_token.IsExpired) + { + _token = OAuth20TokenHelper.RefreshToken(_token); + + using (var dbDao = new CachedProviderAccountDao(CoreContext.TenantManager.GetCurrentTenant().TenantId, FileConstant.DatabaseId)) + { + dbDao.UpdateProviderInfo(ID, new AuthData(token: _token.ToJson())); + } + } + } + private class StorageDisposableWrapper : IDisposable { diff --git a/module/ASC.Files.Thirdparty/GoogleDrive/GoogleDriveFileDao.cs b/module/ASC.Files.Thirdparty/GoogleDrive/GoogleDriveFileDao.cs index bca36fd1a..da1709365 100644 --- a/module/ASC.Files.Thirdparty/GoogleDrive/GoogleDriveFileDao.cs +++ b/module/ASC.Files.Thirdparty/GoogleDrive/GoogleDriveFileDao.cs @@ -582,6 +582,15 @@ namespace ASC.Files.Thirdparty.GoogleDrive return Task.FromResult(IsExistOnStorage(file)); } + public EntryProperties GetProperties(object fileId) + { + return null; + } + + public void SaveProperties(object fileId, EntryProperties entryProperties) + { + } + #endregion } } \ No newline at end of file diff --git a/module/ASC.Files.Thirdparty/OneDrive/OneDriveFileDao.cs b/module/ASC.Files.Thirdparty/OneDrive/OneDriveFileDao.cs index ab5612284..f4f44119f 100644 --- a/module/ASC.Files.Thirdparty/OneDrive/OneDriveFileDao.cs +++ b/module/ASC.Files.Thirdparty/OneDrive/OneDriveFileDao.cs @@ -575,6 +575,15 @@ namespace ASC.Files.Thirdparty.OneDrive public Stream GetThumbnail(File file) { return null; + } + + public EntryProperties GetProperties(object fileId) + { + return null; + } + + public void SaveProperties(object fileId, EntryProperties entryProperties) + { } public Task GetFileStreamAsync(File file) diff --git a/module/ASC.Files.Thirdparty/OneDrive/OneDriveProviderInfo.cs b/module/ASC.Files.Thirdparty/OneDrive/OneDriveProviderInfo.cs index c86468bbf..0c37579c1 100644 --- a/module/ASC.Files.Thirdparty/OneDrive/OneDriveProviderInfo.cs +++ b/module/ASC.Files.Thirdparty/OneDrive/OneDriveProviderInfo.cs @@ -203,16 +203,7 @@ namespace ASC.Files.Thirdparty.OneDrive CacheNotify.Subscribe((i, action) => { if (action != CacheNotifyAction.Remove) return; - if (i.ResetAll) - { - CacheChildItems.Remove(new Regex("^onedrivei-" + i.Key + ".*")); - CacheItem.Remove(new Regex("^onedrive-" + i.Key + ".*")); - } - else - { - CacheChildItems.Remove(new Regex("onedrivei-" + i.Key)); - CacheItem.Remove("onedrive-" + i.Key); - } + ResetMemoryCache(i); }); } @@ -243,15 +234,32 @@ namespace ASC.Files.Thirdparty.OneDrive internal void CacheReset(string onedriveId = null) { var key = ID + "-"; + var item = new OneDriveCacheItem { Key = key }; + if (string.IsNullOrEmpty(onedriveId)) { - CacheNotify.Publish(new OneDriveCacheItem { ResetAll = true, Key = key }, CacheNotifyAction.Remove); + item.ResetAll = true; } else { - key += onedriveId; + item.Key += onedriveId; + } - CacheNotify.Publish(new OneDriveCacheItem { Key = key }, CacheNotifyAction.Remove); + ResetMemoryCache(item); + CacheNotify.Publish(item, CacheNotifyAction.Remove); + } + + private static void ResetMemoryCache(OneDriveCacheItem item) + { + if (item.ResetAll) + { + CacheChildItems.Remove(new Regex("^onedrivei-" + item.Key + ".*")); + CacheItem.Remove(new Regex("^onedrive-" + item.Key + ".*")); + } + else + { + CacheChildItems.Remove(new Regex("onedrivei-" + item.Key)); + CacheItem.Remove("onedrive-" + item.Key); } } diff --git a/module/ASC.Files.Thirdparty/ProviderAccountDao.cs b/module/ASC.Files.Thirdparty/ProviderAccountDao.cs index 438e120de..34880fc05 100644 --- a/module/ASC.Files.Thirdparty/ProviderAccountDao.cs +++ b/module/ASC.Files.Thirdparty/ProviderAccountDao.cs @@ -367,11 +367,22 @@ namespace ASC.Files.Thirdparty if (key == ProviderTypes.SharePoint) { + string passwordSP; + try + { + passwordSP = DecryptPassword(input[4] as string); + } + catch (Exception e) + { + Global.Logger.Error(string.Format("DecryptPassword error: linkId = {0} , user = {1}", id, SecurityContext.CurrentAccount.ID), e); + return null; + } + return new SharePointProviderInfo( id, key.ToString(), providerTitle, - new AuthData(input[9] as string, input[3] as string, DecryptPassword(input[4] as string), token), + new AuthData(input[9] as string, input[3] as string, passwordSP, token), owner, folderType, createOn); @@ -401,11 +412,22 @@ namespace ASC.Files.Thirdparty createOn); } + string password; + try + { + password = DecryptPassword(input[4] as string); + } + catch (Exception e) + { + Global.Logger.Error(string.Format("DecryptPassword error: linkId = {0} , user = {1}", id, SecurityContext.CurrentAccount.ID), e); + return null; + } + return new SharpBoxProviderInfo( id, key.ToString(), providerTitle, - new AuthData(input[9] as string, input[3] as string, DecryptPassword(input[4] as string), token), + new AuthData(input[9] as string, input[3] as string, password, token), owner, folderType, createOn); diff --git a/module/ASC.Files.Thirdparty/ProviderDao/ProviderFileDao.cs b/module/ASC.Files.Thirdparty/ProviderDao/ProviderFileDao.cs index 7a1d9c4df..fac22b06f 100644 --- a/module/ASC.Files.Thirdparty/ProviderDao/ProviderFileDao.cs +++ b/module/ASC.Files.Thirdparty/ProviderDao/ProviderFileDao.cs @@ -620,6 +620,24 @@ namespace ASC.Files.Thirdparty.ProviderDao } } + public EntryProperties GetProperties(object fileId) + { + var selector = GetSelector(fileId); + using (var fileDao = selector.GetFileDao(fileId)) + { + return fileDao.GetProperties(selector.ConvertId(fileId)); + } + } + + public void SaveProperties(object fileId, EntryProperties entryProperties) + { + var selector = GetSelector(fileId); + using (var fileDao = selector.GetFileDao(fileId)) + { + fileDao.SaveProperties(selector.ConvertId(fileId), entryProperties); + } + } + #endregion } } \ No newline at end of file diff --git a/module/ASC.Files.Thirdparty/ProviderDao/ProviderFolderDao.cs b/module/ASC.Files.Thirdparty/ProviderDao/ProviderFolderDao.cs index 7f66d599b..d594fa336 100644 --- a/module/ASC.Files.Thirdparty/ProviderDao/ProviderFolderDao.cs +++ b/module/ASC.Files.Thirdparty/ProviderDao/ProviderFolderDao.cs @@ -21,6 +21,7 @@ using System.Linq; using System.Threading; using ASC.Files.Core; +using ASC.Web.Studio.Core; namespace ASC.Files.Thirdparty.ProviderDao { @@ -295,7 +296,7 @@ namespace ASC.Files.Thirdparty.ProviderDao var storageMaxUploadSize = folderDao.GetMaxUploadSize(selector.ConvertId(folderId), chunkedUpload); if (storageMaxUploadSize == -1 || storageMaxUploadSize == long.MaxValue) - storageMaxUploadSize = 1024L * 1024L * 1024L; + storageMaxUploadSize = SetupInfo.ProviderMaxUploadSize; return storageMaxUploadSize; } diff --git a/module/ASC.Files.Thirdparty/SharePoint/SharePointFileDao.cs b/module/ASC.Files.Thirdparty/SharePoint/SharePointFileDao.cs index 56e4fe6cd..3534d6931 100644 --- a/module/ASC.Files.Thirdparty/SharePoint/SharePointFileDao.cs +++ b/module/ASC.Files.Thirdparty/SharePoint/SharePointFileDao.cs @@ -425,6 +425,15 @@ namespace ASC.Files.Thirdparty.SharePoint return Task.FromResult(IsExistOnStorage(file)); } + public EntryProperties GetProperties(object fileId) + { + return null; + } + + public void SaveProperties(object fileId, EntryProperties entryProperties) + { + } + #endregion } } diff --git a/module/ASC.Files.Thirdparty/Sharpbox/SharpBoxFileDao.cs b/module/ASC.Files.Thirdparty/Sharpbox/SharpBoxFileDao.cs index 3d9d5520c..5def54162 100644 --- a/module/ASC.Files.Thirdparty/Sharpbox/SharpBoxFileDao.cs +++ b/module/ASC.Files.Thirdparty/Sharpbox/SharpBoxFileDao.cs @@ -643,6 +643,15 @@ namespace ASC.Files.Thirdparty.Sharpbox return Task.FromResult(IsExistOnStorage(file)); } + public EntryProperties GetProperties(object fileId) + { + return null; + } + + public void SaveProperties(object fileId, EntryProperties entryProperties) + { + } + #endregion } } \ No newline at end of file diff --git a/module/ASC.Files.Thirdparty/app.config b/module/ASC.Files.Thirdparty/app.config index 0a7200e4e..dc12e2573 100644 --- a/module/ASC.Files.Thirdparty/app.config +++ b/module/ASC.Files.Thirdparty/app.config @@ -2,106 +2,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/module/ASC.Files.ThumbnailBuilder/Builder.cs b/module/ASC.Files.ThumbnailBuilder/Builder.cs index baa848262..aefe068de 100644 --- a/module/ASC.Files.ThumbnailBuilder/Builder.cs +++ b/module/ASC.Files.ThumbnailBuilder/Builder.cs @@ -200,14 +200,31 @@ namespace ASC.Files.ThumbnailBuilder var fileExtension = file.ConvertedExtension; var docKey = DocumentServiceHelper.GetDocKey(file); - var thumbnail = new DocumentService.ThumbnailData + + var thumbnailAspect = config.ThumbnailAspect; + var thumbnail = GetThumbnailData(thumbnailAspect); + var spreadsheetLayout = GetSpreadsheetLayout(thumbnailAspect); + + var operationResultProgress = DocumentServiceConnector.GetConvertedUri(fileUri, fileExtension, toExtension, docKey, null, null, thumbnail, spreadsheetLayout, false, out url); + + operationResultProgress = Math.Min(operationResultProgress, 100); + return operationResultProgress == 100; + } + + private DocumentService.ThumbnailData GetThumbnailData(int thumbnailAspect) + { + return new DocumentService.ThumbnailData { - Aspect = 2, + Aspect = thumbnailAspect, First = true, - //Height = config.ThumbnaillHeight, - //Width = config.ThumbnaillWidth + Height = thumbnailAspect == 3 ? config.ThumbnaillHeight : 0, + Width = thumbnailAspect == 3 ? config.ThumbnaillWidth : 0, }; - var spreadsheetLayout = new DocumentService.SpreadsheetLayout + } + + private DocumentService.SpreadsheetLayout GetSpreadsheetLayout(int thumbnailAspect) + { + return new DocumentService.SpreadsheetLayout { IgnorePrintArea = true, //Orientation = "landscape", // "297mm" x "210mm" @@ -222,16 +239,14 @@ namespace ASC.Files.ThumbnailBuilder Bottom = "0mm", Left = "0mm" }, - PageSize = new DocumentService.SpreadsheetLayout.LayoutPageSize - { - Width = (config.ThumbnaillWidth * 1.5) + "mm", // 192 * 1.5 = "288mm", - Height = (config.ThumbnaillHeight * 1.5) + "mm" // 128 * 1.5 = "192mm" - } + PageSize = thumbnailAspect == 3 ? + new DocumentService.SpreadsheetLayout.LayoutPageSize() : + new DocumentService.SpreadsheetLayout.LayoutPageSize + { + Width = (config.ThumbnaillWidth * 1.5) + "mm", // 192 * 1.5 = "288mm", + Height = (config.ThumbnaillHeight * 1.5) + "mm" // 128 * 1.5 = "192mm" + } }; - var operationResultProgress = DocumentServiceConnector.GetConvertedUri(fileUri, fileExtension, toExtension, docKey, null, thumbnail, spreadsheetLayout, false, out url); - - operationResultProgress = Math.Min(operationResultProgress, 100); - return operationResultProgress == 100; } private void SaveThumbnail(IFileDao fileDao, File file, string thumbnailUrl) @@ -248,7 +263,14 @@ namespace ASC.Files.ThumbnailBuilder using (var stream = new ResponseStream(req.GetResponse())) { - Crop(fileDao, file, stream); + if (config.ThumbnailAspect == 3) + { + fileDao.SaveThumbnail(file, stream); + } + else + { + Crop(fileDao, file, stream); + } } logger.DebugFormat("SaveThumbnail: FileId: {0}. Successfully saved.", file.ID); diff --git a/module/ASC.Files.ThumbnailBuilder/ConfigSection.cs b/module/ASC.Files.ThumbnailBuilder/ConfigSection.cs index 1b42934fb..494d74e88 100644 --- a/module/ASC.Files.ThumbnailBuilder/ConfigSection.cs +++ b/module/ASC.Files.ThumbnailBuilder/ConfigSection.cs @@ -47,7 +47,7 @@ namespace ASC.Files.ThumbnailBuilder get { return (string)this["connectionStringName"]; } } - [ConfigurationProperty("formats", DefaultValue = ".pptx|.pptm|.ppt|.ppsx|.ppsm|.pps|.potx|.potm|.pot|.odp|.fodp|.otp|.gslides|.xlsx|.xlsm|.xls|.xltx|.xltm|.xlt|.ods|.fods|.ots|.gsheet|.csv|.docx|.docxf|.oform|.docm|.doc|.dotx|.dotm|.dot|.odt|.fodt|.ott|.gdoc|.txt|.rtf|.mht|.html|.htm|.fb2|.epub|.pdf|.djvu|.xps|.bmp|.jpeg|.jpg|.png|.gif|.tiff|.tif|.ico")] + [ConfigurationProperty("formats", DefaultValue = ".pptx|.pptm|.ppt|.ppsx|.ppsm|.pps|.potx|.potm|.pot|.odp|.fodp|.otp|.gslides|.xlsx|.xlsm|.xls|.xltx|.xltm|.xlt|.ods|.fods|.ots|.gsheet|.csv|.docx|.docxf|.oform|.docm|.doc|.dotx|.dotm|.dot|.odt|.fodt|.ott|.gdoc|.txt|.rtf|.mht|.html|.htm|.fb2|.epub|.pdf|.djvu|.xps|.oxps|.bmp|.jpeg|.jpg|.png|.gif|.tiff|.tif|.ico")] public string Formats { get { return (string)this["formats"]; } @@ -115,6 +115,12 @@ namespace ASC.Files.ThumbnailBuilder get { return (int)this["thumbnaillWidth"]; } } + [ConfigurationProperty("thumbnailAspect", DefaultValue = 2)] + public int ThumbnailAspect + { + get { return (int)this["thumbnailAspect"]; } + } + #endregion } } \ No newline at end of file diff --git a/module/ASC.HealthCheck/ASC.HealthCheck/ASC.HealthCheck.csproj b/module/ASC.HealthCheck/ASC.HealthCheck/ASC.HealthCheck.csproj index 3c49ad1ae..38c719248 100644 --- a/module/ASC.HealthCheck/ASC.HealthCheck/ASC.HealthCheck.csproj +++ b/module/ASC.HealthCheck/ASC.HealthCheck/ASC.HealthCheck.csproj @@ -102,6 +102,9 @@ + + HealthCheckResource.resx + HealthCheckResource.resx diff --git a/module/ASC.HealthCheck/ASC.HealthCheck/Models/Service.cs b/module/ASC.HealthCheck/ASC.HealthCheck/Models/Service.cs index b0f4c857d..e87c15ead 100644 --- a/module/ASC.HealthCheck/ASC.HealthCheck/Models/Service.cs +++ b/module/ASC.HealthCheck/ASC.HealthCheck/Models/Service.cs @@ -89,8 +89,6 @@ namespace ASC.HealthCheck.Models return new OnlyofficeMailAggregatorService(); case ServiceEnum.OnlyofficeMailWatchdog: return new OnlyofficeMailWatchdogService(); - case ServiceEnum.OnlyofficeAutoreply: - return new OnlyofficeAutoreplyService(); case ServiceEnum.OnlyofficeIndex: return new OnlyofficeIndexService(); case ServiceEnum.EditorsFileConverter: @@ -421,20 +419,6 @@ namespace ASC.HealthCheck.Models } } - public class OnlyofficeAutoreplyService : Service - { - public OnlyofficeAutoreplyService() : base(ServiceEnum.OnlyofficeAutoreply) - { - } - - public override string Title { get { return HealthCheckResource.AutoreplyServiceTitle; } } - - public override string Check(int tenantId) - { - return ""; - } - } - public class OnlyofficeIndexService : Service { public OnlyofficeIndexService() : base(ServiceEnum.OnlyofficeIndex) { } diff --git a/module/ASC.HealthCheck/ASC.HealthCheck/Models/ServiceEnum.cs b/module/ASC.HealthCheck/ASC.HealthCheck/Models/ServiceEnum.cs index 493ae17d9..643434141 100644 --- a/module/ASC.HealthCheck/ASC.HealthCheck/Models/ServiceEnum.cs +++ b/module/ASC.HealthCheck/ASC.HealthCheck/Models/ServiceEnum.cs @@ -26,7 +26,6 @@ namespace ASC.HealthCheck.Models OnlyofficeFeed, OnlyofficeMailAggregator, OnlyofficeMailWatchdog, - OnlyofficeAutoreply, OnlyofficeIndex, EditorsFileConverter, EditorsCoAuthoring, diff --git a/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.Designer.cs b/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.Designer.cs index 550009cce..0c155e96e 100644 --- a/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.Designer.cs +++ b/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.Designer.cs @@ -114,15 +114,6 @@ namespace ASC.HealthCheck.Resources { } } - /// - /// Looks up a localized string similar to ONLYOFFICE Autoreply. - /// - public static string AutoreplyServiceTitle { - get { - return ResourceManager.GetString("AutoreplyServiceTitle", resourceCulture); - } - } - /// /// Looks up a localized string similar to ONLYOFFICE Backup. /// diff --git a/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.az-Latn-AZ.resx b/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.az-Latn-AZ.resx new file mode 100644 index 000000000..948e71043 --- /dev/null +++ b/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.az-Latn-AZ.resx @@ -0,0 +1,319 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Ошибка при добавлении почтового адреса + + + Почтовый адрес добавлен + + + Ошибка при добавлении телефонного номера + + + Телефонный номер добавлен + + + Этот адрес уже был добавлен + + + Этот номер телефона уже был добавлен + + + ONLYOFFICE Avtomatik cavab + + + ONLYOFFICE Yedəkləmə + + + Сервис бэкапа работает некорректно + + + Кеш очищен + + + Ошибка при очистке кеша + + + ONLYOFFICE Redaktorlarının Birgə Müəllifliyi + + + Ошибка при скачивании логов + + + Ошибка доступа + + + Ошибка доступа + + + Ошибка доступа + + + Tarif planınız bu seçimi dəstəkləmir + + + Ошибка при отправке уведомления + + + Новости для ленты не генерируются + + + ONLYOFFICE Lenti + + + ONLYOFFICE Redaktorlarının Fayl Çeviricisi + + + Сервис полнотекстового поиска работает корректно + + + Сервис полнотекстового поиска работает некорректно + + + Ошибка при получении информации о диске + + + Ошибка при получении данных + + + Ошибка при получении настроек уведомления + + + {0} Гб + + + Ошибка + + + Работает + + + Используется для незащищённой загрузки/отправки данных на сайт + + + Используется для защищённой загрузки/отправки данных на сайт + + + Используется для сбора почты сервисами OnlyOffice Mail по протоколу IMAP + + + Используется для сбора почты сервисами OnlyOffice Mail по протоколу IMAPS + + + ONLYOFFICE İndeksi + + + ONLYOFFICE Sürətli Söhbət + + + ONLYOFFICE Poçt Kollektoru + + + ONLYOFFICE Poçt Nəzarətçisi + + + {0} Мб + + + ONLYOFFICE Mini Söhbət + + + Tək server xidmətini yenidən başlatma xətası: {0} + + + Не найден такой почтовый адрес + + + Не найден диск с указанным именем + + + Не найден такой номер телефона + + + Не ожидаемое состояние сервиса + + + ONLYOFFICE Bildiriş + + + Используется для сбора почты сервисами OnlyOffice Mail по протоколу POP3 + + + Используется для сбора почты сервисами OnlyOffice Mail по протоколу POP3S + + + Закрыт + + + Закрыт для входящих запросов + + + Закрыт для исходящих запросов + + + Открыт + + + Ошибка при удалении почтового адреса + + + Почтовый адрес удален + + + Ошибка при удалении номера телефона + + + Номер телефона удален + + + Ошибка при отправке уведомления + + + Ошибка сервера + + + Ошибка при проверке сервиса + + + Ошибка сервиса + + + Сервис не найден на локальной машине + + + Возникла непредвиденная проблема в работе сервиса {0}. Пожалуйста проверьте его состояние и работоспособность. Статус: {1}. Сообщение: {2} + + + Ошибка при рестарте сервиса + + + Сервис запущен + + + Ошибка при запуске сервиса + + + Сервис в процессе запуска + + + Сервис еще не запустился + + + Cервис еще не остановлен + + + Сервис остановлен + + + Ошибка при остановке сервиса + + + Сервис работает + + + Ошибка при сохранении настроек уведомления + + + ONLYOFFICE SignalR + + + Заканчивается свободное дисковое пространство + + + Используется для незащищенной отправки сообщений через web-client OnlyOffice Mail + + + Используется для защищенной отправки сообщений через web-client OnlyOffice Mail + + + ONLYOFFICE Redaktorlarının Orfoqrafiya Yoxlanışı + + + Ошибка + + + Запускается + + + Остановлен + + + Запущен + + + Письмо отправлено в службу поддержки + + + Некорректный почтовый адрес + + + Неверный параметр {0} + + + Некорректный номер телефона + + + Неверное имя сервиса + + + Используется Jabber-сервером для установки соединений и передачи данных + + diff --git a/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.cs.resx b/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.cs.resx index b009bedaf..0293f0608 100644 --- a/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.cs.resx +++ b/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.cs.resx @@ -58,9 +58,6 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - ONLYOFFICE Automatická odpověď - ONLYOFFICE Záloha diff --git a/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.de.resx b/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.de.resx index d60fb153f..f8fa49db6 100644 --- a/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.de.resx +++ b/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.de.resx @@ -76,9 +76,6 @@ Diese Telefonnummer wurde bereits hinzugefügt - - ONLYOFFICE automatische Antwort - ONLYOFFICE-Datensicherung diff --git a/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.es.resx b/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.es.resx index 0c9e15073..9ed63d1a3 100644 --- a/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.es.resx +++ b/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.es.resx @@ -76,9 +76,6 @@ Este número de teléfono ha sido añadido ya - - Autorespuesta de ONLYOFFICE - Copia de seguridad de ONLYOFFICE diff --git a/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.fi.resx b/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.fi.resx index ecd957f9a..7f1409053 100644 --- a/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.fi.resx +++ b/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.fi.resx @@ -58,9 +58,6 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - ONLYOFFICE Automaattinen vastaus - ONLYOFFICE turvavarmenne diff --git a/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.fr.resx b/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.fr.resx index 1b4154f2a..2bf72eca2 100644 --- a/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.fr.resx +++ b/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.fr.resx @@ -70,9 +70,6 @@ Ce numéro de téléphone a été déjà ajouté - - Réponse automatique ONLYOFFICE - Sauvegarde de ONLYOFFICE diff --git a/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.it.resx b/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.it.resx index 61cdbee32..d7fde2a03 100644 --- a/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.it.resx +++ b/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.it.resx @@ -76,9 +76,6 @@ Questo numero di telefono è stato già aggiunto - - Risposta automatica ONLYOFFICE - ONLYOFFICE Backup diff --git a/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.nl.resx b/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.nl.resx index 5614924a4..959d8c518 100644 --- a/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.nl.resx +++ b/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.nl.resx @@ -58,9 +58,6 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - ONLYOFFICE automatisch antwoord - ONLYOFFICE Backup diff --git a/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.pt-BR.resx b/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.pt-BR.resx index 3d8ccab93..9b02a3521 100644 --- a/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.pt-BR.resx +++ b/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.pt-BR.resx @@ -76,9 +76,6 @@ Este número de telefone já foi adicionado - - Resposta automática do ONLYOFFICE - Backup ONLYOFFICE diff --git a/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.resx b/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.resx index 494a60c36..b139cd789 100644 --- a/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.resx +++ b/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.resx @@ -76,9 +76,6 @@ Этот номер телефона уже был добавлен - - ONLYOFFICE Autoreply - ONLYOFFICE Backup diff --git a/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.sk.resx b/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.sk.resx index df40fb826..8b9e88b9a 100644 --- a/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.sk.resx +++ b/module/ASC.HealthCheck/ASC.HealthCheck/Resources/HealthCheckResource.sk.resx @@ -58,9 +58,6 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - ONLYOFFICE Automatická odpoveď - ONLYOFFICE Záloha diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ASC.Mail.Autoreply.csproj b/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ASC.Mail.Autoreply.csproj deleted file mode 100644 index 065efe4d9..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ASC.Mail.Autoreply.csproj +++ /dev/null @@ -1,118 +0,0 @@ - - - - Debug - AnyCPU - 9.0.30729 - 2.0 - {704FC00A-1DE7-4A1B-8896-097E94C97648} - Library - Properties - ASC.Mail.Autoreply - ASC.Mail.Autoreply - v4.8 - 512 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - false - - - none - true - bin\Release\ - TRACE - prompt - 4 - false - - - - - - - ..\..\..\packages\HtmlAgilityPack.1.6.0.1\lib\net462\HtmlAgilityPack.dll - - - ..\..\..\packages\log4net.2.0.8\lib\net45-full\log4net.dll - - - - - - 3.5 - - - - - 3.5 - - - 3.5 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - true - - - - - {76de7717-3d4b-4a5b-b740-15b8913df0cb} - ASC.Common - - - {a51d0454-4afa-46de-89d4-b03d37e1816c} - ASC.Core.Common - - - {7ab36ccf-5ffd-4780-a54e-a102eca028b5} - ASC.Mail - - - - \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AddressParsers/AddressParser.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AddressParsers/AddressParser.cs deleted file mode 100644 index 6d5f62cf0..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AddressParsers/AddressParser.cs +++ /dev/null @@ -1,76 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System.Collections.Generic; -using System.Net.Mail; -using System.Text.RegularExpressions; -using System.Linq; -using ASC.Core; -using ASC.Core.Tenants; - -namespace ASC.Mail.Autoreply.AddressParsers -{ - internal abstract class AddressParser : IAddressParser - { - private Regex _routeRegex; - private Regex RouteRegex - { - get { return _routeRegex ?? (_routeRegex = GetRouteRegex()); } - } - - protected abstract Regex GetRouteRegex(); - protected abstract ApiRequest ParseRequestInfo(IDictionary groups, Tenant t); - - protected bool IsLastVersion(Tenant t) - { - return t.Version > 1; - } - - public ApiRequest ParseRequestInfo(string address) - { - try - { - var mailAddress = new MailAddress(address); - - var match = RouteRegex.Match(mailAddress.User); - - if (!match.Success) - return null; - - var tenant = CoreContext.TenantManager.GetTenant(mailAddress.Host); - - if (tenant == null) - return null; - - var groups = RouteRegex.GetGroupNames().ToDictionary(groupName => groupName, groupName => match.Groups[groupName].Value); - var requestInfo = ParseRequestInfo(groups, tenant); - - requestInfo.Method = "POST"; - requestInfo.Tenant = tenant; - - if (!string.IsNullOrEmpty(requestInfo.Url)) - requestInfo.Url = string.Format("api/2.0/{0}.json", requestInfo.Url); - - return requestInfo; - } - catch - { - return null; - } - } - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AddressParsers/CommentAddressParser.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AddressParsers/CommentAddressParser.cs deleted file mode 100644 index edbe34eb7..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AddressParsers/CommentAddressParser.cs +++ /dev/null @@ -1,98 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using ASC.Core.Tenants; -using ASC.Mail.Autoreply.ParameterResolvers; - -namespace ASC.Mail.Autoreply.AddressParsers -{ - internal class CommentAddressParser : AddressParser - { - protected readonly string[] CommunityTypes = new[] {"blog", @"forum\.topic", "event", "wiki", "bookmark"}; - protected readonly string[] ProjectsTypes = new[] {@"project\.milestone", @"project\.task", @"project\.message"}; - protected readonly string[] FilesTypes = new[] {"file"}; - - private Regex _routeRegex; - - protected override Regex GetRouteRegex() - { - if (_routeRegex == null) - { - var regex = new StringBuilder(); - - regex.Append("^reply_(?'type'"); - regex.Append(string.Join("|", CommunityTypes)); - regex.Append("|"); - regex.Append(string.Join("|", ProjectsTypes)); - regex.Append("|"); - regex.Append(string.Join("|", FilesTypes)); - regex.Append(")_(?'postId'[-0-9a-zA-Z]+)_(?'parentId'[-0-9a-zA-Z]*)$"); - - _routeRegex = new Regex(regex.ToString(), RegexOptions.Compiled); - } - - return _routeRegex; - } - - protected override ApiRequest ParseRequestInfo(IDictionary groups, Tenant t) - { - ApiRequest requestInfo; - if (groups["type"] == @"forum.topic") - { - requestInfo = new ApiRequest(string.Format("community/{0}/{1}", groups["type"].Replace(@"\", "").Replace('.', '/'), groups["postId"])) - { - Parameters = new List - { - new RequestParameter("subject", new TitleResolver()), - new RequestParameter("content", new HtmlContentResolver()) - } - }; - - if (!string.IsNullOrEmpty(groups["parentId"])) - { - requestInfo.Parameters.Add(new RequestParameter("parentPostId", groups["parentId"])); - } - } - else - { - requestInfo = new ApiRequest(string.Format("{0}/{1}/comment", groups["type"].Replace(@"\", "").Replace('.', '/'), groups["postId"])) - { - Parameters = new List - { - new RequestParameter("content", new HtmlContentResolver()) - } - }; - - if (!string.IsNullOrEmpty(groups["parentId"])) - { - requestInfo.Parameters.Add(new RequestParameter("parentId", groups["parentId"])); - } - } - - if (CommunityTypes.Contains(groups["type"])) - { - requestInfo.Url = "community/" + requestInfo.Url; - } - - return requestInfo; - } - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AddressParsers/CommunityAddressParser.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AddressParsers/CommunityAddressParser.cs deleted file mode 100644 index a3b358935..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AddressParsers/CommunityAddressParser.cs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System.Collections.Generic; -using System.Text.RegularExpressions; -using ASC.Core.Tenants; -using ASC.Mail.Autoreply.ParameterResolvers; - -namespace ASC.Mail.Autoreply.AddressParsers -{ - internal class CommunityAddressParser : AddressParser - { - protected override Regex GetRouteRegex() - { - return new Regex(@"^(?'type'blog|event)$", RegexOptions.Compiled); - } - - protected override ApiRequest ParseRequestInfo(IDictionary groups, Tenant t) - { - var callInfo = new ApiRequest("community/" + groups["type"]) - { - Parameters = new List - { - new RequestParameter("content", new HtmlContentResolver()), - new RequestParameter("title", new TitleResolver(BlogTagsResolver.Pattern)), - new RequestParameter("subscribeComments", true), - new RequestParameter("type", 1), - new RequestParameter("tags", new BlogTagsResolver()) - } - }; - - return callInfo; - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AddressParsers/FileAddressParser.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AddressParsers/FileAddressParser.cs deleted file mode 100644 index f6945b7a1..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AddressParsers/FileAddressParser.cs +++ /dev/null @@ -1,44 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System.Collections.Generic; -using System.Text.RegularExpressions; -using ASC.Core.Tenants; - -namespace ASC.Mail.Autoreply.AddressParsers -{ - internal class FileAddressParser : AddressParser - { - protected override Regex GetRouteRegex() - { - return new Regex(@"^file_(?'folder'my|common|share|\d+)$", RegexOptions.Compiled); - } - - protected override ApiRequest ParseRequestInfo(IDictionary groups, Tenant t) - { - var folder = groups["folder"]; - - int id; - if (!int.TryParse(folder, out id)) - { - folder = '@' + folder; - } - - return new ApiRequest(string.Format("files/{0}/upload", folder)) {FilesToPost = new List()}; - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AddressParsers/IAddressParser.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AddressParsers/IAddressParser.cs deleted file mode 100644 index 0c9a701cf..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AddressParsers/IAddressParser.cs +++ /dev/null @@ -1,24 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Autoreply.AddressParsers -{ - internal interface IAddressParser - { - ApiRequest ParseRequestInfo(string address); - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AddressParsers/ProjectAddressParser.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AddressParsers/ProjectAddressParser.cs deleted file mode 100644 index b9cf3b116..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AddressParsers/ProjectAddressParser.cs +++ /dev/null @@ -1,62 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System.Collections.Generic; -using System.Text.RegularExpressions; -using ASC.Core.Tenants; -using ASC.Mail.Autoreply.ParameterResolvers; - -namespace ASC.Mail.Autoreply.AddressParsers -{ - internal class ProjectAddressParser : AddressParser - { - protected override Regex GetRouteRegex() - { - return new Regex(@"^(?'type'task|message)_(?'projectId'\d+)$", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase); - } - - protected override ApiRequest ParseRequestInfo(IDictionary groups, Tenant t) - { - var type = groups["type"]; - if (type == "task") - { - return new ApiRequest(string.Format("project/{0}/task", groups["projectId"])) - { - Parameters = new List - { - new RequestParameter("description", new PlainTextContentResolver()), - new RequestParameter("deadline", new TaskDeadlineResolver()), - new RequestParameter("priority", new TaskPriorityResolver()), - new RequestParameter("milestoneid", new TaskMilestoneResolver()), - new RequestParameter("responsibles", new TaskResponsiblesResolver()), - new RequestParameter("responsible", new TaskResponsibleResolver()), - new RequestParameter("title", new TitleResolver(TaskDeadlineResolver.Pattern, TaskPriorityResolver.Pattern, TaskMilestoneResolver.Pattern, TaskResponsiblesResolver.Pattern)) - } - }; - } - - return new ApiRequest(string.Format("project/{0}/message", groups["projectId"])) - { - Parameters = new List - { - new RequestParameter("title", new TitleResolver()), - new RequestParameter("content", new HtmlContentResolver()) - } - }; - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ApiRequest.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ApiRequest.cs deleted file mode 100644 index 57512f767..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ApiRequest.cs +++ /dev/null @@ -1,78 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System.Collections.Generic; -using ASC.Core.Tenants; -using ASC.Core.Users; -using ASC.Mail.Autoreply.ParameterResolvers; - -namespace ASC.Mail.Autoreply -{ - internal class ApiRequest - { - public string Method { get; set; } - - public string Url { get; set; } - - public List Parameters { get; set; } - - public Tenant Tenant { get; set; } - - public UserInfo User { get; set; } - - public List FilesToPost { get; set; } - - public ApiRequest(string url) - { - if (!string.IsNullOrEmpty(url)) - { - Url = url.Trim('/'); - } - } - - public override string ToString() - { - return string.Format("t:{0}; u:{1}; {2} {3}", Tenant.TenantId, User.ID, Method, Url); - } - } - - internal class RequestParameter - { - public string Name { get; private set; } - public object Value { get; set; } - public IParameterResolver ValueResolver { get; private set; } - - public RequestParameter(string name, object value) - { - Name = name; - Value = value; - } - - public RequestParameter(string name, IParameterResolver valueResolver) - { - Name = name; - ValueResolver = valueResolver; - } - } - - internal class RequestFileInfo - { - public byte[] Body { get; set; } - public string Name { get; set; } - public string ContentType { get; set; } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ApiService.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ApiService.cs deleted file mode 100644 index 6322e28d4..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ApiService.cs +++ /dev/null @@ -1,248 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.Configuration; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Mime; -using System.Text; -using System.Web; -using ASC.Common.Threading.Workers; -using ASC.Core; -using log4net; - -namespace ASC.Mail.Autoreply -{ - internal class ApiService - { - private readonly ILog _log = LogManager.GetLogger(typeof(ApiService)); - private readonly WorkerQueue _messageQueue = new WorkerQueue(4, TimeSpan.FromMinutes(5), 5, false); - private readonly string _adressTemplate; - - public ApiService(bool https) - { - - _adressTemplate = (https ? Uri.UriSchemeHttps : Uri.UriSchemeHttp) + Uri.SchemeDelimiter + "{{0}}{0}/{{1}}"; - var baseDomain = CoreContext.Configuration.BaseDomain.TrimEnd('/'); - if (baseDomain == "localhost") - { - baseDomain = string.Empty; - } - if (!string.IsNullOrEmpty(baseDomain) && baseDomain[0] != '.' && baseDomain[0] != '/') - { - baseDomain = '.' + baseDomain; - } - _adressTemplate = string.Format(_adressTemplate, baseDomain); - } - - public void Start() - { - if (!_messageQueue.IsStarted) - { - _messageQueue.Start(SendMessage); - } - } - - public void Stop() - { - if (_messageQueue.IsStarted) - { - _messageQueue.Stop(); - } - } - - public void EnqueueRequest(ApiRequest request) - { - _log.DebugFormat("queue request \"{0}\"", request); - _messageQueue.Add(request); - } - - private void SendMessage(ApiRequest requestInfo) - { - try - { - _log.DebugFormat("begin send request {0}", requestInfo); - - CoreContext.TenantManager.SetCurrentTenant(requestInfo.Tenant); - var authKey = SecurityContext.AuthenticateMe(requestInfo.User.ID); - - if (string.IsNullOrEmpty(authKey)) - { - _log.ErrorFormat("can't obtain authorization cookie for user {0}", requestInfo.User.ID); - return; - } - - var uri = BuildUri(requestInfo); - _log.Debug(uri); - - _log.DebugFormat("builded uri for request {0}", uri); - - var request = (HttpWebRequest)WebRequest.Create(uri); - request.Method = requestInfo.Method; - request.AllowAutoRedirect = true; - request.Headers["Authorization"] = authKey; - - using (var requestStream = request.GetRequestStream()) - { - if (requestInfo.FilesToPost != null && requestInfo.FilesToPost.Any()) - { - WriteMultipartRequest(request, requestStream, requestInfo); - } - else - { - WriteFormEncoded(request, requestStream, requestInfo, authKey); - } - } - - try - { - using (var response = request.GetResponse()) - using (var responseStream = response.GetResponseStream()) - { - if (responseStream != null) - { - using (var readStream = new StreamReader(responseStream)) - { - _log.DebugFormat("response from server: {0}", readStream.ReadToEnd()); - } - } - } - } - catch (Exception error) - { - _log.Error("error while getting the response", error); - } - - _log.DebugFormat("end send request {0}", requestInfo); - } - catch (Exception x) - { - _log.Error("error while sending request", x); - throw; - } - } - - private Uri BuildUri(ApiRequest requestInfo) - { - return new Uri(string.Format(_adressTemplate, requestInfo.Tenant.TenantAlias, requestInfo.Url.TrimStart('/'))); - } - - private void WriteMultipartRequest(WebRequest request, Stream requestStream, ApiRequest requestInfo) - { - var boundaryId = DateTime.Now.Ticks.ToString("x"); - var boundary = "\r\n--" + boundaryId + "\r\n"; - var formdataTemplate = "\r\n--" + boundaryId + "\r\nContent-Disposition: form-data; name=\"{0}\";\r\n\r\n{1}"; - - if (requestInfo.Parameters != null && requestInfo.Parameters.Any()) - { - foreach (var parameter in requestInfo.Parameters.Where(x => x.Value != null)) - { - var formItem = string.Empty; - if (parameter.Value is IEnumerable) - { - foreach (var value in ((IEnumerable)parameter.Value)) - { - formItem = string.Format(formdataTemplate, parameter.Name, value); - } - } - else if (parameter.Value is DateTime) - { - formItem = string.Format(formdataTemplate, parameter.Name, ((DateTime)parameter.Value).ToString("s")); - } - else - { - formItem = string.Format(formdataTemplate, parameter.Name, parameter.Value); - } - - if (!string.IsNullOrEmpty(formItem)) - { - _log.DebugFormat("writing form item boundary:{0}", formItem); - WriteUtf8String(requestStream, formItem); - } - } - } - - WriteUtf8String(requestStream, boundary); - - var files = requestInfo.FilesToPost.ToArray(); - for (int i = 0; i < files.Length; i++) - { - WriteFile(requestStream, files[i]); - - if (i < files.Length - 1) - WriteUtf8String(requestStream, boundary); - else - WriteUtf8String(requestStream, "\r\n--" + boundaryId + "--\r\n"); - } - - request.ContentType = new ContentType("multipart/form-data") { Boundary = boundaryId }.ToString(); - } - - private static void WriteFile(Stream requestStream, RequestFileInfo fileInfo) - { - var header = string.Format("Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\nContent-Transfer-Encoding: binary\r\n\r\n", - Path.GetFileNameWithoutExtension(fileInfo.Name), - Path.GetFileName(fileInfo.Name), - fileInfo.ContentType ?? "application/octet-stream"); - - WriteUtf8String(requestStream, header); - requestStream.Write(fileInfo.Body, 0, fileInfo.Body.Length); - } - - private void WriteFormEncoded(WebRequest request, Stream requestStream, ApiRequest requestInfo, string authKey) - { - var stringBuilder = new StringBuilder(); - if (requestInfo.Parameters != null && requestInfo.Parameters.Any()) - { - foreach (var parameter in requestInfo.Parameters.Where(x => x.Value != null)) - { - if (parameter.Value is IEnumerable) - { - foreach (var value in ((IEnumerable)parameter.Value)) - { - stringBuilder.AppendFormat("{0}[]={1}&", HttpUtility.UrlEncode(parameter.Name), HttpUtility.UrlEncode(value.ToString())); - } - } - else if (parameter.Value is DateTime) - { - stringBuilder.AppendFormat("{0}={1}&", HttpUtility.UrlEncode(parameter.Name), HttpUtility.UrlEncode(((DateTime)parameter.Value).ToString("s"))); - } - else - { - stringBuilder.AppendFormat("{0}={1}&", HttpUtility.UrlEncode(parameter.Name), HttpUtility.UrlEncode(parameter.Value.ToString())); - } - } - } - - stringBuilder.AppendFormat("asc_auth_key={0}", authKey); - - _log.DebugFormat("writing form data {0}", stringBuilder); - - WriteUtf8String(requestStream, stringBuilder.ToString()); - request.ContentType = "application/x-www-form-urlencoded"; - } - - private static void WriteUtf8String(Stream stream, string str) - { - byte[] headerbytes = Encoding.UTF8.GetBytes(str); - stream.Write(headerbytes, 0, headerbytes.Length); - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AutoreplyService.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AutoreplyService.cs deleted file mode 100644 index d7fdee88b..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AutoreplyService.cs +++ /dev/null @@ -1,287 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Reflection; -using System.Text.RegularExpressions; -using ASC.Common.Web; -using ASC.Core; -using ASC.Core.Users; -using ASC.Mail.Autoreply.AddressParsers; -using ASC.Mail.Net; -using ASC.Mail.Net.MIME; -using ASC.Mail.Net.Mail; -using ASC.Mail.Net.SMTP.Server; -using ASC.Mail.Net.TCP; -using log4net; - -namespace ASC.Mail.Autoreply -{ - internal class AutoreplyService : IDisposable - { - private readonly ILog _log = LogManager.GetLogger(typeof(AutoreplyService)); - - private readonly List _addressParsers = new List(); - - private readonly SMTP_Server _smtpServer; - private readonly ApiService _apiService; - private readonly CooldownInspector _cooldownInspector; - private readonly bool _storeIncomingMail; - private readonly string _mailFolder; - - public AutoreplyService(AutoreplyServiceConfiguration configuration = null) - { - configuration = configuration ?? AutoreplyServiceConfiguration.GetSection(); - - _storeIncomingMail = configuration.IsDebug; - - _mailFolder = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), configuration.MailFolder); - if (!Directory.Exists(_mailFolder)) - Directory.CreateDirectory(_mailFolder); - - _smtpServer = new SMTP_Server - { - MaxBadCommands = configuration.SmtpConfiguration.MaxBadCommands, - MaxTransactions = configuration.SmtpConfiguration.MaxTransactions, - MaxMessageSize = configuration.SmtpConfiguration.MaxMessageSize, - MaxRecipients = configuration.SmtpConfiguration.MaxRecipients, - MaxConnectionsPerIP = configuration.SmtpConfiguration.MaxConnectionsPerIP, - MaxConnections = configuration.SmtpConfiguration.MaxConnections, - Bindings = new[] {new IPBindInfo("localhost", IPAddress.Any, configuration.SmtpConfiguration.Port, SslMode.None, null)}, - }; - - _smtpServer.Error += OnSmtpError; - _smtpServer.SessionCreated += OnSmtpSessionCreated; - - _cooldownInspector = new CooldownInspector(configuration.CooldownConfiguration); - - _apiService = new ApiService(configuration.Https); - } - - public void Start() - { - _smtpServer.Start(); - _apiService.Start(); - _cooldownInspector.Start(); - } - - public void Stop() - { - _smtpServer.Stop(); - _apiService.Stop(); - _cooldownInspector.Stop(); - } - - public void Dispose() - { - Stop(); - _smtpServer.Dispose(); - } - - public void RegisterAddressParser(IAddressParser addressHandler) - { - _addressParsers.Add(addressHandler); - } - - private void OnSmtpSessionCreated(object sender, TCP_ServerSessionEventArgs e) - { - e.Session.Started += (sndr, args) => _log.DebugFormat("session started: {0}", e.Session); - e.Session.MailFrom += OnSessionMailFrom; - e.Session.RcptTo += OnSessionRcptTo; - e.Session.GetMessageStream += OnSessionGetMessageStream; - e.Session.MessageStoringCanceled += OnSessionMessageStoringCancelled; - e.Session.MessageStoringCompleted += OnSessionMessageStoringCompleted; - } - - private void OnSmtpError(object sender, Error_EventArgs e) - { - _log.WarnFormat("smtp error: {0}", e.Exception); - } - - private void OnSessionMailFrom(object sender, SMTP_e_MailFrom e) - { - e.Session.Tag = Regex.Replace(e.MailFrom.Mailbox, "^prvs=[0-9a-zA-Z]+=", "", RegexOptions.Compiled); //Set session mailbox - } - - private void OnSessionRcptTo(object sender, SMTP_e_RcptTo e) - { - _log.Debug("start processing rcpt to event"); - - var addressTo = e.RcptTo.Mailbox; - var addressFrom = (string)e.Session.Tag; - - var requestInfo = _addressParsers.Select(routeParser => routeParser.ParseRequestInfo(addressTo)) - .FirstOrDefault(rInfo => rInfo != null); - - if (requestInfo == null) - { - _log.WarnFormat("could not create request from the address {0}", addressTo); - e.Reply = new SMTP_Reply(501, "Could not create request from the address " + addressTo); - return; - } - - CoreContext.TenantManager.SetCurrentTenant(requestInfo.Tenant); - - UserInfo user = CoreContext.UserManager.GetUserByEmail(addressFrom); - - if (user.Equals(Constants.LostUser)) - { - e.Reply = new SMTP_Reply(501, "Could not find user by email address " + addressFrom); - return; - } - - if (_cooldownInspector != null) - { - var cooldownMinutes = Math.Ceiling(_cooldownInspector.GetCooldownRemainigTime(user.ID).TotalMinutes); - if (cooldownMinutes > 0) - { - e.Reply = new SMTP_Reply(554, string.Format("User {0} can not use the autoreply service for another {1} minutes", addressFrom, cooldownMinutes)); - return; - } - - _cooldownInspector.RegisterServiceUsage(user.ID); - } - - requestInfo.User = user; - - _log.DebugFormat("created request info {0}", requestInfo); - - e.Session.Tags.Add(e.RcptTo.Mailbox, requestInfo); - - _log.Debug("complete processing rcpt to event"); - } - - private void OnSessionGetMessageStream(object sender, SMTP_e_Message e) - { - try - { - var messageFileName = Path.Combine(_mailFolder, DateTime.UtcNow.ToString("yyyy'-'MM'-'dd HH'-'mm'-'ss'Z'") + ".eml"); - e.Stream = new FileStream(messageFileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite, 8096, _storeIncomingMail ? FileOptions.None : FileOptions.DeleteOnClose); - } - catch (Exception error) - { - _log.Error("error while allocating temporary stream for the message", error); - } - } - - private void OnSessionMessageStoringCancelled(object sender, SMTP_e_MessageStored e) - { - try - { - e.Stream.Close(); - } - catch (Exception error) - { - _log.Error("error while closing message stream", error); - } - } - - - private void OnSessionMessageStoringCompleted(object sender, SMTP_e_MessageStored e) - { - _log.Debug("begin processing message storing completed event"); - - try - { - e.Stream.Flush(); - e.Stream.Seek(0, SeekOrigin.Begin); - - Mail_Message message = Mail_Message.ParseFromStream(e.Stream); - message.Subject = Regex.Replace(message.Subject, @"\t", ""); - - foreach (var requestInfo in e.Session.To - .Where(x => e.Session.Tags.ContainsKey(x.Mailbox)) - .Select(x => (ApiRequest)e.Session.Tags[x.Mailbox])) - { - try - { - _log.Debug("begin process request (" + requestInfo + ")"); - - CoreContext.TenantManager.SetCurrentTenant(requestInfo.Tenant); - - if (requestInfo.Parameters != null) - { - foreach (var parameter in requestInfo.Parameters.Where(x => x.ValueResolver != null)) - { - parameter.Value = parameter.ValueResolver.ResolveParameterValue(message); - } - } - if (requestInfo.FilesToPost != null) - { - requestInfo.FilesToPost = message.AllEntities.Where(IsAttachment).Select(GetAsAttachment).ToList(); - } - - if (requestInfo.FilesToPost == null || requestInfo.FilesToPost.Count > 0) - { - _apiService.EnqueueRequest(requestInfo); - } - - _log.Debug("end process request (" + requestInfo + ")"); - } - catch (Exception ex) - { - _log.Error("error while processing request info", ex); - } - } - - } - catch (Exception error) - { - _log.Error("error while processing message storing completed event", error); - } - finally - { - e.Stream.Close(); - } - - _log.Debug("complete processing message storing completed event"); - } - - private static bool IsAttachment(MIME_Entity entity) - { - return entity.Body.ContentType != null && entity.Body.ContentType.TypeWithSubype != null - && entity.ContentDisposition != null && entity.ContentDisposition.DispositionType == MIME_DispositionTypes.Attachment - && (!string.IsNullOrEmpty(entity.ContentDisposition.Param_FileName) || !string.IsNullOrEmpty(entity.ContentType.Param_Name)) - && entity.Body is MIME_b_SinglepartBase; - } - - private static RequestFileInfo GetAsAttachment(MIME_Entity entity) - { - var attachment = new RequestFileInfo - { - Body = ((MIME_b_SinglepartBase)entity.Body).Data - }; - - if (!string.IsNullOrEmpty(entity.ContentDisposition.Param_FileName)) - { - attachment.Name = entity.ContentDisposition.Param_FileName; - } - else if (!string.IsNullOrEmpty(entity.ContentType.Param_Name)) - { - attachment.Name = entity.ContentType.Param_Name; - } - - attachment.ContentType = MimeMapping.GetMimeMapping(attachment.Name); - - return attachment; - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AutoreplyServiceController.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AutoreplyServiceController.cs deleted file mode 100644 index 3b02ed3b0..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/AutoreplyServiceController.cs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using ASC.Common.Module; -using ASC.Mail.Autoreply.AddressParsers; -using log4net.Config; - -[assembly: XmlConfigurator] - -namespace ASC.Mail.Autoreply -{ - - public class AutoreplyServiceController : IServiceController - { - private AutoreplyService _autoreplyService; - - public void Start() - { - if (_autoreplyService == null) - { - _autoreplyService = new AutoreplyService(); - _autoreplyService.RegisterAddressParser(new CommentAddressParser()); - _autoreplyService.RegisterAddressParser(new CommunityAddressParser()); - _autoreplyService.RegisterAddressParser(new FileAddressParser()); - _autoreplyService.RegisterAddressParser(new ProjectAddressParser()); - _autoreplyService.Start(); - } - } - - public void Stop() - { - if (_autoreplyService != null) - { - _autoreplyService.Stop(); - _autoreplyService.Dispose(); - } - } - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/Configuration.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/Configuration.cs deleted file mode 100644 index 4efd068eb..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/Configuration.cs +++ /dev/null @@ -1,141 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Configuration; - -namespace ASC.Mail.Autoreply -{ - public class AutoreplyServiceConfiguration : ConfigurationSection - { - [ConfigurationProperty("debug")] - public bool IsDebug - { - get { return (bool)this["debug"]; } - set { this["debug"] = value; } - } - - [ConfigurationProperty("mailFolder", DefaultValue = "..\\MailDebug")] - public string MailFolder - { - get { return (string)this["mailFolder"]; } - set { this["mailFolder"] = value; } - } - - [ConfigurationProperty("https", DefaultValue = false)] - public bool Https - { - get { return (bool)this["https"]; } - set { this["https"] = value; } - } - - [ConfigurationProperty("smtp")] - public SmtpConfigurationElement SmtpConfiguration - { - get { return (SmtpConfigurationElement)this["smtp"]; } - set { this["smtp"] = value; } - } - - [ConfigurationProperty("cooldown")] - public CooldownConfigurationElement CooldownConfiguration - { - get { return (CooldownConfigurationElement)this["cooldown"]; } - set { this["cooldown"] = value; } - } - - public static AutoreplyServiceConfiguration GetSection() - { - return (AutoreplyServiceConfiguration)ConfigurationManager.GetSection("autoreply"); - } - } - - public class SmtpConfigurationElement : ConfigurationElement - { - [ConfigurationProperty("maxTransactions", DefaultValue = 0)] - public int MaxTransactions - { - get { return (int)this["maxTransactions"]; } - set { this["maxTransactions"] = value; } - } - - [ConfigurationProperty("maxBadCommands", DefaultValue = 0)] - public int MaxBadCommands - { - get { return (int)this["maxBadCommands"]; } - set { this["maxBadCommands"] = value; } - } - - [ConfigurationProperty("maxMessageSize", DefaultValue = 15 * 1024 * 1024)] - public int MaxMessageSize - { - get { return (int)this["maxMessageSize"]; } - set { this["maxMessageSize"] = value; } - } - - [ConfigurationProperty("maxRecipients", DefaultValue = 1)] - public int MaxRecipients - { - get { return (int)this["maxRecipients"]; } - set { this["maxRecipients"] = value; } - } - - [ConfigurationProperty("maxConnectionsPerIP", DefaultValue = 0)] - public int MaxConnectionsPerIP - { - get { return (int)this["maxConnectionsPerIP"]; } - set { this["maxConnectionsPerIP"] = value; } - } - - [ConfigurationProperty("maxConnections", DefaultValue = 0)] - public int MaxConnections - { - get { return (int)this["maxConnections"]; } - set { this["maxConnections"] = value; } - } - - [ConfigurationProperty("port", DefaultValue = 25)] - public int Port - { - get { return (int)this["port"]; } - set { this["port"] = value; } - } - } - - public class CooldownConfigurationElement : ConfigurationElement - { - [ConfigurationProperty("length", DefaultValue = "0:10:0")] - public TimeSpan Length - { - get { return (TimeSpan)this["length"]; } - set { this["length"] = value; } - } - - [ConfigurationProperty("allowedRequests", DefaultValue = 5)] - public int AllowedRequests - { - get { return (int)this["allowedRequests"]; } - set { this["allowedRequests"] = value; } - } - - [ConfigurationProperty("duringInterval", DefaultValue = "0:5:0")] - public TimeSpan DuringTimeInterval - { - get { return (TimeSpan)this["duringInterval"]; } - set { this["duringInterval"] = value; } - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/CooldownInspector.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/CooldownInspector.cs deleted file mode 100644 index a67bbd2c0..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/CooldownInspector.cs +++ /dev/null @@ -1,126 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using log4net; - -namespace ASC.Mail.Autoreply -{ - internal class CooldownInspector - { - private readonly ILog _log = LogManager.GetLogger(typeof(CooldownInspector)); - - private readonly object _syncRoot = new object(); - private readonly Dictionary> _lastUsagesByUser = new Dictionary>(); - - private readonly int _allowedRequests; - private readonly TimeSpan _duringTime; - private readonly TimeSpan _cooldownLength; - - private Timer _clearTimer; - - public CooldownInspector(CooldownConfigurationElement config) - { - _allowedRequests = config.AllowedRequests; - _duringTime = config.DuringTimeInterval; - _cooldownLength = config.Length; - } - - public void Start() - { - if (!IsDisabled() && _clearTimer == null) - { - _clearTimer = new Timer(x => ClearExpiredRecords(), null, _cooldownLength, _cooldownLength); - } - } - - public void Stop() - { - if (_clearTimer != null) - { - _clearTimer.Change(Timeout.Infinite, Timeout.Infinite); - _clearTimer.Dispose(); - _clearTimer = null; - _lastUsagesByUser.Clear(); - } - } - - public TimeSpan GetCooldownRemainigTime(Guid userId) - { - if (IsDisabled()) return TimeSpan.Zero; - - lock (_syncRoot) - { - var lastUsages = GetLastUsages(userId); - return lastUsages.Count >= _allowedRequests ? _cooldownLength - (DateTime.UtcNow - lastUsages.Max()) : TimeSpan.Zero; - } - } - - public void RegisterServiceUsage(Guid userId) - { - if (IsDisabled()) return; - - lock (_syncRoot) - { - var lastUsages = GetLastUsages(userId); - lastUsages.Add(DateTime.UtcNow); - _lastUsagesByUser[userId] = lastUsages; - } - } - - private void ClearExpiredRecords() - { - lock (_syncRoot) - { - _log.Debug("start clearing expired usage records"); - try - { - foreach (var userId in _lastUsagesByUser.Keys.ToList()) - { - _lastUsagesByUser[userId] = GetLastUsages(userId); - if (_lastUsagesByUser[userId].Count == 0) - _lastUsagesByUser.Remove(userId); - } - } - catch (Exception error) - { - _log.Error("error while clearing expired usage records", error); - } - } - } - - private List GetLastUsages(Guid userId) - { - if (!_lastUsagesByUser.ContainsKey(userId)) - return new List(); - - return _lastUsagesByUser[userId] - .Where(timestamp => timestamp > DateTime.UtcNow - _duringTime) - .OrderByDescending(timestamp => timestamp) - .Take(_allowedRequests) - .ToList(); - } - - private bool IsDisabled() - { - return _allowedRequests <= 0 || _duringTime <= TimeSpan.Zero || _cooldownLength <= TimeSpan.Zero; - } - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/BlogTagsResolver.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/BlogTagsResolver.cs deleted file mode 100644 index e6c36e06f..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/BlogTagsResolver.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Linq; -using System.Text.RegularExpressions; -using ASC.Mail.Net.Mail; - -namespace ASC.Mail.Autoreply.ParameterResolvers -{ - internal class BlogTagsResolver : IParameterResolver - { - public static readonly Regex Pattern = new Regex(@"#[\w,]+", RegexOptions.CultureInvariant | RegexOptions.Compiled); - - public object ResolveParameterValue(Mail_Message mailMessage) - { - if (!Pattern.IsMatch(mailMessage.Subject)) - return null; - - var tags = Pattern.Matches(mailMessage.Subject).Cast() - .SelectMany(x => x.Value.TrimStart('#').Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries)) - .ToArray(); - - return string.Join(",", tags); - } - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/ContentResolver.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/ContentResolver.cs deleted file mode 100644 index de1c64e32..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/ContentResolver.cs +++ /dev/null @@ -1,54 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using ASC.Mail.Autoreply.Utility; -using ASC.Mail.Autoreply.Utility.Html; -using ASC.Mail.Net.Mail; -using HtmlAgilityPack; - -namespace ASC.Mail.Autoreply.ParameterResolvers -{ - internal class HtmlContentResolver : IParameterResolver - { - public object ResolveParameterValue(Mail_Message mailMessage) - { - var messageText = !string.IsNullOrEmpty(mailMessage.BodyHtmlText) - ? mailMessage.BodyHtmlText - : Text2HtmlConverter.Convert(mailMessage.BodyText.Trim(' ')); - - messageText = messageText.Replace(Environment.NewLine, "").Replace(@"\t", ""); - messageText = HtmlEntity.DeEntitize(messageText); - messageText = HtmlSanitizer.Sanitize(messageText); - - return messageText.Trim("
    ").Trim("
    ").Trim(' '); - } - } - - internal class PlainTextContentResolver : IParameterResolver - { - public object ResolveParameterValue(Mail_Message mailMessage) - { - var messageText = new HtmlContentResolver().ResolveParameterValue(mailMessage) as string; - - if (!string.IsNullOrEmpty(messageText)) - messageText = Html2TextConverter.Convert(messageText); - - return messageText; - } - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/IParameterResolver.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/IParameterResolver.cs deleted file mode 100644 index a3df1295c..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/IParameterResolver.cs +++ /dev/null @@ -1,26 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using ASC.Mail.Net.Mail; - -namespace ASC.Mail.Autoreply.ParameterResolvers -{ - internal interface IParameterResolver - { - object ResolveParameterValue(Mail_Message mailMessage); - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/TaskDeadlineResolver.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/TaskDeadlineResolver.cs deleted file mode 100644 index 984fff6ca..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/TaskDeadlineResolver.cs +++ /dev/null @@ -1,37 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Text.RegularExpressions; -using ASC.Mail.Net.Mail; - -namespace ASC.Mail.Autoreply.ParameterResolvers -{ - internal class TaskDeadlineResolver : IParameterResolver - { - public static readonly Regex Pattern = new Regex(@"\^(?'year'\d{4})-(?'month'\d{1,2})-(?'day'\d{1,2})", RegexOptions.Compiled); - - public object ResolveParameterValue(Mail_Message mailMessage) - { - if (!Pattern.IsMatch(mailMessage.Subject)) - return null; - - var match = Pattern.Match(mailMessage.Subject); - return new DateTime(Convert.ToInt32(match.Groups["year"].Value), Convert.ToInt32(match.Groups["month"].Value), Convert.ToInt32(match.Groups["day"].Value)); - } - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/TaskMilestoneResolver.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/TaskMilestoneResolver.cs deleted file mode 100644 index 22a5053ad..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/TaskMilestoneResolver.cs +++ /dev/null @@ -1,36 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Text.RegularExpressions; -using ASC.Mail.Net.Mail; - -namespace ASC.Mail.Autoreply.ParameterResolvers -{ - internal class TaskMilestoneResolver : IParameterResolver - { - public static readonly Regex Pattern = new Regex(@"#\d+", RegexOptions.Compiled); - - public object ResolveParameterValue(Mail_Message mailMessage) - { - if (!Pattern.IsMatch(mailMessage.Subject)) - return null; - - return Convert.ToInt32(Pattern.Match(mailMessage.Subject).Value); - } - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/TaskPriorityResolver.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/TaskPriorityResolver.cs deleted file mode 100644 index 9069b3a1c..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/TaskPriorityResolver.cs +++ /dev/null @@ -1,36 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Text.RegularExpressions; -using ASC.Mail.Net.Mail; - -namespace ASC.Mail.Autoreply.ParameterResolvers -{ - internal class TaskPriorityResolver : IParameterResolver - { - public static readonly Regex Pattern = new Regex(@"!(0|1)", RegexOptions.Compiled); - - public object ResolveParameterValue(Mail_Message mailMessage) - { - if (!Pattern.IsMatch(mailMessage.Subject)) - return null; - - return Convert.ToInt32(Pattern.Match(mailMessage.Subject).Value.TrimStart('!')); - } - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/TaskResponsiblesResolver.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/TaskResponsiblesResolver.cs deleted file mode 100644 index 3724887dd..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/TaskResponsiblesResolver.cs +++ /dev/null @@ -1,87 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; -using ASC.Core; -using ASC.Core.Users; -using ASC.Mail.Autoreply.Utility; -using ASC.Mail.Net.Mail; - -namespace ASC.Mail.Autoreply.ParameterResolvers -{ - internal class TaskResponsiblesResolver : IParameterResolver - { - public static readonly Regex Pattern = new Regex(@"@\w+\s+\w+", RegexOptions.CultureInvariant | RegexOptions.Compiled); - - public object ResolveParameterValue(Mail_Message mailMessage) - { - if (!Pattern.IsMatch(mailMessage.Subject)) - return null; - - var users = new List(); - foreach (Match match in Pattern.Matches(mailMessage.Subject)) - { - var words = match.Value.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries); - Guid user; - if (TryGetUser(words[0], words[1], out user)) - { - users.Add(user); - } - } - - return users; - } - - private static bool TryGetUser(string word1, string word2, out Guid userId) - { - userId = Guid.Empty; - - var users = CoreContext.UserManager.GetUsers() - .GroupBy(u => GetDistance(u, word1, word2)) - .OrderBy(g => g.Key) - .FirstOrDefault(g => g.Key < 3); - - if (users != null && users.Count() == 1) - { - userId = users.First().ID; - return true; - } - - return false; - } - - private static int GetDistance(UserInfo user, string word1, string word2) - { - var distance1 = StringDistance.LevenshteinDistance(user.FirstName, word1) + StringDistance.LevenshteinDistance(user.LastName, word2); - var distance2 = StringDistance.LevenshteinDistance(user.FirstName, word2) + StringDistance.LevenshteinDistance(user.LastName, word1); - return Math.Min(distance1, distance2); - } - } - - internal class TaskResponsibleResolver : IParameterResolver - { - public object ResolveParameterValue(Mail_Message mailMessage) - { - var responsibles = new TaskResponsiblesResolver().ResolveParameterValue(mailMessage) as List; - - return responsibles != null ? responsibles.FirstOrDefault() : null; - } - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/TitleResolver.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/TitleResolver.cs deleted file mode 100644 index e52e1545b..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/ParameterResolvers/TitleResolver.cs +++ /dev/null @@ -1,44 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System.Text.RegularExpressions; -using ASC.Mail.Net.Mail; - -namespace ASC.Mail.Autoreply.ParameterResolvers -{ - internal class TitleResolver : IParameterResolver - { - private readonly Regex[] _ignorePatterns; - - public TitleResolver(params Regex[] ignorePatterns) - { - _ignorePatterns = ignorePatterns; - } - - public object ResolveParameterValue(Mail_Message mailMessage) - { - var subject = mailMessage.Subject; - - foreach (var pattern in _ignorePatterns) - { - subject = pattern.Replace(subject, ""); - } - - return Regex.Replace(subject, @"\s+", " ").Trim(' '); - } - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/Properties/AssemblyInfo.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/Properties/AssemblyInfo.cs deleted file mode 100644 index 22de00eab..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. - -[assembly: AssemblyTitle("ASC.Mail.Autoreply")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Ascensio System SIA")] -[assembly: AssemblyProduct("ASC.Mail.Autoreply")] -[assembly: AssemblyCopyright("(c) Ascensio System SIA. All rights reserved")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. - -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM - -[assembly: Guid("ae29172c-87af-44df-bfa6-1c7c94d24a02")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] - -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/Utility/Html/Html2TextConverter.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/Utility/Html/Html2TextConverter.cs deleted file mode 100644 index 46266d7bb..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/Utility/Html/Html2TextConverter.cs +++ /dev/null @@ -1,90 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.IO; -using HtmlAgilityPack; - -namespace ASC.Mail.Autoreply.Utility.Html -{ - public class Html2TextConverter - { - private static readonly List MaliciousTags = new List { "script", "style" }; - private static readonly List LineBreakers = new List {"p", "div", "blockquote", "br"}; - - public static String Convert(String html) - { - if (String.IsNullOrEmpty(html)) - return html; - - var doc = new HtmlDocument(); - doc.LoadHtml(html); - - var sw = new StringWriter(); - ProcessNode(doc.DocumentNode, sw); - sw.Flush(); - return sw.ToString(); - } - - private static void ProcessContent(HtmlNode node, TextWriter outText) - { - foreach (var child in node.ChildNodes) - { - ProcessNode(child, outText); - } - } - - private static void ProcessNode(HtmlNode node, TextWriter outText) - { - switch (node.NodeType) - { - case HtmlNodeType.Comment: - break; - - case HtmlNodeType.Document: - ProcessContent(node, outText); - break; - - case HtmlNodeType.Element: - var name = node.Name.ToLowerInvariant(); - - if (MaliciousTags.Contains(name)) - break; - - if (LineBreakers.Contains(name)) - outText.Write("\r\n"); - - if (node.HasChildNodes) - ProcessContent(node, outText); - - break; - - case HtmlNodeType.Text: - var text = ((HtmlTextNode)node).Text; - - if (HtmlNode.IsOverlappedClosingElement(text)) - break; - - if (text.Trim().Length > 0) - outText.Write(text); - - break; - } - } - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/Utility/Html/HtmlSanitizer.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/Utility/Html/HtmlSanitizer.cs deleted file mode 100644 index 88a6299d2..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/Utility/Html/HtmlSanitizer.cs +++ /dev/null @@ -1,105 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.IO; -using HtmlAgilityPack; - -namespace ASC.Mail.Autoreply.Utility.Html -{ - public class HtmlSanitizer - { - private static readonly List MaliciousTags = new List { "script", "style" }; - private static readonly List LineBreakers = new List { "p", "div", "blockquote", "br" }; - private static readonly List WhiteList = new List {"b", "strong", "it", "em", "dfn", "sub", "sup", "strike", "s", "del", "code", "kbd", "samp", "ins", "h1", "h2", "h3", "h4", "h5", "h6"}; - - public static String Sanitize(String html) - { - if (String.IsNullOrEmpty(html)) - return html; - - var doc = new HtmlDocument(); - doc.LoadHtml(html); - - var sw = new StringWriter(); - ProcessNode(doc.DocumentNode, sw); - sw.Flush(); - return sw.ToString(); - } - - private static void ProcessContent(HtmlNode node, TextWriter outText) - { - foreach (var child in node.ChildNodes) - { - ProcessNode(child, outText); - } - } - - private static void ProcessNode(HtmlNode node, TextWriter outText) - { - switch (node.NodeType) - { - case HtmlNodeType.Comment: - break; - - case HtmlNodeType.Document: - ProcessContent(node, outText); - break; - - case HtmlNodeType.Element: - var name = node.Name.ToLowerInvariant(); - - if (MaliciousTags.Contains(name)) - break; - - if (WhiteList.Contains(name) && node.HasChildNodes && node.Closed) - { - outText.Write("<{0}>", name); - ProcessContent(node, outText); - outText.Write("", name); - break; - } - - if (name.Equals("img") && node.HasAttributes && node.Attributes["src"] != null) - { - outText.Write("", node.Attributes["src"].Value); - } - else if (LineBreakers.Contains(name)) - { - outText.Write("
    "); - } - - if (node.HasChildNodes) - ProcessContent(node, outText); - - break; - - case HtmlNodeType.Text: - var text = ((HtmlTextNode) node).Text; - - if (HtmlNode.IsOverlappedClosingElement(text)) - break; - - if (text.Trim().Length > 0) - outText.Write(text); - - break; - } - } - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/Utility/Html/Text2HtmlConverter.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/Utility/Html/Text2HtmlConverter.cs deleted file mode 100644 index 4342d30e9..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/Utility/Html/Text2HtmlConverter.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.IO; - -namespace ASC.Mail.Autoreply.Utility.Html -{ - public class Text2HtmlConverter - { - public static String Convert(String text) - { - if (String.IsNullOrEmpty(text)) - return text; - - text = text.Replace(" ", "  "); - - var sr = new StringReader(text); - var sw = new StringWriter(); - - while (sr.Peek() > -1) - { - sw.Write(sr.ReadLine() + "
    "); - } - - return sw.ToString(); - } - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/Utility/StringDistance.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/Utility/StringDistance.cs deleted file mode 100644 index d8ccccff8..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/Utility/StringDistance.cs +++ /dev/null @@ -1,72 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; - -namespace ASC.Mail.Autoreply.Utility -{ - public static class StringDistance - { - public static int LevenshteinDistance(string s, string t) - { - return LevenshteinDistance(s, t, true); - } - - public static int LevenshteinDistance(string s, string t, bool ignoreCase) - { - if (String.IsNullOrEmpty(s)) - { - return String.IsNullOrEmpty(t) ? 0 : t.Length; - } - if (String.IsNullOrEmpty(t)) - { - return s.Length; - } - - if (ignoreCase) - { - s = s.ToLowerInvariant(); - t = t.ToLowerInvariant(); - } - - int n = s.Length; - int m = t.Length; - - var d = new int[n + 1, m + 1]; - - for (int i = 0; i <= n; d[i, 0] = i++) - { - } - - for (int j = 0; j <= m; d[0, j] = j++) - { - } - - for (int i = 1; i <= n; i++) - { - for (int j = 1; j <= m; j++) - { - int cost = (t[j - 1] == s[i - 1]) ? 0 : 1; - - d[i, j] = Math.Min(Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1), d[i - 1, j - 1] + cost); - } - } - - return d[n, m]; - } - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/Utility/StringExtensions.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/Utility/StringExtensions.cs deleted file mode 100644 index 533d4b107..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Autoreply/Utility/StringExtensions.cs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Autoreply.Utility -{ - public static class StringExtensions - { - public static string TrimStart(this string str, string trimStr) - { - if (string.IsNullOrEmpty(str)) - return str; - - while (str.StartsWith(trimStr)) - { - str = str.Remove(0, trimStr.Length); - } - - return str; - } - - public static string TrimEnd(this string str, string trimStr) - { - if (string.IsNullOrEmpty(str)) - return str; - - while (str.EndsWith(trimStr)) - { - str = str.Remove(str.Length - trimStr.Length); - } - - return str; - } - - public static string Trim(this string str, string trimStr) - { - return str.TrimStart(trimStr).TrimEnd(trimStr); - } - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABFN.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABFN.cs deleted file mode 100644 index 4f28946a7..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABFN.cs +++ /dev/null @@ -1,150 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.ABNF -{ - #region usings - - using System; - using System.Collections.Generic; - - #endregion - - /// - /// This class implements ABNF parser. Defined in RFC 5234. - /// - public class ABFN - { - #region Members - - private readonly List m_pRules; - - #endregion - - #region Properties - - /// - /// Gets ABNF rules collection. - /// - public List Rules - { - get { return m_pRules; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public ABFN() - { - m_pRules = new List(); - - Init(); - } - - #endregion - - #region Methods - - /// - /// Parses and adds ABNF rules from the specified ABFN string. - /// - /// ABNF string. - /// Is raised when value is null reference. - public void AddRules(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - // TODO: - } - - #endregion - - #region Utility methods - - private void Init() - { - #region Add basic rules - - /* - ALPHA = %x41-5A / %x61-7A ; A-Z / a-z - - BIT = "0" / "1" - - CHAR = %x01-7F - ; any 7-bit US-ASCII character, - ; excluding NUL - - - CR = %x0D - ; carriage return - - CRLF = CR LF - ; Internet standard newline - - CTL = %x00-1F / %x7F - ; controls - - DIGIT = %x30-39 - ; 0-9 - - DQUOTE = %x22 - ; " (Double Quote) - - HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F" - - HTAB = %x09 - ; horizontal tab - - LF = %x0A - ; linefeed - - LWSP = *(WSP / CRLF WSP) - ; Use of this linear-white-space rule - ; permits lines containing only white - ; space that are no longer legal in - ; mail headers and have caused - ; interoperability problems in other - ; contexts. - ; Do not use when defining mail - ; headers and use with caution in - ; other contexts. - - OCTET = %x00-FF - ; 8 bits of data - - SP = %x20 - - VCHAR = %x21-7E - ; visible (printing) characters - - WSP = SP / HTAB - ; white space -*/ - - #endregion - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABFN_Group.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABFN_Group.cs deleted file mode 100644 index 68c5f9dbc..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABFN_Group.cs +++ /dev/null @@ -1,101 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.ABNF -{ - #region usings - - using System; - using System.IO; - - #endregion - - /// - /// This class represent ABNF "group". Defined in RFC 5234 4. - /// - public class ABFN_Group : ABNF_Element - { - #region Members - - private ABNF_Alternation m_pAlternation; - - #endregion - - #region Properties - - /// - /// Gets option alternation elements. - /// - public ABNF_Alternation Alternation - { - get { return m_pAlternation; } - } - - #endregion - - #region Methods - - /// - /// - /// - /// - /// - public static ABFN_Group Parse(StringReader reader) - { - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // group = "(" *c-wsp alternation *c-wsp ")" - - if (reader.Peek() != '(') - { - throw new ParseException("Invalid ABNF 'group' value '" + reader.ReadToEnd() + "'."); - } - - // Eat "(". - reader.Read(); - - // TODO: *c-wsp - - ABFN_Group retVal = new ABFN_Group(); - - // We reached end of stream, no closing ")". - if (reader.Peek() == -1) - { - throw new ParseException("Invalid ABNF 'group' value '" + reader.ReadToEnd() + "'."); - } - - retVal.m_pAlternation = ABNF_Alternation.Parse(reader); - - // We don't have closing ")". - if (reader.Peek() != ')') - { - throw new ParseException("Invalid ABNF 'group' value '" + reader.ReadToEnd() + "'."); - } - else - { - reader.Read(); - } - - return retVal; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_Alternation.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_Alternation.cs deleted file mode 100644 index b0e11d2b7..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_Alternation.cs +++ /dev/null @@ -1,111 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.ABNF -{ - #region usings - - using System; - using System.Collections.Generic; - using System.IO; - - #endregion - - /// - /// This class represent ABNF "alternation". Defined in RFC 5234 4. - /// - public class ABNF_Alternation - { - #region Members - - private readonly List m_pItems; - - #endregion - - #region Properties - - /// - /// Gets alternation items. - /// - public List Items - { - get { return m_pItems; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public ABNF_Alternation() - { - m_pItems = new List(); - } - - #endregion - - #region Methods - - /// - /// - /// - /// - /// - public static ABNF_Alternation Parse(StringReader reader) - { - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // alternation = concatenation *(*c-wsp "/" *c-wsp concatenation) - - ABNF_Alternation retVal = new ABNF_Alternation(); - - while (true) - { - ABNF_Concatenation item = ABNF_Concatenation.Parse(reader); - if (item != null) - { - retVal.m_pItems.Add(item); - } - - // We reached end of string. - if (reader.Peek() == -1) - { - break; - } - // We have next alternation item. - else if (reader.Peek() == '/') - { - reader.Read(); - } - // We have unexpected value, probably alternation ends. - else - { - break; - } - } - - return retVal; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_BinVal.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_BinVal.cs deleted file mode 100644 index 5403127e8..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_BinVal.cs +++ /dev/null @@ -1,29 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.ABNF -{ - /// - /// This class represent ABNF "bin-val". Defined in RFC 5234 4. - /// - public class ABNF_BinVal - { - #region Properties implementation - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_CharVal.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_CharVal.cs deleted file mode 100644 index 16703af14..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_CharVal.cs +++ /dev/null @@ -1,184 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.ABNF -{ - #region usings - - using System; - using System.IO; - using System.Text; - - #endregion - - /// - /// This class represent ABNF "char-val". Defined in RFC 5234 4. - /// - public class ABNF_CharVal : ABNF_Element - { - #region Members - - private readonly string m_Value = ""; - - #endregion - - #region Properties - - /// - /// Gets value. - /// - public string Value - { - get { return m_Value; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// The prose-val value. - /// Is raised when value is null reference. - /// Is raised when any of the arguments has invalid value. - public ABNF_CharVal(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - if (!Validate(value)) - { - //throw new ArgumentException("Invalid argument 'value' value. Value must be: 'DQUOTE *(%x20-21 / %x23-7E) DQUOTE'."); - } - - m_Value = value; - } - - #endregion - - #region Methods - - /// - /// - /// - /// - /// - public static ABNF_CharVal Parse(StringReader reader) - { - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - /* - char-val = DQUOTE *(%x20-21 / %x23-7E) DQUOTE - ; quoted string of SP and VCHAR - ; without DQUOTE - */ - - if (reader.Peek() != '\"') - { - throw new ParseException("Invalid ABNF 'char-val' value '" + reader.ReadToEnd() + "'."); - } - - // Eat DQUOTE - reader.Read(); - - // TODO: *c-wsp - - StringBuilder value = new StringBuilder(); - - while (true) - { - // We reached end of stream, no closing DQUOTE. - if (reader.Peek() == -1) - { - throw new ParseException("Invalid ABNF 'char-val' value '" + reader.ReadToEnd() + "'."); - } - // We have closing DQUOTE. - else if (reader.Peek() == '\"') - { - reader.Read(); - break; - } - // Allowed char. - else if ((reader.Peek() >= 0x20 && reader.Peek() <= 0x21) || - (reader.Peek() >= 0x23 && reader.Peek() <= 0x7E)) - { - value.Append((char) reader.Read()); - } - // Invalid value. - else - { - throw new ParseException("Invalid ABNF 'char-val' value '" + reader.ReadToEnd() + "'."); - } - } - - return new ABNF_CharVal(value.ToString()); - } - - #endregion - - #region Utility methods - - /// - /// Validates "prose-val" value. - /// - /// The "prose-val" value. - /// Returns if value is "prose-val" value, otherwise false. - /// Is raised when value is null reference. - private bool Validate(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - // RFC 5234 4. - // char-val = DQUOTE *(%x20-21 / %x23-7E) DQUOTE - - if (value.Length < 2) - { - return false; - } - - for (int i = 0; i < value.Length; i++) - { - char c = value[i]; - - if (i == 0 && c != '\"') - { - return false; - } - else if (i == (value.Length - 1) && c != '\"') - { - return false; - } - else if (!((c >= 0x20 && c <= 0x21) || (c >= 0x23 && c <= 0x7E))) - { - return false; - } - } - - return true; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_Concatenation.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_Concatenation.cs deleted file mode 100644 index 56e52b8a9..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_Concatenation.cs +++ /dev/null @@ -1,111 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.ABNF -{ - #region usings - - using System; - using System.Collections.Generic; - using System.IO; - - #endregion - - /// - /// This class represent ABNF "concatenation". Defined in RFC 5234 4. - /// - public class ABNF_Concatenation - { - #region Members - - private readonly List m_pItems; - - #endregion - - #region Properties - - /// - /// Gets concatenation items. - /// - public List Items - { - get { return m_pItems; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public ABNF_Concatenation() - { - m_pItems = new List(); - } - - #endregion - - #region Methods - - /// - /// - /// - /// - /// - public static ABNF_Concatenation Parse(StringReader reader) - { - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // concatenation = repetition *(1*c-wsp repetition) - // repetition = [repeat] element - - ABNF_Concatenation retVal = new ABNF_Concatenation(); - - while (true) - { - ABNF_Repetition item = ABNF_Repetition.Parse(reader); - if (item != null) - { - retVal.m_pItems.Add(item); - } - // We reached end of string. - else if (reader.Peek() == -1) - { - break; - } - // We have next concatenation item. - else if (reader.Peek() == ' ') - { - reader.Read(); - } - // We have unexpected value, probably concatenation ends. - else - { - break; - } - } - - return retVal; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_DecVal.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_DecVal.cs deleted file mode 100644 index d28a74a4f..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_DecVal.cs +++ /dev/null @@ -1,206 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.ABNF -{ - #region usings - - using System; - using System.Collections.Generic; - using System.IO; - using System.Text; - - #endregion - - /// - /// This class represent ABNF "dec-val". Defined in RFC 5234 4. - /// - public class ABNF_DecVal : ABNF_Element - { - #region Nested type: ValueType - - private enum ValueType - { - Single = 0, - Concated = 1, - Range = 2, - } - - #endregion - - #region Constructor - - /// - /// Default 'range' value constructor. - /// - /// Range start value. - /// Range end value. - /// Is raised when any of the arguments has invalid value. - public ABNF_DecVal(int start, int end) - { - if (start < 0) - { - throw new ArgumentException("Argument 'start' value must be >= 0."); - } - if (end < 0) - { - throw new ArgumentException("Argument 'end' value must be >= 0."); - } - - // TODO: - } - - /// - /// Default 'concated' value constructor. - /// - /// Concated values. - /// Is raised when values is null reference value. - /// Is raised when any of the arguments has invalid value. - public ABNF_DecVal(int[] values) - { - if (values == null) - { - throw new ArgumentNullException("values"); - } - if (values.Length < 1) - { - throw new ArgumentException("Argument 'values' must contain at least 1 value."); - } - - // TODO: - } - - #endregion - - #region Methods - - /// - /// - /// - /// - /// - public static ABNF_DecVal Parse(StringReader reader) - { - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // dec-val = "d" 1*DIGIT [ 1*("." 1*DIGIT) / ("-" 1*DIGIT) ] - - if (reader.Peek() != 'd') - { - throw new ParseException("Invalid ABNF 'dec-val' value '" + reader.ReadToEnd() + "'."); - } - - // Eat 'd'. - reader.Read(); - - if (!char.IsNumber((char) reader.Peek())) - { - throw new ParseException("Invalid ABNF 'dec-val' value '" + reader.ReadToEnd() + "'."); - } - - ValueType valueType = ValueType.Single; - List values = new List(); - StringBuilder b = new StringBuilder(); - while (true) - { - // We reached end of string. - if (reader.Peek() == -1) - { - // - or . without required 1 DIGIT. - if (b.Length == 0) - { - throw new ParseException("Invalid ABNF 'dec-val' value '" + reader.ReadToEnd() + "'."); - } - break; - } - else if (char.IsNumber((char) reader.Peek())) - { - b.Append((char) reader.Read()); - } - // Concated value. - else if (reader.Peek() == '.') - { - // Range and conacted is not allowed to mix. - if (valueType == ValueType.Range) - { - throw new ParseException("Invalid ABNF 'dec-val' value '" + reader.ReadToEnd() + "'."); - } - if (b.Length == 0) - { - throw new ParseException("Invalid ABNF 'dec-val' value '" + reader.ReadToEnd() + "'."); - } - - values.Add(Convert.ToInt32(b.ToString())); - b = new StringBuilder(); - valueType = ValueType.Concated; - - // Eat '.'. - reader.Read(); - } - // Value range. - else if (reader.Peek() == '-') - { - // Range and conacted is not allowed to mix. Also multiple ranges not allowed. - if (valueType != ValueType.Single) - { - throw new ParseException("Invalid ABNF 'dec-val' value '" + reader.ReadToEnd() + "'."); - } - values.Add(Convert.ToInt32(b.ToString())); - b = new StringBuilder(); - valueType = ValueType.Range; - - // Eat '-'. - reader.Read(); - } - // Not dec-val char, value reading completed. - else - { - // - or . without required 1 DIGIT. - if (b.Length == 0) - { - throw new ParseException("Invalid ABNF 'dec-val' value '" + reader.ReadToEnd() + "'."); - } - break; - } - } - values.Add(Convert.ToInt32(b.ToString())); - - //Console.WriteLine(valueType.ToString()); - //foreach(int v in values){ - // Console.WriteLine(v); - // } - - if (valueType == ValueType.Single) - { - return new ABNF_DecVal(values[0], values[0]); - } - else if (valueType == ValueType.Concated) - { - return new ABNF_DecVal(values.ToArray()); - } - else - { - return new ABNF_DecVal(values[0], values[1]); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_Element.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_Element.cs deleted file mode 100644 index dff9de075..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_Element.cs +++ /dev/null @@ -1,24 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.ABNF -{ - /// - /// Thsi class is base class for any ABNF element. - /// - public abstract class ABNF_Element {} -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_HexVal.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_HexVal.cs deleted file mode 100644 index 46a1f2241..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_HexVal.cs +++ /dev/null @@ -1,29 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.ABNF -{ - /// - /// This class represent ABNF "hex-val". Defined in RFC 5234 4. - /// - public class ABNF_HexVal - { - #region Properties implementation - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_Option.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_Option.cs deleted file mode 100644 index 995327da5..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_Option.cs +++ /dev/null @@ -1,101 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.ABNF -{ - #region usings - - using System; - using System.IO; - - #endregion - - /// - /// This class represent ABNF "option". Defined in RFC 5234 4. - /// - public class ABNF_Option : ABNF_Element - { - #region Members - - private ABNF_Alternation m_pAlternation; - - #endregion - - #region Properties - - /// - /// Gets option alternation elements. - /// - public ABNF_Alternation Alternation - { - get { return m_pAlternation; } - } - - #endregion - - #region Methods - - /// - /// - /// - /// - /// - public static ABNF_Option Parse(StringReader reader) - { - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // option = "[" *c-wsp alternation *c-wsp "]" - - if (reader.Peek() != '[') - { - throw new ParseException("Invalid ABNF 'option' value '" + reader.ReadToEnd() + "'."); - } - - // Eat "[". - reader.Read(); - - // TODO: *c-wsp - - ABNF_Option retVal = new ABNF_Option(); - - // We reached end of stream, no closing "]". - if (reader.Peek() == -1) - { - throw new ParseException("Invalid ABNF 'option' value '" + reader.ReadToEnd() + "'."); - } - - retVal.m_pAlternation = ABNF_Alternation.Parse(reader); - - // We don't have closing ")". - if (reader.Peek() != ']') - { - throw new ParseException("Invalid ABNF 'option' value '" + reader.ReadToEnd() + "'."); - } - else - { - reader.Read(); - } - - return retVal; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_ProseVal.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_ProseVal.cs deleted file mode 100644 index 66b2e660e..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_ProseVal.cs +++ /dev/null @@ -1,189 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.ABNF -{ - #region usings - - using System; - using System.IO; - using System.Text; - - #endregion - - /// - /// This class represent ABNF "prose-val". Defined in RFC 5234 4. - /// - public class ABNF_ProseVal : ABNF_Element - { - #region Members - - private readonly string m_Value = ""; - - #endregion - - #region Properties - - /// - /// Gets value. - /// - public string Value - { - get { return m_Value; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// The prose-val value. - /// Is raised when value is null reference. - /// Is raised when any of the arguments has invalid value. - public ABNF_ProseVal(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - if (!Validate(value)) - { - // Just <> missing - // throw new ArgumentException("Invalid argument 'value' value. Value must be: '*(%x20-3D / %x3F-7E)'."); - } - - m_Value = value; - } - - #endregion - - #region Methods - - /// - /// - /// - /// - /// - public static ABNF_ProseVal Parse(StringReader reader) - { - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - /* - prose-val = "<" *(%x20-3D / %x3F-7E) ">" - ; bracketed string of SP and VCHAR - ; without angles - ; prose description, to be used as - ; last resort - - */ - - if (reader.Peek() != '<') - { - throw new ParseException("Invalid ABNF 'prose-val' value '" + reader.ReadToEnd() + "'."); - } - - // Eat "<" - reader.Read(); - - // TODO: *c-wsp - - StringBuilder value = new StringBuilder(); - - while (true) - { - // We reached end of stream, no closing DQUOTE. - if (reader.Peek() == -1) - { - throw new ParseException("Invalid ABNF 'prose-val' value '" + reader.ReadToEnd() + "'."); - } - // We have closing ">". - else if (reader.Peek() == '>') - { - reader.Read(); - break; - } - // Allowed char. - else if ((reader.Peek() >= 0x20 && reader.Peek() <= 0x3D) || - (reader.Peek() >= 0x3F && reader.Peek() <= 0x7E)) - { - value.Append((char) reader.Read()); - } - // Invalid value. - else - { - throw new ParseException("Invalid ABNF 'prose-val' value '" + reader.ReadToEnd() + - "'."); - } - } - - return new ABNF_ProseVal(value.ToString()); - } - - #endregion - - #region Utility methods - - /// - /// Validates "prose-val" value. - /// - /// The "prose-val" value. - /// Returns if value is "prose-val" value, otherwise false. - /// Is raised when value is null reference. - private bool Validate(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - // RFC 5234 4. - // prose-val = "<" *(%x20-3D / %x3F-7E) ">" - - if (value.Length < 2) - { - return false; - } - - for (int i = 0; i < value.Length; i++) - { - char c = value[i]; - - if (i == 0 && c != '<') - { - return false; - } - else if (i == (value.Length - 1) && c != '>') - { - return false; - } - else if (!((c >= 0x20 && c <= 0x3D) || (c >= 0x3F && c <= 0x7E))) - { - return false; - } - } - - return true; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_Repetition.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_Repetition.cs deleted file mode 100644 index 366054d09..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_Repetition.cs +++ /dev/null @@ -1,204 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.ABNF -{ - #region usings - - using System; - using System.IO; - using System.Text; - - #endregion - - /// - /// This class represent ABNF "repetition". Defined in RFC 5234 4. - /// - public class ABNF_Repetition - { - #region Members - - private readonly int m_Max = int.MaxValue; - private readonly int m_Min; - private readonly ABNF_Element m_pElement; - - #endregion - - #region Properties - - /// - /// Gets minimum repetitions. - /// - public int Min - { - get { return m_Min; } - } - - /// - /// Gets maximum repetitions. - /// - public int Max - { - get { return m_Max; } - } - - /// - /// Gets repeated element. - /// - public ABNF_Element Element - { - get { return m_pElement; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Minimum repetitions. - /// Maximum repetitions. - /// Repeated element. - /// Is raised when any of the arguments has invalid value. - /// Is raised when element is null reference. - public ABNF_Repetition(int min, int max, ABNF_Element element) - { - if (min < 0) - { - throw new ArgumentException("Argument 'min' value must be >= 0."); - } - if (max < 0) - { - throw new ArgumentException("Argument 'max' value must be >= 0."); - } - if (min > max) - { - throw new ArgumentException("Argument 'min' value must be <= argument 'max' value."); - } - if (element == null) - { - throw new ArgumentNullException("element"); - } - - m_Min = min; - m_Max = max; - m_pElement = element; - } - - #endregion - - #region Methods - - /// - /// - /// - /// - /// - public static ABNF_Repetition Parse(StringReader reader) - { - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - /* - repetition = [repeat] element - repeat = 1*DIGIT / (*DIGIT "*" *DIGIT) - element = rulename / group / option / char-val / num-val / prose-val - */ - - int min = 0; - int max = int.MaxValue; - - // --- range ------------------------------------ - if (char.IsDigit((char) reader.Peek())) - { - StringBuilder minString = new StringBuilder(); - while (char.IsDigit((char) reader.Peek())) - { - minString.Append((char) reader.Read()); - } - min = Convert.ToInt32(minString.ToString()); - } - if (reader.Peek() == '*') - { - reader.Read(); - } - if (char.IsDigit((char) reader.Peek())) - { - StringBuilder maxString = new StringBuilder(); - while (char.IsDigit((char) reader.Peek())) - { - maxString.Append((char) reader.Read()); - } - max = Convert.ToInt32(maxString.ToString()); - } - //----------------------------------------------- - - // End of stream reached. - if (reader.Peek() == -1) - { - return null; - } - // We have rulename. - else if (char.IsLetter((char) reader.Peek())) - { - return new ABNF_Repetition(min, max, ABNF_RuleName.Parse(reader)); - } - // We have group. - else if (reader.Peek() == '(') - { - return new ABNF_Repetition(min, max, ABFN_Group.Parse(reader)); - } - // We have option. - else if (reader.Peek() == '[') - { - return new ABNF_Repetition(min, max, ABNF_Option.Parse(reader)); - } - // We have char-val. - else if (reader.Peek() == '\"') - { - return new ABNF_Repetition(min, max, ABNF_CharVal.Parse(reader)); - } - // We have num-val. - else if (reader.Peek() == '%') - { - // Eat '%'. - reader.Read(); - - if (reader.Peek() == 'd') - { - return new ABNF_Repetition(min, max, ABNF_DecVal.Parse(reader)); - } - else - { - throw new ParseException("Invalid 'num-val' value '" + reader.ReadToEnd() + "'."); - } - } - // We have prose-val. - else if (reader.Peek() == '<') - { - return new ABNF_Repetition(min, max, ABNF_ProseVal.Parse(reader)); - } - - return null; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_Rule.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_Rule.cs deleted file mode 100644 index f76b44789..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_Rule.cs +++ /dev/null @@ -1,161 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.ABNF -{ - #region usings - - using System; - using System.IO; - - #endregion - - /// - /// This class represents ABNF "rule". Defined in RFC 5234 2.2. - /// - public class ABNF_Rule - { - #region Members - - private readonly string m_Name; - private readonly ABNF_Alternation m_pElements; - - #endregion - - #region Properties - - /// - /// Gets rule name. - /// - public string Name - { - get { return m_Name; } - } - - /// - /// Gets rule elements. - /// - public ABNF_Alternation Elements - { - get { return m_pElements; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Rule name. - /// Alternation elements. - /// Is raised when name or elements is null reference. - /// Is raised when any of the arguments has invalid value. - public ABNF_Rule(string name, ABNF_Alternation elements) - { - if (name == null) - { - throw new ArgumentNullException("name"); - } - if (name == string.Empty) - { - throw new ArgumentException("Argument 'name' value must be specified."); - } - if (!ValidateName(name)) - { - throw new ArgumentException( - "Invalid argument 'name' value. Value must be 'rulename = ALPHA *(ALPHA / DIGIT / \"-\")'."); - } - if (elements == null) - { - throw new ArgumentNullException("elements"); - } - - m_Name = name; - m_pElements = elements; - } - - #endregion - - #region Methods - - /// - /// - /// - /// - /// - public static ABNF_Rule Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - string[] name_value = value.Split(new[] {'='}, 2); - if (name_value.Length != 2) - { - throw new ParseException("Invalid ABNF rule '" + value + "'."); - } - - ABNF_Rule retVal = new ABNF_Rule(name_value[0].Trim(), - ABNF_Alternation.Parse(new StringReader(name_value[1]))); - - return retVal; - } - - #endregion - - #region Utility methods - - /// - /// Validates 'rulename' value. - /// - /// Rule name. - /// Returns true if rule name is valid, otherwise false. - private bool ValidateName(string name) - { - if (name == null) - { - return false; - } - if (name == string.Empty) - { - return false; - } - - // RFC 5234 4. - // rulename = ALPHA *(ALPHA / DIGIT / "-") - - if (!char.IsLetter(name[0])) - { - return false; - } - for (int i = 1; i < name.Length; i++) - { - char c = name[i]; - if (!(char.IsLetter(c) | char.IsDigit(c) | c == '-')) - { - return false; - } - } - - return true; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_RuleName.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_RuleName.cs deleted file mode 100644 index fce7c17ba..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ABNF/ABNF_RuleName.cs +++ /dev/null @@ -1,164 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.ABNF -{ - #region usings - - using System; - using System.IO; - using System.Text; - - #endregion - - /// - /// This class represent ABNF "rulename". Defined in RFC 5234 4. - /// - public class ABNF_RuleName : ABNF_Element - { - #region Members - - private readonly string m_RuleName; - - #endregion - - #region Properties - - /// - /// Gets rule name. - /// - public string RuleName - { - get { return m_RuleName; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Rule name. - /// Is raised when ruleName is null reference. - /// Is raised when any of the arguments has invalid value. - public ABNF_RuleName(string ruleName) - { - if (ruleName == null) - { - throw new ArgumentNullException("ruleName"); - } - if (!ValidateName(ruleName)) - { - throw new ArgumentException( - "Invalid argument 'ruleName' value. Value must be 'rulename = ALPHA *(ALPHA / DIGIT / \"-\")'."); - } - - m_RuleName = ruleName; - } - - #endregion - - #region Methods - - /// - /// - /// - /// - /// - public static ABNF_RuleName Parse(StringReader reader) - { - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // RFC 5234 4. - // rulename = ALPHA *(ALPHA / DIGIT / "-") - - if (!char.IsLetter((char) reader.Peek())) - { - throw new ParseException("Invalid ABNF 'rulename' value '" + reader.ReadToEnd() + "'."); - } - - StringBuilder ruleName = new StringBuilder(); - - while (true) - { - // We reached end of string. - if (reader.Peek() == -1) - { - break; - } - // We have valid rule name char. - else if (char.IsLetter((char) reader.Peek()) | char.IsDigit((char) reader.Peek()) | - (char) reader.Peek() == '-') - { - ruleName.Append((char) reader.Read()); - } - // Not rule name char, probably readed name. - else - { - break; - } - } - - return new ABNF_RuleName(ruleName.ToString()); - } - - #endregion - - #region Utility methods - - /// - /// Validates 'rulename' value. - /// - /// Rule name. - /// Returns true if rule name is valid, otherwise false. - private bool ValidateName(string name) - { - if (name == null) - { - return false; - } - if (name == string.Empty) - { - return false; - } - - // RFC 5234 4. - // rulename = ALPHA *(ALPHA / DIGIT / "-") - - if (!char.IsLetter(name[0])) - { - return false; - } - for (int i = 1; i < name.Length; i++) - { - char c = name[i]; - if (!(char.IsLetter(c) | char.IsDigit(c) | c == '-')) - { - return false; - } - } - - return true; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ASC.Mail.csproj b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ASC.Mail.csproj deleted file mode 100644 index 89ad336d9..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ASC.Mail.csproj +++ /dev/null @@ -1,843 +0,0 @@ - - - - Local - 9.0.30729 - 2.0 - {7AB36CCF-5FFD-4780-A54E-A102ECA028B5} - Debug - AnyCPU - ASC.Mail.Net - JScript - Grid - IE50 - false - Library - ASC.Mail.Net - OnBuildSuccess - 3.5 - v4.8 - - - - - - publish\ - true - Web - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - ..\..\..\..\web\studio\ASC.Web.Studio\bin\ - - - false - 285212672 - false - - - DEBUG;TRACE - - - true - 4096 - false - - - false - false - false - false - 0 - full - prompt - false - - - false - 285212672 - false - - - TRACE - - - false - 4096 - false - - - true - false - false - false - 0 - none - prompt - false - - - - System - - - 3.5 - - - System.Data - - - - - System.Windows.Forms - - - System.XML - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Code - - - - Code - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Code - - - Component - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Form - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Component - - - - - - - - - - - - - - - - - Code - - - Code - - - Code - - - Code - - - Code - - - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Component - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - - Code - - - Code - - - Code - - - Code - - - Code - - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - - - - Code - - - Code - - - Code - - - Code - - - Code - - - - - - - - - - - - - - - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - - - - - - - - - - - - - - Code - - - - Code - - - - Code - - - Code - - - - Code - - - - Code - - - - - Code - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Code - - - - - - Code - - - Code - - - Code - - - - - Code - - - Code - - - Code - - - Component - - - Code - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Code - - - - - Code - - - Code - - - Code - - - Code - - - - Code - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 2.0 %28x86%29 - true - - - False - .NET Framework 3.0 %28x86%29 - false - - - False - .NET Framework 3.5 - false - - - False - .NET Framework 3.5 SP1 - false - - - - - - - - - - - - - \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AUTH_SASL_ServerMechanism.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AUTH_SASL_ServerMechanism.cs deleted file mode 100644 index f9647f682..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AUTH_SASL_ServerMechanism.cs +++ /dev/null @@ -1,74 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.AUTH -{ - #region usings - - using System; - using System.Collections.Generic; - - #endregion - - /// - /// This base class for server SASL authentication mechanisms. - /// - public abstract class AUTH_SASL_ServerMechanism - { - #region Properties - - /// - /// Gets if user has authenticated sucessfully. - /// - public abstract bool IsAuthenticated { get; } - - /// - /// Gets if the authentication exchange has completed. - /// - public abstract bool IsCompleted { get; } - - /// - /// Gets IANA-registered SASL authentication mechanism name. - /// - /// The registered list is available from: http://www.iana.org/assignments/sasl-mechanisms . - public abstract string Name { get; } - - /// - /// Gets if specified SASL mechanism is available only to SSL connection. - /// - public abstract bool RequireSSL { get; } - - /// - /// Gets user login name. - /// - public abstract string UserName { get; } - - #endregion - - #region Methods - - /// - /// Continues authentication process. - /// - /// Client sent SASL response. - /// Retunrns challange response what must be sent to client or null if authentication has completed. - /// Is raised when clientRespone is null reference. - public abstract string Continue(string clientResponse); - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AUTH_SASL_ServerMechanism_CramMd5.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AUTH_SASL_ServerMechanism_CramMd5.cs deleted file mode 100644 index 3c94c91ef..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AUTH_SASL_ServerMechanism_CramMd5.cs +++ /dev/null @@ -1,257 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.AUTH -{ - #region usings - - using System; - using System.Security.Cryptography; - using System.Text; - - #endregion - - /// - /// Implements "CRAM-MD5" authenticaiton. Defined in RFC 2195. - /// - public class AUTH_SASL_ServerMechanism_CramMd5 : AUTH_SASL_ServerMechanism - { - #region Events - - /// - /// Is called when authentication mechanism needs to get user info to complete atuhentication. - /// - public event EventHandler GetUserInfo = null; - - #endregion - - #region Members - - private readonly bool m_RequireSSL; - private bool m_IsAuthenticated; - private bool m_IsCompleted; - private string m_Key = ""; - private int m_State; - private string m_UserName = ""; - - #endregion - - #region Properties - - /// - /// Gets if the authentication exchange has completed. - /// - public override bool IsCompleted - { - get { return m_IsCompleted; } - } - - /// - /// Gets if user has authenticated sucessfully. - /// - public override bool IsAuthenticated - { - get { return m_IsAuthenticated; } - } - - /// - /// Returns always "CRAM-MD5". - /// - public override string Name - { - get { return "CRAM-MD5"; } - } - - /// - /// Gets if specified SASL mechanism is available only to SSL connection. - /// - public override bool RequireSSL - { - get { return m_RequireSSL; } - } - - /// - /// Gets user login name. - /// - public override string UserName - { - get { return m_UserName; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Specifies if this mechanism is available to SSL connections only. - public AUTH_SASL_ServerMechanism_CramMd5(bool requireSSL) - { - m_RequireSSL = requireSSL; - } - - #endregion - - #region Methods - - /// - /// Continues authentication process. - /// - /// Client sent SASL response. - /// Retunrns challange response what must be sent to client or null if authentication has completed. - /// Is raised when clientResponse is null reference. - public override string Continue(string clientResponse) - { - if (clientResponse == null) - { - throw new ArgumentNullException("clientResponse"); - } - - /* RFC 2195 2. Challenge-Response Authentication Mechanism. - The authentication type associated with CRAM is "CRAM-MD5". - - The data encoded in the first ready response contains an - presumptively arbitrary string of random digits, a timestamp, and the - fully-qualified primary host name of the server. The syntax of the - unencoded form must correspond to that of an RFC 822 'msg-id' - [RFC822] as described in [POP3]. - - The client makes note of the data and then responds with a string - consisting of the user name, a space, and a 'digest'. The latter is - computed by applying the keyed MD5 algorithm from [KEYED-MD5] where - the key is a shared secret and the digested text is the timestamp - (including angle-brackets). - - This shared secret is a string known only to the client and server. - The `digest' parameter itself is a 16-octet value which is sent in - hexadecimal format, using lower-case ASCII characters. - - When the server receives this client response, it verifies the digest - provided. If the digest is correct, the server should consider the - client authenticated and respond appropriately. - - Example: - The examples in this document show the use of the CRAM mechanism with - the IMAP4 AUTHENTICATE command [IMAP-AUTH]. The base64 encoding of - the challenges and responses is part of the IMAP4 AUTHENTICATE - command, not part of the CRAM specification itself. - - S: * OK IMAP4 Server - C: A0001 AUTHENTICATE CRAM-MD5 - S: + PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2UucmVzdG9uLm1jaS5uZXQ+ - C: dGltIGI5MTNhNjAyYzdlZGE3YTQ5NWI0ZTZlNzMzNGQzODkw - S: A0001 OK CRAM authentication successful - - In this example, the shared secret is the string - 'tanstaaftanstaaf'. Hence, the Keyed MD5 digest is produced by - calculating - - MD5((tanstaaftanstaaf XOR opad), - MD5((tanstaaftanstaaf XOR ipad), - <1896.697170952@postoffice.reston.mci.net>)) - - where ipad and opad are as defined in the keyed-MD5 Work in - Progress [KEYED-MD5] and the string shown in the challenge is the - base64 encoding of <1896.697170952@postoffice.reston.mci.net>. The - shared secret is null-padded to a length of 64 bytes. If the - shared secret is longer than 64 bytes, the MD5 digest of the - shared secret is used as a 16 byte input to the keyed MD5 - calculation. - - This produces a digest value (in hexadecimal) of - - b913a602c7eda7a495b4e6e7334d3890 - - The user name is then prepended to it, forming - - tim b913a602c7eda7a495b4e6e7334d3890 - - Which is then base64 encoded to meet the requirements of the IMAP4 - AUTHENTICATE command (or the similar POP3 AUTH command), yielding - - dGltIGI5MTNhNjAyYzdlZGE3YTQ5NWI0ZTZlNzMzNGQzODkw - */ - - if (m_State == 0) - { - m_State++; - m_Key = "<" + Guid.NewGuid() + "@host" + ">"; - - return m_Key; - } - else - { - // Parse client response. response = userName SP hash. - string[] user_hash = clientResponse.Split(' '); - if (user_hash.Length == 2 && !string.IsNullOrEmpty(user_hash[0])) - { - m_UserName = user_hash[0]; - AUTH_e_UserInfo result = OnGetUserInfo(user_hash[0]); - if (result.UserExists) - { - // hash = Hex(HmacMd5(hashKey,password)) - string hash = Net_Utils.Hex(HmacMd5(m_Key, result.Password)); - if (hash == user_hash[1]) - { - m_IsAuthenticated = true; - } - } - } - - m_IsCompleted = true; - } - - return null; - } - - #endregion - - #region Utility methods - - /// - /// Calculates keyed md5 hash from specifieed text and with specified hash key. - /// - /// MD5 key. - /// Text to hash. - /// Returns MD5 hash. - private string HmacMd5(string hashKey, string text) - { - HMACMD5 kMd5 = new HMACMD5(Encoding.Default.GetBytes(text)); - return Encoding.Default.GetString(kMd5.ComputeHash(Encoding.ASCII.GetBytes(hashKey))); - } - - /// - /// Raises GetUserInfo event. - /// - /// User name. - /// Returns specified user info. - private AUTH_e_UserInfo OnGetUserInfo(string userName) - { - AUTH_e_UserInfo retVal = new AUTH_e_UserInfo(userName); - - if (GetUserInfo != null) - { - GetUserInfo(this, retVal); - } - - return retVal; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AUTH_SASL_ServerMechanism_DigestMd5.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AUTH_SASL_ServerMechanism_DigestMd5.cs deleted file mode 100644 index 6d32a4611..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AUTH_SASL_ServerMechanism_DigestMd5.cs +++ /dev/null @@ -1,227 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.AUTH -{ - #region usings - - using System; - - #endregion - - /// - /// Implements "DIGEST-MD5" authenticaiton. Defined in RFC 2831. - /// - public class AUTH_SASL_ServerMechanism_DigestMd5 : AUTH_SASL_ServerMechanism - { - #region Events - - /// - /// Is called when authentication mechanism needs to get user info to complete atuhentication. - /// - public event EventHandler GetUserInfo = null; - - #endregion - - #region Members - - private readonly string m_Nonce = ""; - - private readonly bool m_RequireSSL; - private bool m_IsAuthenticated; - private bool m_IsCompleted; - private string m_Realm = ""; - private int m_State; - private string m_UserName = ""; - - #endregion - - #region Properties - - /// - /// Gets if the authentication exchange has completed. - /// - public override bool IsCompleted - { - get { return m_IsCompleted; } - } - - /// - /// Gets if user has authenticated sucessfully. - /// - public override bool IsAuthenticated - { - get { return m_IsAuthenticated; } - } - - /// - /// Returns always "DIGEST-MD5". - /// - public override string Name - { - get { return "DIGEST-MD5"; } - } - - /// - /// Gets if specified SASL mechanism is available only to SSL connection. - /// - public override bool RequireSSL - { - get { return m_RequireSSL; } - } - - /// - /// Gets or sets realm value. - /// - /// Normally this is host or domain name. - public string Realm - { - get { return m_Realm; } - - set - { - if (value == null) - { - value = ""; - } - m_Realm = value; - } - } - - /// - /// Gets user login name. - /// - public override string UserName - { - get { return m_UserName; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Specifies if this mechanism is available to SSL connections only. - public AUTH_SASL_ServerMechanism_DigestMd5(bool requireSSL) - { - m_RequireSSL = requireSSL; - - m_Nonce = Auth_HttpDigest.CreateNonce(); - } - - #endregion - - #region Methods - - /// - /// Continues authentication process. - /// - /// Client sent SASL response. - /// Retunrns challange response what must be sent to client or null if authentication has completed. - /// Is raised when clientResponse is null reference. - public override string Continue(string clientResponse) - { - if (clientResponse == null) - { - throw new ArgumentNullException("clientResponse"); - } - - /* RFC 2831. - The base64-decoded version of the SASL exchange is: - - S: realm="elwood.innosoft.com",nonce="OA6MG9tEQGm2hh",qop="auth", - algorithm=md5-sess,charset=utf-8 - C: charset=utf-8,username="chris",realm="elwood.innosoft.com", - nonce="OA6MG9tEQGm2hh",nc=00000001,cnonce="OA6MHXh6VqTrRk", - digest-uri="imap/elwood.innosoft.com", - response=d388dad90d4bbd760a152321f2143af7,qop=auth - S: rspauth=ea40f60335c427b5527b84dbabcdfffd - C: - S: ok - - The password in this example was "secret". - */ - - if (m_State == 0) - { - m_State++; - - return "realm=\"" + m_Realm + "\",nonce=\"" + m_Nonce + - "\",qop=\"auth\",algorithm=md5-sess,charset=utf-8"; - } - else if (m_State == 1) - { - m_State++; - - Auth_HttpDigest auth = new Auth_HttpDigest(clientResponse, "AUTHENTICATE"); - auth.Qop = "auth"; - auth.Algorithm = "md5-sess"; - - // Check realm and nonce value. - if (m_Realm != auth.Realm || m_Nonce != auth.Nonce) - { - return "rspauth=\"\""; - } - - m_UserName = auth.UserName; - AUTH_e_UserInfo result = OnGetUserInfo(auth.UserName); - if (result.UserExists) - { - if (auth.Authenticate(result.UserName, result.Password)) - { - m_IsAuthenticated = true; - - return "rspauth=" + auth.CalculateRspAuth(result.UserName, result.Password); - } - } - - return "rspauth=\"\""; - } - else - { - m_IsCompleted = true; - } - - return null; - } - - #endregion - - #region Utility methods - - /// - /// Raises GetUserInfo event. - /// - /// User name. - /// Returns specified user info. - private AUTH_e_UserInfo OnGetUserInfo(string userName) - { - AUTH_e_UserInfo retVal = new AUTH_e_UserInfo(userName); - - if (GetUserInfo != null) - { - GetUserInfo(this, retVal); - } - - return retVal; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AUTH_SASL_ServerMechanism_Login.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AUTH_SASL_ServerMechanism_Login.cs deleted file mode 100644 index 4b905607f..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AUTH_SASL_ServerMechanism_Login.cs +++ /dev/null @@ -1,188 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.AUTH -{ - #region usings - - using System; - - #endregion - - /// - /// Implements "LOGIN" authenticaiton. - /// - public class AUTH_SASL_ServerMechanism_Login : AUTH_SASL_ServerMechanism - { - #region Events - - /// - /// Is called when authentication mechanism needs to authenticate specified user. - /// - public event EventHandler Authenticate = null; - - #endregion - - #region Members - - private readonly bool m_RequireSSL; - private bool m_IsAuthenticated; - private bool m_IsCompleted; - private string m_Password; - private int m_State; - private string m_UserName; - - #endregion - - #region Properties - - /// - /// Gets if the authentication exchange has completed. - /// - public override bool IsCompleted - { - get { return m_IsCompleted; } - } - - /// - /// Gets if user has authenticated sucessfully. - /// - public override bool IsAuthenticated - { - get { return m_IsAuthenticated; } - } - - /// - /// Returns always "LOGIN". - /// - public override string Name - { - get { return "LOGIN"; } - } - - /// - /// Gets if specified SASL mechanism is available only to SSL connection. - /// - public override bool RequireSSL - { - get { return m_RequireSSL; } - } - - /// - /// Gets user login name. - /// - public override string UserName - { - get { return m_UserName; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Specifies if this mechanism is available to SSL connections only. - public AUTH_SASL_ServerMechanism_Login(bool requireSSL) - { - m_RequireSSL = requireSSL; - } - - #endregion - - #region Methods - - /// - /// Continues authentication process. - /// - /// Client sent SASL response. - /// Retunrns challange response what must be sent to client or null if authentication has completed. - /// Is raised when clientResponse is null reference. - public override string Continue(string clientResponse) - { - if (clientResponse == null) - { - throw new ArgumentNullException("clientResponse"); - } - - /* RFC none. - S: "Username:" - C: userName - S: "Password:" - C: password - - NOTE: UserName may be included in initial client response. - */ - - // User name provided, so skip that state. - if (m_State == 0 && !string.IsNullOrEmpty(clientResponse)) - { - m_State++; - } - - if (m_State == 0 && string.IsNullOrEmpty(clientResponse)) - { - m_State++; - - return "UserName:"; - } - else if (m_State == 1) - { - m_State++; - m_UserName = clientResponse; - - return "Password:"; - } - else - { - m_Password = clientResponse; - - AUTH_e_Authenticate result = OnAuthenticate("", m_UserName, m_Password); - m_IsAuthenticated = result.IsAuthenticated; - m_IsCompleted = true; - } - - return null; - } - - #endregion - - #region Utility methods - - /// - /// Raises Authenticate event. - /// - /// Authorization ID. - /// User name. - /// Password. - /// Returns authentication result. - private AUTH_e_Authenticate OnAuthenticate(string authorizationID, string userName, string password) - { - AUTH_e_Authenticate retVal = new AUTH_e_Authenticate(authorizationID, userName, password); - - if (Authenticate != null) - { - Authenticate(this, retVal); - } - - return retVal; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AUTH_SASL_ServerMechanism_Plain.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AUTH_SASL_ServerMechanism_Plain.cs deleted file mode 100644 index b007e508d..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AUTH_SASL_ServerMechanism_Plain.cs +++ /dev/null @@ -1,190 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.AUTH -{ - #region usings - - using System; - - #endregion - - /// - /// Implements "PLAIN" authenticaiton. Defined in RFC 4616. - /// - public class AUTH_SASL_ServerMechanism_Plain : AUTH_SASL_ServerMechanism - { - #region Events - - /// - /// Is called when authentication mechanism needs to authenticate specified user. - /// - public event EventHandler Authenticate = null; - - #endregion - - #region Members - - private readonly bool m_RequireSSL; - private bool m_IsAuthenticated; - private bool m_IsCompleted; - private string m_UserName = ""; - - #endregion - - #region Properties - - /// - /// Gets if the authentication exchange has completed. - /// - public override bool IsCompleted - { - get { return m_IsCompleted; } - } - - /// - /// Gets if user has authenticated sucessfully. - /// - public override bool IsAuthenticated - { - get { return m_IsAuthenticated; } - } - - /// - /// Returns always "PLAIN". - /// - public override string Name - { - get { return "PLAIN"; } - } - - /// - /// Gets if specified SASL mechanism is available only to SSL connection. - /// - public override bool RequireSSL - { - get { return m_RequireSSL; } - } - - /// - /// Gets user login name. - /// - public override string UserName - { - get { return m_UserName; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Specifies if this mechanism is available to SSL connections only. - public AUTH_SASL_ServerMechanism_Plain(bool requireSSL) - { - m_RequireSSL = requireSSL; - } - - #endregion - - #region Methods - - /// - /// Continues authentication process. - /// - /// Client sent SASL response. - /// Retunrns challange response what must be sent to client or null if authentication has completed. - /// Is raised when clientResponse is null reference. - public override string Continue(string clientResponse) - { - if (clientResponse == null) - { - throw new ArgumentNullException("clientResponse"); - } - - /* RFC 4616.2. PLAIN SASL Mechanism. - The mechanism consists of a single message, a string of [UTF-8] - encoded [Unicode] characters, from the client to the server. The - client presents the authorization identity (identity to act as), - followed by a NUL (U+0000) character, followed by the authentication - identity (identity whose password will be used), followed by a NUL - (U+0000) character, followed by the clear-text password. As with - other SASL mechanisms, the client does not provide an authorization - identity when it wishes the server to derive an identity from the - credentials and use that as the authorization identity. - - message = [authzid] UTF8NUL authcid UTF8NUL passwd - - Example: - C: a002 AUTHENTICATE "PLAIN" - S: + "" - C: {21} - C: timtanstaaftanstaaf - S: a002 OK "Authenticated" - */ - - if (clientResponse == string.Empty) - { - return ""; - } - // Parse response - else - { - string[] authzid_authcid_passwd = clientResponse.Split('\0'); - if (authzid_authcid_passwd.Length == 3 && !string.IsNullOrEmpty(authzid_authcid_passwd[1])) - { - m_UserName = authzid_authcid_passwd[1]; - AUTH_e_Authenticate result = OnAuthenticate(authzid_authcid_passwd[0], - authzid_authcid_passwd[1], - authzid_authcid_passwd[2]); - m_IsAuthenticated = result.IsAuthenticated; - } - - m_IsCompleted = true; - } - - return null; - } - - #endregion - - #region Utility methods - - /// - /// Raises Authenticate event. - /// - /// Authorization ID. - /// User name. - /// Password. - /// Returns authentication result. - private AUTH_e_Authenticate OnAuthenticate(string authorizationID, string userName, string password) - { - AUTH_e_Authenticate retVal = new AUTH_e_Authenticate(authorizationID, userName, password); - - if (Authenticate != null) - { - Authenticate(this, retVal); - } - - return retVal; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AUTH_e_Authenticate.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AUTH_e_Authenticate.cs deleted file mode 100644 index c586c135b..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AUTH_e_Authenticate.cs +++ /dev/null @@ -1,100 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.AUTH -{ - #region usings - - using System; - - #endregion - - /// - /// This class provides data for server userName/password authentications. - /// - public class AUTH_e_Authenticate : EventArgs - { - #region Members - - private readonly string m_AuthorizationID = ""; - private readonly string m_Password = ""; - private readonly string m_UserName = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets if specified user is authenticated. - /// - public bool IsAuthenticated { get; set; } - - /// - /// Gets authorization ID. - /// - public string AuthorizationID - { - get { return m_AuthorizationID; } - } - - /// - /// Gets user name. - /// - public string UserName - { - get { return m_UserName; } - } - - /// - /// Gets password. - /// - public string Password - { - get { return m_Password; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Authorization ID. - /// User name. - /// Password. - /// Is raised when userName is null reference. - /// Is raised when any of the argumnets has invalid value. - public AUTH_e_Authenticate(string authorizationID, string userName, string password) - { - if (userName == null) - { - throw new ArgumentNullException("userName"); - } - if (userName == string.Empty) - { - throw new ArgumentException("Argument 'userName' value must be specified.", "userName"); - } - - m_AuthorizationID = authorizationID; - m_UserName = userName; - m_Password = password; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AUTH_e_UserInfo.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AUTH_e_UserInfo.cs deleted file mode 100644 index 80958a005..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AUTH_e_UserInfo.cs +++ /dev/null @@ -1,89 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.AUTH -{ - #region usings - - using System; - - #endregion - - /// - /// This class provides data for server authentication mechanisms GetUserInfo event. - /// - public class AUTH_e_UserInfo : EventArgs - { - #region Members - - private readonly string m_UserName = ""; - private string m_Password = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets if specified user exists. - /// - public bool UserExists { get; set; } - - /// - /// Gets user name. - /// - public string UserName - { - get { return m_UserName; } - } - - /// - /// Gets or sets user password. - /// - public string Password - { - get { return m_Password; } - - set { m_Password = value; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// User name. - /// Is raised when userName is null reference. - /// Is raised when any of the arguments has invalid value. - public AUTH_e_UserInfo(string userName) - { - if (userName == null) - { - throw new ArgumentNullException("userName"); - } - if (userName == string.Empty) - { - throw new ArgumentException("Argument 'userName' value must be specified.", "userName"); - } - - m_UserName = userName; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AuthHelper.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AuthHelper.cs deleted file mode 100644 index 848f6e689..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AuthHelper.cs +++ /dev/null @@ -1,204 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.AUTH -{ - #region usings - - using System; - using System.Security.Cryptography; - using System.Text; - - #endregion - - /// - /// Provides helper methods for authentications(APOP,CRAM-MD5,DIGEST-MD5). - /// - [Obsolete] - public class AuthHelper - { - #region Methods - - /// - /// Calculates APOP authentication compare value. - /// - /// Password. - /// Password tag. - /// Returns value what must be used for comparing passwords. - public static string Apop(string password, string passwordTag) - { - /* RFC 1939 7. APOP - * - * value = Hex(Md5(passwordTag + password)) - */ - - return Hex(Md5(passwordTag + password)); - } - - /// - /// Calculates CRAM-MD5 authentication compare value. - /// - /// Password. - /// Hash calculation key - /// Returns value what must be used for comparing passwords. - public static string Cram_Md5(string password, string hashKey) - { - /* RFC 2195 AUTH CRAM-MD5 - * - * value = Hex(HmacMd5(hashKey,password)) - */ - - return Hex(HmacMd5(hashKey, password)); - } - - /// - /// Calculates DIGEST-MD5 authentication compare value. - /// - /// Specifies if client or server value calculated. - /// Client and server has diffrent calculation method. - /// Use domain or machine name for this. - /// User name. - /// Password. - /// Server password tag. - /// Client password tag. - /// - /// Returns value what must be used for comparing passwords. - public static string Digest_Md5(bool client_server, - string realm, - string userName, - string password, - string nonce, - string cnonce, - string digest_uri) - { - /* RFC 2831 AUTH DIGEST-MD5 - * - * qop = "auth"; // We support auth only auth-int and auth-conf isn't supported - * nc = "00000001" - * - * A1 = Md5(userName + ":" + realm + ":" + passw) + ":" + nonce + ":" + cnonce - * A2(client response) = "AUTHENTICATE:" + digest_uri - * A2(server response) = ":" + digest_uri - * - * resp-value = Hex(Md5(Hex(Md5(a1)) + ":" + (nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + Hex(Md5(a2))))); - */ - - // string realm = "elwood.innosoft.com"; - // string userName = "chris"; - // string passw = "secret"; - // string nonce = "OA6MG9tEQGm2hh"; - // string cnonce = "OA6MHXh6VqTrRk"; - // string digest_uri = "imap/elwood.innosoft.com"; - - string qop = "auth"; - string nc = "00000001"; - //**** - string a1 = Md5(userName + ":" + realm + ":" + password) + ":" + nonce + ":" + cnonce; - string a2 = ""; - if (client_server) - { - a2 = "AUTHENTICATE:" + digest_uri; - } - else - { - a2 = ":" + digest_uri; - } - - return - Hex( - Md5(Hex(Md5(a1)) + ":" + - (nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + Hex(Md5(a2))))); - } - - /// - /// Creates AUTH Digest-md5 server response what server must send to client. - /// - /// Use domain or machine name for this. - /// Server password tag. Random hex string is suggested. - /// - public static string Create_Digest_Md5_ServerResponse(string realm, string nonce) - { - return "realm=\"" + realm + "\",nonce=\"" + nonce + "\",qop=\"auth\",algorithm=md5-sess"; - } - - /// - /// Generates random nonce value. - /// - /// - public static string GenerateNonce() - { - return Guid.NewGuid().ToString().Replace("-", "").Substring(0, 16); - } - - /// - /// Calculates keyed md5 hash from specifieed text and with specified hash key. - /// - /// - /// - /// - public static string HmacMd5(string hashKey, string text) - { - HMACMD5 kMd5 = new HMACMD5(Encoding.Default.GetBytes(text)); - return Encoding.Default.GetString(kMd5.ComputeHash(Encoding.ASCII.GetBytes(hashKey))); - } - - /// - /// Calculates md5 hash from specified string. - /// - /// - /// - public static string Md5(string text) - { - MD5 md5 = new MD5CryptoServiceProvider(); - byte[] hash = md5.ComputeHash(Encoding.Default.GetBytes(text)); - - return Encoding.Default.GetString(hash); - } - - /// - /// Converts specified string to hexa string. - /// - /// - /// - public static string Hex(string text) - { - return BitConverter.ToString(Encoding.Default.GetBytes(text)).ToLower().Replace("-", ""); - } - - /// - /// Encodes specified string to base64 string. - /// - /// Text to encode. - /// Returns encoded string. - public static string Base64en(string text) - { - return Convert.ToBase64String(Encoding.Default.GetBytes(text)); - } - - /// - /// Decodes specified base64 string. - /// - /// Base64 string to decode. - /// Returns decoded string. - public static string Base64de(string text) - { - return Encoding.Default.GetString(Convert.FromBase64String(text)); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AuthType.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AuthType.cs deleted file mode 100644 index 891acec1c..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/AuthType.cs +++ /dev/null @@ -1,47 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; - -namespace LumiSoft.Net.AUTH -{ - /// - /// Authentication type. - /// - public enum AuthType - { - /// - /// Clear text username/password authentication. - /// - PLAIN = 0, - - /// - /// APOP.This is used by POP3 only. RFC 1939 7. APOP. - /// - APOP = 1, - - /// - /// CRAM-MD5 authentication. RFC 2195 AUTH CRAM-MD5. - /// - CRAM_MD5 = 3, - - /// - /// DIGEST-MD5 authentication. RFC 2831 AUTH DIGEST-MD5. - /// - DIGEST_MD5 = 4, - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/Auth_HttpDigest.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/Auth_HttpDigest.cs deleted file mode 100644 index 966a938ba..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/Auth_HttpDigest.cs +++ /dev/null @@ -1,684 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.AUTH -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements http digest access authentication. Defined in RFC 2617. - /// - public class Auth_HttpDigest - { - #region Members - - private string m_Algorithm = ""; - private string m_Charset = ""; - private string m_Cnonce = ""; - private string m_Method = ""; - private string m_Nonce = ""; - private int m_NonceCount = 1; - private string m_Opaque = ""; - private string m_Password = ""; - private string m_Qop = ""; - private string m_Realm = ""; - private string m_Response = ""; - private string m_Uri = ""; - private string m_UserName = ""; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Server/Client returned digest response. - /// Request method. - public Auth_HttpDigest(string digestResponse, string requestMethod) - { - m_Method = requestMethod; - - Parse(digestResponse); - } - - /// - /// Client constructor. This is used to build valid Authorization response to server. - /// - /// User name. - /// Password. - /// Client nonce value. - /// Request URI. - /// Server authenticate resposne. - /// Request method. - public Auth_HttpDigest(string userName, - string password, - string cnonce, - string uri, - string digestResponse, - string requestMethod) - { - Parse(digestResponse); - - m_UserName = userName; - m_Password = password; - m_Method = requestMethod; - m_Cnonce = cnonce; - m_Uri = uri; - m_Qop = "auth"; - m_NonceCount = 1; - m_Response = CalculateResponse(m_UserName, m_Password); - } - - /// - /// Server constructor. This is used to build valid Authenticate response to client. - /// - /// Realm(domain). - /// Nonce value. - /// Opaque value. - public Auth_HttpDigest(string realm, string nonce, string opaque) - { - m_Realm = realm; - m_Nonce = nonce; - m_Opaque = opaque; - } - - #endregion - - #region Properties - - /// - /// Gets or sets algorithm to use to produce the digest and a checksum. - /// This is normally MD5 or MD5-sess. - /// - public string Algorithm - { - get { return m_Algorithm; } - - set { m_Algorithm = value; } - } - - /// - /// Gets or sets Client nonce value. This MUST be specified if a qop directive is sent (see above), and - /// MUST NOT be specified if the server did not send a qop directive in the WWW-Authenticate header field. - /// - public string CNonce - { - get { return m_Cnonce; } - - set - { - if (value == null) - { - value = ""; - } - m_Cnonce = value; - } - } - - /// - /// Gets or sets a server-specified unique data string. It is recommended that this - /// string be base64 or hexadecimal data. - /// Suggested value: base64(time-stamp hex(time-stamp ":" ETag ":" private-key)). - /// - /// Is raised when invalid value is specified. - public string Nonce - { - get { return m_Nonce; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Nonce value can't be null or empty !"); - } - - m_Nonce = value; - } - } - - /// - /// Gets nonce count. This MUST be specified if a qop directive is sent (see above), and - /// MUST NOT be specified if the server did not send a qop directive in the WWW-Authenticate - /// header field. The nc-value is the hexadecimal count of the number of requests. - /// - public int NonceCount - { - get { return m_NonceCount; } - } - - /// - /// Gets or sets string of data, specified by the server, which should be returned by the client unchanged. - /// It is recommended that this string be base64 or hexadecimal data. - /// - /// Is raised when invalid value is specified. - public string Opaque - { - get { return m_Opaque; } - - set { m_Opaque = value; } - } - - /* - public bool Stale - { - get{ return false; } - } - */ - - /// - /// Gets or sets password. - /// - public string Password - { - get { return m_Password; } - - set - { - if (value == null) - { - value = ""; - } - m_Password = value; - } - } - - /// - /// Gets or sets value what indicates "quality of protection" the client has applied to - /// the message. If present, its value MUST be one of the alternatives the server indicated - /// it supports in the WWW-Authenticate header. This directive is optional in order to preserve - /// backward compatibility. - /// - public string Qop - { - get { return m_Qop; } - - set { m_Qop = value; } - } - - /// - /// Gets or sets a string to be displayed to users so they know which username and password - /// to use. This string should contain at least the name of the host performing the - /// authentication and might additionally indicate the collection of users who might have access. - /// An example might be "registered_users@gotham.news.com". - /// - public string Realm - { - get { return m_Realm; } - - set - { - if (value == null) - { - value = ""; - } - m_Realm = value; - } - } - - /// - /// Gets or sets request method. - /// - public string RequestMethod - { - get { return m_Method; } - - set - { - if (value == null) - { - value = ""; - } - m_Method = value; - } - } - - /// - /// Gets a string of 32 hex digits computed by HTTP digest algorithm, - /// which proves that the user knows a password. - /// - public string Response - { - get { return m_Response; } - } - - /// - /// Gets the URI from Request-URI. - /// - public string Uri - { - get { return m_Uri; } - - set { m_Uri = value; } - } - - /// - /// Gets or sets user name. - /// - public string UserName - { - get { return m_UserName; } - - set - { - if (value == null) - { - value = ""; - } - m_UserName = value; - } - } - - #endregion - - #region Methods - - /// - /// Creates valid nonce value. - /// - /// Returns nonce value. - public static string CreateNonce() - { - return Guid.NewGuid().ToString().Replace("-", ""); - } - - /// - /// Creates valid opaque value. - /// - /// Renturn opaque value. - public static string CreateOpaque() - { - return Guid.NewGuid().ToString().Replace("-", ""); - } - - /// - /// Authenticates specified user and password using this class parameters. - /// - /// User name. - /// Password. - /// Returns true if authenticated, otherwise false. - public bool Authenticate(string userName, string password) - { - // Check that our computed digest is same as client provided. - if (Response == CalculateResponse(userName, password)) - { - return true; - } - else - { - return false; - } - } - - /// - /// Calculates 'rspauth' value. - /// - /// User name. - /// Password. - /// Returns 'rspauth' value. - public string CalculateRspAuth(string userName, string password) - { - /* RFC 2617 3.2.3. - The optional response digest in the "response-auth" directive - supports mutual authentication -- the server proves that it knows the - user's secret, and with qop=auth-int also provides limited integrity - protection of the response. The "response-digest" value is calculated - as for the "request-digest" in the Authorization header, except that - if "qop=auth" or is not specified in the Authorization header for the - request, A2 is - - A2 = ":" digest-uri-value - - and if "qop=auth-int", then A2 is - - A2 = ":" digest-uri-value ":" H(entity-body) - - where "digest-uri-value" is the value of the "uri" directive on the - Authorization header in the request. The "cnonce-value" and "nc- - value" MUST be the ones for the client request to which this message - is the response. The "response-auth", "cnonce", and "nonce-count" - directives MUST BE present if "qop=auth" or "qop=auth-int" is - specified. - */ - - string a1 = ""; - string a2 = ""; - // Create A1 - if (Algorithm == "" || Algorithm.ToLower() == "md5") - { - a1 = userName + ":" + Realm + ":" + password; - } - else if (Algorithm.ToLower() == "md5-sess") - { - a1 = Core.ComputeMd5(userName + ":" + Realm + ":" + password, false) + ":" + Nonce + ":" + - CNonce; - } - else - { - throw new ArgumentException("Invalid Algorithm value '" + Algorithm + "' !"); - } - // Create A2 - if (Qop == "" || Qop.ToLower() == "auth") - { - a2 = ":" + Uri; - } - else - { - throw new ArgumentException("Invalid qop value '" + Qop + "' !"); - } - - // Calculate response value. - // qop present - if (!string.IsNullOrEmpty(Qop)) - { - return - Core.ComputeMd5( - Core.ComputeMd5(a1, true) + ":" + Nonce + ":" + NonceCount.ToString("x8") + ":" + - CNonce + ":" + Qop + ":" + Core.ComputeMd5(a2, true), - true); - } - // qop not present - else - { - return - Core.ComputeMd5( - Core.ComputeMd5(a1, true) + ":" + Nonce + ":" + Core.ComputeMd5(a2, true), true); - } - } - - /// - /// Calculates response value. - /// - /// User name. - /// User password. - /// Returns calculated rsponse value. - public string CalculateResponse(string userName, string password) - { - /* - MD5 - A1 = username-value ":" realm-value ":" passwd - - MD5-sess - A1 = md5(username-value ":" realm-value ":" passwd) ":" nonce-value ":" cnonce-value - - qop not peresent or auth - A2 = Method ":" digest-uri-value - - qop auth-int - A2 = Method ":" digest-uri-value ":" md5h(entity-body) - - qop present - response = md5h(md5h(A1) ":" nonce-value ":" nc-value ":" cnonce-value ":" qop-value ":" md5h(A2)) - - qop not present - response = md5h(md5h(A1) ":" nonce-value ":" md5h(A2)) - - */ - - string a1 = ""; - string a2 = ""; - // Create A1 - if (Algorithm == "" || Algorithm.ToLower() == "md5") - { - a1 = userName + ":" + Realm + ":" + password; - } - else if (Algorithm.ToLower() == "md5-sess") - { - a1 = Core.ComputeMd5(userName + ":" + Realm + ":" + password, false) + ":" + Nonce + ":" + - CNonce; - } - else - { - throw new ArgumentException("Invalid Algorithm value '" + Algorithm + "' !"); - } - // Create A2 - if (Qop == "" || Qop.ToLower() == "auth") - { - a2 = m_Method.ToUpper() + ":" + Uri; - } - else - { - throw new ArgumentException("Invalid qop value '" + Qop + "' !"); - } - - // Calculate response value. - // qop present - if (!string.IsNullOrEmpty(Qop)) - { - return - Core.ComputeMd5( - Core.ComputeMd5(a1, true) + ":" + Nonce + ":" + NonceCount.ToString("x8") + ":" + - CNonce + ":" + Qop + ":" + Core.ComputeMd5(a2, true), - true); - } - // qop not present - else - { - return - Core.ComputeMd5( - Core.ComputeMd5(a1, true) + ":" + Nonce + ":" + Core.ComputeMd5(a2, true), true); - } - } - - /// - /// Converts this to valid digest string. - /// - /// Returns digest string. - public override string ToString() - { - StringBuilder retVal = new StringBuilder(); - retVal.Append("realm=\"" + m_Realm + "\","); - retVal.Append("username=\"" + m_UserName + "\","); - if (!string.IsNullOrEmpty(m_Qop)) - { - retVal.Append("qop=\"" + m_Qop + "\","); - } - retVal.Append("nonce=\"" + m_Nonce + "\","); - retVal.Append("nc=\"" + m_NonceCount + "\","); - retVal.Append("cnonce=\"" + m_Cnonce + "\","); - retVal.Append("response=\"" + m_Response + "\","); - retVal.Append("opaque=\"" + m_Opaque + "\","); - retVal.Append("uri=\"" + m_Uri + "\""); - - return retVal.ToString(); - } - - /// - /// Creates 'Challange' data using this class info. - /// - /// Returns Challange data. - public string ToChallange() - { - return ToChallange(true); - } - - /// - /// Creates 'Challange' data using this class info. - /// - /// Specifies if 'digest ' authe method string constant is added. - /// Returns Challange data. - public string ToChallange(bool addAuthMethod) - { - // digest realm="",qop="",nonce="",opaque="" - - StringBuilder retVal = new StringBuilder(); - if (addAuthMethod) - { - retVal.Append("digest "); - } - retVal.Append("realm=" + TextUtils.QuoteString(m_Realm) + ","); - if (!string.IsNullOrEmpty(m_Qop)) - { - retVal.Append("qop=" + TextUtils.QuoteString(m_Qop) + ","); - } - retVal.Append("nonce=" + TextUtils.QuoteString(m_Nonce) + ","); - retVal.Append("opaque=" + TextUtils.QuoteString(m_Opaque)); - - return retVal.ToString(); - } - - /// - /// Creates 'Authorization' data using this class info. - /// - /// Return Authorization data. - public string ToAuthorization() - { - return ToAuthorization(true); - } - - /// - /// Creates 'Authorization' data using this class info. - /// - /// Specifies if 'digest ' authe method string constant is added. - /// Return Authorization data. - public string ToAuthorization(bool addAuthMethod) - { - /* RFC 2831 2.1.2. - digest-response = 1#( username | realm | nonce | cnonce | nonce-count | qop | digest-uri | response | - maxbuf | charset | cipher | authzid | auth-param ) - */ - - string response = ""; - if (string.IsNullOrEmpty(m_Password)) - { - response = m_Response; - } - else - { - response = CalculateResponse(m_UserName, m_Password); - } - - StringBuilder authData = new StringBuilder(); - if (addAuthMethod) - { - authData.Append("digest "); - } - authData.Append("realm=\"" + m_Realm + "\","); - authData.Append("username=\"" + m_UserName + "\","); - authData.Append("nonce=\"" + m_Nonce + "\","); - if (!string.IsNullOrEmpty(m_Uri)) - { - authData.Append("uri=\"" + m_Uri + "\","); - } - if (!string.IsNullOrEmpty(m_Qop)) - { - authData.Append("qop=\"" + m_Qop + "\","); - } - // nc value must be specified only if qop is present. - if (!string.IsNullOrEmpty(m_Qop)) - { - authData.Append("nc=" + m_NonceCount.ToString("x8") + ","); - } - if (!string.IsNullOrEmpty(m_Cnonce)) - { - authData.Append("cnonce=\"" + m_Cnonce + "\","); - } - authData.Append("response=" + response + ","); - if (!string.IsNullOrEmpty(m_Opaque)) - { - authData.Append("opaque=\"" + m_Opaque + "\","); - } - if (!string.IsNullOrEmpty(m_Charset)) - { - authData.Append("charset=" + m_Charset + ","); - } - - string retVal = authData.ToString().Trim(); - if (retVal.EndsWith(",")) - { - retVal = retVal.Substring(0, retVal.Length - 1); - } - - return retVal; - } - - #endregion - - #region Utility methods - - /// - /// Parses authetication info from client digest response. - /// - /// Client returned digest response. - private void Parse(string digestResponse) - { - string[] parameters = TextUtils.SplitQuotedString(digestResponse, ','); - foreach (string parameter in parameters) - { - string[] name_value = parameter.Split(new[] {'='}, 2); - string name = name_value[0].Trim(); - - if (name_value.Length == 2) - { - if (name.ToLower() == "realm") - { - m_Realm = TextUtils.UnQuoteString(name_value[1]); - } - else if (name.ToLower() == "nonce") - { - m_Nonce = TextUtils.UnQuoteString(name_value[1]); - } - // RFC bug ?: RFC 2831. digest-uri = "digest-uri" "=" <"> digest-uri-value <"> - // RFC 2617 digest-uri = "uri" "=" digest-uri-value - else if (name.ToLower() == "uri" || name.ToLower() == "digest-uri") - { - m_Uri = TextUtils.UnQuoteString(name_value[1]); - } - else if (name.ToLower() == "qop") - { - m_Qop = TextUtils.UnQuoteString(name_value[1]); - } - else if (name.ToLower() == "nc") - { - m_NonceCount = Convert.ToInt32(TextUtils.UnQuoteString(name_value[1])); - } - else if (name.ToLower() == "cnonce") - { - m_Cnonce = TextUtils.UnQuoteString(name_value[1]); - } - else if (name.ToLower() == "response") - { - m_Response = TextUtils.UnQuoteString(name_value[1]); - } - else if (name.ToLower() == "opaque") - { - m_Opaque = TextUtils.UnQuoteString(name_value[1]); - } - else if (name.ToLower() == "username") - { - m_UserName = TextUtils.UnQuoteString(name_value[1]); - } - else if (name.ToLower() == "algorithm") - { - m_Algorithm = TextUtils.UnQuoteString(name_value[1]); - } - else if (name.ToLower() == "charset") - { - m_Charset = TextUtils.UnQuoteString(name_value[1]); - } - } - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/Auth_HttpDigest_NonceManager.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/Auth_HttpDigest_NonceManager.cs deleted file mode 100644 index 3303aa027..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AUTH/Auth_HttpDigest_NonceManager.cs +++ /dev/null @@ -1,236 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.AUTH -{ - #region usings - - using System; - using System.Collections.Generic; - using System.Timers; - - #endregion - - /// - /// HTTP digest authentication nonce manager. - /// - public class Auth_HttpDigest_NonceManager : IDisposable - { - #region Nested type: NonceEntry - - /// - /// This class represents nonce entry in active nonces collection. - /// - private class NonceEntry - { - #region Members - - private readonly DateTime m_CreateTime; - private readonly string m_Nonce = ""; - - #endregion - - #region Properties - - /// - /// Gets nonce value. - /// - public string Nonce - { - get { return m_Nonce; } - } - - /// - /// Gets time when this nonce entry was created. - /// - public DateTime CreateTime - { - get { return m_CreateTime; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// - public NonceEntry(string nonce) - { - m_Nonce = nonce; - m_CreateTime = DateTime.Now; - } - - #endregion - } - - #endregion - - #region Members - - private int m_ExpireTime = 30; - private List m_pNonces; - private Timer m_pTimer; - - #endregion - - #region Properties - - /// - /// Gets or sets nonce expire time in seconds. - /// - public int ExpireTime - { - get { return m_ExpireTime; } - - set - { - if (value < 5) - { - throw new ArgumentException("Property ExpireTime value must be >= 5 !"); - } - - m_ExpireTime = value; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public Auth_HttpDigest_NonceManager() - { - m_pNonces = new List(); - - m_pTimer = new Timer(15000); - m_pTimer.Elapsed += m_pTimer_Elapsed; - m_pTimer.Enabled = true; - } - - #endregion - - #region Methods - - /// - /// Cleans up nay resource being used. - /// - public void Dispose() - { - if (m_pNonces == null) - { - m_pNonces.Clear(); - m_pNonces = null; - } - - if (m_pTimer != null) - { - m_pTimer.Dispose(); - m_pTimer = null; - } - } - - /// - /// Creates new nonce and adds it to active nonces collection. - /// - /// Returns new created nonce. - public string CreateNonce() - { - string nonce = Guid.NewGuid().ToString().Replace("-", ""); - m_pNonces.Add(new NonceEntry(nonce)); - - return nonce; - } - - /// - /// Checks if specified nonce exists in active nonces collection. - /// - /// Nonce to check. - /// Returns true if nonce exists in active nonces collection, otherwise returns false. - public bool NonceExists(string nonce) - { - lock (m_pNonces) - { - foreach (NonceEntry e in m_pNonces) - { - if (e.Nonce == nonce) - { - return true; - } - } - } - - return false; - } - - /// - /// Removes specified nonce from active nonces collection. - /// - /// Nonce to remove. - public void RemoveNonce(string nonce) - { - lock (m_pNonces) - { - for (int i = 0; i < m_pNonces.Count; i++) - { - if (m_pNonces[i].Nonce == nonce) - { - m_pNonces.RemoveAt(i); - i--; - } - } - } - } - - #endregion - - #region Event handlers - - private void m_pTimer_Elapsed(object sender, ElapsedEventArgs e) - { - RemoveExpiredNonces(); - } - - #endregion - - #region Utility methods - - /// - /// Removes not used nonces what has expired. - /// - private void RemoveExpiredNonces() - { - lock (m_pNonces) - { - for (int i = 0; i < m_pNonces.Count; i++) - { - // Nonce expired, remove it. - if (m_pNonces[i].CreateTime.AddSeconds(m_ExpireTime) > DateTime.Now) - { - m_pNonces.RemoveAt(i); - i--; - } - } - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AsyncOP.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AsyncOP.cs deleted file mode 100644 index 6c7a04a11..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/AsyncOP.cs +++ /dev/null @@ -1,44 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - /// - /// This is base class for asynchronous operation. - /// - public abstract class AsyncOP - { - #region Properties - - /// - /// Gets if asynchronous operation has completed. - /// - public abstract bool IsCompleted { get; } - - /// - /// Gets if operation completed synchronously. - /// - public abstract bool IsCompletedSynchronously { get; } - - /// - /// Gets if this object is disposed. - /// - public abstract bool IsDisposed { get; } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/BalanceMode.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/BalanceMode.cs deleted file mode 100644 index 547e56881..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/BalanceMode.cs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - /// - /// This enum specified balance mode. - /// - public enum BalanceMode - { - /// - /// Operation is load balanched by all workers. - /// - LoadBalance, - - /// - /// Operation will be handed over to next worker, if last one fails. - /// - FailOver, - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/BindInfoProtocol.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/BindInfoProtocol.cs deleted file mode 100644 index f39601349..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/BindInfoProtocol.cs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - /// - /// Specifies BindInfo protocol. - /// - public enum BindInfoProtocol - { - /// - /// TCP protocol. - /// - TCP, - - /// - /// UDP protocol. - /// - UDP - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/CircleCollection.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/CircleCollection.cs deleted file mode 100644 index 1c5f9af58..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/CircleCollection.cs +++ /dev/null @@ -1,214 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - #region usings - - using System; - using System.Collections.Generic; - - #endregion - - /// - /// Circle collection. Elements will be circled clockwise. - /// - public class CircleCollection - { - #region Members - - private readonly List m_pItems; - private int m_Index; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public CircleCollection() - { - m_pItems = new List(); - } - - #endregion - - #region Properties - - /// - /// Gets number of items in the collection. - /// - public int Count - { - get { return m_pItems.Count; } - } - - /// - /// Gets item at the specified index. - /// - /// Item zero based index. - /// Returns item at the specified index. - public T this[int index] - { - get { return m_pItems[index]; } - } - - #endregion - - #region Methods - - /// - /// Adds specified items to the collection. - /// - /// Items to add. - /// Is raised when items is null. - public void Add(T[] items) - { - if (items == null) - { - throw new ArgumentNullException("items"); - } - - foreach (T item in items) - { - Add(item); - } - } - - /// - /// Adds specified item to the collection. - /// - /// Item to add. - /// Is raised when item is null. - public void Add(T item) - { - if (item == null) - { - throw new ArgumentNullException("item"); - } - - m_pItems.Add(item); - - // Reset loop index. - m_Index = 0; - } - - /// - /// Removes specified item from the collection. - /// - /// Item to remove. - /// Is raised when item is null. - public void Remove(T item) - { - if (item == null) - { - throw new ArgumentNullException("item"); - } - - m_pItems.Remove(item); - - // Reset loop index. - m_Index = 0; - } - - /// - /// Clears all items from collection. - /// - public void Clear() - { - m_pItems.Clear(); - - // Reset loop index. - m_Index = 0; - } - - /// - /// Gets if the collection contain the specified item. - /// - /// Item to check. - /// Returns true if the collection contain the specified item, otherwise false. - public bool Contains(T item) - { - return m_pItems.Contains(item); - } - - /// - /// Gets next item from the collection. This method is thread-safe. - /// - /// Is raised when thre is no items in the collection. - public T Next() - { - if (m_pItems.Count == 0) - { - throw new InvalidOperationException("There is no items in the collection."); - } - - lock (m_pItems) - { - T item = m_pItems[m_Index]; - - m_Index++; - if (m_Index >= m_pItems.Count) - { - m_Index = 0; - } - - return item; - } - } - - /// - /// Copies all elements to new array, all elements will be in order they added. This method is thread-safe. - /// - /// Returns elements in a new array. - public T[] ToArray() - { - lock (m_pItems) - { - return m_pItems.ToArray(); - } - } - - /// - /// Copies all elements to new array, all elements will be in current circle order. This method is thread-safe. - /// - /// Returns elements in a new array. - public T[] ToCurrentOrderArray() - { - lock (m_pItems) - { - int index = m_Index; - T[] retVal = new T[m_pItems.Count]; - for (int i = 0; i < m_pItems.Count; i++) - { - retVal[i] = m_pItems[index]; - - index++; - if (index >= m_pItems.Count) - { - index = 0; - } - } - - return retVal; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_Client.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_Client.cs deleted file mode 100644 index fd15e78ae..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_Client.cs +++ /dev/null @@ -1,808 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Dns.Client -{ - #region usings - - using System; - using System.Collections.Generic; - using System.Net; - using System.Net.NetworkInformation; - using System.Net.Sockets; - using System.Text; - - #endregion - - /// - /// Dns client. - /// - /// - /// - /// // Set dns servers - /// Dns_Client.DnsServers = new string[]{"194.126.115.18"}; - /// - /// Dns_Client dns = Dns_Client(); - /// - /// // Get MX records. - /// DnsServerResponse resp = dns.Query("lumisoft.ee",QTYPE.MX); - /// if(resp.ConnectionOk && resp.ResponseCode == RCODE.NO_ERROR){ - /// MX_Record[] mxRecords = resp.GetMXRecords(); - /// - /// // Do your stuff - /// } - /// else{ - /// // Handle error there, for more exact error info see RCODE - /// } - /// - /// - /// - public class Dns_Client - { - #region Members - - private static IPAddress[] m_DnsServers; - private static int m_ID = 100; - private static bool m_UseDnsCache = true; - - #endregion - - #region Constructor - - /// - /// Static constructor. - /// - static Dns_Client() - { - // Try to get system dns servers - try - { - List dnsServers = new List(); - foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) - { - if (nic.OperationalStatus == OperationalStatus.Up) - { - foreach (IPAddress ip in nic.GetIPProperties().DnsAddresses) - { - if (ip.AddressFamily == AddressFamily.InterNetwork) - { - if (!dnsServers.Contains(ip)) - { - dnsServers.Add(ip); - } - } - } - } - } - - m_DnsServers = dnsServers.ToArray(); - } - catch {} - } - - #endregion - - #region Properties - - /// - /// Gets or sets dns servers. - /// - /// Is raised when null value is passed. - public static string[] DnsServers - { - get - { - string[] retVal = new string[m_DnsServers.Length]; - for (int i = 0; i < m_DnsServers.Length; i++) - { - retVal[i] = m_DnsServers[i].ToString(); - } - - return retVal; - } - - set - { - if (value == null) - { - throw new ArgumentNullException(); - } - - IPAddress[] retVal = new IPAddress[value.Length]; - for (int i = 0; i < value.Length; i++) - { - retVal[i] = IPAddress.Parse(value[i]); - } - - m_DnsServers = retVal; - } - } - - /// - /// Gets or sets if to use dns caching. - /// - public static bool UseDnsCache - { - get { return m_UseDnsCache; } - - set { m_UseDnsCache = value; } - } - - /// - /// Get next query ID. - /// - internal static int ID - { - get - { - if (m_ID >= 65535) - { - m_ID = 100; - } - return m_ID++; - } - } - - #endregion - - #region Methods - - /// - /// Resolves host names to IP addresses. - /// - /// Host names to resolve. - /// Returns specified hosts IP addresses. - /// Is raised when hosts is null. - public static IPAddress[] Resolve(string[] hosts) - { - if (hosts == null) - { - throw new ArgumentNullException("hosts"); - } - - List retVal = new List(); - foreach (string host in hosts) - { - IPAddress[] addresses = Resolve(host); - foreach (IPAddress ip in addresses) - { - if (!retVal.Contains(ip)) - { - retVal.Add(ip); - } - } - } - - return retVal.ToArray(); - } - - /// - /// Resolves host name to IP addresses. - /// - /// Host name or IP address. - /// Return specified host IP addresses. - /// Is raised when host is null. - public static IPAddress[] Resolve(string host) - { - if (host == null) - { - throw new ArgumentNullException("host"); - } - - // If hostName_IP is IP - try - { - return new[] {IPAddress.Parse(host)}; - } - catch {} - - // This is probably NetBios name - if (host.IndexOf(".") == -1) - { - return Dns.GetHostEntry(host).AddressList; - } - else - { - // hostName_IP must be host name, try to resolve it's IP - Dns_Client dns = new Dns_Client(); - DnsServerResponse resp = dns.Query(host, QTYPE.A); - if (resp.ResponseCode == RCODE.NO_ERROR) - { - DNS_rr_A[] records = resp.GetARecords(); - IPAddress[] retVal = new IPAddress[records.Length]; - for (int i = 0; i < records.Length; i++) - { - retVal[i] = records[i].IP; - } - - return retVal; - } - else - { - throw new Exception(resp.ResponseCode.ToString()); - } - } - } - - /// - /// Queries server with specified query. - /// - /// Query text. It depends on queryType. - /// Query type. - /// - public DnsServerResponse Query(string queryText, QTYPE queryType) - { - if (queryType == QTYPE.PTR) - { - string ip = queryText; - - // See if IP is ok. - IPAddress ipA = IPAddress.Parse(ip); - queryText = ""; - - // IPv6 - if (ipA.AddressFamily == AddressFamily.InterNetworkV6) - { - // 4321:0:1:2:3:4:567:89ab - // would be - // b.a.9.8.7.6.5.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.0.0.0.0.1.2.3.4.IP6.ARPA - - char[] ipChars = ip.Replace(":", "").ToCharArray(); - for (int i = ipChars.Length - 1; i > -1; i--) - { - queryText += ipChars[i] + "."; - } - queryText += "IP6.ARPA"; - } - // IPv4 - else - { - // 213.35.221.186 - // would be - // 186.221.35.213.in-addr.arpa - - string[] ipParts = ip.Split('.'); - //--- Reverse IP ---------- - for (int i = 3; i > -1; i--) - { - queryText += ipParts[i] + "."; - } - queryText += "in-addr.arpa"; - } - } - - return QueryServer(2000, queryText, queryType, 1); - } - - /// - /// Gets specified host IP addresses(A and AAAA). - /// - /// Host name. - /// Returns specified host IP addresses. - /// Is raised when host is null reference. - public IPAddress[] GetHostAddresses(string host) - { - if (host == null) - { - throw new ArgumentNullException("host"); - } - - List retVal = new List(); - - // This is probably NetBios name - if (host.IndexOf(".") == -1) - { - return Dns.GetHostEntry(host).AddressList; - } - else - { - DnsServerResponse response = Query(host, QTYPE.A); - if (response.ResponseCode != RCODE.NO_ERROR) - { - throw new DNS_ClientException(response.ResponseCode); - } - - foreach (DNS_rr_A record in response.GetARecords()) - { - retVal.Add(record.IP); - } - - response = Query(host, QTYPE.AAAA); - if (response.ResponseCode != RCODE.NO_ERROR) - { - throw new DNS_ClientException(response.ResponseCode); - } - - foreach (DNS_rr_A record in response.GetARecords()) - { - retVal.Add(record.IP); - } - } - - return retVal.ToArray(); - } - - #endregion - - #region Internal methods - - internal static bool GetQName(byte[] reply, ref int offset, ref string name) - { - try - { - // Do while not terminator - while (reply[offset] != 0) - { - // Check if it's pointer(In pointer first two bits always 1) - bool isPointer = ((reply[offset] & 0xC0) == 0xC0); - - // If pointer - if (isPointer) - { - // Pointer location number is 2 bytes long - // 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 # byte 2 # 0 | 1 | 2 | | 3 | 4 | 5 | 6 | 7 - // empty | < ---- pointer location number ---------------------------------> - int pStart = ((reply[offset] & 0x3F) << 8) | (reply[++offset]); - offset++; - return GetQName(reply, ref pStart, ref name); - } - else - { - // label length (length = 8Bit and first 2 bits always 0) - // 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 - // empty | lablel length in bytes - int labelLength = (reply[offset] & 0x3F); - offset++; - - // Copy label into name - name += Encoding.ASCII.GetString(reply, offset, labelLength); - offset += labelLength; - } - - // If the next char isn't terminator, - // label continues - add dot between two labels - if (reply[offset] != 0) - { - name += "."; - } - } - - // Move offset by terminator length - offset++; - - return true; - } - catch - { - return false; - } - } - - /// - /// Reads character-string from spefcified data and offset. - /// - /// Data from where to read. - /// Offset from where to start reading. - /// Returns readed string. - internal static string ReadCharacterString(byte[] data, ref int offset) - { - /* RFC 1035 3.3. - is a single length octet followed by that number of characters. - is treated as binary information, and can be up to 256 characters - in length (including the length octet). - */ - - int dataLength = data[offset++]; - string retVal = Encoding.Default.GetString(data, offset, dataLength); - offset += dataLength; - - return retVal; - } - - #endregion - - #region Utility methods - - /// - /// Sends query to server. - /// - /// Query timeout in milli seconds. - /// Query text. - /// Query type. - /// Query class. - /// - private DnsServerResponse QueryServer(int timeout, string qname, QTYPE qtype, int qclass) - { - if (m_DnsServers == null || m_DnsServers.Length == 0) - { - throw new Exception("Dns server isn't specified !"); - } - - // See if query is in cache - if (m_UseDnsCache) - { - DnsServerResponse resopnse = DnsCache.GetFromCache(qname, (int) qtype); - if (resopnse != null) - { - return resopnse; - } - } - - int queryID = ID; - byte[] query = CreateQuery(queryID, qname, qtype, qclass); - - // Create sending UDP socket. - Socket udpClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - udpClient.SendTimeout = 500; - - // Send parallel query to all dns servers and get first answer. - DateTime startTime = DateTime.Now; - List responses = new List(); - while (startTime.AddMilliseconds(timeout) > DateTime.Now) - { - foreach (IPAddress dnsServer in m_DnsServers) - { - try - { - udpClient.SendTo(query, new IPEndPoint(dnsServer, 53)); - } - catch {} - } - - // Wait 10 ms response to arrive, if no response, retransmit query. - if (udpClient.Poll(10, SelectMode.SelectRead)) - { - try - { - byte[] retVal = new byte[1024]; - int countRecieved = udpClient.Receive(retVal); - - // If reply is ok, return it - DnsServerResponse serverResponse = ParseQuery(retVal, queryID); - - // Cache query - if (m_UseDnsCache && serverResponse.ResponseCode == RCODE.NO_ERROR) - { - DnsCache.AddToCache(qname, (int) qtype, serverResponse); - } - responses.Add(serverResponse); - } - catch {} - } - } - - udpClient.Close(); - - // If we reach so far, we probably won't get connection to dsn server - return responses.Count>0?responses[0]:new DnsServerResponse(false, - RCODE.SERVER_FAILURE, - new List(), - new List(), - new List()); - } - - /// - /// Creates new query. - /// - /// Query ID. - /// Query text. - /// Query type. - /// Query class. - /// - private byte[] CreateQuery(int ID, string qname, QTYPE qtype, int qclass) - { - byte[] query = new byte[512]; - - //---- Create header --------------------------------------------// - // Header is first 12 bytes of query - - /* 4.1.1. Header section format - 1 1 1 1 1 1 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | ID | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - |QR| Opcode |AA|TC|RD|RA| Z | RCODE | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | QDCOUNT | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | ANCOUNT | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | NSCOUNT | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | ARCOUNT | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - - QR A one bit field that specifies whether this message is a - query (0), or a response (1). - - OPCODE A four bit field that specifies kind of query in this - message. This value is set by the originator of a query - and copied into the response. The values are: - - 0 a standard query (QUERY) - - 1 an inverse query (IQUERY) - - 2 a server status request (STATUS) - - */ - - //--------- Header part -----------------------------------// - query[0] = (byte) (ID >> 8); - query[1] = (byte) (ID & 0xFF); - query[2] = 1; - query[3] = 0; - query[4] = 0; - query[5] = 1; - query[6] = 0; - query[7] = 0; - query[8] = 0; - query[9] = 0; - query[10] = 0; - query[11] = 0; - //---------------------------------------------------------// - - //---- End of header --------------------------------------------// - - //----Create query ------------------------------------// - - /* Rfc 1035 4.1.2. Question section format - 1 1 1 1 1 1 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | | - / QNAME / - / / - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | QTYPE | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | QCLASS | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - - QNAME - a domain name represented as a sequence of labels, where - each label consists of a length octet followed by that - number of octets. The domain name terminates with the - zero length octet for the null label of the root. Note - that this field may be an odd number of octets; no - padding is used. - */ - string[] labels = qname.Split(new[] {'.'}); - int position = 12; - - // Copy all domain parts(labels) to query - // eg. lumisoft.ee = 2 labels, lumisoft and ee. - // format = label.length + label(bytes) - foreach (string label in labels) - { - // add label lenght to query - query[position++] = (byte) (label.Length); - - // convert label string to byte array - byte[] b = Encoding.ASCII.GetBytes(label); - b.CopyTo(query, position); - - // Move position by label length - position += b.Length; - } - - // Terminate domain (see note above) - query[position++] = 0; - - // Set QTYPE - query[position++] = 0; - query[position++] = (byte) qtype; - - // Set QCLASS - query[position++] = 0; - query[position++] = (byte) qclass; - //-------------------------------------------------------// - string queryStr = Encoding.ASCII.GetString(query); - return query; - } - - /// - /// Parses query. - /// - /// Dns server reply. - /// Query id of sent query. - /// - private DnsServerResponse ParseQuery(byte[] reply, int queryID) - { - //--- Parse headers ------------------------------------// - - /* RFC 1035 4.1.1. Header section format - - 1 1 1 1 1 1 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | ID | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - |QR| Opcode |AA|TC|RD|RA| Z | RCODE | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | QDCOUNT | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | ANCOUNT | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | NSCOUNT | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | ARCOUNT | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - - QDCOUNT - an unsigned 16 bit integer specifying the number of - entries in the question section. - - ANCOUNT - an unsigned 16 bit integer specifying the number of - resource records in the answer section. - - NSCOUNT - an unsigned 16 bit integer specifying the number of name - server resource records in the authority records section. - - ARCOUNT - an unsigned 16 bit integer specifying the number of - resource records in the additional records section. - - */ - - // Get reply code - int id = (reply[0] << 8 | reply[1]); - OPCODE opcode = (OPCODE) ((reply[2] >> 3) & 15); - RCODE replyCode = (RCODE) (reply[3] & 15); - int queryCount = (reply[4] << 8 | reply[5]); - int answerCount = (reply[6] << 8 | reply[7]); - int authoritiveAnswerCount = (reply[8] << 8 | reply[9]); - int additionalAnswerCount = (reply[10] << 8 | reply[11]); - //---- End of headers ---------------------------------// - - // Check that it's query what we want - if (queryID != id) - { - throw new Exception("This isn't query with ID what we expected"); - } - - int pos = 12; - - //----- Parse question part ------------// - for (int q = 0; q < queryCount; q++) - { - string dummy = ""; - GetQName(reply, ref pos, ref dummy); - //qtype + qclass - pos += 4; - } - //--------------------------------------// - - // 1) parse answers - // 2) parse authoritive answers - // 3) parse additional answers - List answers = ParseAnswers(reply, answerCount, ref pos); - List authoritiveAnswers = ParseAnswers(reply, authoritiveAnswerCount, ref pos); - List additionalAnswers = ParseAnswers(reply, additionalAnswerCount, ref pos); - - return new DnsServerResponse(true, replyCode, answers, authoritiveAnswers, additionalAnswers); - } - - /// - /// Parses specified count of answers from query. - /// - /// Server returned query. - /// Number of answers to parse. - /// Position from where to start parsing answers. - /// - private List ParseAnswers(byte[] reply, int answerCount, ref int offset) - { - /* RFC 1035 4.1.3. Resource record format - - 1 1 1 1 1 1 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | | - / / - / NAME / - | | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | TYPE | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | CLASS | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | TTL | - | | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | RDLENGTH | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--| - / RDATA / - / / - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - */ - - List answers = new List(); - //---- Start parsing answers ------------------------------------------------------------------// - for (int i = 0; i < answerCount; i++) - { - string name = ""; - if (!GetQName(reply, ref offset, ref name)) - { - throw new Exception("Error parsing anser"); - } - - int type = reply[offset++] << 8 | reply[offset++]; - int rdClass = reply[offset++] << 8 | reply[offset++]; - int ttl = reply[offset++] << 24 | reply[offset++] << 16 | reply[offset++] << 8 | - reply[offset++]; - int rdLength = reply[offset++] << 8 | reply[offset++]; - - if ((QTYPE) type == QTYPE.A) - { - answers.Add(DNS_rr_A.Parse(reply, ref offset, rdLength, ttl)); - } - else if ((QTYPE) type == QTYPE.NS) - { - answers.Add(DNS_rr_NS.Parse(reply, ref offset, rdLength, ttl)); - } - else if ((QTYPE) type == QTYPE.CNAME) - { - answers.Add(DNS_rr_CNAME.Parse(reply, ref offset, rdLength, ttl)); - } - else if ((QTYPE) type == QTYPE.SOA) - { - answers.Add(DNS_rr_SOA.Parse(reply, ref offset, rdLength, ttl)); - } - else if ((QTYPE) type == QTYPE.PTR) - { - answers.Add(DNS_rr_PTR.Parse(reply, ref offset, rdLength, ttl)); - } - else if ((QTYPE) type == QTYPE.HINFO) - { - answers.Add(DNS_rr_HINFO.Parse(reply, ref offset, rdLength, ttl)); - } - else if ((QTYPE) type == QTYPE.MX) - { - answers.Add(DNS_rr_MX.Parse(reply, ref offset, rdLength, ttl)); - } - else if ((QTYPE) type == QTYPE.TXT) - { - answers.Add(DNS_rr_TXT.Parse(reply, ref offset, rdLength, ttl)); - } - else if ((QTYPE) type == QTYPE.AAAA) - { - answers.Add(DNS_rr_AAAA.Parse(reply, ref offset, rdLength, ttl)); - } - else if ((QTYPE) type == QTYPE.SRV) - { - answers.Add(DNS_rr_SRV.Parse(reply, ref offset, rdLength, ttl)); - } - else if ((QTYPE) type == QTYPE.NAPTR) - { - answers.Add(DNS_rr_NAPTR.Parse(reply, ref offset, rdLength, ttl)); - } - else - { - // Unknown record, skip it. - offset += rdLength; - } - } - - return answers; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_ClientException.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_ClientException.cs deleted file mode 100644 index 5a9a75778..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_ClientException.cs +++ /dev/null @@ -1,62 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Dns.Client -{ - #region usings - - using System; - - #endregion - - /// - /// DNS client exception. - /// - public class DNS_ClientException : Exception - { - #region Members - - private readonly RCODE m_RCode; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// DNS server returned error code. - public DNS_ClientException(RCODE rcode) - { - m_RCode = rcode; - } - - #endregion - - #region Properties - - /// - /// Gets DNS server returned error code. - /// - public RCODE ErrorCode - { - get { return m_RCode; } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_A.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_A.cs deleted file mode 100644 index 3a0478645..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_A.cs +++ /dev/null @@ -1,87 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Dns.Client -{ - #region usings - - using System; - using System.Net; - - #endregion - - /// - /// A record class. - /// - [Serializable] - public class DNS_rr_A : DNS_rr_base - { - #region Members - - private readonly IPAddress m_IP; - - #endregion - - #region Properties - - /// - /// Gets host IP address. - /// - public IPAddress IP - { - get { return m_IP; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// IP address. - /// TTL value. - public DNS_rr_A(IPAddress ip, int ttl) : base(QTYPE.A, ttl) - { - m_IP = ip; - } - - #endregion - - #region Methods - - /// - /// Parses resource record from reply data. - /// - /// DNS server reply data. - /// Current offset in reply data. - /// Resource record data length. - /// Time to live in seconds. - public static DNS_rr_A Parse(byte[] reply, ref int offset, int rdLength, int ttl) - { - // IPv4 = byte byte byte byte - - byte[] ip = new byte[rdLength]; - Array.Copy(reply, offset, ip, 0, rdLength); - offset += rdLength; - - return new DNS_rr_A(new IPAddress(ip), ttl); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_AAAA.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_AAAA.cs deleted file mode 100644 index 9e32c3fd6..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_AAAA.cs +++ /dev/null @@ -1,85 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Dns.Client -{ - #region usings - - using System; - using System.Net; - - #endregion - - /// - /// DNS AAAA resource record. - /// - public class DNS_rr_AAAA : DNS_rr_base - { - #region Members - - private readonly IPAddress m_IP; - - #endregion - - #region Properties - - /// - /// Gets host IP address. - /// - public IPAddress IP - { - get { return m_IP; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// IP address. - /// Time to live in seconds. - public DNS_rr_AAAA(IPAddress ip, int ttl) : base(QTYPE.AAAA, ttl) - { - m_IP = ip; - } - - #endregion - - #region Methods - - /// - /// Parses resource record from reply data. - /// - /// DNS server reply data. - /// Current offset in reply data. - /// Resource record data length. - /// Time to live in seconds. - public static DNS_rr_AAAA Parse(byte[] reply, ref int offset, int rdLength, int ttl) - { - // IPv6 = 16xbyte - - byte[] ip = new byte[rdLength]; - Array.Copy(reply, offset, ip, 0, rdLength); - - return new DNS_rr_AAAA(new IPAddress(ip), ttl); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_CNAME.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_CNAME.cs deleted file mode 100644 index b25fbbc2c..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_CNAME.cs +++ /dev/null @@ -1,88 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Dns.Client -{ - #region usings - - using System; - - #endregion - - /// - /// CNAME record class. - /// - [Serializable] - public class DNS_rr_CNAME : DNS_rr_base - { - #region Members - - private readonly string m_Alias = ""; - - #endregion - - #region Properties - - /// - /// Gets alias. - /// - public string Alias - { - get { return m_Alias; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Alias. - /// TTL value. - public DNS_rr_CNAME(string alias, int ttl) : base(QTYPE.CNAME, ttl) - { - m_Alias = alias; - } - - #endregion - - #region Methods - - /// - /// Parses resource record from reply data. - /// - /// DNS server reply data. - /// Current offset in reply data. - /// Resource record data length. - /// Time to live in seconds. - public static DNS_rr_CNAME Parse(byte[] reply, ref int offset, int rdLength, int ttl) - { - string name = ""; - if (Dns_Client.GetQName(reply, ref offset, ref name)) - { - return new DNS_rr_CNAME(name, ttl); - } - else - { - throw new ArgumentException("Invalid CNAME resource record data !"); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_HINFO.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_HINFO.cs deleted file mode 100644 index 1a1b2aa79..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_HINFO.cs +++ /dev/null @@ -1,107 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Dns.Client -{ - /// - /// HINFO record. - /// - public class DNS_rr_HINFO : DNS_rr_base - { - #region Members - - private readonly string m_CPU = ""; - private readonly string m_OS = ""; - - #endregion - - #region Properties - - /// - /// Gets host's CPU. - /// - public string CPU - { - get { return m_CPU; } - } - - /// - /// Gets host's OS. - /// - public string OS - { - get { return m_OS; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Host CPU. - /// Host OS. - /// TTL value. - public DNS_rr_HINFO(string cpu, string os, int ttl) : base(QTYPE.HINFO, ttl) - { - m_CPU = cpu; - m_OS = os; - } - - #endregion - - #region Methods - - /// - /// Parses resource record from reply data. - /// - /// DNS server reply data. - /// Current offset in reply data. - /// Resource record data length. - /// Time to live in seconds. - public static DNS_rr_HINFO Parse(byte[] reply, ref int offset, int rdLength, int ttl) - { - /* RFC 1035 3.3.2. HINFO RDATA format - - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - / CPU / - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - / OS / - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - - CPU A which specifies the CPU type. - - OS A which specifies the operating - system type. - - Standard values for CPU and OS can be found in [RFC-1010]. - - */ - - // CPU - string cpu = Dns_Client.ReadCharacterString(reply, ref offset); - - // OS - string os = Dns_Client.ReadCharacterString(reply, ref offset); - - return new DNS_rr_HINFO(cpu, os, ttl); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_MX.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_MX.cs deleted file mode 100644 index 468fdf2fd..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_MX.cs +++ /dev/null @@ -1,154 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Dns.Client -{ - #region usings - - using System; - - #endregion - - /// - /// MX record class. - /// - [Serializable] - public class DNS_rr_MX : DNS_rr_base, IComparable - { - #region Members - - private readonly string m_Host = ""; - private readonly int m_Preference; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// MX record preference. - /// Mail host dns name. - /// TTL value. - public DNS_rr_MX(int preference, string host, int ttl) : base(QTYPE.MX, ttl) - { - m_Preference = preference; - m_Host = host; - } - - #endregion - - #region Properties - - /// - /// Gets mail host dns name. - /// - public string Host - { - get { return m_Host; } - } - - /// - /// Gets MX record preference. The lower number is the higher priority server. - /// - public int Preference - { - get { return m_Preference; } - } - - #endregion - - #region Methods - - /// - /// Parses resource record from reply data. - /// - /// DNS server reply data. - /// Current offset in reply data. - /// Resource record data length. - /// Time to live in seconds. - public static DNS_rr_MX Parse(byte[] reply, ref int offset, int rdLength, int ttl) - { - /* RFC 1035 3.3.9. MX RDATA format - - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | PREFERENCE | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - / EXCHANGE / - / / - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - - where: - - PREFERENCE - A 16 bit integer which specifies the preference given to - this RR among others at the same owner. Lower values - are preferred. - - EXCHANGE - A which specifies a host willing to act as - a mail exchange for the owner name. - */ - - int pref = reply[offset++] << 8 | reply[offset++]; - - string name = ""; - if (Dns_Client.GetQName(reply, ref offset, ref name)) - { - return new DNS_rr_MX(pref, name, ttl); - } - else - { - throw new ArgumentException("Invalid MX resource record data !"); - } - } - - /// - /// Compares the current instance with another object of the same type. - /// - /// An object to compare with this instance. - /// Returns 0 if two objects are equal, returns negative value if this object is less, - /// returns positive value if this object is grater. - public int CompareTo(object obj) - { - if (obj == null) - { - throw new ArgumentNullException("obj"); - } - if (!(obj is DNS_rr_MX)) - { - throw new ArgumentException("Argument obj is not MX_Record !"); - } - - DNS_rr_MX mx = (DNS_rr_MX) obj; - if (Preference > mx.Preference) - { - return 1; - } - else if (Preference < mx.Preference) - { - return -1; - } - else - { - return 0; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_NAPTR.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_NAPTR.cs deleted file mode 100644 index 9d13c334b..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_NAPTR.cs +++ /dev/null @@ -1,177 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Dns.Client -{ - #region usings - - using System; - - #endregion - - /// - /// NAPRT(Naming Authority Pointer) resource record. Defined in RFC 3403. - /// - [Serializable] - public class DNS_rr_NAPTR : DNS_rr_base - { - #region Members - - private readonly string m_Flags = ""; - private readonly int m_Order; - private readonly int m_Preference; - private readonly string m_Regexp = ""; - private readonly string m_Replacement = ""; - private readonly string m_Services = ""; - - #endregion - - #region Properties - - /// - /// Gets order in which the NAPTR records MUST be processed in order to accurately - /// represent the ordered list of Rules. - /// - public int Order - { - get { return m_Order; } - } - - /// - /// Gets the order in which NAPTR records with equal Order values SHOULD be processed, - /// low numbers being processed before high numbers. - /// - public int Preference - { - get { return m_Preference; } - } - - /// - /// Gets flags which control the rewriting and interpretation of the fields in the record. - /// - public string Flags - { - get { return m_Flags; } - } - - /// - /// Gets services related to this record. Known values can be get from: http://www.iana.org/assignments/enum-services. - /// - public string Services - { - get { return m_Services; } - } - - /// - /// Gets regular expression that is applied to the original string held by the client in order to - /// construct the next domain name to lookup. - /// - public string Regexp - { - get { return m_Regexp; } - } - - /// - /// Gets regular expressions replacement value. - /// - public string Replacement - { - get { return m_Replacement; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Oorder in which the NAPTR records MUST be processed. - /// Order in which NAPTR records with equal Order values SHOULD be processed. - /// Flags which control the rewriting and interpretation of the fields in the record. - /// Services related to this record. - /// Regular expression that is applied to the original string. - /// Regular expressions replacement value. - /// Time to live value in seconds. - public DNS_rr_NAPTR(int order, - int preference, - string flags, - string services, - string regexp, - string replacement, - int ttl) : base(QTYPE.NAPTR, ttl) - { - m_Order = order; - m_Preference = preference; - m_Flags = flags; - m_Services = services; - m_Regexp = regexp; - m_Replacement = replacement; - } - - #endregion - - #region Methods - - /// - /// Parses resource record from reply data. - /// - /// DNS server reply data. - /// Current offset in reply data. - /// Resource record data length. - /// Time to live in seconds. - public static DNS_rr_NAPTR Parse(byte[] reply, ref int offset, int rdLength, int ttl) - { - /* RFC 3403. - The packet format for the NAPTR record is as follows - 1 1 1 1 1 1 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | ORDER | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | PREFERENCE | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - / FLAGS / - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - / SERVICES / - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - / REGEXP / - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - / REPLACEMENT / - / / - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - */ - - int order = reply[offset++] << 8 | reply[offset++]; - - int preference = reply[offset++] << 8 | reply[offset++]; - - string flags = Dns_Client.ReadCharacterString(reply, ref offset); - - string services = Dns_Client.ReadCharacterString(reply, ref offset); - - string regexp = Dns_Client.ReadCharacterString(reply, ref offset); - - string replacement = ""; - Dns_Client.GetQName(reply, ref offset, ref replacement); - - return new DNS_rr_NAPTR(order, preference, flags, services, regexp, replacement, ttl); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_NS.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_NS.cs deleted file mode 100644 index 04d6c471f..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_NS.cs +++ /dev/null @@ -1,90 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Dns.Client -{ - #region usings - - using System; - - #endregion - - /// - /// NS record class. - /// - [Serializable] - public class DNS_rr_NS : DNS_rr_base - { - #region Members - - private readonly string m_NameServer = ""; - - #endregion - - #region Properties - - /// - /// Gets name server name. - /// - public string NameServer - { - get { return m_NameServer; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Name server name. - /// TTL value. - public DNS_rr_NS(string nameServer, int ttl) : base(QTYPE.NS, ttl) - { - m_NameServer = nameServer; - } - - #endregion - - #region Methods - - /// - /// Parses resource record from reply data. - /// - /// DNS server reply data. - /// Current offset in reply data. - /// Resource record data length. - /// Time to live in seconds. - public static DNS_rr_NS Parse(byte[] reply, ref int offset, int rdLength, int ttl) - { - // Name server name - - string name = ""; - if (Dns_Client.GetQName(reply, ref offset, ref name)) - { - return new DNS_rr_NS(name, ttl); - } - else - { - throw new ArgumentException("Invalid NS resource record data !"); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_PTR.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_PTR.cs deleted file mode 100644 index a860e2686..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_PTR.cs +++ /dev/null @@ -1,88 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Dns.Client -{ - #region usings - - using System; - - #endregion - - /// - /// PTR record class. - /// - [Serializable] - public class DNS_rr_PTR : DNS_rr_base - { - #region Members - - private readonly string m_DomainName = ""; - - #endregion - - #region Properties - - /// - /// Gets domain name. - /// - public string DomainName - { - get { return m_DomainName; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// DomainName. - /// TTL value. - public DNS_rr_PTR(string domainName, int ttl) : base(QTYPE.PTR, ttl) - { - m_DomainName = domainName; - } - - #endregion - - #region Methods - - /// - /// Parses resource record from reply data. - /// - /// DNS server reply data. - /// Current offset in reply data. - /// Resource record data length. - /// Time to live in seconds. - public static DNS_rr_PTR Parse(byte[] reply, ref int offset, int rdLength, int ttl) - { - string name = ""; - if (Dns_Client.GetQName(reply, ref offset, ref name)) - { - return new DNS_rr_PTR(name, ttl); - } - else - { - throw new ArgumentException("Invalid PTR resource record data !"); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_SOA.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_SOA.cs deleted file mode 100644 index 57cbe158d..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_SOA.cs +++ /dev/null @@ -1,244 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Dns.Client -{ - #region usings - - using System; - - #endregion - - /// - /// SOA record class. - /// - [Serializable] - public class DNS_rr_SOA : DNS_rr_base - { - #region Members - - private readonly string m_AdminEmail = ""; - private readonly long m_Expire; - private readonly long m_Minimum; - private readonly string m_NameServer = ""; - private readonly long m_Refresh; - private readonly long m_Retry; - private readonly long m_Serial; - - #endregion - - #region Properties - - /// - /// Gets name server. - /// - public string NameServer - { - get { return m_NameServer; } - } - - /// - /// Gets zone administrator email. - /// - public string AdminEmail - { - get { return m_AdminEmail; } - } - - /// - /// Gets version number of the original copy of the zone. - /// - public long Serial - { - get { return m_Serial; } - } - - /// - /// Gets time interval(in seconds) before the zone should be refreshed. - /// - public long Refresh - { - get { return m_Refresh; } - } - - /// - /// Gets time interval(in seconds) that should elapse before a failed refresh should be retried. - /// - public long Retry - { - get { return m_Retry; } - } - - /// - /// Gets time value(in seconds) that specifies the upper limit on the time interval that can elapse before the zone is no longer authoritative. - /// - public long Expire - { - get { return m_Expire; } - } - - /// - /// Gets minimum TTL(in seconds) field that should be exported with any RR from this zone. - /// - public long Minimum - { - get { return m_Minimum; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Name server. - /// Zone administrator email. - /// Version number of the original copy of the zone. - /// Time interval(in seconds) before the zone should be refreshed. - /// Time interval(in seconds) that should elapse before a failed refresh should be retried. - /// Time value(in seconds) that specifies the upper limit on the time interval that can elapse before the zone is no longer authoritative. - /// Minimum TTL(in seconds) field that should be exported with any RR from this zone. - /// TTL value. - public DNS_rr_SOA(string nameServer, - string adminEmail, - long serial, - long refresh, - long retry, - long expire, - long minimum, - int ttl) : base(QTYPE.SOA, ttl) - { - m_NameServer = nameServer; - m_AdminEmail = adminEmail; - m_Serial = serial; - m_Refresh = refresh; - m_Retry = retry; - m_Expire = expire; - m_Minimum = minimum; - } - - #endregion - - #region Methods - - /// - /// Parses resource record from reply data. - /// - /// DNS server reply data. - /// Current offset in reply data. - /// Resource record data length. - /// Time to live in seconds. - public static DNS_rr_SOA Parse(byte[] reply, ref int offset, int rdLength, int ttl) - { - /* RFC 1035 3.3.13. SOA RDATA format - - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - / MNAME / - / / - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - / RNAME / - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | SERIAL | - | | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | REFRESH | - | | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | RETRY | - | | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | EXPIRE | - | | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - | MINIMUM | - | | - +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ - - where: - - MNAME The of the name server that was the - original or primary source of data for this zone. - - RNAME A which specifies the mailbox of the - person responsible for this zone. - - SERIAL The unsigned 32 bit version number of the original copy - of the zone. Zone transfers preserve this value. This - value wraps and should be compared using sequence space - arithmetic. - - REFRESH A 32 bit time interval before the zone should be - refreshed. - - RETRY A 32 bit time interval that should elapse before a - failed refresh should be retried. - - EXPIRE A 32 bit time value that specifies the upper limit on - the time interval that can elapse before the zone is no - longer authoritative. - - MINIMUM The unsigned 32 bit minimum TTL field that should be - exported with any RR from this zone. - */ - - //---- Parse record -------------------------------------------------------------// - // MNAME - string nameserver = ""; - Dns_Client.GetQName(reply, ref offset, ref nameserver); - - // RNAME - string adminMailBox = ""; - Dns_Client.GetQName(reply, ref offset, ref adminMailBox); - char[] adminMailBoxAr = adminMailBox.ToCharArray(); - for (int i = 0; i < adminMailBoxAr.Length; i++) - { - if (adminMailBoxAr[i] == '.') - { - adminMailBoxAr[i] = '@'; - break; - } - } - adminMailBox = new string(adminMailBoxAr); - - // SERIAL - long serial = reply[offset++] << 24 | reply[offset++] << 16 | reply[offset++] << 8 | - reply[offset++]; - - // REFRESH - long refresh = reply[offset++] << 24 | reply[offset++] << 16 | reply[offset++] << 8 | - reply[offset++]; - - // RETRY - long retry = reply[offset++] << 24 | reply[offset++] << 16 | reply[offset++] << 8 | - reply[offset++]; - - // EXPIRE - long expire = reply[offset++] << 24 | reply[offset++] << 16 | reply[offset++] << 8 | - reply[offset++]; - - // MINIMUM - long minimum = reply[offset++] << 24 | reply[offset++] << 16 | reply[offset++] << 8 | - reply[offset++]; - //--------------------------------------------------------------------------------// - - return new DNS_rr_SOA(nameserver, adminMailBox, serial, refresh, retry, expire, minimum, ttl); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_SRV.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_SRV.cs deleted file mode 100644 index b5fe93451..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_SRV.cs +++ /dev/null @@ -1,129 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Dns.Client -{ - #region usings - - using System; - - #endregion - - /// - /// DNS SRV record. SRV record specifies the location of services. Defined in RFC 2782. - /// - [Serializable] - public class DNS_rr_SRV : DNS_rr_base - { - #region Members - - private readonly int m_Port; - private readonly int m_Priority = 1; - private readonly string m_Target = ""; - private readonly int m_Weight = 1; - - #endregion - - #region Properties - - /// - /// Gets service priority. Lowest value means greater priority. - /// - public int Priority - { - get { return m_Priority; } - } - - /// - /// Gets weight. The weight field specifies a relative weight for entries with the same priority. - /// Larger weights SHOULD be given a proportionately higher probability of being selected. - /// - public int Weight - { - get { return m_Weight; } - } - - /// - /// Port where service runs. - /// - public int Port - { - get { return m_Port; } - } - - /// - /// Service provider host name or IP address. - /// - public string Target - { - get { return m_Target; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Service priority. - /// Weight value. - /// Service port. - /// Service provider host name or IP address. - /// Time to live value in seconds. - public DNS_rr_SRV(int priority, int weight, int port, string target, int ttl) : base(QTYPE.SRV, ttl) - { - m_Priority = priority; - m_Weight = weight; - m_Port = port; - m_Target = target; - } - - #endregion - - #region Methods - - /// - /// Parses resource record from reply data. - /// - /// DNS server reply data. - /// Current offset in reply data. - /// Resource record data length. - /// Time to live in seconds. - public static DNS_rr_SRV Parse(byte[] reply, ref int offset, int rdLength, int ttl) - { - // Priority Weight Port Target - - // Priority - int priority = reply[offset++] << 8 | reply[offset++]; - - // Weight - int weight = reply[offset++] << 8 | reply[offset++]; - - // Port - int port = reply[offset++] << 8 | reply[offset++]; - - // Target - string target = ""; - Dns_Client.GetQName(reply, ref offset, ref target); - - return new DNS_rr_SRV(priority, weight, port, target, ttl); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_TXT.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_TXT.cs deleted file mode 100644 index 421f2a080..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_TXT.cs +++ /dev/null @@ -1,84 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Dns.Client -{ - #region usings - - using System; - - #endregion - - /// - /// TXT record class. - /// - [Serializable] - public class DNS_rr_TXT : DNS_rr_base - { - #region Members - - private readonly string m_Text = ""; - - #endregion - - #region Properties - - /// - /// Gets text. - /// - public string Text - { - get { return m_Text; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Text. - /// TTL value. - public DNS_rr_TXT(string text, int ttl) : base(QTYPE.TXT, ttl) - { - m_Text = text; - } - - #endregion - - #region Methods - - /// - /// Parses resource record from reply data. - /// - /// DNS server reply data. - /// Current offset in reply data. - /// Resource record data length. - /// Time to live in seconds. - public static DNS_rr_TXT Parse(byte[] reply, ref int offset, int rdLength, int ttl) - { - // TXT RR - - string text = Dns_Client.ReadCharacterString(reply, ref offset); - - return new DNS_rr_TXT(text, ttl); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_base.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_base.cs deleted file mode 100644 index c9392f6eb..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DNS_rr_base.cs +++ /dev/null @@ -1,67 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Dns.Client -{ - /// - /// Base class for DNS records. - /// - public abstract class DNS_rr_base - { - #region Members - - private readonly int m_TTL = -1; - private readonly QTYPE m_Type = QTYPE.A; - - #endregion - - #region Properties - - /// - /// Gets record type (A,MX,...). - /// - public QTYPE RecordType - { - get { return m_Type; } - } - - /// - /// Gets TTL (time to live) value in seconds. - /// - public int TTL - { - get { return m_TTL; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Record type (A,MX, ...). - /// TTL (time to live) value in seconds. - public DNS_rr_base(QTYPE recordType, int ttl) - { - m_Type = recordType; - m_TTL = ttl; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DnsCache.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DnsCache.cs deleted file mode 100644 index 8448256e6..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DnsCache.cs +++ /dev/null @@ -1,221 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Dns.Client -{ - #region usings - - using System; - using System.Collections; - using System.IO; - using System.Runtime.Serialization.Formatters.Binary; - - #endregion - - #region struct CacheEntry - - /// - /// Dns cache entry. - /// - [Serializable] - internal struct DnsCacheEntry - { - #region Members - - private readonly DnsServerResponse m_pResponse; - private readonly DateTime m_Time; - - #endregion - - #region Properties - - /// - /// Gets dns answers. - /// - public DnsServerResponse Answers - { - get { return m_pResponse; } - } - - /// - /// Gets entry add time. - /// - public DateTime Time - { - get { return m_Time; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Dns answers. - /// Entry add time. - public DnsCacheEntry(DnsServerResponse answers, DateTime addTime) - { - m_pResponse = answers; - m_Time = addTime; - } - - #endregion - } - - #endregion - - /// - /// This class implements dns query cache. - /// - public class DnsCache - { - #region Members - - private static long m_CacheTime = 10000; - private static Hashtable m_pCache; - - #endregion - - #region Properties - - /// - /// Gets or sets how long(seconds) to cache dns query. - /// - public static long CacheTime - { - get { return m_CacheTime; } - - set { m_CacheTime = value; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - static DnsCache() - { - m_pCache = new Hashtable(); - } - - #endregion - - #region Methods - - /// - /// Tries to get dns records from cache, if any. - /// - /// - /// - /// Returns null if not in cache. - public static DnsServerResponse GetFromCache(string qname, int qtype) - { - try - { - if (m_pCache.Contains(qname + qtype)) - { - DnsCacheEntry entry = (DnsCacheEntry) m_pCache[qname + qtype]; - - // If cache object isn't expired - if (entry.Time.AddSeconds(m_CacheTime) > DateTime.Now) - { - return entry.Answers; - } - } - } - catch {} - - return null; - } - - /// - /// Adds dns records to cache. If old entry exists, it is replaced. - /// - /// - /// - /// - public static void AddToCache(string qname, int qtype, DnsServerResponse answers) - { - if (answers == null) - { - return; - } - - try - { - lock (m_pCache) - { - // Remove old cache entry, if any. - if (m_pCache.Contains(qname + qtype)) - { - m_pCache.Remove(qname + qtype); - } - m_pCache.Add(qname + qtype, new DnsCacheEntry(answers, DateTime.Now)); - } - } - catch {} - } - - /// - /// Clears DNS cache. - /// - public static void ClearCache() - { - lock (m_pCache) - { - m_pCache.Clear(); - } - } - - /// - /// Serializes current cache. - /// - /// Return serialized cache. - public static byte[] SerializeCache() - { - lock (m_pCache) - { - MemoryStream retVal = new MemoryStream(); - - BinaryFormatter b = new BinaryFormatter(); - b.Serialize(retVal, m_pCache); - - return retVal.ToArray(); - } - } - - /// - /// DeSerializes stored cache. - /// - /// This value must be DnsCache.SerializeCache() method value. - public static void DeSerializeCache(byte[] cacheData) - { - lock (m_pCache) - { - MemoryStream retVal = new MemoryStream(cacheData); - - BinaryFormatter b = new BinaryFormatter(); - m_pCache = (Hashtable) b.Deserialize(retVal); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DnsServerResponse.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DnsServerResponse.cs deleted file mode 100644 index c7d4b7921..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/DnsServerResponse.cs +++ /dev/null @@ -1,367 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Dns.Client -{ - #region usings - - using System; - using System.Collections.Generic; - - #endregion - - /// - /// This class represents dns server response. - /// - [Serializable] - public class DnsServerResponse - { - #region Members - - private readonly List m_pAdditionalAnswers; - private readonly List m_pAnswers; - private readonly List m_pAuthoritiveAnswers; - private readonly RCODE m_RCODE = RCODE.NO_ERROR; - private readonly bool m_Success = true; - - #endregion - - #region Properties - - /// - /// Gets if connection to dns server was successful. - /// - public bool ConnectionOk - { - get { return m_Success; } - } - - /// - /// Gets dns server response code. - /// - public RCODE ResponseCode - { - get { return m_RCODE; } - } - - /// - /// Gets all resource records returned by server (answer records section + authority records section + additional records section). - /// NOTE: Before using this property ensure that ConnectionOk=true and ResponseCode=RCODE.NO_ERROR. - /// - public DNS_rr_base[] AllAnswers - { - get - { - List retVal = new List(); - retVal.AddRange(m_pAnswers.ToArray()); - retVal.AddRange(m_pAuthoritiveAnswers.ToArray()); - retVal.AddRange(m_pAdditionalAnswers.ToArray()); - - return retVal.ToArray(); - } - } - - /// - /// Gets dns server returned answers. NOTE: Before using this property ensure that ConnectionOk=true and ResponseCode=RCODE.NO_ERROR. - /// - /// - /// // NOTE: DNS server may return diffrent record types even if you query MX. - /// // For example you query lumisoft.ee MX and server may response: - /// // 1) MX - mail.lumisoft.ee - /// // 2) A - lumisoft.ee - /// // - /// // Before casting to right record type, see what type record is ! - /// - /// - /// foreach(DnsRecordBase record in Answers){ - /// // MX record, cast it to MX_Record - /// if(record.RecordType == QTYPE.MX){ - /// MX_Record mx = (MX_Record)record; - /// } - /// } - /// - public DNS_rr_base[] Answers - { - get { return m_pAnswers.ToArray(); } - } - - /// - /// Gets name server resource records in the authority records section. NOTE: Before using this property ensure that ConnectionOk=true and ResponseCode=RCODE.NO_ERROR. - /// - public DNS_rr_base[] AuthoritiveAnswers - { - get { return m_pAuthoritiveAnswers.ToArray(); } - } - - /// - /// Gets resource records in the additional records section. NOTE: Before using this property ensure that ConnectionOk=true and ResponseCode=RCODE.NO_ERROR. - /// - public DNS_rr_base[] AdditionalAnswers - { - get { return m_pAdditionalAnswers.ToArray(); } - } - - #endregion - - #region Constructor - - internal DnsServerResponse(bool connectionOk, - RCODE rcode, - List answers, - List authoritiveAnswers, - List additionalAnswers) - { - m_Success = connectionOk; - m_RCODE = rcode; - m_pAnswers = answers; - m_pAuthoritiveAnswers = authoritiveAnswers; - m_pAdditionalAnswers = additionalAnswers; - } - - #endregion - - #region Methods - - /// - /// Gets IPv4 host addess records. - /// - /// - public DNS_rr_A[] GetARecords() - { - List retVal = new List(); - foreach (DNS_rr_base record in m_pAnswers) - { - if (record.RecordType == QTYPE.A) - { - retVal.Add((DNS_rr_A) record); - } - } - - return retVal.ToArray(); - } - - /// - /// Gets name server records. - /// - /// - public DNS_rr_NS[] GetNSRecords() - { - List retVal = new List(); - foreach (DNS_rr_base record in m_pAnswers) - { - if (record.RecordType == QTYPE.NS) - { - retVal.Add((DNS_rr_NS) record); - } - } - - return retVal.ToArray(); - } - - /// - /// Gets CNAME records. - /// - /// - public DNS_rr_CNAME[] GetCNAMERecords() - { - List retVal = new List(); - foreach (DNS_rr_base record in m_pAnswers) - { - if (record.RecordType == QTYPE.CNAME) - { - retVal.Add((DNS_rr_CNAME) record); - } - } - - return retVal.ToArray(); - } - - /// - /// Gets SOA records. - /// - /// - public DNS_rr_SOA[] GetSOARecords() - { - List retVal = new List(); - foreach (DNS_rr_base record in m_pAnswers) - { - if (record.RecordType == QTYPE.SOA) - { - retVal.Add((DNS_rr_SOA) record); - } - } - - return retVal.ToArray(); - } - - /// - /// Gets PTR records. - /// - /// - public DNS_rr_PTR[] GetPTRRecords() - { - List retVal = new List(); - foreach (DNS_rr_base record in m_pAnswers) - { - if (record.RecordType == QTYPE.PTR) - { - retVal.Add((DNS_rr_PTR) record); - } - } - - return retVal.ToArray(); - } - - /// - /// Gets HINFO records. - /// - /// - public DNS_rr_HINFO[] GetHINFORecords() - { - List retVal = new List(); - foreach (DNS_rr_base record in m_pAnswers) - { - if (record.RecordType == QTYPE.HINFO) - { - retVal.Add((DNS_rr_HINFO) record); - } - } - - return retVal.ToArray(); - } - - /// - /// Gets MX records.(MX records are sorted by preference, lower array element is prefered) - /// - /// - public DNS_rr_MX[] GetMXRecords() - { - List mx = new List(); - foreach (DNS_rr_base record in m_pAnswers) - { - if (record.RecordType == QTYPE.MX) - { - mx.Add((DNS_rr_MX) record); - } - } - - // Sort MX records by preference. - DNS_rr_MX[] retVal = mx.ToArray(); - Array.Sort(retVal); - - return retVal; - } - - /// - /// Gets text records. - /// - /// - public DNS_rr_TXT[] GetTXTRecords() - { - List retVal = new List(); - foreach (DNS_rr_base record in m_pAnswers) - { - if (record.RecordType == QTYPE.TXT) - { - retVal.Add((DNS_rr_TXT) record); - } - } - - return retVal.ToArray(); - } - - /// - /// Gets IPv6 host addess records. - /// - /// - public DNS_rr_A[] GetAAAARecords() - { - List retVal = new List(); - foreach (DNS_rr_base record in m_pAnswers) - { - if (record.RecordType == QTYPE.AAAA) - { - retVal.Add((DNS_rr_A) record); - } - } - - return retVal.ToArray(); - } - - /// - /// Gets SRV resource records. - /// - /// - public DNS_rr_SRV[] GetSRVRecords() - { - List retVal = new List(); - foreach (DNS_rr_base record in m_pAnswers) - { - if (record.RecordType == QTYPE.SRV) - { - retVal.Add((DNS_rr_SRV) record); - } - } - - return retVal.ToArray(); - } - - /// - /// Gets NAPTR resource records. - /// - /// - public DNS_rr_NAPTR[] GetNAPTRRecords() - { - List retVal = new List(); - foreach (DNS_rr_base record in m_pAnswers) - { - if (record.RecordType == QTYPE.NAPTR) - { - retVal.Add((DNS_rr_NAPTR) record); - } - } - - return retVal.ToArray(); - } - - #endregion - - #region Utility methods - - /// - /// Filters out specified type of records from answer. - /// - /// - /// - /// - private List FilterRecordsX(List answers, QTYPE type) - { - List retVal = new List(); - foreach (DNS_rr_base record in answers) - { - if (record.RecordType == type) - { - retVal.Add(record); - } - } - - return retVal; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/QTYPE.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/QTYPE.cs deleted file mode 100644 index a2af095ea..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/QTYPE.cs +++ /dev/null @@ -1,108 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Dns.Client -{ - /// - /// Query type. - /// - public enum QTYPE - { - /// - /// IPv4 host address - /// - A = 1, - - /// - /// An authoritative name server. - /// - NS = 2, - - // MD = 3, Obsolete - // MF = 4, Obsolete - - /// - /// The canonical name for an alias. - /// - CNAME = 5, - - /// - /// Marks the start of a zone of authority. - /// - SOA = 6, - - // MB = 7, EXPERIMENTAL - // MG = 8, EXPERIMENTAL - // MR = 9, EXPERIMENTAL - // NULL = 10, EXPERIMENTAL - - /* /// - /// A well known service description. - /// - WKS = 11, */ - - /// - /// A domain name pointer. - /// - PTR = 12, - - /// - /// Host information. - /// - HINFO = 13, - /* - /// - /// Mailbox or mail list information. - /// - MINFO = 14, */ - - /// - /// Mail exchange. - /// - MX = 15, - - /// - /// Text strings. - /// - TXT = 16, - - /// - /// IPv6 host address. - /// - AAAA = 28, - - /// - /// SRV record specifies the location of services. - /// - SRV = 33, - - /// - /// NAPTR(Naming Authority Pointer) record. - /// - NAPTR = 35, - - /// - /// All records what server returns. - /// - ANY = 255, - - /* /// - /// UnKnown - /// - UnKnown = 9999, */ - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/RCODE.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/RCODE.cs deleted file mode 100644 index 1715e72ee..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/RCODE.cs +++ /dev/null @@ -1,56 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Dns.Client -{ - /// - /// Dns server reply codes. - /// - public enum RCODE - { - /// - /// No error condition. - /// - NO_ERROR = 0, - - /// - /// Format error - The name server was unable to interpret the query. - /// - FORMAT_ERRROR = 1, - - /// - /// Server failure - The name server was unable to process this query due to a problem with the name server. - /// - SERVER_FAILURE = 2, - - /// - /// Name Error - Meaningful only for responses from an authoritative name server, this code signifies that the - /// domain name referenced in the query does not exist. - /// - NAME_ERROR = 3, - - /// - /// Not Implemented - The name server does not support the requested kind of query. - /// - NOT_IMPLEMENTED = 4, - - /// - /// Refused - The name server refuses to perform the specified operation for policy reasons. - /// - REFUSED = 5, - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/_OPCODE.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/_OPCODE.cs deleted file mode 100644 index 83354e3d3..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/DNS/Client/_OPCODE.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Dns.Client -{ - /// - /// - /// - internal enum OPCODE - { - /// - /// A standard query. - /// - QUERY = 0, - - /// - /// An inverse query. - /// - IQUERY = 1, - - /// - /// A server status request. - /// - STATUS = 2, - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/DbFile.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/DbFile.cs deleted file mode 100644 index 011e7bbf6..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/DbFile.cs +++ /dev/null @@ -1,823 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Data.lsDB -{ - #region usings - - using System; - using System.Collections; - using System.IO; - using System.Threading; - using Net; - - #endregion - - /// - /// LumiSoft database file. - /// - public class DbFile : IDisposable - { - #region Members - - private readonly LDB_DataColumnCollection m_pColumns; - private int m_DataPageDataAreaSize = 1000; - private long m_DatapagesStartOffset = -1; - private string m_DbFileName = ""; - - private long m_FileLength; - private long m_FilePosition; - private LDB_Record m_pCurrentRecord; - private FileStream m_pDbFile; - private bool m_TableLocked; - - #endregion - - #region Properties - - /// - /// Gets if there is active database file. - /// - public bool IsDatabaseOpen - { - get { return m_pDbFile != null; } - } - - /// - /// Gets open database file name. Throws exception if database isn't open. - /// - public string FileName - { - get - { - if (!IsDatabaseOpen) - { - throw new Exception("Database isn't open, please open database first !"); - } - - return m_DbFileName; - } - } - - /// - /// Gets table columns. Throws exception if database isn't open. - /// - public LDB_DataColumnCollection Columns - { - get - { - if (!IsDatabaseOpen) - { - throw new Exception("Database isn't open, please open database first !"); - } - - return m_pColumns; - } - } - - /// - /// Gets current record. Returns null if there isn't current record. - /// - public LDB_Record CurrentRecord - { - get { return m_pCurrentRecord; } - } - - /// - /// Gets table is locked. - /// - public bool TableLocked - { - get { return m_TableLocked; } - } - - /// - /// Gets how much data data page can store. - /// - public int DataPageDataAreaSize - { - get { return m_DataPageDataAreaSize; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public DbFile() - { - m_pColumns = new LDB_DataColumnCollection(this); - } - - #endregion - - #region Methods - - /// - /// Clean up any resources being used. - /// - public void Dispose() - { - Close(); - } - - /// - /// Opens specified data file. - /// - /// File name. - public void Open(string fileName) - { - Open(fileName, 0); - } - - /// - /// Opens specified data file. - /// - /// File name. - /// If data base file is exclusively locked, then how many seconds to wait file to unlock before raising a error. - public void Open(string fileName, int waitTime) - { - DateTime lockExpireTime = DateTime.Now.AddSeconds(waitTime); - while (true) - { - try - { - m_pDbFile = File.Open(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); - - break; - } - catch (IOException x) - { - // Make this because to get rid of "The variable 'x' is declared but never used" - string dummy = x.Message; - - Thread.Sleep(15); - - // Lock wait time timed out - if (DateTime.Now > lockExpireTime) - { - throw new Exception("Database file is locked and lock wait time expired !"); - } - } - } - - /* Table structure: - 50 bytes - version - 2 bytes - CRLF - 8 bytes - free datapages count - 2 bytes - CRLF - 4 bytes - datapage data area size - 2 bytes - CRLF - 100 x 500 bytes - 100 columns info store - 2 bytes - CRLF - ... data pages - */ - - m_DbFileName = fileName; - StreamLineReader r = new StreamLineReader(m_pDbFile); - - // TODO: check if LDB file - - // Read version line (50 bytes + CRLF) - byte[] version = r.ReadLine(); - - // Skip free data pages count - byte[] freeDataPagesCount = new byte[10]; - m_pDbFile.Read(freeDataPagesCount, 0, freeDataPagesCount.Length); - - // 4 bytes datapage data area size + CRLF - byte[] dataPageDataAreaSize = new byte[6]; - m_pDbFile.Read(dataPageDataAreaSize, 0, dataPageDataAreaSize.Length); - m_DataPageDataAreaSize = ldb_Utils.ByteToInt(dataPageDataAreaSize, 0); - - // Read 100 column lines (500 + CRLF bytes each) - for (int i = 0; i < 100; i++) - { - byte[] columnInfo = r.ReadLine(); - if (columnInfo == null) - { - throw new Exception("Invalid columns data area length !"); - } - - if (columnInfo[0] != '\0') - { - m_pColumns.Parse(columnInfo); - } - } - - // Header terminator \0 - m_pDbFile.Position++; - - // No we have rows start offset - m_DatapagesStartOffset = m_pDbFile.Position; - - // Store file length and position - m_FileLength = m_pDbFile.Length; - m_FilePosition = m_pDbFile.Position; - } - - /// - /// Closes database file. - /// - public void Close() - { - if (m_pDbFile != null) - { - m_pDbFile.Close(); - m_pDbFile = null; - m_DbFileName = ""; - m_FileLength = 0; - m_FilePosition = 0; - } - } - - /// - /// Creates new database file. - /// - /// File name. - public void Create(string fileName) - { - Create(fileName, 1000); - } - - /// - /// Creates new database file. - /// - /// File name. - /// Specifies how many data can data page store. - public void Create(string fileName, int dataPageDataAreaSize) - { - m_pDbFile = new FileStream(fileName, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None); - - /* Table structure: - 50 bytes - version - 2 bytes - CRLF - 8 bytes - free datapages count - 2 bytes - CRLF - 4 bytes - datapage data area size - 2 bytes - CRLF - 100 x 500 bytes - 100 columns info store - 2 bytes - CRLF - ... data pages - */ - - // Version 50 + CRLF bytes - byte[] versionData = new byte[52]; - versionData[0] = (byte) '1'; - versionData[1] = (byte) '.'; - versionData[2] = (byte) '0'; - versionData[50] = (byte) '\r'; - versionData[51] = (byte) '\n'; - m_pDbFile.Write(versionData, 0, versionData.Length); - - // 8 bytes free data pages count + CRLF - byte[] freeDataPagesCount = new byte[10]; - freeDataPagesCount[8] = (byte) '\r'; - freeDataPagesCount[9] = (byte) '\n'; - m_pDbFile.Write(freeDataPagesCount, 0, freeDataPagesCount.Length); - - // 4 bytes datapage data area size + CRLF - byte[] dataPageDataAreaSizeB = new byte[6]; - Array.Copy(ldb_Utils.IntToByte(dataPageDataAreaSize), 0, dataPageDataAreaSizeB, 0, 4); - dataPageDataAreaSizeB[4] = (byte) '\r'; - dataPageDataAreaSizeB[5] = (byte) '\n'; - m_pDbFile.Write(dataPageDataAreaSizeB, 0, dataPageDataAreaSizeB.Length); - - // 100 x 100 + CRLF bytes header lines - for (int i = 0; i < 100; i++) - { - byte[] data = new byte[100]; - m_pDbFile.Write(data, 0, data.Length); - m_pDbFile.Write(new byte[] {(int) '\r', (int) '\n'}, 0, 2); - } - - // Headers terminator char(0) - m_pDbFile.WriteByte((int) '\0'); - - // Data pages start pointer - m_DatapagesStartOffset = m_pDbFile.Position - 1; - - m_DbFileName = fileName; - m_DataPageDataAreaSize = dataPageDataAreaSize; - - // Store file length and position - m_FileLength = m_pDbFile.Length; - m_FilePosition = m_pDbFile.Position; - } - - /// - /// Locks table. - /// - /// If table is locked, then how many sconds to wait table to unlock, before teturning error. - public void LockTable(int waitTime) - { - if (!IsDatabaseOpen) - { - throw new Exception("Database isn't open, please open database first !"); - } - // Table is locked already, just skip locking - if (m_TableLocked) - { - return; - } - - DateTime lockExpireTime = DateTime.Now.AddSeconds(waitTime); - while (true) - { - try - { - // We just lock first byte - m_pDbFile.Lock(0, 1); - m_TableLocked = true; - - break; - } - // Catch the IOException generated if the - // specified part of the file is locked. - catch (IOException x) - { - // Make this because to get rid of "The variable 'x' is declared but never used" - string dummy = x.Message; - - Thread.Sleep(15); - - // Lock wait time timed out - if (DateTime.Now > lockExpireTime) - { - throw new Exception("Table is locked and lock wait time expired !"); - } - } - } - } - - /// - /// Unlock table. - /// - public void UnlockTable() - { - if (!IsDatabaseOpen) - { - throw new Exception("Database isn't open, please open database first !"); - } - - if (m_TableLocked) - { - // We just unlock first byte - m_pDbFile.Unlock(0, 1); - } - } - - /* - /// - /// Locks current record. - /// - public void LockRecord() - { - if(!this.IsDatabaseOpen){ - throw new Exception("Database isn't open, please open database first !"); - } - - } -*/ - - /* - /// - /// Unlocks current record. - /// - public void UnlockRecord() - { - if(!this.IsDatabaseOpen){ - throw new Exception("Database isn't open, please open database first !"); - } - - } -*/ - - /// - /// Gets next record. Returns true if end of file reached and there are no more records. - /// - /// Returns true if end of file reached and there are no more records. - public bool NextRecord() - { - if (!IsDatabaseOpen) - { - throw new Exception("Database isn't open, please open database first !"); - } - - //--- Find next record ---------------------------------------------------// - long nextRowStartOffset = 0; - if (m_pCurrentRecord == null) - { - nextRowStartOffset = m_DatapagesStartOffset; - } - else - { - nextRowStartOffset = m_pCurrentRecord.DataPage.Pointer + m_DataPageDataAreaSize + 33; - } - - while (true) - { - if (m_FileLength > nextRowStartOffset) - { - DataPage dataPage = new DataPage(m_DataPageDataAreaSize, this, nextRowStartOffset); - - // We want datapage with used space - if (dataPage.Used && dataPage.OwnerDataPagePointer < 1) - { - m_pCurrentRecord = new LDB_Record(this, dataPage); - break; - } - } - else - { - return true; - } - - nextRowStartOffset += m_DataPageDataAreaSize + 33; - } - //-------------------------------------------------------------------------// - - return false; - } - - /// - /// Appends new record to table. - /// - public void AppendRecord(object[] values) - { - if (!IsDatabaseOpen) - { - throw new Exception("Database isn't open, please open database first !"); - } - if (m_pColumns.Count != values.Length) - { - throw new Exception("Each column must have corresponding value !"); - } - - bool unlock = true; - // Table is already locked, don't lock it - if (TableLocked) - { - unlock = false; - } - else - { - LockTable(15); - } - - // Construct record data - byte[] record = LDB_Record.CreateRecord(this, values); - - // Get free data pages - DataPage[] dataPages = GetDataPages(0, - (int) - Math.Ceiling(record.Length/(double) m_DataPageDataAreaSize)); - - StoreDataToDataPages(m_DataPageDataAreaSize, record, dataPages); - - if (unlock) - { - UnlockTable(); - } - } - - /// - /// Deletes current record. - /// - public void DeleteCurrentRecord() - { - if (!IsDatabaseOpen) - { - throw new Exception("Database isn't open, please open database first !"); - } - if (m_pCurrentRecord == null) - { - throw new Exception("There is no current record !"); - } - - bool unlock = true; - // Table is already locked, don't lock it - if (TableLocked) - { - unlock = false; - } - else - { - LockTable(15); - } - - // Release all data pages hold by this row - DataPage[] dataPages = m_pCurrentRecord.DataPages; - for (int i = 0; i < dataPages.Length; i++) - { - DataPage p = dataPages[i]; - - byte[] dataPage = DataPage.CreateDataPage(m_DataPageDataAreaSize, - false, - 0, - 0, - 0, - new byte[m_DataPageDataAreaSize]); - SetFilePosition(p.Pointer); - WriteToFile(dataPage, 0, dataPage.Length); - } - - // Increase free data pages count info in table header - byte[] freeDataPagesCount = new byte[8]; - SetFilePosition(52); - ReadFromFile(freeDataPagesCount, 0, freeDataPagesCount.Length); - freeDataPagesCount = - ldb_Utils.LongToByte(ldb_Utils.ByteToLong(freeDataPagesCount, 0) + dataPages.Length); - SetFilePosition(52); - WriteToFile(freeDataPagesCount, 0, freeDataPagesCount.Length); - - if (unlock) - { - UnlockTable(); - } - - // Activate next record **** Change it ??? - NextRecord(); - } - - #endregion - - #region Utility methods - - /// - /// Moves position to the end of file. - /// - private void GoToFileEnd() - { - m_pDbFile.Position = m_pDbFile.Length; - m_FileLength = m_pDbFile.Length; - m_FilePosition = m_FileLength; - } - - #endregion - - #region Internal methods - - /// - /// Stores data to specified data pages. - /// - /// Data page data area size. - /// Data to store. - /// Data pages where to store data. - internal static void StoreDataToDataPages(int dataPageDataAreaSize, byte[] data, DataPage[] dataPages) - { - if ((int) Math.Ceiling(data.Length/(double) dataPageDataAreaSize) > dataPages.Length) - { - throw new Exception("There isn't enough data pages to store data ! Data needs '" + - (int) Math.Ceiling(data.Length/(double) dataPageDataAreaSize) + - "' , but available '" + dataPages.Length + "'."); - } - - //--- Store data to data page(s) -----------------------// - long position = 0; - for (int i = 0; i < dataPages.Length; i++) - { - if ((data.Length - position) > dataPageDataAreaSize) - { - byte[] d = new byte[dataPageDataAreaSize]; - Array.Copy(data, position, d, 0, d.Length); - dataPages[i].WriteData(d); - position += dataPageDataAreaSize; - } - else - { - byte[] d = new byte[data.Length - position]; - Array.Copy(data, position, d, 0, d.Length); - dataPages[i].WriteData(d); - } - } - //------------------------------------------------------// - } - - /// - /// Gets specified number of free data pages. If free data pages won't exist, creates new ones. - /// Data pages are marked as used and OwnerDataPagePointer and NextDataPagePointer is set as needed. - /// - /// Owner data page pointer that own first requested data page. If no owner then this value is 0. - /// Number of data pages wanted. - internal DataPage[] GetDataPages(long ownerDataPagePointer, int count) - { - if (!TableLocked) - { - throw new Exception("Table must be locked to acess GetDataPages() method !"); - } - - ArrayList freeDataPages = new ArrayList(); - - // Get free data pages count from table header - byte[] freeDataPagesCount = new byte[8]; - SetFilePosition(52); - ReadFromFile(freeDataPagesCount, 0, freeDataPagesCount.Length); - long nFreeDataPages = ldb_Utils.ByteToLong(freeDataPagesCount, 0); - - // We have plenty free data pages and enough for count requested, find requested count free data pages - if (nFreeDataPages > 1000 && nFreeDataPages > count) - { - long nextDataPagePointer = m_DatapagesStartOffset + 1; - while (freeDataPages.Count < count) - { - DataPage dataPage = new DataPage(m_DataPageDataAreaSize, this, nextDataPagePointer); - if (!dataPage.Used) - { - dataPage.Used = true; - freeDataPages.Add(dataPage); - } - - nextDataPagePointer += m_DataPageDataAreaSize + 33; - } - - // Decrease free data pages count in table header - SetFilePosition(52); - ReadFromFile(ldb_Utils.LongToByte(nFreeDataPages - count), 0, 8); - } - // Just create new data pages - else - { - for (int i = 0; i < count; i++) - { - byte[] dataPage = DataPage.CreateDataPage(m_DataPageDataAreaSize, - true, - 0, - 0, - 0, - new byte[m_DataPageDataAreaSize]); - GoToFileEnd(); - long dataPageStartPointer = GetFilePosition(); - WriteToFile(dataPage, 0, dataPage.Length); - - freeDataPages.Add(new DataPage(m_DataPageDataAreaSize, this, dataPageStartPointer)); - } - } - - // Relate data pages (chain) - for (int i = 0; i < freeDataPages.Count; i++) - { - // First data page - if (i == 0) - { - // Owner data page poitner specified for first data page - if (ownerDataPagePointer > 0) - { - ((DataPage) freeDataPages[i]).OwnerDataPagePointer = ownerDataPagePointer; - } - - // There is continuing data page - if (freeDataPages.Count > 1) - { - ((DataPage) freeDataPages[i]).NextDataPagePointer = - ((DataPage) freeDataPages[i + 1]).Pointer; - } - } - // Last data page - else if (i == (freeDataPages.Count - 1)) - { - ((DataPage) freeDataPages[i]).OwnerDataPagePointer = - ((DataPage) freeDataPages[i - 1]).Pointer; - } - // Middle range data page - else - { - ((DataPage) freeDataPages[i]).OwnerDataPagePointer = - ((DataPage) freeDataPages[i - 1]).Pointer; - ((DataPage) freeDataPages[i]).NextDataPagePointer = - ((DataPage) freeDataPages[i + 1]).Pointer; - } - } - - DataPage[] retVal = new DataPage[freeDataPages.Count]; - freeDataPages.CopyTo(retVal); - - return retVal; - } - - /// - /// Adds column to db file. - /// - /// - internal void AddColumn(LDB_DataColumn column) - { - // Find free column data area - - // Set position over version, free data pages count and data page data area size - m_pDbFile.Position = 68; - - long freeColumnPosition = -1; - StreamLineReader r = new StreamLineReader(m_pDbFile); - // Loop all columns data areas, see it there any free left - for (int i = 0; i < 100; i++) - { - byte[] columnInfo = r.ReadLine(); - if (columnInfo == null) - { - throw new Exception("Invalid columns data area length !"); - } - - // We found unused column data area - if (columnInfo[0] == '\0') - { - freeColumnPosition = m_pDbFile.Position; - break; - } - } - m_FilePosition = m_pDbFile.Position; - - if (freeColumnPosition != -1) - { - // TODO: If there is data ??? - - // Move to row start - SetFilePosition(GetFilePosition() - 102); - - // Store column - byte[] columnData = column.ToColumnInfo(); - WriteToFile(columnData, 0, columnData.Length); - } - else - { - throw new Exception("Couldn't find free column space ! "); - } - } - - /// - /// Removes specified column from database file. - /// - /// - internal void RemoveColumn(LDB_DataColumn column) - { - throw new Exception("TODO:"); - } - - /// - /// Reads data from file. - /// - /// Buffer where to store readed data.. - /// Offset in array to where to start storing readed data. - /// Number of bytes to read. - /// - internal int ReadFromFile(byte[] data, int offset, int count) - { - int readed = m_pDbFile.Read(data, offset, count); - m_FilePosition += readed; - - return readed; - } - - /// - /// Writes data to file. - /// - /// Data to write. - /// Offset in array from where to start writing data. - /// Number of bytes to write. - /// - internal void WriteToFile(byte[] data, int offset, int count) - { - m_pDbFile.Write(data, offset, count); - m_FilePosition += count; - } - - /// - /// Gets current position in file. - /// - /// - internal long GetFilePosition() - { - return m_FilePosition; - } - - /// - /// Sets file position. - /// - /// Position in file. - internal void SetFilePosition(long position) - { - if (m_FilePosition != position) - { - m_pDbFile.Position = position; - m_FilePosition = position; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/LDB_DataColumn.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/LDB_DataColumn.cs deleted file mode 100644 index e9f0a0f20..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/LDB_DataColumn.cs +++ /dev/null @@ -1,221 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Data.lsDB -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// - /// - public class LDB_DataColumn - { - #region Members - - private string m_ColumnName = ""; - private int m_ColumSize = -1; - private LDB_DataType m_DataType = LDB_DataType.String; - - #endregion - - #region Properties - - /// - /// Gets LDB data type. - /// - public LDB_DataType DataType - { - get { return m_DataType; } - } - - /// - /// Gets column name. - /// - public string ColumnName - { - get { return m_ColumnName; } - } - - /// - /// Gets column size. Returns -1 if column is with variable length. - /// - public int ColumnSize - { - get { return m_ColumSize; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Column name. - /// Column data type. - public LDB_DataColumn(string columnName, LDB_DataType dataType) : this(columnName, dataType, -1) {} - - /// - /// Default constructor. - /// - /// Column name. - /// Column data type. - /// Specifies column data size. This is available for String datatype only. - public LDB_DataColumn(string columnName, LDB_DataType dataType, int columnSize) - { - m_ColumnName = columnName; - m_DataType = dataType; - - // TODO: check that column name won't exceed 50 bytes - - if (dataType == LDB_DataType.Bool) - { - m_ColumSize = 1; - } - else if (dataType == LDB_DataType.DateTime) - { - m_ColumSize = 13; - } - else if (dataType == LDB_DataType.Int) - { - m_ColumSize = 4; - } - else if (dataType == LDB_DataType.Long) - { - m_ColumSize = 8; - } - else - { - m_ColumSize = columnSize; - } - } - - /// - /// Internal constructor. - /// - internal LDB_DataColumn() {} - - #endregion - - #region Internal methods - - /// - /// Gets string from char(0) terminated text. - /// - /// Text. - /// - internal static string GetChar0TerminatedString(string text) - { - if (text.IndexOf('\0') > -1) - { - return text.Substring(0, text.IndexOf('\0')); - } - else - { - return text; - } - } - - /// - /// Parses column from byte[] data. - /// - /// Column data. - internal void Parse(byte[] columnData) - { - if (columnData.Length != 102) - { - throw new Exception("Invalid column data length '" + columnData.Length + "' !"); - } - - //-- total 102 bytes - // 1 byte - column type - // 4 bytes - column size - // 45 bytes - reserved - // 50 bytes - column name - // 2 bytes - CRLF - - // column type - m_DataType = (LDB_DataType) columnData[0]; - // column size - m_ColumSize = (columnData[1] << 24) | (columnData[2] << 16) | (columnData[3] << 8) | - (columnData[4] << 0); - // reserved - // 45 bytes - // column name - byte[] columnName = new byte[50]; - Array.Copy(columnData, 50, columnName, 0, columnName.Length); - m_ColumnName = GetChar0TerminatedString(Encoding.UTF8.GetString(columnName)); - - if (m_DataType == LDB_DataType.Bool) - { - m_ColumSize = 1; - } - else if (m_DataType == LDB_DataType.DateTime) - { - m_ColumSize = 13; - } - else if (m_DataType == LDB_DataType.Int) - { - m_ColumSize = 4; - } - else if (m_DataType == LDB_DataType.Long) - { - m_ColumSize = 8; - } - } - - /// - /// Convert column to byte[] data. - /// - /// - internal byte[] ToColumnInfo() - { - //-- total 100 + CRLF bytes - // 1 byte - column type - // 4 bytes - column size - // 45 bytes - reserved - // 50 bytes - column name - // CRLF - - byte[] columnData = new byte[102]; - // column type - columnData[0] = (byte) m_DataType; - // column size - columnData[1] = (byte) ((m_ColumSize & (1 << 24)) >> 24); - columnData[2] = (byte) ((m_ColumSize & (1 << 16)) >> 16); - columnData[3] = (byte) ((m_ColumSize & (1 << 8)) >> 8); - columnData[4] = (byte) ((m_ColumSize & (1 << 0)) >> 0); - // reserved - // 45 bytes - // column name - byte[] columnName = Encoding.UTF8.GetBytes(m_ColumnName); - Array.Copy(columnName, 0, columnData, 50, columnName.Length); - // CRLF - columnData[100] = (byte) '\r'; - columnData[101] = (byte) '\n'; - - return columnData; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/LDB_DataColumnCollection.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/LDB_DataColumnCollection.cs deleted file mode 100644 index e402b96c2..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/LDB_DataColumnCollection.cs +++ /dev/null @@ -1,208 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Data.lsDB -{ - #region usings - - using System; - using System.Collections.Generic; - - #endregion - - /// - /// lsDB data column collection. - /// - public class LDB_DataColumnCollection //: IEnumerable - { - #region Members - - private readonly List m_pColumns; - private readonly object m_pOwner; - - #endregion - - #region Properties - - /// - /// Gets column from specified index. - /// - public LDB_DataColumn this[int index] - { - get { return m_pColumns[index]; } - } - - /// - /// Gets column count in the collection. - /// - public int Count - { - get { return m_pColumns.Count; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Table that owns this collection. - internal LDB_DataColumnCollection(object owner) - { - m_pOwner = owner; - - m_pColumns = new List(); - } - - #endregion - - #region Methods - - /// - /// Ads specified data column to collection. - /// - /// - public void Add(LDB_DataColumn column) - { - if (Contains(column.ColumnName)) - { - throw new Exception("Data column with specified name '" + column.ColumnName + - "' already exists !"); - } - - if (m_pOwner.GetType() == typeof (DbFile)) - { - ((DbFile) m_pOwner).AddColumn(column); - } - else if (m_pOwner.GetType() == typeof (lsDB_FixedLengthTable)) - { - ((lsDB_FixedLengthTable) m_pOwner).AddColumn(column); - } - - m_pColumns.Add(column); - } - - /// - /// Removes specified data column from collection. - /// - /// Column name which to remove. - public void Remove(string columName) - { - foreach (LDB_DataColumn column in m_pColumns) - { - if (column.ColumnName.ToLower() == columName.ToLower()) - { - Remove(column); - break; - } - } - } - - /// - /// Removes specified data column from collection. - /// - /// Data column which to remove. - public void Remove(LDB_DataColumn column) - { - m_pColumns.Remove(column); - } - - /// - /// Gets specified data column index in collection. Returns -1 if no such column. - /// - /// - /// - public int IndexOf(string columnName) - { - for (int i = 0; i < m_pColumns.Count; i++) - { - if ((m_pColumns[i]).ColumnName.ToLower() == columnName.ToLower()) - { - return i; - } - } - - return -1; - } - - /// - /// Gets specified data column index in collection. Returns -1 if no such column. - /// - /// Data column. - /// - public int IndexOf(LDB_DataColumn column) - { - return m_pColumns.IndexOf(column); - } - - /// - /// Gets if data column collection contains specified column. - /// - /// Column name. - /// - public bool Contains(string columnName) - { - foreach (LDB_DataColumn column in m_pColumns) - { - if (column.ColumnName.ToLower() == columnName.ToLower()) - { - return true; - } - } - - return false; - } - - /// - /// Gets if data column collection contains specified column. - /// - /// Data column. - /// - public bool Contains(LDB_DataColumn column) - { - return m_pColumns.Contains(column); - } - - /// - /// Gets enumerator. - /// - /// - public IEnumerator GetEnumerator() - { - return m_pColumns.GetEnumerator(); - } - - #endregion - - #region Internal methods - - /// - /// Parses and adds data column to the collection. - /// - /// - internal void Parse(byte[] columnData) - { - LDB_DataColumn column = new LDB_DataColumn(); - column.Parse(columnData); - - m_pColumns.Add(column); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/LDB_DataType.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/LDB_DataType.cs deleted file mode 100644 index 139b95ce7..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/LDB_DataType.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Data.lsDB -{ - /// - /// lsDB data types. - /// - public enum LDB_DataType - { - /// - /// Unicode string. - /// - String = (int) 's', - - /// - /// Long (64-bit integer). - /// - Long = (int) 'l', - - /// - /// Integer (32-bit integer). - /// - Int = (int) 'i', - - /// - /// Date time. - /// - DateTime = (int) 't', - - /// - /// Boolean. - /// - Bool = (int) 'b', - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/LDB_Record.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/LDB_Record.cs deleted file mode 100644 index 8ca61f721..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/LDB_Record.cs +++ /dev/null @@ -1,554 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Data.lsDB -{ - #region usings - - using System; - using System.Collections; - using System.IO; - using System.Text; - - #endregion - - /// - /// lsDB database record. - /// - public class LDB_Record - { - #region Members - - private readonly DataPage m_pDataPage; - private readonly DbFile m_pOwnerDb; - private int[] m_ColumnValueSize; - - #endregion - - #region Properties - - /// - /// Gets or set all data column values. - /// - public object[] Values - { - get - { - object[] retVal = new object[m_pOwnerDb.Columns.Count]; - for (int i = 0; i < m_pOwnerDb.Columns.Count; i++) - { - retVal[i] = this[i]; - } - - return retVal; - } - - set { UpdateRecord(value); } - } - - /// - /// Gets or sets specified data column value. - /// - public object this[int columnIndex] - { - get - { - if (columnIndex < 0) - { - throw new Exception("The columnIndex can't be negative value !"); - } - if (columnIndex > m_pOwnerDb.Columns.Count) - { - throw new Exception("The columnIndex out of columns count !"); - } - - return GetColumnData(columnIndex); - } - - set - { - if (columnIndex < 0) - { - throw new Exception("The columnIndex can't be negative value !"); - } - if (columnIndex > m_pOwnerDb.Columns.Count) - { - throw new Exception("The columnIndex out of columns count !"); - } - - // Get current row values - object[] rowValues = Values; - // Update specified column value - rowValues[columnIndex] = value; - // Update record - UpdateRecord(rowValues); - } - } - - /// - /// Gets or sets specified data column value. - /// - public object this[string columnName] - { - get - { - int index = m_pOwnerDb.Columns.IndexOf(columnName); - if (index == -1) - { - throw new Exception("Table doesn't contain column '" + columnName + "' !"); - } - - return this[index]; - } - - set - { - int index = m_pOwnerDb.Columns.IndexOf(columnName); - if (index == -1) - { - throw new Exception("Table doesn't contain column '" + columnName + "' !"); - } - - this[index] = value; - } - } - - /// - /// Gets or sets specified data column value. - /// - public object this[LDB_DataColumn column] - { - get - { - int index = m_pOwnerDb.Columns.IndexOf(column); - if (index == -1) - { - throw new Exception("Table doesn't contain column '" + column.ColumnName + "' !"); - } - - return this[index]; - } - - set - { - int index = m_pOwnerDb.Columns.IndexOf(column); - if (index == -1) - { - throw new Exception("Table doesn't contain column '" + column.ColumnName + "' !"); - } - - this[index] = value; - } - } - - /// - /// Gets data page on what row starts. - /// - internal DataPage DataPage - { - get { return m_pDataPage; } - } - - /// - /// Gets data pages held by this row. - /// - internal DataPage[] DataPages - { - get - { - ArrayList dataPages = new ArrayList(); - DataPage page = m_pDataPage; - dataPages.Add(page); - while (page.NextDataPagePointer > 0) - { - page = new DataPage(m_pOwnerDb.DataPageDataAreaSize, m_pOwnerDb, page.NextDataPagePointer); - dataPages.Add(page); - } - - DataPage[] retVal = new DataPage[dataPages.Count]; - dataPages.CopyTo(retVal); - - return retVal; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Table that owns this row. - /// Data page on what row starts. - internal LDB_Record(DbFile ownerDb, DataPage rowStartDataPage) - { - m_pOwnerDb = ownerDb; - m_pDataPage = rowStartDataPage; - - ParseRowInfo(); - } - - #endregion - - #region Utility methods - - /// - /// Parse row info. - /// - private void ParseRowInfo() - { - /* RowInfo structure - (4 bytes) * columnCount - holds column data data length - xx bytes columns values - */ - - m_ColumnValueSize = new int[m_pOwnerDb.Columns.Count]; - byte[] columnValueSizes = m_pDataPage.ReadData(0, 4*m_pOwnerDb.Columns.Count); - for (int i = 0; i < m_pOwnerDb.Columns.Count; i++) - { - m_ColumnValueSize[i] = ldb_Utils.ByteToInt(columnValueSizes, i*4); - } - } - - /// - /// Gets specified column data. - /// - /// Column index. - /// - private object GetColumnData(int columnIndex) - { - // Get column data start offset - int columnStartOffset = 4*m_pOwnerDb.Columns.Count; - for (int i = 0; i < columnIndex; i++) - { - columnStartOffset += m_ColumnValueSize[i]; - } - - int dataLength = m_ColumnValueSize[columnIndex]; - int startDataPage = (int) Math.Floor(columnStartOffset/(double) m_pOwnerDb.DataPageDataAreaSize); - int offsetInStartDataPage = columnStartOffset - (startDataPage*m_pOwnerDb.DataPageDataAreaSize); - - int dataOffset = 0; - int currentDataPageIndex = 0; - byte[] columnData = new byte[dataLength]; - DataPage currentDataPage = DataPage; - while (dataOffset < dataLength) - { - // We haven't reached to data page on what data starts, just go to next continuing data page - if (currentDataPageIndex < startDataPage) - { - // Get next continuing data page - currentDataPage = new DataPage(m_pOwnerDb.DataPageDataAreaSize, - m_pOwnerDb, - currentDataPage.NextDataPagePointer); - currentDataPageIndex++; - } - // We need all this data page data + addtitional data pages data - else if ((dataLength - dataOffset + offsetInStartDataPage) > currentDataPage.StoredDataLength) - { - currentDataPage.ReadData(columnData, - dataOffset, - m_pOwnerDb.DataPageDataAreaSize - offsetInStartDataPage, - offsetInStartDataPage); - dataOffset += m_pOwnerDb.DataPageDataAreaSize - offsetInStartDataPage; - - // Get next continuing data page - currentDataPage = new DataPage(m_pOwnerDb.DataPageDataAreaSize, - m_pOwnerDb, - currentDataPage.NextDataPagePointer); - currentDataPageIndex++; - - offsetInStartDataPage = 0; - } - // This data page has all data we need - else - { - currentDataPage.ReadData(columnData, - dataOffset, - dataLength - dataOffset, - offsetInStartDataPage); - dataOffset += dataLength - dataOffset; - - offsetInStartDataPage = 0; - } - } - - // Convert to column data type - return ConvertFromInternalData(m_pOwnerDb.Columns[columnIndex], columnData); - } - - /// - /// Updates this record values. - /// - /// Row new values. - private void UpdateRecord(object[] rowValues) - { - bool unlock = true; - // Table is already locked, don't lock it - if (m_pOwnerDb.TableLocked) - { - unlock = false; - } - else - { - m_pOwnerDb.LockTable(15); - } - - // Create new record - byte[] rowData = CreateRecord(m_pOwnerDb, rowValues); - - DataPage[] dataPages = DataPages; - // Clear old data ?? do we need that - // for(int i=0;i dataPages.Length) - { - int dataPagesNeeded = - (int) Math.Ceiling(rowData.Length/(double) m_pOwnerDb.DataPageDataAreaSize) - - dataPages.Length; - DataPage[] additionalDataPages = - m_pOwnerDb.GetDataPages(dataPages[dataPages.Length - 1].Pointer, dataPagesNeeded); - - // Append new data pages to existing data pages chain - dataPages[dataPages.Length - 1].NextDataPagePointer = additionalDataPages[0].Pointer; - - DataPage[] newVal = new DataPage[dataPages.Length + additionalDataPages.Length]; - Array.Copy(dataPages, 0, newVal, 0, dataPages.Length); - Array.Copy(additionalDataPages, 0, newVal, dataPages.Length, additionalDataPages.Length); - dataPages = newVal; - } - - // Store new record - DbFile.StoreDataToDataPages(m_pOwnerDb.DataPageDataAreaSize, rowData, dataPages); - - // Update row info - ParseRowInfo(); - - if (unlock) - { - m_pOwnerDb.UnlockTable(); - } - } - - #endregion - - #region Internal methods - - /// - /// Creates record. Contains record info + record values. - /// - /// Roecord owner table. - /// Row values what to store to record. - /// - internal static byte[] CreateRecord(DbFile ownerDb, object[] rowValues) - { - if (ownerDb.Columns.Count != rowValues.Length) - { - throw new Exception("LDB_Record.CreateRecord m_pOwnerDb.Columns.Count != rowValues.Length !"); - } - - // Convert values to internal store format - ArrayList rowByteValues = new ArrayList(); - for (int i = 0; i < rowValues.Length; i++) - { - rowByteValues.Add(ConvertToInternalData(ownerDb.Columns[i], rowValues[i])); - } - - /* RowInfo structure - (4 bytes) * columnCount - holds column data data length - xx bytes columns values - */ - - MemoryStream msRecord = new MemoryStream(); - // Write values sizes - for (int i = 0; i < rowByteValues.Count; i++) - { - msRecord.Write(ldb_Utils.IntToByte(((byte[]) rowByteValues[i]).Length), 0, 4); - } - - // Write values - for (int i = 0; i < rowByteValues.Count; i++) - { - byte[] val = (byte[]) rowByteValues[i]; - msRecord.Write(val, 0, val.Length); - } - - return msRecord.ToArray(); - } - - /// - /// Converts data to specied column internal store data. - /// - /// Column where to store data. - /// Data to convert. - /// - internal static byte[] ConvertToInternalData(LDB_DataColumn coulmn, object val) - { - if (val == null) - { - throw new Exception("Null values aren't supported !"); - } - - if (coulmn.DataType == LDB_DataType.Bool) - { - if (val.GetType() != typeof (bool)) - { - throw new Exception("Column '" + coulmn.ColumnName + - "' requires datatype of bool, but value contains '" + val.GetType() + - "' !"); - } - - return new[] {Convert.ToByte((bool) val)}; - } - else if (coulmn.DataType == LDB_DataType.DateTime) - { - if (val.GetType() != typeof (DateTime)) - { - throw new Exception("Column '" + coulmn.ColumnName + - "' requires datatype of DateTime, but value contains '" + - val.GetType() + "' !"); - } - - /* Data structure - 1 byte day - 1 byte month - 4 byte year (int) - 1 byte hour - 1 byte minute - 1 byte second - */ - - DateTime d = (DateTime) val; - byte[] dateBytes = new byte[13]; - // day - dateBytes[0] = (byte) d.Day; - // month - dateBytes[1] = (byte) d.Month; - // year - Array.Copy(ldb_Utils.IntToByte(d.Year), 0, dateBytes, 2, 4); - // hour - dateBytes[6] = (byte) d.Hour; - // minute - dateBytes[7] = (byte) d.Minute; - // second - dateBytes[8] = (byte) d.Second; - - return dateBytes; - } - else if (coulmn.DataType == LDB_DataType.Long) - { - if (val.GetType() != typeof (long)) - { - throw new Exception("Column '" + coulmn.ColumnName + - "' requires datatype of Long, but value contains '" + val.GetType() + - "' !"); - } - - return ldb_Utils.LongToByte((long) val); - } - else if (coulmn.DataType == LDB_DataType.Int) - { - if (val.GetType() != typeof (int)) - { - throw new Exception("Column '" + coulmn.ColumnName + - "' requires datatype of Int, but value contains '" + val.GetType() + - "' !"); - } - - return ldb_Utils.IntToByte((int) val); - } - else if (coulmn.DataType == LDB_DataType.String) - { - if (val.GetType() != typeof (string)) - { - throw new Exception("Column '" + coulmn.ColumnName + - "' requires datatype of String, but value contains '" + val.GetType() + - "' !"); - } - - return Encoding.UTF8.GetBytes(val.ToString()); - } - else - { - throw new Exception("Invalid column data type, never must reach here !"); - } - } - - /// - /// Converts internal data to .NET data type. - /// - /// Column what data it is. - /// Internal data value. - /// - internal static object ConvertFromInternalData(LDB_DataColumn coulmn, byte[] val) - { - if (coulmn.DataType == LDB_DataType.Bool) - { - return Convert.ToBoolean(val[0]); - } - else if (coulmn.DataType == LDB_DataType.DateTime) - { - /* Data structure - 1 byte day - 1 byte month - 4 byte year (int) - 1 byte hour - 1 byte minute - 1 byte second - */ - - byte[] dateBytes = new byte[13]; - // day - int day = val[0]; - // month - int month = val[1]; - // year - int year = ldb_Utils.ByteToInt(val, 2); - // hour - int hour = val[6]; - // minute - int minute = val[7]; - // second - int second = val[8]; - - return new DateTime(year, month, day, hour, minute, second); - } - else if (coulmn.DataType == LDB_DataType.Long) - { - return ldb_Utils.ByteToLong(val, 0); - } - else if (coulmn.DataType == LDB_DataType.Int) - { - return ldb_Utils.ByteToInt(val, 0); - } - else if (coulmn.DataType == LDB_DataType.String) - { - return Encoding.UTF8.GetString(val); - } - else - { - throw new Exception("Invalid column data type, never must reach here !"); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/_DataPage.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/_DataPage.cs deleted file mode 100644 index 8c27f8843..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/_DataPage.cs +++ /dev/null @@ -1,416 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Data.lsDB -{ - #region usings - - using System; - - #endregion - - /// - /// Data page. - /// - internal class DataPage - { - #region Members - - private readonly byte[] m_Data; - private readonly int m_DataAreaSize = 1000; - private readonly long m_OwnerID; - private readonly DbFile m_pOwnerDB; - private readonly long m_StartPointer; - private readonly bool m_Used; - private long m_NextDataPagePointer; - private long m_OwnerDataPagePointer; - private int m_StoredDataLength; - - #endregion - - #region Properties - - /// - /// Gets data page size on disk in bytes. - /// - public int DataPageSize - { - get { return 33 + DataAreaSize; } - } - - /// - /// Gets this data page address (offset in database file). - /// - public long Pointer - { - get { return m_StartPointer; } - } - - /// - /// Gets or sets if data page used or free space. - /// - public bool Used - { - get { return m_Used; } - - set - { - m_pOwnerDB.SetFilePosition(m_StartPointer + 2); - m_pOwnerDB.WriteToFile(new[] {Convert.ToByte(value)}, 0, 1); - } - } - - /// - /// Gets owner object id what owns this data page. - /// - public long OwnerID - { - get { return m_OwnerID; } - } - - /// - /// Gets or sets owner data page pointer. - /// Returns 0 if this is first data page of multiple data pages or only data page. - /// - public long OwnerDataPagePointer - { - get { return m_OwnerDataPagePointer; } - - set - { - // owner data page pointer - m_pOwnerDB.SetFilePosition(m_StartPointer + 11); - m_pOwnerDB.WriteToFile(ldb_Utils.LongToByte(value), 0, 8); - - m_OwnerDataPagePointer = value; - } - } - - /// - /// Gets or sets pointer to data page what continues this data page. - /// Returns 0 if data page has enough room for data and there isn't continuing data page. - /// - public long NextDataPagePointer - { - get { return m_NextDataPagePointer; } - - set - { - // continuing data page pointer - m_pOwnerDB.SetFilePosition(m_StartPointer + 19); - m_pOwnerDB.WriteToFile(ldb_Utils.LongToByte(value), 0, 8); - - m_NextDataPagePointer = value; - } - } - - /* - /// - /// Gets or sets data that data page holds. Maximum size is this.DataAreaSize. Returns null if no data stored. - /// - public byte[] Data - { - get{ - byte[] data = new byte[m_StoredDataLength]; - m_pDbFileStream.Position = m_StartPointer + 33; - m_pDbFileStream.Read(data,0,data.Length); - - return data; - } - - set{ - if(value.Length > this.DataAreaSize){ - throw new Exception("Data page can't store more than " + this.DataAreaSize + " bytes, use mutliple data pages !"); - } - - // Set stored data length - m_pDbFileStream.Position = m_StartPointer + 27; - byte[] dataLength = ldb_Utils.IntToByte(value.Length); - m_pDbFileStream.Write(dataLength,0,dataLength.Length); - - // Store data - m_pDbFileStream.Position = m_StartPointer + 33; - m_pDbFileStream.Write(value,0,value.Length); - - m_StoredDataLength = value.Length; - } - } -*/ - - /// - /// Gets how many data data page can store. - /// - public int DataAreaSize - { - get { return m_DataAreaSize; } - } - - /// - /// Gets stored data length. - /// - public int StoredDataLength - { - get { return m_StoredDataLength; } - } - - /// - /// Gets how much free data space is availabe in data page. - /// - public long SpaceAvailable - { - get { return DataAreaSize - m_StoredDataLength; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Specifies how much data data page can store. - /// Owner DB file.. - /// Data page start offset pointer. - public DataPage(int dataPageDataAreaSize, DbFile ownerDB, long startOffset) - { - /* DataPage structure - 2 bytes - CRLF - 1 byte - used (f - unused,u - used) - 8 byte - owner object id - 8 bytes - owner data page pointer - 8 bytes - continuing data page pointer - 4 bytes - stored data length in data area - 2 bytes - CRLF - 1000 bytes - data area - */ - - m_DataAreaSize = dataPageDataAreaSize; - m_pOwnerDB = ownerDB; - m_StartPointer = startOffset; - - byte[] dataPageInfo = new byte[33]; - ownerDB.SetFilePosition(startOffset); - ownerDB.ReadFromFile(dataPageInfo, 0, dataPageInfo.Length); - m_Data = new byte[dataPageDataAreaSize]; - ownerDB.ReadFromFile(m_Data, 0, dataPageDataAreaSize); - - // CRLF - if (dataPageInfo[0] != (byte) '\r') - { - throw new Exception( - "Not right data page startOffset, or invalid data page is expected but is '" + - (int) dataPageInfo[0] + "' !"); - } - if (dataPageInfo[1] != (byte) '\n') - { - throw new Exception( - "Not right data page startOffset, or invalid data page is expected but is '" + - (int) dataPageInfo[1] + "' !"); - } - - // used - if (dataPageInfo[2] == (byte) 'u') - { - m_Used = true; - } - else - { - m_Used = false; - } - - // owner object id - m_OwnerID = ldb_Utils.ByteToLong(dataPageInfo, 3); - - // owner data page pointer - m_OwnerDataPagePointer = ldb_Utils.ByteToLong(dataPageInfo, 11); - - // continuing data page pointer - m_NextDataPagePointer = ldb_Utils.ByteToLong(dataPageInfo, 19); - - // stored data length in data area - m_StoredDataLength = ldb_Utils.ByteToInt(dataPageInfo, 27); - - // CRLF - if (dataPageInfo[31] != (byte) '\r') - { - throw new Exception( - "Not right data page startOffset, or invalid data page is expected but is '" + - (int) dataPageInfo[31] + "' !"); - } - if (dataPageInfo[32] != (byte) '\n') - { - throw new Exception( - "Not right data page startOffset, or invalid data page is expected but is '" + - (int) dataPageInfo[32] + "' !"); - } - } - - #endregion - - #region Methods - - /// - /// Creates new data page structure. - /// - /// Specifies how much data can data page store. - /// Specifies if data page is used or free space. If this value is false, all toher parameters aren't stored. - /// Owner data object ID. - /// This data page owner data page pointer. This value can be 0, if no owner. - /// Data page pointer, what continues this data page. This value can be 0 if, data page won't spread to multiple data pages. - /// Data what data page stores. Maximum length is dataPageDataAreaSize. - /// - public static byte[] CreateDataPage(int dataPageDataAreaSize, - bool used, - long ownerID, - long ownerDataPagePointer, - long nextDataPagePointer, - byte[] data) - { - /* DataPage structure - 2 bytes - CRLF - 1 byte - used (f - unused,u - used) - 8 byte - owner object id - 8 bytes - owner data page pointer - 8 bytes - continuing data page pointer - 4 bytes - stored data length in data area - 2 bytes - CRLF - dataPageDataAreaSize bytes - data area - */ - - if (data.Length > dataPageDataAreaSize) - { - throw new Exception("Data page can store only " + dataPageDataAreaSize + - " bytes, data conatins '" + data.Length + "' bytes !"); - } - - byte[] dataPage = new byte[dataPageDataAreaSize + 33]; - // CRLF - dataPage[0] = (byte) '\r'; - dataPage[1] = (byte) '\n'; - if (used) - { - // used - dataPage[2] = (byte) 'u'; - // owner object id - Array.Copy(ldb_Utils.LongToByte(ownerID), 0, dataPage, 3, 8); - // owner data page pointer - Array.Copy(ldb_Utils.LongToByte(ownerDataPagePointer), 0, dataPage, 11, 8); - // continuing data page pointer - Array.Copy(ldb_Utils.LongToByte(nextDataPagePointer), 0, dataPage, 19, 8); - // stored data length in data area - Array.Copy(ldb_Utils.IntToByte(data.Length), 0, dataPage, 27, 4); - // CRLF - dataPage[31] = (byte) '\r'; - dataPage[32] = (byte) '\n'; - // data area - Array.Copy(data, 0, dataPage, 33, data.Length); - } - else - { - // used - dataPage[2] = (byte) 'f'; - // CRLF - dataPage[31] = (byte) '\r'; - dataPage[32] = (byte) '\n'; - } - - return dataPage; - } - - /// - /// Reads specified amount data to buffer. - /// - /// Buffer where to store data. - /// Start index in buffer where data storing begins. Start index is included. - /// Number of bytes to read. - /// Zero based offset of data area. - /// - public void ReadData(byte[] buffer, int startIndexInBuffer, int length, int startOffset) - { - if (startOffset < 0) - { - throw new Exception("startOffset can't negative value !"); - } - if ((length + startOffset) > DataAreaSize) - { - throw new Exception("startOffset and length are out of range data page data area !"); - } - if ((length + startOffset) > m_StoredDataLength) - { - throw new Exception( - "There isn't so much data stored in data page as requested ! Stored data length = " + - m_StoredDataLength + "; start offset = " + startOffset + "; length wanted = " + length); - } - - Array.Copy(m_Data, startOffset, buffer, startIndexInBuffer, length); - } - - /// - /// Reads data page data. Offset byte is included. - /// - /// Zero based offset of data area. - /// Specifies how much data to read. - /// - public byte[] ReadData(int startOffset, int length) - { - if (startOffset < 0) - { - throw new Exception("startOffset can't negative value !"); - } - if ((length + startOffset) > DataAreaSize) - { - throw new Exception("startOffset and length are out of range data page data area !"); - } - if ((length + startOffset) > m_StoredDataLength) - { - throw new Exception( - "There isn't so much data stored in data page as requested ! Stored data length = " + - m_StoredDataLength + "; start offset = " + startOffset + "; length wanted = " + length); - } - - byte[] data = new byte[length]; - Array.Copy(m_Data, startOffset, data, 0, length); - - return data; - } - - /// - /// Writed data to data page. - /// - /// Data to write. - public void WriteData(byte[] data) - { - if (data.Length > DataAreaSize) - { - throw new Exception("Data page can't store more than " + DataAreaSize + - " bytes, use mutliple data pages !"); - } - - // Set stored data length - m_pOwnerDB.SetFilePosition(m_StartPointer + 27); - m_pOwnerDB.WriteToFile(ldb_Utils.IntToByte(data.Length), 0, 4); - - // Store data - m_pOwnerDB.SetFilePosition(m_StartPointer + 33); - m_pOwnerDB.WriteToFile(data, 0, data.Length); - - m_StoredDataLength = data.Length; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/_ldb_Utils.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/_ldb_Utils.cs deleted file mode 100644 index eb324d8b8..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/_ldb_Utils.cs +++ /dev/null @@ -1,103 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Data.lsDB -{ - /// - ///LDB utility methods. - /// - internal class ldb_Utils - { - #region Methods - - /// - /// Convert long value to byte[8]. - /// - /// Long value. - /// - public static byte[] LongToByte(long val) - { - byte[] retVal = new byte[8]; - retVal[0] = (byte) ((val >> 56) & 0xFF); - retVal[1] = (byte) ((val >> 48) & 0xFF); - retVal[2] = (byte) ((val >> 40) & 0xFF); - retVal[3] = (byte) ((val >> 32) & 0xFF); - retVal[4] = (byte) ((val >> 24) & 0xFF); - retVal[5] = (byte) ((val >> 16) & 0xFF); - retVal[6] = (byte) ((val >> 8) & 0xFF); - retVal[7] = (byte) ((val >> 0) & 0xFF); - - return retVal; - } - - /// - /// Converts 8 bytes to long value. Offset byte is included. - /// - /// Data array. - /// Offset where 8 bytes long value starts. Offset byte is included. - /// - public static long ByteToLong(byte[] array, int offset) - { - long retVal = 0; - retVal |= (long) array[offset + 0] << 56; - retVal |= (long) array[offset + 1] << 48; - retVal |= (long) array[offset + 2] << 40; - retVal |= (long) array[offset + 3] << 32; - retVal |= (long) array[offset + 4] << 24; - retVal |= (long) array[offset + 5] << 16; - retVal |= (long) array[offset + 6] << 8; - retVal |= (long) array[offset + 7] << 0; - - return retVal; - } - - /// - /// Convert int value to byte[4]. - /// - /// Int value. - /// - public static byte[] IntToByte(int val) - { - byte[] retVal = new byte[4]; - retVal[0] = (byte) ((val >> 24) & 0xFF); - retVal[1] = (byte) ((val >> 16) & 0xFF); - retVal[2] = (byte) ((val >> 8) & 0xFF); - retVal[3] = (byte) ((val >> 0) & 0xFF); - - return retVal; - } - - /// - /// Converts 4 bytes to int value. Offset byte is included. - /// - /// Data array. - /// Offset where 4 bytes int value starts. Offset byte is included. - /// - public static int ByteToInt(byte[] array, int offset) - { - int retVal = 0; - retVal |= array[offset + 0] << 24; - retVal |= array[offset + 1] << 16; - retVal |= array[offset + 2] << 8; - retVal |= array[offset + 3] << 0; - - return retVal; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/lsDB_FixedLengthRecord.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/lsDB_FixedLengthRecord.cs deleted file mode 100644 index 4410021a2..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/lsDB_FixedLengthRecord.cs +++ /dev/null @@ -1,235 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Data.lsDB -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// lsDB_FixedLengthTable table record. - /// - public class lsDB_FixedLengthRecord - { - #region Members - - private readonly int m_ColumnCount = -1; - private readonly int[] m_ColumnDataSizes; - private readonly int[] m_ColumnDataStartOffsets; - private readonly LDB_DataType[] m_ColumnDataTypes; - private long m_Pointer = -1; - private lsDB_FixedLengthTable m_pOwnerDb; - private byte[] m_RowData; - - #endregion - - #region Properties - - /// - /// Gets or sets specified data column value. - /// - /// Zero based column index. - /// - public object this[int columnIndex] - { - /* Fixed record structure: - 1 byte - specified is row is used or free space - u - used - f - free space - x bytes - columns data - 2 bytes - CRLF - */ - - get - { - if (columnIndex < 0) - { - throw new Exception("The columnIndex can't be negative value !"); - } - if (columnIndex > m_ColumnCount) - { - throw new Exception("The columnIndex out of columns count !"); - } - - return ConvertFromInternalData(m_ColumnDataTypes[columnIndex], - m_RowData, - m_ColumnDataStartOffsets[columnIndex], - m_ColumnDataSizes[columnIndex]); - } - - set - { - if (columnIndex < 0) - { - throw new Exception("The columnIndex can't be negative value !"); - } - if (columnIndex > m_ColumnCount) - { - throw new Exception("The columnIndex out of columns count !"); - } - - byte[] val = LDB_Record.ConvertToInternalData(m_pOwnerDb.Columns[columnIndex], value); - // Check that value won't exceed maximum cloumn allowed size - if (val.Length > m_ColumnDataSizes[columnIndex]) - { - throw new Exception("Value exceeds maximum allowed value for column '" + - m_pOwnerDb.Columns[columnIndex].ColumnName + "' !"); - } - - // TODO: String, string must be char(0) terminated and padded to column length - if (m_ColumnDataTypes[columnIndex] == LDB_DataType.String) - { - throw new Exception("TODO: String not implemented !"); - } - - // Update value in database file - m_pOwnerDb.WriteToFile(m_Pointer + m_ColumnDataStartOffsets[columnIndex], val, 0, val.Length); - - // Update value in buffer - Array.Copy(val, 0, m_RowData, m_ColumnDataStartOffsets[columnIndex], val.Length); - } - } - - /// - /// Gets row pointer. - /// - internal long Pointer - { - get { return m_Pointer; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Table that owns this row. - /// Row start offset in data base file. - /// Row data. - internal lsDB_FixedLengthRecord(lsDB_FixedLengthTable ownerDb, long pointer, byte[] rowData) - { - m_pOwnerDb = ownerDb; - m_Pointer = pointer; - m_RowData = rowData; - - m_ColumnCount = ownerDb.Columns.Count; - - m_ColumnDataTypes = new LDB_DataType[m_ColumnCount]; - m_ColumnDataSizes = new int[m_ColumnCount]; - for (int i = 0; i < m_ColumnDataSizes.Length; i++) - { - m_ColumnDataTypes[i] = m_pOwnerDb.Columns[i].DataType; - m_ColumnDataSizes[i] = m_pOwnerDb.Columns[i].ColumnSize; - } - - m_ColumnDataStartOffsets = new int[m_ColumnCount]; - int columnDataStartOffset = 1; - for (int i = 0; i < m_ColumnDataStartOffsets.Length; i++) - { - m_ColumnDataStartOffsets[i] = columnDataStartOffset; - columnDataStartOffset += m_pOwnerDb.Columns[i].ColumnSize; - } - } - - #endregion - - #region Methods - - /// - /// Converts internal data to .NET data type. - /// - /// Data type. - /// Data buffer. - /// Offset in data buffer where to start reading data. - /// Lenght of data to read from data buffer. - /// - public static object ConvertFromInternalData(LDB_DataType dataType, byte[] val, int offset, int length) - { - if (dataType == LDB_DataType.Bool) - { - return Convert.ToBoolean(val[offset + 0]); - } - else if (dataType == LDB_DataType.DateTime) - { - /* Data structure - 1 byte day - 1 byte month - 4 byte year (int) - 1 byte hour - 1 byte minute - 1 byte second - */ - - // day - int day = val[offset + 0]; - // month - int month = val[offset + 1]; - // year - int year = ldb_Utils.ByteToInt(val, offset + 2); - // hour - int hour = val[offset + 6]; - // minute - int minute = val[offset + 7]; - // second - int second = val[offset + 8]; - - return new DateTime(year, month, day, hour, minute, second); - } - else if (dataType == LDB_DataType.Long) - { - return ldb_Utils.ByteToLong(val, offset + 0); - } - else if (dataType == LDB_DataType.Int) - { - return ldb_Utils.ByteToInt(val, offset + 0); - } - else if (dataType == LDB_DataType.String) - { - return Encoding.UTF8.GetString(val, offset, length); - } - else - { - throw new Exception("Invalid column data type, never must reach here !"); - } - } - - #endregion - - #region Internal methods - - /// - /// Reuses lsDB_FixedLengthRecord object, - /// - /// Table that owns this row. - /// Row start offset in data base file. - /// Row data. - internal void ReuseRecord(lsDB_FixedLengthTable ownerDb, long pointer, byte[] rowData) - { - m_pOwnerDb = ownerDb; - m_Pointer = pointer; - m_RowData = rowData; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/lsDB_FixedLengthTable.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/lsDB_FixedLengthTable.cs deleted file mode 100644 index bd1551cad..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Data/lsDB_FixedLengthTable.cs +++ /dev/null @@ -1,738 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Data.lsDB -{ - #region usings - - using System; - using System.IO; - using System.Threading; - - #endregion - - /// - /// Table what all columns are with fixed length. - /// - public class lsDB_FixedLengthTable : IDisposable - { - #region Members - - private readonly LDB_DataColumnCollection m_pColumns; - private long m_ColumnsStartOffset = -1; - private string m_DbFileName = ""; - private long m_FileLength; - private long m_FilePosition; - private lsDB_FixedLengthRecord m_pCurrentRecord; - private FileStream m_pDbFile; - private byte[] m_RowDataBuffer; - private int m_RowLength = -1; - private long m_RowsStartOffset = -1; - private bool m_TableLocked; - - #endregion - - #region Properties - - /// - /// Gets if there is active database file. - /// - public bool IsDatabaseOpen - { - get { return m_pDbFile != null; } - } - - /// - /// Gets open database file name. Throws exception if database isn't open. - /// - public string FileName - { - get - { - if (!IsDatabaseOpen) - { - throw new Exception("Database isn't open, please open database first !"); - } - - return m_DbFileName; - } - } - - /// - /// Gets table columns. Throws exception if database isn't open. - /// - public LDB_DataColumnCollection Columns - { - get - { - if (!IsDatabaseOpen) - { - throw new Exception("Database isn't open, please open database first !"); - } - - return m_pColumns; - } - } - - /// - /// Gets current record. Returns null if there isn't current record. - /// - public lsDB_FixedLengthRecord CurrentRecord - { - get { return m_pCurrentRecord; } - } - - /// - /// Gets table is locked. - /// - public bool TableLocked - { - get { return m_TableLocked; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public lsDB_FixedLengthTable() - { - m_pColumns = new LDB_DataColumnCollection(this); - } - - #endregion - - #region Methods - - /// - /// Clean up any resources being used. - /// - public void Dispose() - { - Close(); - } - - /// - /// Opens specified data file. - /// - /// File name. - public void Open(string fileName) - { - Open(fileName, 0); - } - - /// - /// Opens specified data file. - /// - /// File name. - /// If data base file is exclusively locked, then how many seconds to wait file to unlock before raising a error. - public void Open(string fileName, int waitTime) - { - DateTime lockExpireTime = DateTime.Now.AddSeconds(waitTime); - while (true) - { - try - { - m_pDbFile = File.Open(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); - - break; - } - catch (IOException x) - { - if (!File.Exists(fileName)) - { - throw new Exception("Specified database file '" + fileName + "' does not exists !"); - } - - // Make this because to get rid of "The variable 'x' is declared but never used" - string dummy = x.Message; - - Thread.Sleep(15); - - // Lock wait time timed out - if (DateTime.Now > lockExpireTime) - { - throw new Exception("Database file is locked and lock wait time expired !"); - } - } - } - - /* Table structure: - 50 bytes - version - 2 bytes - CRLF - 4 bytes - Free rows count - 2 bytes - CRLF - 100 x 500 bytes - 100 columns info store - 2 bytes - CRLF - ... data pages - */ - - m_DbFileName = fileName; - - // TODO: check if LDB file - - // Read version line (50 bytes + CRLF) - byte[] version = new byte[52]; - ReadFromFile(0, version, 0, version.Length); - - // Read free rows count - byte[] freeRows = new byte[6]; - ReadFromFile(0, freeRows, 0, freeRows.Length); - - long currentColumnOffset = 58; - // Read 100 column lines (500 + CRLF bytes each) - for (int i = 0; i < 100; i++) - { - byte[] columnInfo = new byte[102]; - if (ReadFromFile(currentColumnOffset, columnInfo, 0, columnInfo.Length) != columnInfo.Length) - { - throw new Exception("Invalid columns data area length !"); - } - - if (columnInfo[0] != '\0') - { - m_pColumns.Parse(columnInfo); - } - - currentColumnOffset += 102; - } - - // Header terminator \0 - m_pDbFile.Position++; - - // No we have rows start offset - m_RowsStartOffset = m_pDbFile.Position; - - // Store file length and position - m_FileLength = m_pDbFile.Length; - m_FilePosition = m_pDbFile.Position; - - // Calculate row length - m_RowLength = 1 + 2; - for (int i = 0; i < m_pColumns.Count; i++) - { - m_RowLength += m_pColumns[i].ColumnSize; - } - - m_RowDataBuffer = new byte[m_RowLength]; - } - - /// - /// Closes database file. - /// - public void Close() - { - if (m_pDbFile != null) - { - m_pDbFile.Close(); - m_pDbFile = null; - m_DbFileName = ""; - m_FileLength = 0; - m_FilePosition = 0; - m_RowLength = 0; - m_ColumnsStartOffset = 0; - m_RowsStartOffset = 0; - m_TableLocked = false; - m_RowDataBuffer = null; - m_pCurrentRecord = null; - } - } - - /// - /// Creates new database file. - /// - /// File name. - public void Create(string fileName) - { - m_pDbFile = new FileStream(fileName, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None); - - /* Table structure: - 50 bytes - version - 2 bytes - CRLF - 4 bytes - Free rows count - 2 bytes - CRLF - 100 x 500 bytes - 100 columns info store - 2 bytes - CRLF - ... data pages - */ - - // Version 50 + CRLF bytes - byte[] versionData = new byte[52]; - versionData[0] = (byte) '1'; - versionData[1] = (byte) '.'; - versionData[2] = (byte) '0'; - versionData[50] = (byte) '\r'; - versionData[51] = (byte) '\n'; - m_pDbFile.Write(versionData, 0, versionData.Length); - - // Free rows count - byte[] freeRows = new byte[6]; - freeRows[4] = (byte) '\r'; - freeRows[5] = (byte) '\n'; - m_pDbFile.Write(freeRows, 0, freeRows.Length); - - m_ColumnsStartOffset = m_pDbFile.Position; - - // 100 x 100 + CRLF bytes header lines - for (int i = 0; i < 100; i++) - { - byte[] data = new byte[100]; - m_pDbFile.Write(data, 0, data.Length); - m_pDbFile.Write(new byte[] {(int) '\r', (int) '\n'}, 0, 2); - } - - // Headers terminator char(0) - m_pDbFile.WriteByte((int) '\0'); - - // Rows start pointer - m_RowsStartOffset = m_pDbFile.Position - 1; - - m_DbFileName = fileName; - - // Store file length and position - m_FileLength = m_pDbFile.Length; - m_FilePosition = m_pDbFile.Position; - - // Calculate row length - m_RowLength = 1 + 2; - for (int i = 0; i < m_pColumns.Count; i++) - { - m_RowLength += m_pColumns[i].ColumnSize; - } - - m_RowDataBuffer = new byte[m_RowLength]; - } - - /// - /// Locks table. - /// - /// If table is locked, then how many sconds to wait table to unlock, before teturning error. - public void LockTable(int waitTime) - { - if (!IsDatabaseOpen) - { - throw new Exception("Database isn't open, please open database first !"); - } - // Table is locked already, just skip locking - if (m_TableLocked) - { - return; - } - - DateTime lockExpireTime = DateTime.Now.AddSeconds(waitTime); - while (true) - { - try - { - // We just lock first byte - m_pDbFile.Lock(0, 1); - m_TableLocked = true; - - break; - } - // Catch the IOException generated if the - // specified part of the file is locked. - catch (IOException x) - { - // Make this because to get rid of "The variable 'x' is declared but never used" - string dummy = x.Message; - - Thread.Sleep(15); - - // Lock wait time timed out - if (DateTime.Now > lockExpireTime) - { - throw new Exception("Table is locked and lock wait time expired !"); - } - } - } - } - - /// - /// Unlock table. - /// - public void UnlockTable() - { - if (!IsDatabaseOpen) - { - throw new Exception("Database isn't open, please open database first !"); - } - - if (m_TableLocked) - { - // We just unlock first byte - m_pDbFile.Unlock(0, 1); - } - } - - /// - /// Moves to first record. - /// - public void MoveFirstRecord() - { - m_pCurrentRecord = null; - // NextRecord(); - } - - /// - /// Gets next record. Returns true if end of file reached and there are no more records. - /// - /// Returns true if end of file reached and there are no more records. - public bool NextRecord() - { - if (!IsDatabaseOpen) - { - throw new Exception("Database isn't open, please open database first !"); - } - - //--- Find next record ---------------------------------------------------// - long nextRowStartOffset = 0; - if (m_pCurrentRecord == null) - { - nextRowStartOffset = m_RowsStartOffset; - } - else - { - nextRowStartOffset = m_pCurrentRecord.Pointer + m_RowLength; - } - - while (true) - { - if (m_FileLength > nextRowStartOffset) - { - ReadFromFile(nextRowStartOffset, m_RowDataBuffer, 0, m_RowLength); - - // We want used row - if (m_RowDataBuffer[0] == 'u') - { - if (m_pCurrentRecord == null) - { - m_pCurrentRecord = new lsDB_FixedLengthRecord(this, - nextRowStartOffset, - m_RowDataBuffer); - } - else - { - m_pCurrentRecord.ReuseRecord(this, nextRowStartOffset, m_RowDataBuffer); - } - break; - } - } - else - { - return true; - } - - nextRowStartOffset += m_RowLength; - } - //-------------------------------------------------------------------------// - - return false; - } - - /// - /// Appends new record to table. - /// - public void AppendRecord(object[] values) - { - if (!IsDatabaseOpen) - { - throw new Exception("Database isn't open, please open database first !"); - } - if (m_pColumns.Count != values.Length) - { - throw new Exception("Each column must have corresponding value !"); - } - - bool unlock = true; - // Table is already locked, don't lock it - if (TableLocked) - { - unlock = false; - } - else - { - LockTable(15); - } - - /* Fixed record structure: - 1 byte - specified is row is used or free space - u - used - f - free space - x bytes - columns data - 2 bytes - CRLF - */ - - int rowLength = 1 + 2; - for (int i = 0; i < m_pColumns.Count; i++) - { - rowLength += m_pColumns[i].ColumnSize; - } - - int position = 1; - byte[] record = new byte[rowLength]; - record[0] = (int) 'u'; - record[rowLength - 2] = (int) '\r'; - record[rowLength - 1] = (int) '\n'; - for (int i = 0; i < values.Length; i++) - { - byte[] columnData = LDB_Record.ConvertToInternalData(m_pColumns[i], values[i]); - // Check that column won't exceed maximum length. - if (columnData.Length > m_pColumns[i].ColumnSize) - { - throw new Exception("Column '" + m_pColumns[i] + "' exceeds maximum value length !"); - } - - Array.Copy(columnData, 0, record, position, columnData.Length); - position += columnData.Length; - } - - // Find free row - byte[] freeRowsBuffer = new byte[4]; - ReadFromFile(52, freeRowsBuffer, 0, freeRowsBuffer.Length); - int freeRows = ldb_Utils.ByteToInt(freeRowsBuffer, 0); - // There are plenty free rows, find first - - if (freeRows > 100) - { - //--- Find free record ---------------------------------------------------// - long nextRowStartOffset = m_RowsStartOffset; - long rowOffset = 0; - - byte[] rowData = new byte[m_RowLength]; - while (true) - { - ReadFromFile(nextRowStartOffset, rowData, 0, m_RowLength); - - // We want used row - if (rowData[0] == 'f') - { - rowOffset = nextRowStartOffset; - break; - } - - nextRowStartOffset += m_RowLength; - } - //-------------------------------------------------------------------------// - - // Write new record to file - WriteToFile(rowOffset, record, 0, record.Length); - - // Update free rows count - WriteToFile(52, ldb_Utils.IntToByte(freeRows - 1), 0, 4); - } - // There are few empty rows, just append it - else - { - AppendToFile(record, 0, record.Length); - } - - if (unlock) - { - UnlockTable(); - } - } - - /// - /// Deletes current record. - /// - public void DeleteCurrentRecord() - { - if (!IsDatabaseOpen) - { - throw new Exception("Database isn't open, please open database first !"); - } - if (m_pCurrentRecord == null) - { - throw new Exception("There is no current record !"); - } - - bool unlock = true; - // Table is already locked, don't lock it - if (TableLocked) - { - unlock = false; - } - else - { - LockTable(15); - } - - byte[] data = new byte[m_RowLength]; - data[0] = (byte) 'f'; - data[m_RowLength - 2] = (byte) '\r'; - data[m_RowLength - 1] = (byte) '\n'; - WriteToFile(m_pCurrentRecord.Pointer, data, 0, data.Length); - - // Update free rows count - byte[] freeRowsBuffer = new byte[4]; - ReadFromFile(52, freeRowsBuffer, 0, freeRowsBuffer.Length); - int freeRows = ldb_Utils.ByteToInt(freeRowsBuffer, 0); - WriteToFile(52, ldb_Utils.IntToByte(freeRows + 1), 0, 4); - - if (unlock) - { - UnlockTable(); - } - - // Activate next record **** Change it ??? - NextRecord(); - } - - #endregion - - #region Utility methods - - /// - /// Sets file position. - /// - /// Position in file. - private void SetFilePosition(long position) - { - if (m_FilePosition != position) - { - m_pDbFile.Position = position; - m_FilePosition = position; - } - } - - #endregion - - #region Internal methods - - /// - /// Adds column to db file. - /// - /// - internal void AddColumn(LDB_DataColumn column) - { - if (column.ColumnSize < 1) - { - throw new Exception("Invalid column size '" + column.ColumnSize + "' for column '" + - column.ColumnName + "' !"); - } - - // Find free column data area - - long currentColumnOffset = m_ColumnsStartOffset; - long freeColumnPosition = -1; - // Loop all columns data areas, see it there any free left - for (int i = 0; i < 100; i++) - { - byte[] columnInfo = new byte[102]; - if (ReadFromFile(currentColumnOffset, columnInfo, 0, columnInfo.Length) != columnInfo.Length) - { - throw new Exception("Invalid columns data area length !"); - } - - // We found unused column data area - if (columnInfo[0] == '\0') - { - freeColumnPosition = currentColumnOffset - 102; - break; - } - - currentColumnOffset += 102; - } - - if (freeColumnPosition != -1) - { - // TODO: If there is data ??? - - // Store column - byte[] columnData = column.ToColumnInfo(); - WriteToFile(currentColumnOffset, columnData, 0, columnData.Length); - } - else - { - throw new Exception("Couldn't find free column space ! "); - } - } - - /// - /// Removes specified column from database file. - /// - /// - internal void RemoveColumn(LDB_DataColumn column) - { - throw new Exception("TODO:"); - } - - /// - /// Reads data from file. - /// - /// Offset in database file from where to start reading data. - /// Buffer where to store readed data. - /// Offset in array to where to start storing readed data. - /// Number of bytes to read. - /// - internal int ReadFromFile(long readOffset, byte[] data, int offset, int count) - { - SetFilePosition(readOffset); - - int readed = m_pDbFile.Read(data, offset, count); - m_FilePosition += readed; - - return readed; - } - - /// - /// Writes data to file. - /// - /// Offset in database file from where to start writing data. - /// Data to write. - /// Offset in array from where to start writing data. - /// Number of bytes to write. - /// - internal void WriteToFile(long writeOffset, byte[] data, int offset, int count) - { - SetFilePosition(writeOffset); - - m_pDbFile.Write(data, offset, count); - m_FilePosition += count; - } - - /// - /// Appends specified data at the end of file. - /// - /// Data to write. - /// Offset in array from where to start writing data. - /// Number of bytes to write. - internal void AppendToFile(byte[] data, int offset, int count) - { - m_pDbFile.Position = m_pDbFile.Length; - - m_pDbFile.Write(data, offset, count); - - m_FileLength = m_pDbFile.Length; - m_FilePosition = m_pDbFile.Position; - } - - /// - /// Gets current position in file. - /// - /// - internal long GetFilePosition() - { - return m_FilePosition; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/EncodingTools.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/EncodingTools.cs deleted file mode 100644 index 622a5cddb..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/EncodingTools.cs +++ /dev/null @@ -1,661 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.Text; -using System.Runtime.InteropServices; -using System.IO; - -namespace ASC.Mail.Net -{ - public static class EncodingTools - { - // this only contains ascii, default windows code page and unicode - public static int[] PreferedEncodingsForStream; - - // this contains all codepages, sorted by preference and byte usage - public static int[] PreferedEncodings; - - // this contains all codepages, sorted by preference and byte usage - public static int[] AllEncodings; - - - public static Dictionary encoding_aliases; - - - - /// - /// Static constructor that fills the default preferred codepages - /// - static EncodingTools() - { - - List streamEcodings = new List(); - List allEncodings = new List(); - List mimeEcodings = new List(); - - // asscii - most simple so put it in first place... - streamEcodings.Add(Encoding.ASCII.CodePage); - mimeEcodings.Add(Encoding.ASCII.CodePage); - allEncodings.Add(Encoding.ASCII.CodePage); - - - // add default 2nd for all encodings - allEncodings.Add(Encoding.Default.CodePage); - // default is single byte? - if (Encoding.Default.IsSingleByte) - { - // put it in second place - streamEcodings.Add(Encoding.Default.CodePage); - mimeEcodings.Add(Encoding.Default.CodePage); - } - - - - // prefer JIS over JIS-SHIFT (JIS is detected better than JIS-SHIFT) - // this one does include cyrilic (strange but true) - allEncodings.Add(50220); - mimeEcodings.Add(50220); - - - // always allow unicode flavours for streams (they all have a preamble) - streamEcodings.Add(Encoding.Unicode.CodePage); - foreach (EncodingInfo enc in Encoding.GetEncodings()) - { - if (!streamEcodings.Contains(enc.CodePage)) - { - Encoding encoding = Encoding.GetEncoding(enc.CodePage); - if (encoding.GetPreamble().Length > 0) - streamEcodings.Add(enc.CodePage); - } - } - - - // stream is done here - PreferedEncodingsForStream = streamEcodings.ToArray(); - - - // all singlebyte encodings - foreach (EncodingInfo enc in Encoding.GetEncodings()) - { - - - if (!enc.GetEncoding().IsSingleByte) - continue; - - if (!allEncodings.Contains(enc.CodePage)) - allEncodings.Add(enc.CodePage); - - // only add iso and IBM encodings to mime encodings - if (enc.CodePage <= 1258) - { - mimeEcodings.Add(enc.CodePage); - } - } - - // add the rest (multibyte) - foreach (EncodingInfo enc in Encoding.GetEncodings()) - { - if (!enc.GetEncoding().IsSingleByte) - { - if (!allEncodings.Contains(enc.CodePage)) - allEncodings.Add(enc.CodePage); - - // only add iso and IBM encodings to mime encodings - if (enc.CodePage <= 1258) - { - mimeEcodings.Add(enc.CodePage); - } - } - } - - // add unicodes - mimeEcodings.Add(Encoding.Unicode.CodePage); - - - PreferedEncodings = mimeEcodings.ToArray(); - AllEncodings = allEncodings.ToArray(); - - #region Fill in codepage aliases map - encoding_aliases = new Dictionary(); - - string[] known_aliases = new string[] - { - // first name is an alias. The second one is a registered in IANA name - // All aliases (first column) must be in lower case - // Please don't append aliases to aliases. This will lead to exception in GetEncodingByCodepageName() - - // It was investigated that only Java aliases are not present in Encoding class internal lists. - // All windows aliases may be found here: http://www.lingoes.net/en/translator/codepage.htm - // Java aliases have been obtained from here: http://docs.oracle.com/javase/1.4.2/docs/guide/intl/encoding.doc.html - - "cp1250", "windows-1250", // Windows Eastern European - "cp-1250", "windows-1250", - "cp1251", "windows-1251", // Windows Cyrillic - "cp-1251", "windows-1251", - "cp1252", "windows-1252", // Windows Latin-1 - "cp-1252", "windows-1252", - "cp1253", "windows-1253", // Windows Greek - "cp-1253", "windows-1253", - "cp1254", "windows-1254", // Windows Turkish - "cp-1254", "windows-1254", - "cp1257", "windows-1257", // Windows Baltic - "cp-1257", "windows-1257", - "iso8859_1", "iso-8859-1", // ISO 8859-1, Latin Alphabet No. 1 - "iso8859_2", "iso-8859-2", // Latin Alphabet No. 2 - "iso8859_4", "iso-8859-4", // Latin Alphabet No. 4 - "iso8859_5", "iso-8859-5", // Latin/Cyrillic Alphabet - "iso8859_7", "iso-8859-7", // Latin/Greek Alphabet - "iso8859_9", "iso-8859-9", // Latin Alphabet No. 9 - "iso8859_13", "iso-8859-13", // Latin Alphabet No. 13 - "iso8859_15", "iso-8859-15", // Latin Alphabet No. 15 - "koi8_r", "koi8-r", // KOI8-R, Russian - "utf8", "utf-8", // Eight-bit UCS Transformation Format - "utf16", "utf-16", // Sixteen-bit UCS Transformation Format, byte order identified by an optional byte-order mark - "unicodebigunmarked", "utf-16be", // Sixteen-bit Unicode Transformation Format, big-endian byte order - "unicodelittleunmarked", "utf-16le", // Sixteen-bit Unicode Transformation Format, little-endian byte order - - "cp1255", "windows-1255", // Windows Hebrew - "cp-1255", "windows-1255", - "cp1256", "windows-1256", // Windows Arabic - "cp-1256", "windows-1256", - "cp1258", "windows-1258", // Windows Vietnamese - "cp-1258", "windows-1258", - "iso8859_3", "iso-8859-3", // Latin Alphabet No. 3 - "iso8859_6", "iso-8859-6", // Latin/Arabic Alphabet - "iso8859_8", "iso-8859-8", // Latin/Hebrew Alphabet - "ms932", "shift_jis", // Windows Japanese - "windows-31j", "shift_jis", // Windows Japanese - "euc_jp", "euc-jp", // JISX 0201, 0208 and 0212, EUC encoding Japanese - "euc_jp_linux", "x-euc-jp-linux", // JISX 0201, 0208 , EUC encoding Japanese - "iso2022jp", "iso-2022-jp", // JIS X 0201, 0208, in ISO 2022 form, Japanese - "ms936", "x-mswin-936", // Windows Simplified Chinese - "euc_cn", "x-euc-cn", // GB2312, EUC encoding, Simplified Chinese - "iscii91", "iscii91", // Windows Japanese - "ms949", "x-windows-949", // Windows Korean - "iso2022kr", "iso-2022-kr", // ISO 2022 KR, Korean - "ms950", "x-windows-950", // Windows Traditional Chinese - "ms950_hkscs", "x-ms950-hkscs", // Windows Traditional Chinese with Hong Kong extensions - "euc-tw", "x-euc-tw", // CNS11643 (Plane 1-3), EUC encoding, Traditional Chinese - "tis620", "tis-620", // TIS620, Thai - - }; - - for (int i = 0; i < known_aliases.Length; i += 2) - { - encoding_aliases[known_aliases[i]] = known_aliases[i + 1]; - } - - #endregion - } - - - /// - /// Checks if specified string data is acii data. - /// - /// - /// - public static bool IsAscii(string data) - { - // assume empty string to be ascii - if ((data == null) || (data.Length == 0)) - return true; - foreach (char c in data) - { - if ((int)c > 127) - { - return false; - } - } - - return true; - } - - /// - /// Gets the best Encoding for usage in mime encodings - /// - /// text to detect - /// the suggested encoding - public static Encoding GetMostEfficientEncoding(string input) - { - return GetMostEfficientEncoding(input, PreferedEncodings); - } - - /// - /// Gets the best ISO Encoding for usage in a stream - /// - /// text to detect - /// the suggested encoding - public static Encoding GetMostEfficientEncodingForStream(string input) - { - return GetMostEfficientEncoding(input, PreferedEncodingsForStream); - } - - /// - /// Gets the best fitting encoding from a list of possible encodings - /// - /// text to detect - /// an array of codepages - /// the suggested encoding - public static Encoding GetMostEfficientEncoding(string input, int[] preferedEncodings) - { - Encoding enc = DetectOutgoingEncoding(input, preferedEncodings, true); - // unicode.. hmmm... check for smallest encoding - if (enc.CodePage == Encoding.Unicode.CodePage) - { - int byteCount = Encoding.UTF7.GetByteCount(input); - enc = Encoding.UTF7; - int bestByteCount = byteCount; - - // utf8 smaller? - byteCount = Encoding.UTF8.GetByteCount(input); - if (byteCount < bestByteCount) - { - enc = Encoding.UTF8; - bestByteCount = byteCount; - } - - // unicode smaller? - byteCount = Encoding.Unicode.GetByteCount(input); - if (byteCount < bestByteCount) - { - enc = Encoding.Unicode; - bestByteCount = byteCount; - } - } - else - { - - } - return enc; - } - - public static Encoding DetectOutgoingEncoding(string input) - { - return DetectOutgoingEncoding(input, PreferedEncodings, true); - } - - public static Encoding DetectOutgoingStreamEncoding(string input) - { - return DetectOutgoingEncoding(input, PreferedEncodingsForStream, true); - } - - public static Encoding[] DetectOutgoingEncodings(string input) - { - return DetectOutgoingEncodings(input, PreferedEncodings, true); - } - - public static Encoding[] DetectOutgoingStreamEncodings(string input) - { - return DetectOutgoingEncodings(input, PreferedEncodingsForStream, true); - } - - private static Encoding DetectOutgoingEncoding(string input, int[] preferedEncodings, bool preserveOrder) - { - - if (input == null) - throw new ArgumentNullException("input"); - - // empty strings can always be encoded as ASCII - if (input.Length == 0) - return Encoding.ASCII; - - Encoding result = Encoding.ASCII; - - // get the IMultiLanguage3 interface - MultiLanguage.IMultiLanguage3 multilang3 = new MultiLanguage.CMultiLanguageClass(); - if (multilang3 == null) - throw new System.Runtime.InteropServices.COMException("Failed to get IMultilang3"); - try - { - int[] resultCodePages = new int[preferedEncodings != null ? preferedEncodings.Length : Encoding.GetEncodings().Length]; - uint detectedCodepages = (uint)resultCodePages.Length; - ushort specialChar = (ushort)'?'; - - - // get unmanaged arrays - IntPtr pPrefEncs = preferedEncodings == null ? IntPtr.Zero : Marshal.AllocCoTaskMem(sizeof(uint) * preferedEncodings.Length); - IntPtr pDetectedEncs = Marshal.AllocCoTaskMem(sizeof(uint) * resultCodePages.Length); - - try - { - if (preferedEncodings != null) - Marshal.Copy(preferedEncodings, 0, pPrefEncs, preferedEncodings.Length); - - Marshal.Copy(resultCodePages, 0, pDetectedEncs, resultCodePages.Length); - - MultiLanguage.MLCPF options = MultiLanguage.MLCPF.MLDETECTF_VALID_NLS; - if (preserveOrder) - options |= MultiLanguage.MLCPF.MLDETECTF_PRESERVE_ORDER; - - if (preferedEncodings != null) - options |= MultiLanguage.MLCPF.MLDETECTF_PREFERRED_ONLY; - - multilang3.DetectOutboundCodePage(options, - input, (uint)input.Length, - pPrefEncs, (uint)(preferedEncodings == null ? 0 : preferedEncodings.Length), - - pDetectedEncs, ref detectedCodepages, - ref specialChar); - - // get result - if (detectedCodepages > 0) - { - int[] theResult = new int[detectedCodepages]; - Marshal.Copy(pDetectedEncs, theResult, 0, theResult.Length); - result = Encoding.GetEncoding(theResult[0]); - } - - } - finally - { - if (pPrefEncs != IntPtr.Zero) - Marshal.FreeCoTaskMem(pPrefEncs); - Marshal.FreeCoTaskMem(pDetectedEncs); - } - } - finally - { - Marshal.FinalReleaseComObject(multilang3); - } - return result; - } - - public static Encoding[] DetectOutgoingEncodings(string input, int[] preferedEncodings, bool preserveOrder) - { - - if (input == null) - throw new ArgumentNullException("input"); - - // empty strings can always be encoded as ASCII - if (input.Length == 0) - return new Encoding[] { Encoding.ASCII }; - - List result = new List(); - - // get the IMultiLanguage3 interface - MultiLanguage.IMultiLanguage3 multilang3 = new MultiLanguage.CMultiLanguageClass(); - if (multilang3 == null) - throw new System.Runtime.InteropServices.COMException("Failed to get IMultilang3"); - try - { - int[] resultCodePages = new int[preferedEncodings.Length]; - uint detectedCodepages = (uint)resultCodePages.Length; - ushort specialChar = (ushort)'?'; - - - // get unmanaged arrays - IntPtr pPrefEncs = Marshal.AllocCoTaskMem(sizeof(uint) * preferedEncodings.Length); - IntPtr pDetectedEncs = preferedEncodings == null ? IntPtr.Zero : Marshal.AllocCoTaskMem(sizeof(uint) * resultCodePages.Length); - - try - { - if (preferedEncodings != null) - Marshal.Copy(preferedEncodings, 0, pPrefEncs, preferedEncodings.Length); - - Marshal.Copy(resultCodePages, 0, pDetectedEncs, resultCodePages.Length); - - MultiLanguage.MLCPF options = MultiLanguage.MLCPF.MLDETECTF_VALID_NLS | MultiLanguage.MLCPF.MLDETECTF_PREFERRED_ONLY; - if (preserveOrder) - options |= MultiLanguage.MLCPF.MLDETECTF_PRESERVE_ORDER; - - if (preferedEncodings != null) - options |= MultiLanguage.MLCPF.MLDETECTF_PREFERRED_ONLY; - - // finally... call to DetectOutboundCodePage - multilang3.DetectOutboundCodePage(options, - input, (uint)input.Length, - pPrefEncs, (uint)(preferedEncodings == null ? 0 : preferedEncodings.Length), - pDetectedEncs, ref detectedCodepages, - ref specialChar); - - // get result - if (detectedCodepages > 0) - { - int[] theResult = new int[detectedCodepages]; - Marshal.Copy(pDetectedEncs, theResult, 0, theResult.Length); - - - // get the encodings for the codepages - for (int i = 0; i < detectedCodepages; i++) - result.Add(Encoding.GetEncoding(theResult[i])); - - } - - } - finally - { - if (pPrefEncs != IntPtr.Zero) - Marshal.FreeCoTaskMem(pPrefEncs); - Marshal.FreeCoTaskMem(pDetectedEncs); - } - } - finally - { - Marshal.FinalReleaseComObject(multilang3); - } - // nothing found - return result.ToArray(); - } - - - /// - /// Detect the most probable codepage from an byte array - /// - /// array containing the raw data - /// the detected encoding or the default encoding if the detection failed - public static Encoding DetectInputCodepage(byte[] input) - { - try - { - Encoding[] detected = DetectInputCodepages(input, 1); - if (detected.Length > 0) - return detected[0]; - return Encoding.Default; - } - catch (COMException) - { - // return default codepage on error - return Encoding.Default; - } - } - - /// - /// Rerurns up to maxEncodings codpages that are assumed to be apropriate - /// - /// array containing the raw data - /// maxiumum number of encodings to detect - /// an array of Encoding with assumed encodings - public static Encoding[] DetectInputCodepages(byte[] input, int maxEncodings) - { - if (Path.DirectorySeparatorChar == '/') - { - // unix - return new Encoding[0]; - } - - if (maxEncodings < 1) - throw new ArgumentOutOfRangeException("at least one encoding must be returend", "maxEncodings"); - - if (input == null) - throw new ArgumentNullException("input"); - - // empty strings can always be encoded as ASCII - if (input.Length == 0) - return new Encoding[] { Encoding.ASCII }; - - // expand the string to be at least 256 bytes - if (input.Length < 256) - { - byte[] newInput = new byte[256]; - int steps = 256 / input.Length; - for (int i = 0; i < steps; i++) - Array.Copy(input, 0, newInput, input.Length * i, input.Length); - - int rest = 256 % input.Length; - if (rest > 0) - Array.Copy(input, 0, newInput, steps * input.Length, rest); - input = newInput; - } - - List result = new List(); - - // get the IMultiLanguage" interface - MultiLanguage.IMultiLanguage2 multilang2 = new MultiLanguage.CMultiLanguageClass(); - if (multilang2 == null) - throw new System.Runtime.InteropServices.COMException("Failed to get IMultilang2"); - try - { - MultiLanguage.DetectEncodingInfo[] detectedEncdings = new MultiLanguage.DetectEncodingInfo[maxEncodings]; - - int scores = detectedEncdings.Length; - int srcLen = input.Length; - - // setup options (none) - MultiLanguage.MLDETECTCP options = MultiLanguage.MLDETECTCP.MLDETECTCP_NONE; - - // finally... call to DetectInputCodepage - multilang2.DetectInputCodepage(options, 0, - ref input[0], ref srcLen, ref detectedEncdings[0], ref scores); - - // get result - if (scores > 0) - { - for (int i = 0; i < scores; i++) - { - // add the result - result.Add(Encoding.GetEncoding((int)detectedEncdings[i].nCodePage)); - } - } - } - finally - { - Marshal.FinalReleaseComObject(multilang2); - } - // nothing found - return result.ToArray(); - } - - - /// - /// Opens a text file and returns the content - /// encoded in the most probable encoding - /// - /// path to the souce file - /// the text content of the file - public static string ReadTextFile(string path) - { - if (path == null) - throw new ArgumentNullException("path"); - - using (Stream fs = File.Open(path, FileMode.Open)) - { - byte[] rawData = new byte[fs.Length]; - Encoding enc = DetectInputCodepage(rawData); - return enc.GetString(rawData); - } - } - - /// - /// Returns a stream reader for the given - /// text file with the best encoding applied - /// - /// path to the file - /// a StreamReader for the file - public static StreamReader OpenTextFile(string path) - { - if (path == null) - throw new ArgumentNullException("path"); - return OpenTextStream(File.Open(path, FileMode.Open)); - } - - /// - /// Creates a stream reader from a stream and detects - /// the encoding form the first bytes in the stream - /// - /// a stream to wrap - /// the newly created StreamReader - public static StreamReader OpenTextStream(Stream stream) - { - // check stream parameter - if (stream == null) - throw new ArgumentNullException("stream"); - if (!stream.CanSeek) - throw new ArgumentException("the stream must support seek operations", "stream"); - - // assume default encoding at first place - Encoding detectedEncoding = Encoding.Default; - - // seek to stream start - stream.Seek(0, SeekOrigin.Begin); - - // buffer for preamble and up to 512b sample text for dection - byte[] buf = new byte[System.Math.Min(stream.Length, 512)]; - - stream.Read(buf, 0, buf.Length); - detectedEncoding = DetectInputCodepage(buf); - // seek back to stream start - stream.Seek(0, SeekOrigin.Begin); - - - return new StreamReader(stream, detectedEncoding); - - } - - - /// - /// Create an Encoding instance by codepage name using additional jdk aliases - /// Doesn't throw if codepage name is not supported - /// - /// codepage name - /// Created Encoding instance or null if codepage name is not supported - public static Encoding GetEncodingByCodepageName(string codepage_name) - { - try - { - return GetEncodingByCodepageName_Throws(codepage_name); - } - catch (System.ArgumentException) - { - return null; - } - } - - - /// - /// Create an Encoding instance by codepage name using additional jdk aliases - /// Throws if codepage name is not supported - /// - /// codepage name - /// Created Encoding instance - /// Throws if codepage name is not supported - public static Encoding GetEncodingByCodepageName_Throws(string codepage_name) - { - string dealiased_name; - if (!encoding_aliases.TryGetValue(codepage_name, out dealiased_name)) - { - dealiased_name = codepage_name; - } - - return System.Text.Encoding.GetEncoding(dealiased_name); - } - - } - - -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/EventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/EventArgs.cs deleted file mode 100644 index 9e8fc1b5a..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/EventArgs.cs +++ /dev/null @@ -1,63 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - #region usings - - using System; - - #endregion - - /// - /// This class universal event arguments for transporting single value. - /// - /// Event data. - public class EventArgs : EventArgs - { - #region Members - - private readonly T m_pValue; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Event data. - public EventArgs(T value) - { - m_pValue = value; - } - - #endregion - - #region Properties - - /// - /// Gets event data. - /// - public T Value - { - get { return m_pValue; } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ExceptionEventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ExceptionEventArgs.cs deleted file mode 100644 index f26e5bba5..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ExceptionEventArgs.cs +++ /dev/null @@ -1,68 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - #region usings - - using System; - - #endregion - - /// - /// This class provides data for error events and methods. - /// - public class ExceptionEventArgs : EventArgs - { - #region Members - - private readonly Exception m_pException; - - #endregion - - #region Properties - - /// - /// Gets exception. - /// - public Exception Exception - { - get { return m_pException; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Exception. - /// Is raised when exception is null reference value. - public ExceptionEventArgs(Exception exception) - { - if (exception == null) - { - throw new ArgumentNullException("exception"); - } - - m_pException = exception; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/Client/FTP_Client.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/Client/FTP_Client.cs deleted file mode 100644 index ed3c5f2a1..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/Client/FTP_Client.cs +++ /dev/null @@ -1,1808 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.FTP.Client -{ - #region usings - - using System; - using System.Collections.Generic; - using System.Globalization; - using System.IO; - using System.Net; - using System.Net.Sockets; - using System.Security.Principal; - using System.Text; - using System.Threading; - using IO; - using TCP; - using StringReader=StringReader; - - #endregion - - #region enum TransferType - - /// - /// Transfer type. - /// - internal enum TransferType - { - /// - /// ASCII transfer data. - /// - Ascii = 0, - /// - /// Binary transfer data. - /// - Binary = 1, - } - - #endregion - - /// - /// This class implements FTP client. Defined in RFC 959. - /// - public class FTP_Client : TCP_Client - { - #region Nested type: DataConnection - - /// - /// This class implements FTP client data connection. - /// - private class DataConnection : IDisposable - { - #region Members - - private int m_ActivePort = -1; - private bool m_IsActive; - private DateTime m_LastActivity; - private FTP_Client m_pOwner; - private Socket m_pSocket; - private FTP_TransferMode m_TransferMode = FTP_TransferMode.Active; - - #endregion - - #region Properties - - /// - /// Gets data connection local IP end point. - /// - public IPEndPoint LocalEndPoint - { - get { return (IPEndPoint) m_pSocket.LocalEndPoint; } - } - - /// - /// Gets last time when data connection has read or written data. - /// - public DateTime LastActivity - { - get { return m_LastActivity; } - } - - /// - /// Gets if there is active read or write job in data stream. - /// - public bool IsActive - { - get { return m_IsActive; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Data connection owner FTP client. - public DataConnection(FTP_Client owner) - { - m_pOwner = owner; - - CreateSocket(); - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public void Dispose() - { - if (m_pSocket != null) - { - m_pSocket.Close(); - m_pSocket = null; - } - m_pOwner = null; - } - - /// - /// Swtiches FTP data connection to active mode. - /// - public void SwitchToActive() - { - // In acvtive mode we must start listening incoming FTP server connection. - m_pSocket.Listen(1); - - m_TransferMode = FTP_TransferMode.Active; - - m_pOwner.LogAddText( - "FTP data channel switched to Active mode, listening FTP server connect to '" + - m_pSocket.LocalEndPoint + "'."); - } - - /// - /// Swtiches FTP data connection to passive mode and connects to the sepcified FTP server. - /// - /// FTP server IP end point. - /// Is raised when remoteEP is null. - public void SwitchToPassive(IPEndPoint remoteEP) - { - if (remoteEP == null) - { - throw new ArgumentNullException("remoteEP"); - } - - m_pOwner.LogAddText("FTP data channel switched to Passive mode, connecting to FTP server '" + - remoteEP + "'."); - - // In passive mode we just need to connect to the specified FTP host. - m_pSocket.Connect(remoteEP); - - m_TransferMode = FTP_TransferMode.Passive; - - m_pOwner.LogAddText("FTP Passive data channel established, localEP='" + - m_pSocket.LocalEndPoint + "' remoteEP='" + m_pSocket.RemoteEndPoint + "'."); - } - - /// - /// Reads all data from FTP data connection and stores to the specified stream. - /// - /// Stream where to store data. - /// Is raised when stream is null. - public void ReadAll(Stream stream) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - m_IsActive = true; - try - { - if (m_TransferMode == FTP_TransferMode.Active) - { - using (NetworkStream dataStream = WaitFtpServerToConnect(20)) - { - long bytesReaded = TransferStream(dataStream, stream); - m_pOwner.LogAddRead(bytesReaded, - "Data connection readed " + bytesReaded + " bytes."); - } - } - else if (m_TransferMode == FTP_TransferMode.Passive) - { - using (NetworkStream dataStream = new NetworkStream(m_pSocket, true)) - { - long bytesReaded = TransferStream(dataStream, stream); - m_pOwner.LogAddRead(bytesReaded, - "Data connection readed " + bytesReaded + " bytes."); - } - } - } - finally - { - m_IsActive = false; - CleanUpSocket(); - } - } - - /// - /// Writes all data from the specified stream to FTP data connection. - /// - /// Stream which data to write. - /// Is raised when stream is null. - public void WriteAll(Stream stream) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - try - { - if (m_TransferMode == FTP_TransferMode.Active) - { - using (NetworkStream dataStream = WaitFtpServerToConnect(20)) - { - long bytesWritten = TransferStream(stream, dataStream); - m_pOwner.LogAddWrite(bytesWritten, - "Data connection wrote " + bytesWritten + " bytes."); - } - } - else if (m_TransferMode == FTP_TransferMode.Passive) - { - using (NetworkStream dataStream = new NetworkStream(m_pSocket, true)) - { - long bytesWritten = TransferStream(stream, dataStream); - m_pOwner.LogAddWrite(bytesWritten, - "Data connection wrote " + bytesWritten + " bytes."); - } - } - } - finally - { - m_IsActive = false; - CleanUpSocket(); - } - } - - /// - /// Cleans up socket for reuse. - /// - public void CleanUpSocket() - { - if (m_pSocket != null) - { - m_pSocket.Close(); - } - - // We can't reuse socket, so we need to recreate new one for each transfer. - CreateSocket(); - } - - #endregion - - #region Utility methods - - /// - /// Waits FTP server to connect to this data connection. - /// - /// Wait time out in seconds. - /// Returns connected network stream. - private NetworkStream WaitFtpServerToConnect(int waitTime) - { - try - { - m_pOwner.LogAddText("FTP Active data channel waiting FTP server connect to '" + - m_pSocket.LocalEndPoint + "'."); - - //--- Wait ftp server connection -----------------------------// - DateTime startTime = DateTime.Now; - while (!m_pSocket.Poll(0, SelectMode.SelectRead)) - { - Thread.Sleep(50); - - if (startTime.AddSeconds(waitTime) < DateTime.Now) - { - m_pOwner.LogAddText("FTP server didn't connect during expected time."); - - throw new IOException("FTP server didn't connect during expected time."); - } - } - //-----------------------------------------------------------// - - // Accpet FTP server connection. - Socket socket = m_pSocket.Accept(); - - m_pOwner.LogAddText("FTP Active data channel established, localEP='" + - socket.LocalEndPoint + "' remoteEP='" + socket.RemoteEndPoint + "'."); - - return new NetworkStream(socket, true); - } - finally - { - CleanUpSocket(); - } - } - - /// - /// Creates new socket for data connection. - /// - private void CreateSocket() - { - // IPv4 - if (m_pOwner.LocalEndPoint.Address.AddressFamily == AddressFamily.InterNetwork) - { - m_pSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - } - // IPv6 - else - { - m_pSocket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp); - } - - int port = 0; - // Data connection port range not specified, let system to allocate port for us. - if (m_pOwner.DataPortRange == null) - { - port = 0; - } - // Data connection port specified, use next port from range. - else - { - // There is no acitve port or we have reached end of range, just reset range. - if (m_ActivePort == -1 || (m_ActivePort + 1) > m_pOwner.DataPortRange.End) - { - m_ActivePort = m_pOwner.DataPortRange.Start; - } - else - { - m_ActivePort++; - } - port = m_ActivePort; - } - - // Data connection IP address not specified, use default. - if (m_pOwner.DataIP == null || m_pOwner.DataIP == IPAddress.Any) - { - m_pSocket.Bind(new IPEndPoint(m_pOwner.LocalEndPoint.Address, port)); - } - // Data connection IP specified, use it. - else - { - m_pSocket.Bind(new IPEndPoint(m_pOwner.DataIP, port)); - } - m_pSocket.SendTimeout = 30000; - m_pSocket.ReceiveTimeout = 30000; - } - - /// - /// Copies all source stream data to the specified target stream. - /// - /// Source stream. - /// Target stream. - private long TransferStream(Stream source, Stream target) - { - long totalReadedCount = 0; - byte[] buffer = new byte[Workaround.Definitions.MaxStreamLineLength]; - while (true) - { - int readedCount = source.Read(buffer, 0, buffer.Length); - // End of stream reached, we readed all data sucessfully. - if (readedCount == 0) - { - return totalReadedCount; - } - else - { - target.Write(buffer, 0, readedCount); - totalReadedCount += readedCount; - m_LastActivity = DateTime.Now; - } - } - } - - #endregion - } - - #endregion - - #region Members - - private string m_GreetingText = ""; - private GenericIdentity m_pAuthdUserIdentity; - private DataConnection m_pDataConnection; - private IPAddress m_pDataConnectionIP; - private PortRange m_pDataPortRange; - private List m_pExtCapabilities; - private FTP_TransferMode m_TransferMode = FTP_TransferMode.Passive; - - #endregion - - #region Properties - - /// - /// Gets or sets data connection establish mode. - /// - /// Is raised when this object is disposed and this property is accessed. - public FTP_TransferMode TransferMode - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_TransferMode; - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - m_TransferMode = value; - } - } - - /// - /// Gets or sets local IP address to use for data connection. Value null means that system will allocate it. - /// - /// Is raised when this object is disposed and this property is accessed. - public IPAddress DataIP - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pDataConnectionIP; - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - m_pDataConnectionIP = value; - - // If We are connected, we need to reset data connection. - if (IsConnected) - { - m_pDataConnection.CleanUpSocket(); - } - } - } - - /// - /// Gets or sets ports what data connection may use. Value null means that system will allocate it. - /// - /// Is raised when this object is disposed and this property is accessed. - public PortRange DataPortRange - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pDataPortRange; - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - m_pDataPortRange = value; - - // If We are connected, we need to reset data connection. - if (IsConnected) - { - m_pDataConnection.CleanUpSocket(); - } - } - } - - /// - /// Gets greeting text which was sent by FTP server. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when this property is accessed and FTP client is not connected. - public string GreetingText - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - - return m_GreetingText; - } - } - - /// - /// Gets FTP exteneded capabilities supported by FTP server. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when this property is accessed and FTP client is not connected. - public string[] ExtenededCapabilities - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - - return m_pExtCapabilities.ToArray(); - } - } - - /// - /// Gets session authenticated user identity, returns null if not authenticated. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when this property is accessed and FTP client is not connected. - public override GenericIdentity AuthenticatedUserIdentity - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - - return m_pAuthdUserIdentity; - } - } - - #endregion - - #region Methods - - /// - /// Clean up any resources being used. This method is thread-safe. - /// - public override void Dispose() - { - lock (this) - { - base.Dispose(); - - m_pDataConnectionIP = null; - } - } - - /// - /// Closes connection to FTP server. - /// - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when FTP client is not connected. - public override void Disconnect() - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("FTP client is not connected."); - } - - try - { - // Send QUIT command to server. - WriteLine("QUIT"); - } - catch {} - - try - { - base.Disconnect(); - } - catch {} - - m_pExtCapabilities = null; - m_pAuthdUserIdentity = null; - if (m_pDataConnection != null) - { - m_pDataConnection.Dispose(); - m_pDataConnection = null; - } - } - - /// - /// Terminates the user and flushes all state information on the server. The connection is left open. - /// - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when FTP client is not connected. - /// Is raised when FTP server returns error. - public void Reinitialize() - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - - WriteLine("REIN"); - - string[] response = ReadResponse(); - if (!response[0].StartsWith("2")) - { - throw new FTP_ClientException(response[0]); - } - } - - /// - /// Authenticates user. Authenticate method chooses strongest possible authentication method supported by server. - /// - /// User login name. - /// Password. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when FTP client is not connected or is already authenticated. - /// Is raised when userName is null. - /// Is raised when FTP server returns error. - public void Authenticate(string userName, string password) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (IsAuthenticated) - { - throw new InvalidOperationException("Session is already authenticated."); - } - if (string.IsNullOrEmpty(userName)) - { - throw new ArgumentNullException("userName"); - } - if (password == null) - { - password = ""; - } - - WriteLine("USER " + userName); - - string[] response = ReadResponse(); - if (response[0].StartsWith("331")) - { - WriteLine("PASS " + password); - - /* FTP server may give multiline reply here - For example: - 230-User someuser has group access to: someuser - 230 OK. Current restricted directory is / - */ - response = ReadResponse(); - if (!response[0].StartsWith("230")) - { - throw new FTP_ClientException(response[0]); - } - - m_pAuthdUserIdentity = new GenericIdentity(userName, "ftp-user/pass"); - } - else - { - throw new FTP_ClientException(response[0]); - } - } - - /// - /// Send NOOP command to server. This method can be used for keeping connection alive(not timing out). - /// - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when FTP client is not connected. - /// Is raised when FTP server returns error. - public void Noop() - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - - WriteLine("NOOP"); - - string[] response = ReadResponse(); - if (!response[0].StartsWith("2")) - { - throw new FTP_ClientException(response[0]); - } - } - - /// - /// Aborts an active file transfer. - /// - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when FTP client is not connected. - /// Is raised when FTP server returns error. - public void Abort() - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - - WriteLine("ABOR"); - - string line = ReadLine(); - if (!line.StartsWith("2")) - { - throw new FTP_ClientException(line); - } - } - - /// - /// Gets current working directory in the sFTP server. - /// - /// Returns current working directory. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when FTP client is not connected. - /// Is raised when FTP server returns error. - public string GetCurrentDir() - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - - WriteLine("PWD"); - - string[] response = ReadResponse(); - if (!response[0].StartsWith("2")) - { - throw new FTP_ClientException(response[0]); - } - - StringReader r = new StringReader(response[0]); - // Skip status code. - r.ReadWord(); - - return r.ReadWord(); - } - - /// - /// Changes the current working directory on the server. - /// - /// Directory absolute or relative path to the current working directory. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when FTP client is not connected. - /// Is raised when path is null. - /// Is raised when any of the arguments has invalid value. - /// Is raised when FTP server returns error. - public void SetCurrentDir(string path) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (path == null) - { - throw new ArgumentNullException("path"); - } - if (path == "") - { - throw new ArgumentException("Argumnet 'path' must be specified."); - } - - WriteLine("CWD " + path); - - string[] response = ReadResponse(); - if (!response[0].StartsWith("2")) - { - throw new FTP_ClientException(response[0]); - } - } - - /// - /// Gets files and directories in the current server directory. - /// - /// Returns current working directory listing. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when FTP client is not connected. - /// Is raised when FTP server returns error. - public FTP_ListItem[] GetList() - { - return GetList(null); - } - - /// - /// Gets files and directories in the current server directory. - /// - /// Directory or file name which listing to get. Value null means current directory will be listed. - /// Returns current working directory listing. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when FTP client is not connected or FTP data connection has active read/write operation. - /// Is raised when FTP server returns error. - public FTP_ListItem[] GetList(string path) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (m_pDataConnection.IsActive) - { - throw new InvalidOperationException( - "There is already active read/write operation on data connection."); - } - - List retVal = new List(); - - // Set transfer mode - SetTransferType(TransferType.Binary); - - if (m_TransferMode == FTP_TransferMode.Passive) - { - Pasv(); - } - else - { - Port(); - } - - // If FTP server supports MLSD command, use it to get directory listing. - // MLSD is standard way to get dir listing, while LIST command isn't any strict standard. - bool mlsdSupported = false; - foreach (string feature in m_pExtCapabilities) - { - if (feature.ToLower().StartsWith("mlsd")) - { - mlsdSupported = true; - break; - } - } - - #region MLSD - - if (mlsdSupported) - { - if (string.IsNullOrEmpty(path)) - { - WriteLine("MLSD"); - } - else - { - WriteLine("MLSD " + path); - } - - string[] response = ReadResponse(); - if (!response[0].StartsWith("1")) - { - throw new FTP_ClientException(response[0]); - } - - MemoryStream ms = new MemoryStream(); - m_pDataConnection.ReadAll(ms); - - response = ReadResponse(); - if (!response[0].StartsWith("2")) - { - throw new FTP_ClientException(response[0]); - } - - byte[] lineBuffer = new byte[8000]; - ms.Position = 0; - SmartStream mlsdStream = new SmartStream(ms, true); - while (true) - { - SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(lineBuffer, - SizeExceededAction. - JunkAndThrowException); - mlsdStream.ReadLine(args, false); - if (args.Error != null) - { - throw args.Error; - } - string line = args.LineUtf8; - - // We reached end of stream, we readed whole list sucessfully. - if (line == null) - { - break; - } - else - { - string[] parameters = line.Substring(0, line.LastIndexOf(';')).Split(';'); - string name = line.Substring(line.LastIndexOf(';') + 1).Trim(); - - string type = ""; - long size = 0; - DateTime modified = DateTime.MinValue; - foreach (string parameter in parameters) - { - string[] name_value = parameter.Split('='); - if (name_value[0].ToLower() == "type") - { - type = name_value[1].ToLower(); - } - else if (name_value[0].ToLower() == "size") - { - size = Convert.ToInt32(name_value[1]); - } - else if (name_value[0].ToLower() == "modify") - { - modified = DateTime.ParseExact(name_value[1], - "yyyyMMddHHmmss", - DateTimeFormatInfo.InvariantInfo); - } - else - { - // Other options won't interest us, skip them. - } - } - - if (type == "dir") - { - retVal.Add(new FTP_ListItem(name, 0, modified, true)); - } - else if (type == "file") - { - retVal.Add(new FTP_ListItem(name, size, modified, false)); - } - } - } - } - - #endregion - - #region LIST - - else - { - if (string.IsNullOrEmpty(path)) - { - WriteLine("LIST"); - } - else - { - WriteLine("LIST " + path); - } - - string[] response = ReadResponse(); - if (!response[0].StartsWith("1")) - { - throw new FTP_ClientException(response[0]); - } - - MemoryStream ms = new MemoryStream(); - m_pDataConnection.ReadAll(ms); - - response = ReadResponse(); - if (!response[0].StartsWith("2")) - { - throw new FTP_ClientException(response[0]); - } - - ms.Position = 0; - SmartStream listStream = new SmartStream(ms, true); - SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(new byte[8000], - SizeExceededAction. - JunkAndThrowException); - listStream.ReadLine(args, false); - if (args.Error != null) - { - throw args.Error; - } - string line = args.LineUtf8; - - string listingType = "unix"; - // Dedect listing. - if (line != null) - { - try - { - StringReader r = new StringReader(line); - DateTime modified = DateTime.ParseExact(r.ReadWord() + " " + r.ReadWord(), - new[] {"MM-dd-yy hh:mmtt"}, - DateTimeFormatInfo.InvariantInfo, - DateTimeStyles.None); - listingType = "win"; - } - catch {} - } - - string[] winDateFormats = new[] {"M-d-yy h:mmtt"}; - string[] unixFormats = new[] {"MMM d H:mm", "MMM d yyyy"}; - - byte[] lineBuffer = new byte[8000]; - while (line != null) - { - // Windows listing. - if (listingType == "win") - { - // MM-dd-yy hh:mm directoryName - // MM-dd-yy hh:mm size fileName - - StringReader r = new StringReader(line); - // Read date - DateTime modified = DateTime.ParseExact(r.ReadWord() + " " + r.ReadWord(), - winDateFormats, - DateTimeFormatInfo.InvariantInfo, - DateTimeStyles.None); - - r.ReadToFirstChar(); - // We have directory. - if (r.StartsWith("", false)) - { - r.ReadSpecifiedLength(5); - r.ReadToFirstChar(); - - retVal.Add(new FTP_ListItem(r.ReadToEnd(), 0, modified, true)); - } - // We have file - else - { - // Read file size - long size = Convert.ToInt64(r.ReadWord()); - r.ReadToFirstChar(); - - retVal.Add(new FTP_ListItem(r.ReadToEnd(), size, modified, false)); - } - } - // Unix listing - else - { - // "d"directoryAtttributes xx xx xx 0 MMM d HH:mm/yyyy directoryName - // fileAtttributes xx xx xx fileSize MMM d HH:mm/yyyy fileName - - StringReader r = new StringReader(line); - string attributes = r.ReadWord(); - r.ReadWord(); - r.ReadWord(); - r.ReadWord(); - long size = Convert.ToInt64(r.ReadWord()); - DateTime modified = - DateTime.ParseExact(r.ReadWord() + " " + r.ReadWord() + " " + r.ReadWord(), - unixFormats, - DateTimeFormatInfo.InvariantInfo, - DateTimeStyles.None); - r.ReadToFirstChar(); - string name = r.ReadToEnd(); - if (name != "." && name != "..") - { - if (attributes.StartsWith("d")) - { - retVal.Add(new FTP_ListItem(name, 0, modified, true)); - } - else - { - retVal.Add(new FTP_ListItem(name, size, modified, false)); - } - } - } - - listStream.ReadLine(args, false); - if (args.Error != null) - { - throw args.Error; - } - line = args.LineUtf8; - } - } - - #endregion - - return retVal.ToArray(); - } - - /// - /// Gets specified file from FTP server. - /// - /// File absolute or relative path to the current working directory. - /// Local file path where to store received file. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when FTP client is not connected or FTP data connection has active read/write operation. - /// Is raised when path or storePath is null. - /// Is raised when any of the arguments has invalid value. - /// Is raised when FTP server returns error. - public void GetFile(string path, string storePath) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (m_pDataConnection.IsActive) - { - throw new InvalidOperationException( - "There is already active read/write operation on data connection."); - } - if (path == null) - { - throw new ArgumentNullException("path"); - } - if (path == "") - { - throw new ArgumentException("Argument 'path' value must be specified."); - } - if (storePath == null) - { - throw new ArgumentNullException("storePath"); - } - if (storePath == "") - { - throw new ArgumentException("Argument 'storePath' value must be specified."); - } - - using (FileStream fs = File.Create(storePath)) - { - GetFile(path, fs); - } - } - - /// - /// Gets specified file from FTP server. - /// - /// File absolute or relative path to the current working directory. - /// Stream where to store received file. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when FTP client is not connected or FTP data connection has active read/write operation. - /// Is raised when path or stream is null. - /// Is raised when any of the arguments has invalid value. - /// Is raised when FTP server returns error. - public void GetFile(string path, Stream stream) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (m_pDataConnection.IsActive) - { - throw new InvalidOperationException( - "There is already active read/write operation on data connection."); - } - if (path == null) - { - throw new ArgumentNullException("path"); - } - if (path == "") - { - throw new ArgumentException("Argument 'path' value must be specified."); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - // Set transfer mode - SetTransferType(TransferType.Binary); - - if (m_TransferMode == FTP_TransferMode.Passive) - { - Pasv(); - } - else - { - Port(); - } - - // Send RETR command - WriteLine("RETR " + path); - - string[] response = ReadResponse(); - if (!response[0].StartsWith("1")) - { - throw new FTP_ClientException(response[0]); - } - - m_pDataConnection.ReadAll(stream); - - /* FTP server may give multiline reply here - / For example: - / 226-File successfully transferred - / 226 0.002 seconds (measured here), 199.65 Mbytes per second 339163 bytes received in 00:00 (8.11 MB/s) - */ - response = ReadResponse(); - if (!response[0].StartsWith("2")) - { - throw new FTP_ClientException(response[0]); - } - } - - /// - /// Appends specified data to the existing file. If existing file doesn't exist, it will be created. - /// - /// FTP server file absolute or relative path to the current working directory. - /// Stream which data append to the specified FTP server file. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when FTP client is not connected or FTP data connection has active read/write operation. - /// Is raied when file or stream is null. - /// Is raised when any of the arguments has invalid value. - /// Is raised when FTP server returns error. - public void AppendToFile(string path, Stream stream) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (m_pDataConnection.IsActive) - { - throw new InvalidOperationException( - "There is already active read/write operation on data connection."); - } - if (path == null) - { - throw new ArgumentNullException("path"); - } - if (path == "") - { - throw new ArgumentException("Argument 'path' value must be specified."); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - // Set transfer mode - SetTransferType(TransferType.Binary); - - if (m_TransferMode == FTP_TransferMode.Passive) - { - Pasv(); - } - else - { - Port(); - } - - // Send APPE command - WriteLine("APPE " + path); - - string[] response = ReadResponse(); - if (!response[0].StartsWith("1")) - { - throw new FTP_ClientException(response[0]); - } - - m_pDataConnection.WriteAll(stream); - - /* FTP server may give multiline reply here - / For example: - / 226-File successfully transferred - / 226 0.002 seconds (measured here), 199.65 Mbytes per second 339163 bytes received in 00:00 (8.11 MB/s) - */ - response = ReadResponse(); - if (!response[0].StartsWith("2")) - { - throw new FTP_ClientException(response[0]); - } - } - - /// - /// Stores specified file to FTP server. - /// - /// File absolute or relative path to the current working directory. - /// File path which to store to FTP server. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when FTP client is not connected or FTP data connection has active read/write operation. - /// Is raised when path or sourcePath is null. - /// Is raised when any of the arguments has invalid value. - /// Is raised when FTP server returns error. - public void StoreFile(string path, string sourcePath) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (m_pDataConnection.IsActive) - { - throw new InvalidOperationException( - "There is already active read/write operation on data connection."); - } - if (path == null) - { - throw new ArgumentNullException("path"); - } - if (path == "") - { - throw new ArgumentException("Argument 'path' value must be specified."); - } - if (sourcePath == null) - { - throw new ArgumentNullException("sourcePath"); - } - if (sourcePath == "") - { - throw new ArgumentException("Argument 'sourcePath' value must be specified."); - } - - using (FileStream fs = File.OpenRead(sourcePath)) - { - StoreFile(path, fs); - } - } - - /// - /// Stores specified file to FTP server. - /// - /// File absolute or relative path to the current working directory. - /// Stream which data to store to FTP server. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when FTP client is not connected or FTP data connection has active read/write operation. - /// Is raised when path or stream is null. - /// Is raised when any of the arguments has invalid value. - /// Is raised when FTP server returns error. - public void StoreFile(string path, Stream stream) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (m_pDataConnection.IsActive) - { - throw new InvalidOperationException( - "There is already active read/write operation on data connection."); - } - if (path == null) - { - throw new ArgumentNullException("path"); - } - if (path == "") - { - throw new ArgumentException("Argument 'path' value must be specified."); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - // Set transfer mode - SetTransferType(TransferType.Binary); - - if (m_TransferMode == FTP_TransferMode.Passive) - { - Pasv(); - } - else - { - Port(); - } - - // Send STOR command - WriteLine("STOR " + path); - - string[] response = ReadResponse(); - if (!response[0].StartsWith("1")) - { - throw new FTP_ClientException(response[0]); - } - - m_pDataConnection.WriteAll(stream); - - /* FTP server may give multiline reply here - / For example: - / 226-File successfully transferred - / 226 0.002 seconds (measured here), 199.65 Mbytes per second 339163 bytes received in 00:00 (8.11 MB/s) - */ - response = ReadResponse(); - if (!response[0].StartsWith("2")) - { - throw new FTP_ClientException(response[0]); - } - } - - /// - /// Deletes specified file from ftp server. - /// - /// File absolute or relative path to the current working directory. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when FTP client is not connected. - /// Is raised when path is null. - /// Is raised when any of the arguments has invalid value. - /// Is raised when FTP server returns error. - public void DeleteFile(string path) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (path == null) - { - throw new ArgumentNullException("path"); - } - if (path == "") - { - throw new ArgumentException("Argument 'path' value must be specified."); - } - - WriteLine("DELE " + path); - - string reply = ReadLine(); - if (!reply.StartsWith("250")) - { - throw new FTP_ClientException(reply); - } - } - - /// - /// Renames file or directory to the new specified name. - /// - /// Exisitng file or directory absolute or relative path to the current working directory. - /// New file or directory absolute or relative path to the current working directory. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when FTP client is not connected. - /// Is raised when fromPath or toPath is null. - /// Is raised when any of the arguments has invalid value. - /// Is raised when FTP server returns error. - public void Rename(string fromPath, string toPath) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (fromPath == null) - { - throw new ArgumentNullException("fromPath"); - } - if (fromPath == "") - { - throw new ArgumentException("Argument 'fromPath' value must be specified."); - } - if (toPath == null) - { - throw new ArgumentNullException("toPath"); - } - if (toPath == "") - { - throw new ArgumentException("Argument 'toPath' value must be specified."); - } - - WriteLine("RNFR " + fromPath); - - string reply = ReadLine(); - if (!reply.StartsWith("350")) - { - throw new FTP_ClientException(reply); - } - - WriteLine("RNTO " + toPath); - - reply = ReadLine(); - if (!reply.StartsWith("250")) - { - throw new FTP_ClientException(reply); - } - } - - /// - /// Creates a directory on the FTP server. - /// - /// Directory absolute or relative path to the current working directory. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when FTP client is not connected. - /// Is raised when path is null. - /// Is raised when any of the arguments has invalid value. - /// Is raised when FTP server returns error. - public void CreateDirectory(string path) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (path == null) - { - throw new ArgumentNullException("path"); - } - if (path == "") - { - throw new ArgumentException("Argument 'path' value must be specified."); - } - - WriteLine("MKD " + path); - - string reply = ReadLine(); - if (!reply.StartsWith("257")) - { - throw new FTP_ClientException(reply); - } - } - - /// - /// Deletes specified directory from FTP server. - /// - /// Directory absolute or relative path to the current working directory. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when FTP client is not connected. - /// Is raised when path is null. - /// Is raised when any of the arguments has invalid value. - /// Is raised when FTP server returns error. - public void DeleteDirectory(string path) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (path == null) - { - throw new ArgumentNullException("path"); - } - if (path == "") - { - throw new ArgumentException("Argument 'path' value must be specified."); - } - - WriteLine("RMD " + path); - - string reply = ReadLine(); - if (!reply.StartsWith("250")) - { - throw new FTP_ClientException(reply); - } - } - - #endregion - - #region Overrides - - /// - /// This method is called after TCP client has sucessfully connected. - /// - protected override void OnConnected() - { - m_pDataConnection = new DataConnection(this); - - /* - Notes: Greeting may be single or multiline response. - - Examples: - 220FTP server ready - - 220-FTP server ready - 220-Addtitional text - 220final row - */ - - string line = ReadLine(); - if (line.StartsWith("220")) - { - StringBuilder greetText = new StringBuilder(); - greetText.Append(line.Substring(4)); - - // Read multiline greet text. - while (line.StartsWith("220-")) - { - line = ReadLine(); - - greetText.AppendLine(line.Substring(4)); - } - - m_GreetingText = greetText.ToString(); - } - else - { - throw new FTP_ClientException(line); - } - - #region FEAT - - /* Try to get FTP server supported capabilities, if command not supported, just skip tat command. - RFC 2389 3. - - Examples: - C: FEAT - S: 211-Extensions supported: - S: MLST size*;create;modify*;perm;media-type - S: SIZE - S: COMPRESSION - S: MDTM - S: 211 END - - */ - - WriteLine("FEAT"); - - line = ReadLine(); - m_pExtCapabilities = new List(); - if (line.StartsWith("211")) - { - line = ReadLine(); - while (line.StartsWith(" ")) - { - m_pExtCapabilities.Add(line.Trim()); - - line = ReadLine(); - } - } - - #endregion - } - - #endregion - - #region Utility methods - - /// - /// Sets transfer typr. - /// - /// Transfer type. - private void SetTransferType(TransferType type) - { - if (type == TransferType.Ascii) - { - WriteLine("TYPE A"); - } - else if (type == TransferType.Binary) - { - WriteLine("TYPE I"); - } - else - { - throw new ArgumentException("Not supported argument 'type' value '" + type + "'."); - } - - string[] response = ReadResponse(); - if (!response[0].StartsWith("2")) - { - throw new FTP_ClientException(response[0]); - } - } - - /// - /// Sends PORT command to server. - /// - private void Port() - { - string[] response = null; - // We will try all IP addresses assigned to this machine, the first one that the remote machine likes will be chosen. - foreach (IPAddress ip in Dns.GetHostAddresses("")) - { - if (ip.AddressFamily == m_pDataConnection.LocalEndPoint.AddressFamily) - { - WriteLine("PORT " + ip.ToString().Replace(".", ",") + "," + - (m_pDataConnection.LocalEndPoint.Port >> 8) + "," + - (m_pDataConnection.LocalEndPoint.Port & 0xFF)); - - response = ReadResponse(); - if (response[0].StartsWith("2")) - { - m_pDataConnection.SwitchToActive(); - return; - } - } - } - - // If we reach so far PORT commant didn't suceed. - throw new FTP_ClientException(response[0]); - } - - /// - /// Sends PASV command to server. - /// - private void Pasv() - { - WriteLine("PASV"); - - string[] response = ReadResponse(); - if (!response[0].StartsWith("227")) - { - throw new FTP_ClientException(response[0]); - } - - // Parse IP:port from 227 Entering Passive Mode (192,168,1,10,1,10). - string[] parts = - response[0].Substring(response[0].IndexOf("(") + 1, - response[0].IndexOf(")") - response[0].IndexOf("(") - 1).Split(','); - - m_pDataConnection.SwitchToPassive( - new IPEndPoint(IPAddress.Parse(parts[0] + "." + parts[1] + "." + parts[2] + "." + parts[3]), - (Convert.ToInt32(parts[4]) << 8) | Convert.ToInt32(parts[5]))); - } - - /// - /// Reads FTP server response line(s). - /// - /// Returns FTP server response. - private string[] ReadResponse() - { - /* - There can be single or multiline response. - - Examples: - 226 File successfully transferred - - 226-File successfully transferred - 226 0.002 seconds (measured here), 199.65 Mbytes per second 339163 bytes received in 00:00 (8.11 MB/s) - */ - - List retVal = new List(); - while (true) - { - string response = ReadLine(); - // Server closed connection for some reason. - if (response == null) - { - throw new Exception("Remote host disconnected connection unexpectedly."); - } - retVal.Add(response); - // Multiline response. - if (response.Length >= 4 && response[3] == '-') - { - // Fall to next loop cycle. - } - // Single line response. - else - { - break; - } - } - - return retVal.ToArray(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/Client/FTP_ClientException.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/Client/FTP_ClientException.cs deleted file mode 100644 index 08ab92152..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/Client/FTP_ClientException.cs +++ /dev/null @@ -1,104 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.FTP.Client -{ - #region usings - - using System; - - #endregion - - /// - /// FTP client exception. - /// - public class FTP_ClientException : Exception - { - #region Members - - private readonly string m_ResponseText = ""; - private readonly int m_StatusCode = 500; - - #endregion - - #region Properties - - /// - /// Gets FTP status code. - /// - public int StatusCode - { - get { return m_StatusCode; } - } - - /// - /// Gets FTP server response text after status code. - /// - public string ResponseText - { - get { return m_ResponseText; } - } - - /// - /// Gets if it is permanent FTP(5xx) error. - /// - public bool IsPermanentError - { - get - { - if (m_StatusCode >= 500 && m_StatusCode <= 599) - { - return true; - } - else - { - return false; - } - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// FTP server response line. - /// Is raised when responseLine is null. - public FTP_ClientException(string responseLine) : base(responseLine) - { - if (responseLine == null) - { - throw new ArgumentNullException("responseLine"); - } - - string[] code_text = responseLine.Split(new[] {' '}, 2); - try - { - m_StatusCode = Convert.ToInt32(code_text[0]); - } - catch {} - if (code_text.Length == 2) - { - m_ResponseText = code_text[1]; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/FTP_ListItem.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/FTP_ListItem.cs deleted file mode 100644 index dcb26628a..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/FTP_ListItem.cs +++ /dev/null @@ -1,114 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.FTP -{ - #region usings - - using System; - - #endregion - - /// - /// This class holds single file or directory in the FTP server. - /// - public class FTP_ListItem - { - #region Members - - private readonly bool m_IsDir; - private readonly DateTime m_Modified; - private readonly string m_Name = ""; - private readonly long m_Size; - - #endregion - - #region Properties - - /// - /// Gets if current item is directory. - /// - public bool IsDir - { - get { return m_IsDir; } - } - - /// - /// Gets if current item is file. - /// - public bool IsFile - { - get { return !m_IsDir; } - } - - /// - /// Gets the name of the file or directory. - /// - public string Name - { - get { return m_Name; } - } - - /// - /// Gets file size in bytes. - /// - public long Size - { - get { return m_Size; } - } - - /// - /// Gets last time file or direcory was modified. - /// - public DateTime Modified - { - get { return m_Modified; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Directory or file name. - /// File size in bytes, zero for directory. - /// Directory or file last modification time. - /// Specifies if list item is directory or file. - /// Is raised when name is null. - /// Is raised when any of the arguments has invalid value. - public FTP_ListItem(string name, long size, DateTime modified, bool isDir) - { - if (name == null) - { - throw new ArgumentNullException("name"); - } - if (name == "") - { - throw new ArgumentException("Argument 'name' value must be specified."); - } - - m_Name = name; - m_Size = size; - m_Modified = modified; - m_IsDir = isDir; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/FTP_TransferMode.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/FTP_TransferMode.cs deleted file mode 100644 index d9de2faff..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/FTP_TransferMode.cs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.FTP -{ - /// - /// Specifies FTP data connection transfer mode. - /// - public enum FTP_TransferMode - { - /// - /// Active transfer mode - FTP server opens data connection FTP client. - /// - Active, - - /// - /// Passive transfer mode - FTP client opens data connection FTP server. - /// - Passive - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/Server/AuthUser_EventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/Server/AuthUser_EventArgs.cs deleted file mode 100644 index 7aead83c0..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/Server/AuthUser_EventArgs.cs +++ /dev/null @@ -1,115 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.FTP.Server -{ - /// - /// Provides data for the AuthUser event for FTP_Server. - /// - public class AuthUser_EventArgs - { - #region Members - - private readonly AuthType m_AuthType; - private readonly string m_Data = ""; - private readonly string m_PasswData = ""; - private readonly FTP_Session m_pSession; - private readonly string m_UserName = ""; - private bool m_Validated = true; - - #endregion - - #region Properties - - /// - /// Gets reference to pop3 session. - /// - public FTP_Session Session - { - get { return m_pSession; } - } - - /// - /// User name. - /// - public string UserName - { - get { return m_UserName; } - } - - /// - /// Password data. eg. for AUTH=PLAIN it's password and for AUTH=APOP it's md5HexHash. - /// - public string PasswData - { - get { return m_PasswData; } - } - - /// - /// Authentication specific data(as tag). - /// - public string AuthData - { - get { return m_Data; } - } - - /// - /// Authentication type. - /// - public AuthType AuthType - { - get { return m_AuthType; } - } - - /// - /// Gets or sets if user is valid. - /// - public bool Validated - { - get { return m_Validated; } - - set { m_Validated = value; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Reference to pop3 session. - /// Username. - /// Password data. - /// Authentication specific data(as tag). - /// Authentication type. - public AuthUser_EventArgs(FTP_Session session, - string userName, - string passwData, - string data, - AuthType authType) - { - m_pSession = session; - m_UserName = userName; - m_PasswData = passwData; - m_Data = data; - m_AuthType = authType; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/Server/FTP_Server.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/Server/FTP_Server.cs deleted file mode 100644 index b1a17ed5d..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/Server/FTP_Server.cs +++ /dev/null @@ -1,349 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.FTP.Server -{ - #region usings - - using System; - using System.IO; - using System.Net; - using System.Net.Sockets; - - #endregion - - #region Event delegates - - /// - /// Represents the method that will handle the AuthUser event for FTP_Server. - /// - /// The source of the event. - /// A AuthUser_EventArgs that contains the event data. - public delegate void AuthUserEventHandler(object sender, AuthUser_EventArgs e); - - /// - /// Represents the method that will handle the filsystem rerlated events for FTP_Server. - /// - public delegate void FileSysEntryEventHandler(object sender, FileSysEntry_EventArgs e); - - #endregion - - /// - /// FTP Server component. - /// - public class FTP_Server : SocketServer - { - #region Events - - /// - /// Occurs when connected user tryes to authenticate. - /// - public event AuthUserEventHandler AuthUser = null; - - /// - /// Occurs when server needs needs to create directory. - /// - public event FileSysEntryEventHandler CreateDir = null; - - /// - /// Occurs when server needs needs to delete directory. - /// - public event FileSysEntryEventHandler DeleteDir = null; - - /// - /// Occurs when server needs needs to delete file. - /// - public event FileSysEntryEventHandler DeleteFile = null; - - /// - /// Occurs when server needs to validatee directory. - /// - public event FileSysEntryEventHandler DirExists = null; - - /// - /// Occurs when server needs needs validate file. - /// - public event FileSysEntryEventHandler FileExists = null; - - /// - /// Occurs when server needs directory info (directories,files in deirectory). - /// - public event FileSysEntryEventHandler GetDirInfo = null; - - /// - /// Occurs when server needs needs to get file. - /// - public event FileSysEntryEventHandler GetFile = null; - - /// - /// Occurs when server needs needs to rname directory or file. - /// - public event FileSysEntryEventHandler RenameDirFile = null; - - /// - /// Occurs when POP3 session has finished and session log is available. - /// - public event LogEventHandler SessionLog = null; - - /// - /// Occurs when server needs needs to store file. - /// - public event FileSysEntryEventHandler StoreFile = null; - - /// - /// Occurs when new computer connected to FTP server. - /// - public event ValidateIPHandler ValidateIPAddress = null; - - #endregion - - #region Members - - private int m_PassiveStartPort = 20000; - - #endregion - - #region Properties - - /// - /// Gets active sessions. - /// - public new FTP_Session[] Sessions - { - get - { - SocketServerSession[] sessions = base.Sessions; - FTP_Session[] ftpSessions = new FTP_Session[sessions.Length]; - sessions.CopyTo(ftpSessions, 0); - - return ftpSessions; - } - } - - /// - /// Gets or sets passive mode public IP address what is reported to clients. - /// This property is manly needed if FTP server is running behind NAT. - /// Value null means not spcified. - /// - public IPAddress PassivePublicIP { get; set; } - - /// - /// Gets or sets passive mode start port form which server starts using ports. - /// - /// Is raised when ivalid value is passed. - public int PassiveStartPort - { - get { return m_PassiveStartPort; } - - set - { - if (value < 1) - { - throw new ArgumentException("Valu must be > 0 !"); - } - - m_PassiveStartPort = value; - } - } - - #endregion - - #region Constructor - - /// - /// Defalut constructor. - /// - public FTP_Server() - { - BindInfo = new[] {new IPBindInfo("", IPAddress.Any, 21, SslMode.None, null)}; - } - - #endregion - - #region Overrides - - /// - /// Initialize and start new session here. Session isn't added to session list automatically, - /// session must add itself to server session list by calling AddSession(). - /// - /// Connected client socket. - /// BindInfo what accepted socket. - protected override void InitNewSession(Socket socket, IPBindInfo bindInfo) - { - string sessionID = Guid.NewGuid().ToString(); - SocketEx socketEx = new SocketEx(socket); - if (LogCommands) - { - socketEx.Logger = new SocketLogger(socket, SessionLog); - socketEx.Logger.SessionID = sessionID; - } - FTP_Session session = new FTP_Session(sessionID, socketEx, bindInfo, this); - } - - #endregion - - #region Virtual methods - - /// - /// Raises event ValidateIP event. - /// - /// Server IP. - /// Connected client IP. - /// Returns true if connection allowed. - internal virtual bool OnValidate_IpAddress(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint) - { - ValidateIP_EventArgs oArg = new ValidateIP_EventArgs(localEndPoint, remoteEndPoint); - if (ValidateIPAddress != null) - { - ValidateIPAddress(this, oArg); - } - - return oArg.Validated; - } - - /// - /// Authenticates user. - /// - /// Reference to current pop3 session. - /// User name. - /// - /// - /// - /// - internal virtual bool OnAuthUser(FTP_Session session, - string userName, - string passwData, - string data, - AuthType authType) - { - AuthUser_EventArgs oArg = new AuthUser_EventArgs(session, userName, passwData, data, authType); - if (AuthUser != null) - { - AuthUser(this, oArg); - } - - return oArg.Validated; - } - - #endregion - - #region Internal methods - - internal FileSysEntry_EventArgs OnGetDirInfo(FTP_Session session, string dir) - { - FileSysEntry_EventArgs oArg = new FileSysEntry_EventArgs(session, dir, ""); - if (GetDirInfo != null) - { - GetDirInfo(this, oArg); - } - return oArg; - } - - internal bool OnDirExists(FTP_Session session, string dir) - { - FileSysEntry_EventArgs oArg = new FileSysEntry_EventArgs(session, dir, ""); - if (DirExists != null) - { - DirExists(this, oArg); - } - - return oArg.Validated; - } - - internal bool OnCreateDir(FTP_Session session, string dir) - { - FileSysEntry_EventArgs oArg = new FileSysEntry_EventArgs(session, dir, ""); - if (CreateDir != null) - { - CreateDir(this, oArg); - } - - return oArg.Validated; - } - - internal bool OnDeleteDir(FTP_Session session, string dir) - { - FileSysEntry_EventArgs oArg = new FileSysEntry_EventArgs(session, dir, ""); - if (DeleteDir != null) - { - DeleteDir(this, oArg); - } - - return oArg.Validated; - } - - internal bool OnRenameDirFile(FTP_Session session, string from, string to) - { - FileSysEntry_EventArgs oArg = new FileSysEntry_EventArgs(session, from, to); - if (RenameDirFile != null) - { - RenameDirFile(this, oArg); - } - - return oArg.Validated; - } - - internal bool OnFileExists(FTP_Session session, string file) - { - // Remove last / - file = file.Substring(0, file.Length - 1); - - FileSysEntry_EventArgs oArg = new FileSysEntry_EventArgs(session, file, ""); - if (FileExists != null) - { - FileExists(this, oArg); - } - - return oArg.Validated; - } - - internal Stream OnGetFile(FTP_Session session, string file) - { - FileSysEntry_EventArgs oArg = new FileSysEntry_EventArgs(session, file, ""); - if (GetFile != null) - { - GetFile(this, oArg); - } - - return oArg.FileStream; - } - - internal Stream OnStoreFile(FTP_Session session, string file) - { - FileSysEntry_EventArgs oArg = new FileSysEntry_EventArgs(session, file, ""); - if (StoreFile != null) - { - StoreFile(this, oArg); - } - - return oArg.FileStream; - } - - internal bool OnDeleteFile(FTP_Session session, string file) - { - FileSysEntry_EventArgs oArg = new FileSysEntry_EventArgs(session, file, ""); - if (DeleteFile != null) - { - DeleteFile(this, oArg); - } - - return oArg.Validated; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/Server/FTP_Session.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/Server/FTP_Session.cs deleted file mode 100644 index 7af26a24e..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/Server/FTP_Session.cs +++ /dev/null @@ -1,1271 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.FTP.Server -{ - #region usings - - using System; - using System.Collections; - using System.Data; - using System.IO; - using System.Net; - using System.Net.Sockets; - using System.Text; - using System.Threading; - - #endregion - - /// - /// FTP Session. - /// - public class FTP_Session : SocketServerSession - { - #region Members - - private readonly FTP_Server m_pServer; - private int m_BadCmdCount; - private string m_CurrentDir = "/"; - private bool m_PassiveMode; - private IPEndPoint m_pDataConEndPoint; - private TcpListener m_pPassiveListener; - private string m_RenameFrom = ""; - private string m_UserName = ""; - - #endregion - - #region Properties - - /// - /// Gets if sessions is in passive mode. - /// - public bool PassiveMode - { - get { return m_PassiveMode; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Session ID. - /// Server connected socket. - /// BindInfo what accepted socket. - /// Reference to server. - internal FTP_Session(string sessionID, SocketEx socket, IPBindInfo bindInfo, FTP_Server server) - : base(sessionID, socket, bindInfo, server) - { - m_pServer = server; - - // Start session proccessing - StartSession(); - } - - #endregion - - #region Methods - - /// - /// Kill this session. - /// - public override void Kill() - { - EndSession(); - } - - #endregion - - #region Overrides - - /// - /// Is called by server when session has timed out. - /// - protected internal override void OnSessionTimeout() - { - try - { - Socket.WriteLine("500 Session timeout, closing transmission channel"); - } - catch {} - - EndSession(); - } - - #endregion - - #region Utility methods - - /// - /// Starts session. - /// - private void StartSession() - { - // Add session to session list - m_pServer.AddSession(this); - - // Check if ip is allowed to connect this computer - if (m_pServer.OnValidate_IpAddress(LocalEndPoint, RemoteEndPoint)) - { - // Notify that server is ready - Socket.WriteLine("220 " + BindInfo.HostName + " FTP server ready"); - - BeginRecieveCmd(); - } - else - { - EndSession(); - } - } - - /// - /// Ends session, closes socket. - /// - private void EndSession() - { - m_pServer.RemoveSession(this); - - // Write logs to log file, if needed - if (m_pServer.LogCommands) - { - // m_pLogWriter.AddEntry("//----- Sys: 'Session:'" + this.SessionID + " removed " + DateTime.Now); - - // m_pLogWriter.Flush(); - - Socket.Logger.Flush(); - } - - if (Socket != null) - { - Socket.Shutdown(SocketShutdown.Both); - Socket.Disconnect(); - //this.Socket = null; - } - } - - /// - /// Is called when error occures. - /// - /// - private void OnError(Exception x) - { - try - { - if (x is SocketException) - { - SocketException xs = (SocketException) x; - - // Client disconnected without shutting down - if (xs.ErrorCode == 10054 || xs.ErrorCode == 10053) - { - if (m_pServer.LogCommands) - { - // m_pLogWriter.AddEntry("Client aborted/disconnected",this.SessionID,this.RemoteEndPoint.Address.ToString(),"C"); - Socket.Logger.AddTextEntry("Client aborted/disconnected"); - } - - EndSession(); - - // Exception handled, return - return; - } - } - - m_pServer.OnSysError("", x); - } - catch (Exception ex) - { - m_pServer.OnSysError("", ex); - } - } - - /// - /// Starts recieveing command. - /// - private void BeginRecieveCmd() - { - MemoryStream strm = new MemoryStream(); - Socket.BeginReadLine(strm, 1024, strm, EndRecieveCmd); - } - - /// - /// Is called if command is recieved. - /// - /// - /// - /// - /// - private void EndRecieveCmd(SocketCallBackResult result, long count, Exception exception, object tag) - { - try - { - switch (result) - { - case SocketCallBackResult.Ok: - MemoryStream strm = (MemoryStream) tag; - - string cmdLine = Encoding.Default.GetString(strm.ToArray()); - - // if(m_pServer.LogCommands){ - // m_pLogWriter.AddEntry(cmdLine + "",this.SessionID,this.RemoteEndPoint.Address.ToString(),"C"); - // } - - // Exceute command - if (SwitchCommand(cmdLine)) - { - // Session end, close session - EndSession(); - } - break; - - case SocketCallBackResult.LengthExceeded: - Socket.WriteLine("500 Line too long."); - - BeginRecieveCmd(); - break; - - case SocketCallBackResult.SocketClosed: - EndSession(); - break; - - case SocketCallBackResult.Exception: - OnError(exception); - break; - } - } - catch (Exception x) - { - OnError(x); - } - } - - /// - /// Parses and executes POP3 commmand. - /// - /// FTP command text. - /// Returns true,if session must be terminated. - private bool SwitchCommand(string commandTxt) - { - //---- Parse command --------------------------------------------------// - string[] cmdParts = commandTxt.TrimStart().Split(new[] {' '}); - string command = cmdParts[0].ToUpper().Trim(); - string argsText = Core.GetArgsText(commandTxt, command); - //---------------------------------------------------------------------// - - /* - USER - PASS - ACCT - CWD - CDUP - SMNT - QUIT - REIN - PORT - PASV - TYPE - STRU - MODE - RETR - STOR - STOU - APPE - ALLO - [ R ] - REST - RNFR - RNTO - ABOR - DELE - RMD - MKD - PWD - LIST [ ] - NLST [ ] - SITE - SYST - STAT [ ] - HELP [ ] - NOOP - */ - - switch (command) - { - case "USER": - USER(argsText); - break; - - case "PASS": - PASS(argsText); - break; - - case "CWD": - CWD(argsText); - break; - - case "CDUP": - CDUP(argsText); - break; - - // case "REIN": - // break; - - case "QUIT": - QUIT(); - return true; - - case "PORT": - PORT(argsText); - break; - - case "PASV": - PASV(argsText); - break; - - case "TYPE": - TYPE(argsText); - break; - - case "RETR": - RETR(argsText); - break; - - case "STOR": - STOR(argsText); - break; - - // case "STOU": - // break; - - case "APPE": - APPE(argsText); - break; - - // case "REST": - // break; - - case "RNFR": - RNFR(argsText); - break; - - case "RNTO": - RNTO(argsText); - break; - - // case "ABOR": - // break; - - case "DELE": - DELE(argsText); - break; - - case "RMD": - RMD(argsText); - break; - - case "MKD": - MKD(argsText); - break; - - case "PWD": - PWD(); - break; - - case "LIST": - LIST(argsText); - break; - - case "NLST": - NLST(argsText); - break; - - case "SYST": - SYST(); - break; - - // case "STAT": - // break; - - // case "HELP": - // break; - - case "NOOP": - NOOP(); - break; - - case "": - break; - - default: - Socket.WriteLine("500 Invalid command " + command); - - //---- Check that maximum bad commands count isn't exceeded ---------------// - if (m_BadCmdCount > m_pServer.MaxBadCommands - 1) - { - Socket.WriteLine("421 Too many bad commands, closing transmission channel"); - return true; - } - m_BadCmdCount++; - //-------------------------------------------------------------------------// - - break; - } - - BeginRecieveCmd(); - - return false; - } - - private void USER(string argsText) - { - if (Authenticated) - { - Socket.WriteLine("500 You are already authenticated"); - return; - } - if (m_UserName.Length > 0) - { - Socket.WriteLine("500 username is already specified, please specify password"); - return; - } - - string[] param = argsText.Split(new[] {' '}); - - // There must be only one parameter - userName - if (argsText.Length > 0 && param.Length == 1) - { - string userName = param[0]; - - Socket.WriteLine("331 Password required or user:'" + userName + "'"); - m_UserName = userName; - } - else - { - Socket.WriteLine("500 Syntax error. Syntax:{USER username}"); - } - } - - private void PASS(string argsText) - { - if (Authenticated) - { - Socket.WriteLine("500 You are already authenticated"); - return; - } - if (m_UserName.Length == 0) - { - Socket.WriteLine("503 please specify username first"); - return; - } - - string[] param = argsText.Split(new[] {' '}); - - // There may be only one parameter - password - if (param.Length == 1) - { - string password = param[0]; - - // Authenticate user - if (m_pServer.OnAuthUser(this, m_UserName, password, "", AuthType.Plain)) - { - Socket.WriteLine("230 Password ok"); - - SetUserName(m_UserName); - } - else - { - Socket.WriteLine("530 UserName or Password is incorrect"); - m_UserName = ""; // Reset userName !!! - } - } - else - { - Socket.WriteLine("500 Syntax error. Syntax:{PASS userName}"); - } - } - - private void CWD(string argsText) - { - /* - This command allows the user to work with a different - directory or dataset for file storage or retrieval without - altering his login or accounting information. Transfer - parameters are similarly unchanged. The argument is a - pathname specifying a directory or other system dependent - file group designator. - */ - if (!Authenticated) - { - Socket.WriteLine("530 Please authenticate firtst !"); - return; - } - - string dir = GetAndNormailizePath(argsText); - - //Check if dir exists and is accesssible for this user - if (m_pServer.OnDirExists(this, dir)) - { - m_CurrentDir = dir; - - Socket.WriteLine("250 CDW command successful."); - } - else - { - Socket.WriteLine("550 System can't find directory '" + dir + "'."); - } - } - - private void CDUP(string argsText) - { - /* - This command is a special case of CWD, and is included to - simplify the implementation of programs for transferring - directory trees between operating systems having different - syntaxes for naming the parent directory. The reply codes - shall be identical to the reply codes of CWD. - */ - if (!Authenticated) - { - Socket.WriteLine("530 Please authenticate firtst !"); - return; - } - - // Move dir up if possible - string[] pathParts = m_CurrentDir.Split('/'); - if (pathParts.Length > 1) - { - m_CurrentDir = ""; - for (int i = 0; i < (pathParts.Length - 2); i++) - { - m_CurrentDir += pathParts[i] + "/"; - } - - if (m_CurrentDir.Length == 0) - { - m_CurrentDir = "/"; - } - } - - Socket.WriteLine("250 CDUP command successful."); - } - - private void PWD() - { - /* - This command causes the name of the current working - directory to be returned in the reply. - */ - if (!Authenticated) - { - Socket.WriteLine("530 Please authenticate firtst !"); - return; - } - - Socket.WriteLine("257 \"" + m_CurrentDir + "\" is current directory."); - } - - private void RETR(string argsText) - { - /* - This command causes the server-DTP to transfer a copy of the - file, specified in the pathname, to the server- or user-DTP - at the other end of the data connection. The status and - contents of the file at the server site shall be unaffected. - */ - if (!Authenticated) - { - Socket.WriteLine("530 Please authenticate firtst !"); - return; - } - - // ToDo: custom errors - //---- See if path accessible - Stream fileStream = null; - try - { - string file = GetAndNormailizePath(argsText); - file = file.Substring(0, file.Length - 1); - - fileStream = m_pServer.OnGetFile(this, file); - } - catch - { - Socket.WriteLine("550 Access denied or directory dosen't exist !"); - return; - } - - Socket socket = GetDataConnection(); - if (socket == null) - { - return; - } - - try - { - // string file = GetAndNormailizePath(argsText); - // file = file.Substring(0,file.Length - 1); - - // using(Stream fileStream = m_pServer.OnGetFile(file)){ - if (fileStream != null) - { - // ToDo: bandwidth limiting here - - int readed = 1; - while (readed > 0) - { - byte[] data = new byte[10000]; - readed = fileStream.Read(data, 0, data.Length); - socket.Send(data, readed, SocketFlags.None); - } - } - // } - - socket.Shutdown(SocketShutdown.Both); - socket.Close(); - - Socket.WriteLine("226 Transfer Complete."); - } - catch - { - Socket.WriteLine("426 Connection closed; transfer aborted."); - } - - fileStream.Close(); - } - - private void STOR(string argsText) - { - /* - This command causes the server-DTP to transfer a copy of the - file, specified in the pathname, to the server- or user-DTP - at the other end of the data connection. The status and - contents of the file at the server site shall be unaffected. - */ - if (!Authenticated) - { - Socket.WriteLine("530 Please authenticate firtst !"); - return; - } - - // ToDo: custom errors - //---- See if path accessible - Stream fileStream = null; - try - { - string file = GetAndNormailizePath(argsText); - file = file.Substring(0, file.Length - 1); - - fileStream = m_pServer.OnStoreFile(this, file); - } - catch - { - Socket.WriteLine("550 Access denied or directory dosen't exist !"); - return; - } - - Socket socket = GetDataConnection(); - if (socket == null) - { - return; - } - try - { - string file = GetAndNormailizePath(argsText); - file = file.Substring(0, file.Length - 1); - - // using(Stream fileStream = m_pServer.OnStoreFile(file)){ - if (fileStream != null) - { - // ToDo: bandwidth limiting here - - int readed = 1; - while (readed > 0) - { - byte[] data = new byte[10000]; - readed = socket.Receive(data); - fileStream.Write(data, 0, readed); - } - } - // } - - socket.Shutdown(SocketShutdown.Both); - socket.Close(); - - Socket.WriteLine("226 Transfer Complete."); - } - catch - { - // ToDo: report right errors here. eg. DataConnection read time out, ... . - Socket.WriteLine("426 Connection closed; transfer aborted."); - } - - fileStream.Close(); - } - - private void DELE(string argsText) - { - /* - This command causes the file specified in the pathname to be - deleted at the server site. If an extra level of protection - is desired (such as the query, "Do you really wish to - delete?"), it should be provided by the user-FTP process. - */ - if (!Authenticated) - { - Socket.WriteLine("530 Please authenticate firtst !"); - return; - } - - string file = GetAndNormailizePath(argsText); - file = file.Substring(0, file.Length - 1); - - m_pServer.OnDeleteFile(this, file); - - Socket.WriteLine("250 file deleted."); - } - - private void APPE(string argsText) - { - /* - This command causes the server-DTP to accept the data - transferred via the data connection and to store the data in - a file at the server site. If the file specified in the - pathname exists at the server site, then the data shall be - appended to that file; otherwise the file specified in the - pathname shall be created at the server site. - */ - if (!Authenticated) - { - Socket.WriteLine("530 Please authenticate firtst !"); - return; - } - - Socket.WriteLine("500 unimplemented"); - } - - private void RNFR(string argsText) - { - /* - This command specifies the old pathname of the file which is - to be renamed. This command must be immediately followed by - a "rename to" command specifying the new file pathname. - */ - if (!Authenticated) - { - Socket.WriteLine("530 Please authenticate firtst !"); - return; - } - - string dir = GetAndNormailizePath(argsText); - - if (m_pServer.OnDirExists(this, dir) || m_pServer.OnFileExists(this, dir)) - { - Socket.WriteLine("350 Please specify destination name."); - - m_RenameFrom = dir; - } - else - { - Socket.WriteLine("550 File or directory doesn't exist."); - } - } - - private void RNTO(string argsText) - { - /* - This command specifies the new pathname of the file - specified in the immediately preceding "rename from" - command. Together the two commands cause a file to be - renamed. - */ - if (!Authenticated) - { - Socket.WriteLine("530 Please authenticate firtst !"); - return; - } - if (m_RenameFrom.Length == 0) - { - Socket.WriteLine("503 Bad sequence of commands."); - return; - } - - string dir = GetAndNormailizePath(argsText); - - if (m_pServer.OnRenameDirFile(this, m_RenameFrom, dir)) - { - Socket.WriteLine("250 Directory renamed."); - - m_RenameFrom = ""; - } - else - { - Socket.WriteLine("550 Error renameing directory or file ."); - } - } - - private void RMD(string argsText) - { - /* - This command causes the directory specified in the pathname - to be removed as a directory (if the pathname is absolute) - or as a subdirectory of the current working directory (if - the pathname is relative). - */ - if (!Authenticated) - { - Socket.WriteLine("530 Please authenticate firtst !"); - return; - } - - string dir = GetAndNormailizePath(argsText); - - if (m_pServer.OnDeleteDir(this, dir)) - { - Socket.WriteLine("250 \"" + dir + "\" directory deleted."); - } - else - { - Socket.WriteLine("550 Directory deletion failed."); - } - } - - private void MKD(string argsText) - { - /* - This command causes the directory specified in the pathname - to be created as a directory (if the pathname is absolute) - or as a subdirectory of the current working directory (if - the pathname is relative). - */ - if (!Authenticated) - { - Socket.WriteLine("530 Please authenticate firtst !"); - return; - } - - string dir = GetAndNormailizePath(argsText); - - if (m_pServer.OnCreateDir(this, dir)) - { - Socket.WriteLine("257 \"" + dir + "\" directory created."); - } - else - { - Socket.WriteLine("550 Directory creation failed."); - } - } - - private void LIST(string argsText) - { - /* - This command causes a list to be sent from the server to the - passive DTP. If the pathname specifies a directory or other - group of files, the server should transfer a list of files - in the specified directory. If the pathname specifies a - file then the server should send current information on the - file. A null argument implies the user's current working or - default directory. The data transfer is over the data - connection in type ASCII or type EBCDIC. (The user must - ensure that the TYPE is appropriately ASCII or EBCDIC). - Since the information on a file may vary widely from system - to system, this information may be hard to use automatically - in a program, but may be quite useful to a human user. - */ - if (!Authenticated) - { - Socket.WriteLine("530 Please authenticate firtst !"); - return; - } - - // ToDo: custom errors - //---- See if path accessible - FileSysEntry_EventArgs eArgs = m_pServer.OnGetDirInfo(this, GetAndNormailizePath(argsText)); - if (!eArgs.Validated) - { - Socket.WriteLine("550 Access denied or directory dosen't exist !"); - return; - } - DataSet ds = eArgs.DirInfo; - - Socket socket = GetDataConnection(); - if (socket == null) - { - return; - } - - try - { - // string dir = GetAndNormailizePath(argsText); - // DataSet ds = m_pServer.OnGetDirInfo(dir); - - foreach (DataRow dr in ds.Tables["DirInfo"].Rows) - { - string name = dr["Name"].ToString(); - string date = Convert.ToDateTime(dr["Date"]).ToString("MM-dd-yy hh:mmtt"); - bool isDir = Convert.ToBoolean(dr["IsDirectory"]); - - if (isDir) - { - socket.Send(Encoding.Default.GetBytes(date + " " + name + "\r\n")); - } - else - { - socket.Send(Encoding.Default.GetBytes(date + " " + dr["Size"] + " " + name + "\r\n")); - } - } - - socket.Shutdown(SocketShutdown.Both); - socket.Close(); - - Socket.WriteLine("226 Transfer Complete."); - } - catch - { - Socket.WriteLine("426 Connection closed; transfer aborted."); - } - } - - private void NLST(string argsText) - { - /* - This command causes a directory listing to be sent from - server to user site. The pathname should specify a - directory or other system-specific file group descriptor; a - null argument implies the current directory. The server - will return a stream of names of files and no other - information. The data will be transferred in ASCII or - EBCDIC type over the data connection as valid pathname - strings separated by or . (Again the user must - ensure that the TYPE is correct.) This command is intended - to return information that can be used by a program to - further process the files automatically. For example, in - the implementation of a "multiple get" function. - */ - if (!Authenticated) - { - Socket.WriteLine("530 Please authenticate firtst !"); - return; - } - - //---- See if path accessible - FileSysEntry_EventArgs eArgs = m_pServer.OnGetDirInfo(this, GetAndNormailizePath(argsText)); - if (!eArgs.Validated) - { - Socket.WriteLine("550 Access denied or directory dosen't exist !"); - return; - } - DataSet ds = eArgs.DirInfo; - - Socket socket = GetDataConnection(); - if (socket == null) - { - return; - } - - try - { - // string dir = GetAndNormailizePath(argsText); - // DataSet ds = m_pServer.OnGetDirInfo(dir); - - foreach (DataRow dr in ds.Tables["DirInfo"].Rows) - { - socket.Send(Encoding.Default.GetBytes(dr["Name"] + "\r\n")); - } - socket.Send(Encoding.Default.GetBytes("aaa\r\n")); - - socket.Shutdown(SocketShutdown.Both); - socket.Close(); - - Socket.WriteLine("226 Transfer Complete."); - } - catch - { - Socket.WriteLine("426 Connection closed; transfer aborted."); - } - } - - private void TYPE(string argsText) - { - /* - The argument specifies the representation type as described - in the Section on Data Representation and Storage. Several - types take a second parameter. The first parameter is - denoted by a single Telnet character, as is the second - Format parameter for ASCII and EBCDIC; the second parameter - for local byte is a decimal integer to indicate Bytesize. - The parameters are separated by a (Space, ASCII code - 32). - - The following codes are assigned for type: - - \ / - A - ASCII | | N - Non-print - |-><-| T - Telnet format effectors - E - EBCDIC| | C - Carriage Control (ASA) - / \ - I - Image - - L - Local byte Byte size - - The default representation type is ASCII Non-print. If the - Format parameter is changed, and later just the first - argument is changed, Format then returns to the Non-print - default. - */ - if (!Authenticated) - { - Socket.WriteLine("530 Please authenticate firtst !"); - return; - } - - if (argsText.Trim().ToUpper() == "A" || argsText.Trim().ToUpper() == "I") - { - Socket.WriteLine("200 Type is set to " + argsText + "."); - } - else - { - Socket.WriteLine("500 Invalid type " + argsText + "."); - } - } - - private void PORT(string argsText) - { - /* - The argument is a HOST-PORT specification for the data port - to be used in data connection. There are defaults for both - the user and server data ports, and under normal - circumstances this command and its reply are not needed. If - this command is used, the argument is the concatenation of a - 32-bit internet host address and a 16-bit TCP port address. - This address information is broken into 8-bit fields and the - value of each field is transmitted as a decimal number (in - character string representation). The fields are separated - by commas. A port command would be: - - PORT h1,h2,h3,h4,p1,p2 - - where h1 is the high order 8 bits of the internet host - address. - */ - if (!Authenticated) - { - Socket.WriteLine("530 Please authenticate firtst !"); - return; - } - - string[] parts = argsText.Split(','); - if (parts.Length != 6) - { - Socket.WriteLine("550 Invalid arguments."); - return; - } - - string ip = parts[0] + "." + parts[1] + "." + parts[2] + "." + parts[3]; - int port = (Convert.ToInt32(parts[4]) << 8) | Convert.ToInt32(parts[5]); - - m_pDataConEndPoint = new IPEndPoint(Dns.GetHostEntry(ip).AddressList[0], port); - - Socket.WriteLine("200 PORT Command successful."); - } - - private void PASV(string argsText) - { - /* - This command requests the server-DTP to "listen" on a data - port (which is not its default data port) and to wait for a - connection rather than initiate one upon receipt of a - transfer command. The response to this command includes the - host and port address this server is listening on. - */ - if (!Authenticated) - { - Socket.WriteLine("530 Please authenticate firtst !"); - return; - } - - //--- Find free port -------------------------------// - int port = m_pServer.PassiveStartPort; - while (true) - { - try - { - m_pPassiveListener = new TcpListener(IPAddress.Any, port); - m_pPassiveListener.Start(); - - // If we reach here then port is free - break; - } - catch {} - - port++; - } - //--------------------------------------------------// - - // Notify client on what IP and port server is listening client to connect. - // PORT h1,h2,h3,h4,p1,p2 - if (m_pServer.PassivePublicIP != null) - { - Socket.WriteLine("227 Entering Passive Mode (" + m_pServer.PassivePublicIP + "," + (port >> 8) + - "," + (port & 255) + ")."); - } - else - { - Socket.WriteLine("227 Entering Passive Mode (" + ((IPEndPoint) Socket.LocalEndPoint).Address + - "," + (port >> 8) + "," + (port & 255) + ")."); - } - m_PassiveMode = true; - } - - private void SYST() - { - /* - This command is used to find out the type of operating - system at the server. The reply shall have as its first - word one of the system names listed in the current version - of the Assigned Numbers document [4]. - */ - if (!Authenticated) - { - Socket.WriteLine("530 Please authenticate firtst !"); - return; - } - - Socket.WriteLine("215 Windows_NT"); - } - - private void NOOP() - { - /* - This command does not affect any parameters or previously - entered commands. It specifies no action other than that the - server send an OK reply. - */ - Socket.WriteLine("200 OK"); - } - - private void QUIT() - { - /* - This command terminates a USER and if file transfer is not - in progress, the server closes the control connection. If - file transfer is in progress, the connection will remain - open for result response and the server will then close it. - If the user-process is transferring files for several USERs - but does not wish to close and then reopen connections for - each, then the REIN command should be used instead of QUIT. - - An unexpected close on the control connection will cause the - server to take the effective action of an abort (ABOR) and a - logout (QUIT). - */ - Socket.WriteLine("221 FTP server signing off"); - } - - private string GetAndNormailizePath(string path) - { - // If conatins \, replace with / - string dir = path.Replace("\\", "/"); - - // If doesn't end with /, append it - if (!dir.EndsWith("/")) - { - dir += "/"; - } - - // Current path + path wanted if path not starting / - if (!path.StartsWith("/")) - { - dir = m_CurrentDir + dir; - } - - //--- Normalize path, eg. /ivx/../test must be /test -----// - ArrayList pathParts = new ArrayList(); - string[] p = dir.Split('/'); - pathParts.AddRange(p); - - for (int i = 0; i < pathParts.Count; i++) - { - if (pathParts[i].ToString() == "..") - { - // Don't let go over root path, eg. /../ - there wanted directory upper root. - // Just skip this movement. - if (i > 0) - { - pathParts.RemoveAt(i - 1); - i--; - } - - pathParts.RemoveAt(i); - i--; - } - } - - dir = dir.Replace("//", "/"); - - return dir; - } - - private Socket GetDataConnection() - { - Socket socket = null; - try - { - if (m_PassiveMode) - { - //--- Wait ftp client connection ---------------------------// - long startTime = DateTime.Now.Ticks; - // Wait ftp server to connect - while (!m_pPassiveListener.Pending()) - { - Thread.Sleep(50); - - // Time out after 30 seconds - if ((DateTime.Now.Ticks - startTime)/10000 > 20000) - { - throw new Exception("Ftp server didn't respond !"); - } - } - //-----------------------------------------------------------// - - socket = m_pPassiveListener.AcceptSocket(); - - Socket.WriteLine("125 Data connection open, Transfer starting."); - } - else - { - Socket.WriteLine("150 Opening data connection."); - - socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - socket.Connect(m_pDataConEndPoint); - } - } - catch - { - Socket.WriteLine("425 Can't open data connection."); - return null; - } - - m_PassiveMode = false; - - return socket; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/Server/FileSysEntry_EventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/Server/FileSysEntry_EventArgs.cs deleted file mode 100644 index b6dee46ff..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/FTP/Server/FileSysEntry_EventArgs.cs +++ /dev/null @@ -1,118 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.FTP.Server -{ - #region usings - - using System; - using System.Data; - using System.IO; - - #endregion - - /// - /// Provides data for the filesytem related events for FTP_Server. - /// - public class FileSysEntry_EventArgs - { - #region Members - - private readonly DataSet m_DsDirInfo; - private readonly string m_Name = ""; - private readonly string m_NewName = ""; - private readonly FTP_Session m_pSession; - private bool m_Validated = true; - - #endregion - - #region Properties - - /// - /// Gets reference to FTP session. - /// - public FTP_Session Session - { - get { return m_pSession; } - } - - /// - /// Gets directory or file name with path. - /// - public string Name - { - get { return m_Name; } - } - - /// - /// Gets new directory or new file name with path. This filled for Rename event only. - /// - public string NewName - { - get { return m_NewName; } - } - - /// - /// Gets or sets file stream. - /// - public Stream FileStream { get; set; } - - /// - /// Gets or sets if operation was successful. NOTE: default value is true. - /// - public bool Validated - { - get { return m_Validated; } - - set { m_Validated = value; } - } - - /// - /// Gets reference to dir listing info. Please Fill .Tables["DirInfo"] table with required fields. - /// - public DataSet DirInfo - { - get { return m_DsDirInfo; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// - /// - /// - public FileSysEntry_EventArgs(FTP_Session session, string name, string newName) - { - m_Name = name; - m_NewName = newName; - m_pSession = session; - - m_DsDirInfo = new DataSet(); - DataTable dt = m_DsDirInfo.Tables.Add("DirInfo"); - dt.Columns.Add("Name"); - dt.Columns.Add("Date", typeof (DateTime)); - dt.Columns.Add("Size", typeof (long)); - dt.Columns.Add("IsDirectory", typeof (bool)); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/HTTP/Server/HTTP_Server.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/HTTP/Server/HTTP_Server.cs deleted file mode 100644 index 934cefe3e..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/HTTP/Server/HTTP_Server.cs +++ /dev/null @@ -1,85 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.Text; -using System.Net; -using System.Net.Sockets; - -namespace LumiSoft.Net.HTTP.Server -{ - /// - /// HTTP server component. - /// - public class HTTP_Server : SocketServer - { - /// - /// Defalut constructor. - /// - public HTTP_Server() : base() - { - this.BindInfo = new BindInfo[]{new BindInfo(IPAddress.Any,80,false,null)}; - } - - - #region override InitNewSession - - /// - /// Initialize and start new session here. Session isn't added to session list automatically, - /// session must add itself to server session list by calling AddSession(). - /// - /// Connected client socket. - /// BindInfo what accepted socket. - protected override void InitNewSession(Socket socket,BindInfo bindInfo) - {/* - // Check maximum conncurent connections from 1 IP. - if(m_MaxConnectionsPerIP > 0){ - lock(this.Sessions){ - int nSessions = 0; - foreach(SocketServerSession s in this.Sessions){ - IPEndPoint ipEndpoint = s.RemoteEndPoint; - if(ipEndpoint != null){ - if(ipEndpoint.Address.Equals(((IPEndPoint)socket.RemoteEndPoint).Address)){ - nSessions++; - } - } - - // Maximum allowed exceeded - if(nSessions >= m_MaxConnectionsPerIP){ - socket.Send(System.Text.Encoding.ASCII.GetBytes("-ERR Maximum connections from your IP address is exceeded, try again later !\r\n")); - socket.Shutdown(SocketShutdown.Both); - socket.Close(); - return; - } - } - } - }*/ - - string sessionID = Guid.NewGuid().ToString(); - SocketEx socketEx = new SocketEx(socket); - if(LogCommands){ - //socketEx.Logger = new SocketLogger(socket,this.SessionLog); - //socketEx.Logger.SessionID = sessionID; - } - HTTP_Session session = new HTTP_Session(sessionID,socketEx,bindInfo,this); - } - - #endregion - - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/HTTP/Server/HTTP_Session.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/HTTP/Server/HTTP_Session.cs deleted file mode 100644 index 1d0daf455..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/HTTP/Server/HTTP_Session.cs +++ /dev/null @@ -1,284 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.IO; -using System.Collections.Generic; -using System.Text; -using System.Net; -using System.Net.Sockets; - -namespace LumiSoft.Net.HTTP.Server -{ - /// - /// Default constructor. - /// - public class HTTP_Session : SocketServerSession - { - private HTTP_Server m_pServer = null; - - /// - /// Default constructor. - /// - /// Session ID. - /// Server connected socket. - /// BindInfo what accepted socket. - /// Reference to server. - internal HTTP_Session(string sessionID,SocketEx socket,BindInfo bindInfo,HTTP_Server server) : base(sessionID,socket,bindInfo,server) - { - m_pServer = server; - - StartSession(); - } - - - #region method StartSession - - /// - /// Starts session. - /// - private void StartSession() - { - // Add session to session list - m_pServer.AddSession(this); - - try{ - BeginRecieveCmd();/* - - // Check if ip is allowed to connect this computer - if(m_pServer.OnValidate_IpAddress(this.LocalEndPoint,this.RemoteEndPoint)){ - //--- Dedicated SSL connection, switch to SSL -----------------------------------// - if(this.BindInfo.SSL){ - try{ - this.Socket.SwitchToSSL(this.BindInfo.SSL_Certificate); - - if(this.Socket.Logger != null){ - this.Socket.Logger.AddTextEntry("SSL negotiation completed successfully."); - } - } - catch(Exception x){ - if(this.Socket.Logger != null){ - this.Socket.Logger.AddTextEntry("SSL handshake failed ! " + x.Message); - - EndSession(); - return; - } - } - } - //-------------------------------------------------------------------------------// - - BeginRecieveCmd(); - } - else{ - EndSession(); - }*/ - } - catch(Exception x){ - OnError(x); - } - } - - #endregion - - #region method EndSession - - /// - /// Ends session, closes socket. - /// - private void EndSession() - { - try{ - // Write logs to log file, if needed - if(m_pServer.LogCommands){ - this.Socket.Logger.Flush(); - } - - if(this.Socket != null){ - this.Socket.Shutdown(SocketShutdown.Both); - this.Socket.Disconnect(); - //this.Socket = null; - } - } - catch{ // We don't need to check errors here, because they only may be Socket closing errors. - } - finally{ - m_pServer.RemoveSession(this); - } - } - - #endregion - - - #region method OnError - - /// - /// Is called when error occures. - /// - /// - private void OnError(Exception x) - { - try{ - // We must see InnerException too, SocketException may be as inner exception. - SocketException socketException = null; - if(x is SocketException){ - socketException = (SocketException)x; - } - else if(x.InnerException != null && x.InnerException is SocketException){ - socketException = (SocketException)x.InnerException; - } - - if(socketException != null){ - // Client disconnected without shutting down - if(socketException.ErrorCode == 10054 || socketException.ErrorCode == 10053){ - if(m_pServer.LogCommands){ - this.Socket.Logger.AddTextEntry("Client aborted/disconnected"); - } - - EndSession(); - - // Exception handled, return - return; - } - } - - m_pServer.OnSysError("",x); - } - catch(Exception ex){ - m_pServer.OnSysError("",ex); - } - } - - #endregion - - - #region method BeginRecieveCmd - - /// - /// Starts recieveing command. - /// - private void BeginRecieveCmd() - { - MemoryStream strm = new MemoryStream(); - this.Socket.BeginReadLine(strm,1024,strm,new SocketCallBack(this.EndRecieveCmd)); - } - - #endregion - - #region method EndRecieveCmd - - /// - /// Is called if command is recieved. - /// - /// - /// - /// - /// - private void EndRecieveCmd(SocketCallBackResult result,long count,Exception exception,object tag) - { - try{ - switch(result) - { - case SocketCallBackResult.Ok: - MemoryStream strm = (MemoryStream)tag; - - string cmdLine = System.Text.Encoding.Default.GetString(strm.ToArray()); - - // Exceute command - if(SwitchCommand(cmdLine)){ - // Session end, close session - EndSession(); - } - break; - - case SocketCallBackResult.LengthExceeded: - this.Socket.WriteLine("-ERR Line too long."); - - BeginRecieveCmd(); - break; - - case SocketCallBackResult.SocketClosed: - EndSession(); - break; - - case SocketCallBackResult.Exception: - OnError(exception); - break; - } - } - catch(Exception x){ - OnError(x); - } - } - - #endregion - - - #region method SwitchCommand - - /// - /// Parses and executes HTTP commmand. - /// - /// Command line. - /// Returns true,if session must be terminated. - private bool SwitchCommand(string commandLine) - { - /* RFC 2616 5.1 Request-Line - - The Request-Line begins with a method token, followed by the - Request-URI and the protocol version, and ending with CRLF. The - elements are separated by SP characters. No CR or LF is allowed - except in the final CRLF sequence. - - Request-Line = Method SP Request-URI SP HTTP-Version CRLF - */ - - string[] parts = TextUtils.SplitQuotedString(commandLine,' '); - string method = parts[0].ToUpper(); - string uri = parts[1]; - string httpVersion = parts[2]; - - //if(method == "OPTIONS"){ - //} - if(method == "GET"){ - }/* - else if(method == "HEAD"){ - } - else if(method == "POST"){ - } - else if(method == "PUT"){ - } - else if(method == "DELETE"){ - } - else if(method == "TRACE"){ - } - else if(method == "CONNECT"){ - }*/ - else{ - } - - return false; - } - - #endregion - - - private void GET() - { - } - - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/HostEndPoint.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/HostEndPoint.cs deleted file mode 100644 index 8b59c870e..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/HostEndPoint.cs +++ /dev/null @@ -1,191 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - #region usings - - using System; - using System.Net; - - #endregion - - /// - /// Represents a network endpoint as an host(name or IP address) and a port number. - /// - public class HostEndPoint - { - #region Members - - private readonly string m_Host = ""; - private readonly int m_Port; - - #endregion - - #region Properties - - /// - /// Gets if Host is IP address. - /// - public bool IsIPAddress - { - get { return Net_Utils.IsIPAddress(m_Host); } - } - - /// - /// Gets host name or IP address. - /// - public string Host - { - get { return m_Host; } - } - - /// - /// Gets the port number of the endpoint. Value -1 means port not specified. - /// - public int Port - { - get { return m_Port; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Host name or IP address. - /// The port number associated with the host. Value -1 means port not specified. - /// Is raised when host is null. - /// Is raised when any of the arguments has invalid value. - public HostEndPoint(string host, int port) - { - if (host == null) - { - throw new ArgumentNullException("host"); - } - if (host == "") - { - throw new ArgumentException("Argument 'host' value must be specified."); - } - - m_Host = host; - m_Port = port; - } - - /// - /// Default constructor. - /// - /// Host IP end point. - /// Is raised when endPoint is null reference. - public HostEndPoint(IPEndPoint endPoint) - { - if (endPoint == null) - { - throw new ArgumentNullException("endPoint"); - } - - m_Host = endPoint.Address.ToString(); - m_Port = endPoint.Port; - } - - #endregion - - #region Methods - - /// - /// Parses HostEndPoint from the specified string. - /// - /// HostEndPoint value. - /// Returns parsed HostEndPoint value. - /// Is raised when value is null. - /// Is raised when any of the arguments has invalid value. - public static HostEndPoint Parse(string value) - { - return Parse(value, -1); - } - - /// - /// Parses HostEndPoint from the specified string. - /// - /// HostEndPoint value. - /// If port isn't specified in value, specified port will be used. - /// Returns parsed HostEndPoint value. - /// Is raised when value is null. - /// Is raised when any of the arguments has invalid value. - public static HostEndPoint Parse(string value, int defaultPort) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - if (value == "") - { - throw new ArgumentException("Argument 'value' value must be specified."); - } - - // We have IP address without a port. - try - { - IPAddress.Parse(value); - - return new HostEndPoint(value, defaultPort); - } - catch - { - // We have host name with port. - if (value.IndexOf(':') > -1) - { - string[] host_port = value.Split(new[] {':'}, 2); - - try - { - return new HostEndPoint(host_port[0], Convert.ToInt32(host_port[1])); - } - catch - { - throw new ArgumentException("Argument 'value' has invalid value."); - } - } - // We have host name without port. - else - { - return new HostEndPoint(value, defaultPort); - } - } - } - - /// - /// Returns HostEndPoint as string. - /// - /// Returns HostEndPoint as string. - public override string ToString() - { - if (m_Port == -1) - { - return m_Host; - } - else - { - return m_Host + ":" + m_Port; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ICMP/Icmp.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ICMP/Icmp.cs deleted file mode 100644 index ad8e73792..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ICMP/Icmp.cs +++ /dev/null @@ -1,336 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.ICMP -{ - #region usings - - using System; - using System.Collections.Generic; - using System.Net; - using System.Net.Sockets; - - #endregion - - /// - /// ICMP type. - /// - public enum ICMP_Type - { - /// - /// Echo rely. - /// - EchoReply = 0, - - /// - /// Time to live exceeded reply. - /// - TimeExceeded = 11, - - /// - /// Echo. - /// - Echo = 8, - } - - #region class EchoMessage - - /// - /// Echo reply message. - /// - public class EchoMessage - { - #region Members - - private readonly IPAddress m_pIP; - private readonly int m_Time; - private readonly int m_TTL; - - #endregion - - #region Properties - - /// - /// Gets IP address what sent echo message. - /// - public IPAddress IPAddress - { - get { return m_pIP; } - } - - /* - /// - /// Gets time to live in milli seconds. - /// - public int TTL - { - get{ return m_TTL; } - }*/ - - /// - /// Gets time in milliseconds what toke to get reply. - /// - public int ReplyTime - { - get { return m_Time; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// IP address what sent echo message. - /// Time to live in milli seconds. - /// Time what elapsed before getting echo response. - internal EchoMessage(IPAddress ip, int ttl, int time) - { - m_pIP = ip; - m_TTL = ttl; - m_Time = time; - } - - #endregion - - #region Methods - - /// - /// - /// - /// - /// - [Obsolete("Will be removed !")] - public static string ToStringEx(EchoMessage[] messages) - { - string retVal = ""; - - foreach (EchoMessage m in messages) - { - retVal += m.ToStringEx() + "\r\n"; - } - - return retVal; - } - - /// - /// - /// - /// - [Obsolete("Will be removed !")] - public string ToStringEx() - { - return "TTL=" + m_TTL + "\tTime=" + m_Time + "ms" + "\tIP=" + m_pIP; - } - - #endregion - } - - #endregion - - /// - /// Icmp utils. - /// - public class Icmp - { - #region Methods - - /// - /// Traces specified ip. - /// - /// Destination IP address. - /// - public static EchoMessage[] Trace(string destIP) - { - return Trace(IPAddress.Parse(destIP), 2000); - } - - /// - /// Traces specified ip. - /// - /// IP address to tracce. - /// Send recieve timeout in milli seconds. - /// - public static EchoMessage[] Trace(IPAddress ip, int timeout) - { - List retVal = new List(); - - //Create Raw ICMP Socket - Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp); - - IPEndPoint ipdest = new IPEndPoint(ip, 80); - EndPoint endpoint = (new IPEndPoint(Dns.GetHostEntry(Dns.GetHostName()).AddressList[0], 80)); - - ushort id = (ushort) DateTime.Now.Millisecond; - byte[] sendPacket = CreatePacket(id); - - int continuesNoReply = 0; - //send requests with increasing number of TTL - for (int ittl = 1; ittl <= 30; ittl++) - { - byte[] buffer = new byte[1024]; - - try - { - //Socket options to set TTL and Timeouts - s.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.IpTimeToLive, ittl); - s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, timeout); - s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, timeout); - - //Get current time - DateTime startTime = DateTime.Now; - - //Send Request - s.SendTo(sendPacket, sendPacket.Length, SocketFlags.None, ipdest); - - //Receive - s.ReceiveFrom(buffer, buffer.Length, SocketFlags.None, ref endpoint); - - //Calculate time required - TimeSpan ts = DateTime.Now - startTime; - retVal.Add(new EchoMessage(((IPEndPoint) endpoint).Address, ittl, ts.Milliseconds)); - - // Endpoint reached - if (buffer[20] == (byte) ICMP_Type.EchoReply) - { - break; - } - - // Un wanted reply - if (buffer[20] != (byte) ICMP_Type.TimeExceeded) - { - throw new Exception("UnKnown error !"); - } - - continuesNoReply = 0; - } - catch - { - //ToDo: Handle recive/send timeouts - continuesNoReply++; - } - - // If there is 3 continues no reply, consider that destination host won't accept ping. - if (continuesNoReply >= 3) - { - break; - } - } - - return retVal.ToArray(); - } - - /// - /// Pings specified destination host. - /// - /// IP address to ping. - /// Send recieve timeout in milli seconds. - /// - public static EchoMessage Ping(IPAddress ip, int timeout) - { - //Create Raw ICMP Socket - Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp); - - IPEndPoint ipdest = new IPEndPoint(ip, 80); - EndPoint endpoint = (new IPEndPoint(Dns.GetHostEntry(Dns.GetHostName()).AddressList[0], 80)); - - ushort id = (ushort) DateTime.Now.Millisecond; - byte[] sendPacket = CreatePacket(id); - - //Socket options to set TTL and Timeouts - s.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.IpTimeToLive, 30); - s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, timeout); - s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, timeout); - - //Get current time - DateTime startTime = DateTime.Now; - - //Send Request - s.SendTo(sendPacket, sendPacket.Length, SocketFlags.None, ipdest); - - //Receive - byte[] buffer = new byte[1024]; - s.ReceiveFrom(buffer, buffer.Length, SocketFlags.None, ref endpoint); - - // Endpoint reached - if (buffer[20] == (byte) ICMP_Type.EchoReply) {} - // Un wanted reply - else if (buffer[20] != (byte) ICMP_Type.TimeExceeded) - { - throw new Exception("UnKnown error !"); - } - - //Calculate time elapsed - TimeSpan ts = DateTime.Now - startTime; - return new EchoMessage(((IPEndPoint) endpoint).Address, 0, ts.Milliseconds); - } - - #endregion - - #region Utility methods - - private static byte[] CreatePacket(ushort id) - { - /*Rfc 792 Echo or Echo Reply Message - 0 8 16 24 - +---------------+---------------+---------------+---------------+ - | Type | Code | Checksum | - +---------------+---------------+---------------+---------------+ - | ID Number | Sequence Number | - +---------------+---------------+---------------+---------------+ - | Data... - +---------------+---------------+---------------+---------------+ - */ - - byte[] packet = new byte[8 + 2]; - packet[0] = (byte) ICMP_Type.Echo; // Type - packet[1] = 0; // Code - packet[2] = 0; // Checksum - packet[3] = 0; // Checksum - packet[4] = 0; // ID - packet[5] = 0; // ID - packet[6] = 0; // Sequence - packet[7] = 0; // Sequence - - // Set id - Array.Copy(BitConverter.GetBytes(id), 0, packet, 4, 2); - - // Fill data 2 byte data - for (int i = 0; i < 2; i++) - { - packet[i + 8] = (byte) 'x'; // Data - } - - // calculate checksum - int checkSum = 0; - for (int i = 0; i < packet.Length; i += 2) - { - checkSum += Convert.ToInt32(BitConverter.ToUInt16(packet, i)); - } - - // The checksum is the 16-bit ones's complement of the one's - // complement sum of the ICMP message starting with the ICMP Type. - checkSum = (checkSum & 0xffff); - Array.Copy(BitConverter.GetBytes((ushort) ~checkSum), 0, packet, 2, 2); - - return packet; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_Acl.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_Acl.cs deleted file mode 100644 index 9722f24c1..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_Acl.cs +++ /dev/null @@ -1,88 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Client -{ - #region usings - - using Server; - - #endregion - - /// - /// IMAP ACL entry. Defined in RFC 2086. - /// - public class IMAP_Acl - { - #region Members - - private readonly string m_Name = ""; - private readonly IMAP_ACL_Flags m_Rights = IMAP_ACL_Flags.None; - - #endregion - - #region Properties - - /// - /// Gets authentication identifier name. Normally this is user or group name. - /// - public string Name - { - get { return m_Name; } - } - - /// - /// Gets the rights associated with this ACL entry. - /// - public IMAP_ACL_Flags Rights - { - get { return m_Rights; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Authentication identifier name. Normally this is user or group name. - /// Rights associated with this ACL entry. - public IMAP_Acl(string name, IMAP_ACL_Flags rights) - { - m_Name = name; - m_Rights = rights; - } - - #endregion - - #region Internal methods - - /// - /// Parses ACL entry from IMAP ACL response string. - /// - /// IMAP ACL response string. - /// - internal static IMAP_Acl Parse(string aclResponseString) - { - string[] args = TextUtils.SplitQuotedString(aclResponseString, ' ', true); - return new IMAP_Acl(args[1], IMAP_Utils.ACL_From_String(args[2])); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_Client.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_Client.cs deleted file mode 100644 index 3cd1466e9..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_Client.cs +++ /dev/null @@ -1,2910 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Client -{ - #region usings - - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.Globalization; - using System.IO; - using System.Security.Principal; - using System.Timers; - using IO; - using Server; - using TCP; - using StringReader=StringReader; - using System.Text; - - #endregion - - /// - /// IMAP client. - /// - /// - /// - /// using(IMAP_Client c = new IMAP_Client()){ - /// c.Connect("ivx",143); - /// c.Authenticate("test","test"); - /// - /// c.SelectFolder("Inbox"); - /// - /// IMAP_SequenceSet sequence_set = new IMAP_SequenceSet(); - /// // First message - /// sequence_set.Parse("1"); - /// // All messages - /// // sequence_set.Parse("1:*"); - /// // Messages 1,3,6 and 100 to last - /// // sequence_set.Parse("1,3,6,100:*"); - /// - /// // Get messages flags and header - /// IMAP_FetchItem msgsInfo = c.FetchMessages(sequence_set,IMAP_FetchItem_Flags.MessageFlags | IMAP_FetchItem_Flags.Header,true,false); - /// - /// // Do your suff - /// } - /// - /// - public class IMAP_Client : TCP_Client - { - private const int NoopInterval = 60000; - private const int NoopDelay = 5000; - - #region Members - - //private readonly TimerEx m_Timer = new TimerEx(NoopInterval); - private int m_CmdNumber; - private int m_MsgCount; - private int m_NewMsgCount; - private char m_PathSeparator = '\0'; - private GenericIdentity m_pAuthdUserIdentity; - private string m_SelectedFolder = ""; - private long m_UIDNext; - private long m_UIDValidity; - - #endregion - - #region Properties - - /// - /// Gets session authenticated user identity, returns null if not authenticated. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when this property is accessed and POP3 client is not connected. - public override GenericIdentity AuthenticatedUserIdentity - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - - return m_pAuthdUserIdentity; - } - } - - /// - /// Gets IMAP server path separator char. - /// - public char PathSeparator - { - get - { - // Get separator - if (m_PathSeparator == '\0') - { - m_PathSeparator = GetFolderSeparator()[0]; - } - - return m_PathSeparator; - } - } - - /// - /// Gets selected folder. - /// - public string SelectedFolder - { - get { return m_SelectedFolder; } - } - - /// - /// Gets folder UID. - /// - public long UIDValidity - { - get { return m_UIDValidity; } - } - - /// - /// Gets next predicted message UID. - /// - public long UIDNext - { - get { return m_UIDNext; } - } - - /// - /// Gets numbers of recent(not accessed messages) in selected folder. - /// - public int RecentMessagesCount - { - get { return NewMsgCount; } - } - - /// - /// Gets numbers of messages in selected folder. - /// - public int MessagesCount - { - get { return MsgCount; } - } - - /// - /// - /// - public bool HasNewMessages { get; set; } - - private int MsgCount - { - get { return m_MsgCount; } - set - { - if (value > m_MsgCount) - { - HasNewMessages = true; - m_NewMsgCount = value - m_MsgCount; - } - m_MsgCount = value; - } - } - - private int NewMsgCount - { - get { return m_NewMsgCount; } - set - { - HasNewMessages = m_NewMsgCount < value; - m_NewMsgCount = value; - } - } - - public List Capabilities - { - get { return capabilities; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public IMAP_Client() - { - //m_Timer.AutoReset = true; - //m_Timer.Elapsed += Timer_Elapsed; - //m_Timer.Enabled = true; - } - - #endregion - - #region Methods - - /// - /// Clean up any resources being used. - /// - public override void Dispose() - { - Debug.Print("IMAP client disposed"); - //m_Timer.Enabled = false; - //m_Timer.Dispose(); - base.Dispose(); - } - - /// - /// Closes connection to POP3 server. - /// - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when POP3 client is not connected. - public override void Disconnect() - { - Debug.Print("IMAP client disconect"); - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("IMAP client is not connected."); - } - - try - { - // Send LOGOUT command to server. - int countWritten = TcpStream.WriteLine("a1 LOGOUT"); - LogAddWrite(countWritten, "a1 LOGOUT"); - string line = ""; - while (true) - { - line = ReadLine(); - - if (string.IsNullOrEmpty(line)) - { - break; - } - if (line.StartsWith("a1 OK")) - { - break; - } - } - } - catch {} - - try - { - base.Disconnect(); - } - catch {} - } - - private List capabilities = new List(); - - private void GetCapabilities() - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new Exception("You must connect first !"); - } - - string line = GetNextCmdTag() + " CAPABILITY"; - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - Capabilities.Clear(); - while (true) - { - line = ReadLine(); - - // We have un-tagged resposne. - if (line.StartsWith("*")) - { - ProcessCapsResponse(line); - } - else - { - break; - } - } - - if (!RemoveCmdTag(line).ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - } - - private void ProcessCapsResponse(string line) - { - if (line.IndexOf("CAPABILITY") > -1) - { - string[] idsLine = line.Substring(line.IndexOf(' ', line.IndexOf("CAPABILITY"))).Split( - new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - Capabilities.AddRange(idsLine); - } - } - - /// - /// Switches IMAP connection to SSL. - /// - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when IMAP client is not connected or is authenticated or is already secure connection. - public void StartTLS() - { - /* RFC 2595 3. IMAP STARTTLS extension. - - Example: C: a001 CAPABILITY - S: * CAPABILITY IMAP4rev1 STARTTLS LOGINDISABLED - S: a001 OK CAPABILITY completed - C: a002 STARTTLS - S: a002 OK Begin TLS negotiation now - - */ - - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (IsAuthenticated) - { - throw new InvalidOperationException( - "The STARTTLS command is only valid in non-authenticated state !"); - } - if (IsSecureConnection) - { - throw new InvalidOperationException("Connection is already secure."); - } - - string line = GetNextCmdTag() + " STARTTLS"; - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength], - SizeExceededAction. - JunkAndThrowException); - TcpStream.ReadLine(args, false); - if (args.Error != null) - { - throw args.Error; - } - line = args.LineUtf8; - LogAddRead(args.BytesInBuffer, line); - line = RemoveCmdTag(line); - if (!line.ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - - SwitchToSecure(); - } - - /// - /// Authenticates user. - /// - /// User name. - /// Password. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when IMAP client is not connected or is already authenticated. - public void Authenticate(string userName, string password) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (IsAuthenticated) - { - throw new InvalidOperationException("Session is already authenticated."); - } - - string line = GetNextCmdTag() + " LOGIN " + TextUtils.QuoteString(userName) + " " + - TextUtils.QuoteString(password); - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, - string.IsNullOrEmpty(password) ? line : line.Replace(password, "<***REMOVED***>")); - - SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength], - SizeExceededAction. - JunkAndThrowException); - TcpStream.ReadLine(args, false); - if (args.Error != null) - { - throw args.Error; - } - line = args.LineUtf8; - LogAddRead(args.BytesInBuffer, line); - line = RemoveCmdTag(line); - if (line.ToUpper().StartsWith("OK")) - { - m_pAuthdUserIdentity = new GenericIdentity(userName, "login"); - } - else - { - throw new IMAP_ClientException(line); - } - } - - /// - /// Creates specified folder. - /// - /// Folder name. Eg. test, Inbox/SomeSubFolder. NOTE: use GetFolderSeparator() to get right folder separator. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when IMAP client is not connected and authenticated. - public void CreateFolder(string folderName) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException("The CREATE command is only valid in authenticated state."); - } - - // Ensure that we send right separator to server, we accept both \ and /. - folderName = folderName.Replace('\\', PathSeparator).Replace('/', PathSeparator); - - string line = GetNextCmdTag() + " CREATE " + - TextUtils.QuoteString(Core.Encode_IMAP_UTF7_String(folderName)); - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength], - SizeExceededAction. - JunkAndThrowException); - TcpStream.ReadLine(args, false); - if (args.Error != null) - { - throw args.Error; - } - line = args.LineUtf8; - LogAddRead(args.BytesInBuffer, line); - line = RemoveCmdTag(line); - if (!line.ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - } - - /// - /// Deletes specified folder. - /// - /// Folder name. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when IMAP client is not connected and authenticated. - public void DeleteFolder(string folderName) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException("The DELETE command is only valid in authenticated state."); - } - - string line = GetNextCmdTag() + " DELETE " + - TextUtils.QuoteString(Core.Encode_IMAP_UTF7_String(folderName)); - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength], - SizeExceededAction. - JunkAndThrowException); - TcpStream.ReadLine(args, false); - if (args.Error != null) - { - throw args.Error; - } - line = args.LineUtf8; - LogAddRead(args.BytesInBuffer, line); - line = RemoveCmdTag(line); - if (!line.ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - } - - /// - /// Renames specified folder. - /// - /// Source folder name. - /// Destination folder name. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when IMAP client is not connected and authenticated. - public void RenameFolder(string sourceFolderName, string destinationFolderName) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException("The RENAME command is only valid in authenticated state."); - } - - string line = GetNextCmdTag() + " RENAME " + - TextUtils.QuoteString(Core.Encode_IMAP_UTF7_String(sourceFolderName)) + " " + - TextUtils.QuoteString(Core.Encode_IMAP_UTF7_String(destinationFolderName)); - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength], - SizeExceededAction. - JunkAndThrowException); - TcpStream.ReadLine(args, false); - if (args.Error != null) - { - throw args.Error; - } - line = args.LineUtf8; - LogAddRead(args.BytesInBuffer, line); - line = RemoveCmdTag(line); - if (!line.ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - } - - /// - /// Gets all available folders. - /// - /// Returns user folders. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when IMAP client is not connected and authenticated. - public string[] GetFolders() - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException("The LIST command is only valid in authenticated state."); - } - - List list = new List(); - - string line = GetNextCmdTag() + " LIST \"\" \"*\""; - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - // Must get lines with * and cmdTag + OK or cmdTag BAD/NO. - while (true) - { - line = ReadLine(); - - if (line.StartsWith("*")) - { - // don't show not selectable folders - if (line.ToLower().IndexOf("\\noselect") == -1) - { - line = line.Substring(line.IndexOf(")") + 1).Trim(); // Remove * LIST(..) - line = line.Substring(line.IndexOf(" ")).Trim(); // Remove Folder separator - - list.Add(TextUtils.UnQuoteString(Core.Decode_IMAP_UTF7_String(line.Trim()))); - } - } - else - { - break; - } - } - - line = RemoveCmdTag(line); - if (!line.ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - - return list.ToArray(); - } - - /// - /// Gets all subscribed folders. - /// - /// Returns user subscribed folders. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when IMAP client is not connected and authenticated. - public string[] GetSubscribedFolders() - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException("The LSUB command is only valid in authenticated state."); - } - - List list = new List(); - - string line = GetNextCmdTag() + " LSUB \"\" \"*\""; - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - // Must get lines with * and cmdTag + OK or cmdTag BAD/NO. - while (true) - { - line = ReadLine(); - - if (line.StartsWith("*")) - { - // don't show not selectable folders - if (line.ToLower().IndexOf("\\noselect") == -1) - { - line = line.Substring(line.IndexOf(")") + 1).Trim(); // Remove * LIST(..) - line = line.Substring(line.IndexOf(" ")).Trim(); // Remove Folder separator - - list.Add(TextUtils.UnQuoteString(Core.Decode_IMAP_UTF7_String(line.Trim()))); - } - } - else - { - break; - } - } - - line = RemoveCmdTag(line); - if (!line.ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - - return list.ToArray(); - } - - /// - /// Subscribes specified folder. - /// - /// Folder name. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when IMAP client is not connected and authenticated. - public void SubscribeFolder(string folderName) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException( - "The SUBSCRIBE command is only valid in authenticated state."); - } - - // Ensure that we send right separator to server, we accept both \ and /. - folderName = folderName.Replace('\\', PathSeparator).Replace('/', PathSeparator); - - string line = GetNextCmdTag() + " SUBSCRIBE " + - TextUtils.QuoteString(Core.Encode_IMAP_UTF7_String(folderName)); - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - line = ReadLine(); - line = RemoveCmdTag(line); - if (!line.ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - } - - /// - /// UnSubscribes specified folder. - /// - /// Folder name. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when IMAP client is not connected and authenticated. - public void UnSubscribeFolder(string folderName) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException( - "The UNSUBSCRIBE command is only valid in authenticated state."); - } - - // Ensure that we send right separator to server, we accept both \ and /. - folderName = folderName.Replace('\\', PathSeparator).Replace('/', PathSeparator); - - string line = GetNextCmdTag() + " UNSUBSCRIBE " + - TextUtils.QuoteString(Core.Encode_IMAP_UTF7_String(folderName)); - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - line = ReadLine(); - line = RemoveCmdTag(line); - if (!line.ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - } - - /// - /// Selects specified folder. - /// - /// Folder name. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when IMAP client is not connected and authenticated. - public void ExamineFolder(string folderName) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException("The SELECT command is only valid in authenticated state."); - } - - // Ensure that we send right separator to server, we accept both \ and /. - folderName = folderName.Replace('\\', PathSeparator).Replace('/', PathSeparator); - - string line = GetNextCmdTag() + " EXAMINE " + - TextUtils.QuoteString(Core.Encode_IMAP_UTF7_String(folderName)); - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - // Must get lines with * and cmdTag + OK or cmdTag BAD/NO. - while (true) - { - line = ReadLine(); - - if (line.StartsWith("*")) - { - // Get rid of * - line = line.Substring(1).Trim(); - - if (line.ToUpper().IndexOf("EXISTS") > -1 && line.ToUpper().IndexOf("FLAGS") == -1) - { - MsgCount = Convert.ToInt32(line.Substring(0, line.IndexOf(" ")).Trim()); - } - else if (line.ToUpper().IndexOf("RECENT") > -1 && line.ToUpper().IndexOf("FLAGS") == -1) - { - NewMsgCount = Convert.ToInt32(line.Substring(0, line.IndexOf(" ")).Trim()); - } - else if (line.ToUpper().IndexOf("UIDNEXT") > -1) - { - m_UIDNext = - Convert.ToInt64(line.Substring(line.ToUpper().IndexOf("UIDNEXT") + 8, - line.IndexOf(']') - - line.ToUpper().IndexOf("UIDNEXT") - 8)); - } - else if (line.ToUpper().IndexOf("UIDVALIDITY") > -1) - { - m_UIDValidity = - Convert.ToInt64(line.Substring(line.ToUpper().IndexOf("UIDVALIDITY") + 12, - line.IndexOf(']') - - line.ToUpper().IndexOf("UIDVALIDITY") - 12)); - } - } - else - { - break; - } - } - - line = RemoveCmdTag(line); - if (!line.ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - } - - /// - /// Selects specified folder. - /// - /// Folder name. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when IMAP client is not connected and authenticated. - public void SelectFolder(string folderName) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException("The SELECT command is only valid in authenticated state."); - } - - // Ensure that we send right separator to server, we accept both \ and /. - folderName = folderName.Replace('\\', PathSeparator).Replace('/', PathSeparator); - - string line = GetNextCmdTag() + " SELECT " + - TextUtils.QuoteString(Core.Encode_IMAP_UTF7_String(folderName)); - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - // Must get lines with * and cmdTag + OK or cmdTag BAD/NO. - while (true) - { - line = ReadLine(); - - if (line.StartsWith("*")) - { - // Get rid of * - line = line.Substring(1).Trim(); - - if (line.ToUpper().IndexOf("EXISTS") > -1 && line.ToUpper().IndexOf("FLAGS") == -1) - { - MsgCount = Convert.ToInt32(line.Substring(0, line.IndexOf(" ")).Trim()); - } - else if (line.ToUpper().IndexOf("RECENT") > -1 && line.ToUpper().IndexOf("FLAGS") == -1) - { - NewMsgCount = Convert.ToInt32(line.Substring(0, line.IndexOf(" ")).Trim()); - } - else if (line.ToUpper().IndexOf("UIDNEXT") > -1) - { - m_UIDNext = - Convert.ToInt64(line.Substring(line.ToUpper().IndexOf("UIDNEXT") + 8, - line.IndexOf(']') - - line.ToUpper().IndexOf("UIDNEXT") - 8)); - } - else if (line.ToUpper().IndexOf("UIDVALIDITY") > -1) - { - m_UIDValidity = - Convert.ToInt64(line.Substring(line.ToUpper().IndexOf("UIDVALIDITY") + 12, - line.IndexOf(']') - - line.ToUpper().IndexOf("UIDVALIDITY") - 12)); - } - } - else - { - break; - } - } - - line = RemoveCmdTag(line); - if (!line.ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - - m_SelectedFolder = folderName; - } - - /// - /// Gets specified folder quota info. Throws Exception if server doesn't support QUOTA. - /// - /// Folder name. - /// Returns specified folder quota info. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when IMAP client is not connected and authenticated. - public IMAP_Quota GetFolderQuota(string folder) - { - /* RFC 2087 4.3. GETQUOTAROOT Command - - Arguments: mailbox name - - Data: untagged responses: QUOTAROOT, QUOTA - - Result: OK - getquota completed - NO - getquota error: no such mailbox, permission denied - BAD - command unknown or arguments invalid - - The GETQUOTAROOT command takes the name of a mailbox and returns the - list of quota roots for the mailbox in an untagged QUOTAROOT - response. For each listed quota root, it also returns the quota - root's resource usage and limits in an untagged QUOTA response. - - Example: C: A003 GETQUOTAROOT INBOX - S: * QUOTAROOT INBOX "" - S: * QUOTA "" (STORAGE 10 512) - S: A003 OK Getquota completed - */ - - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException("The command is only valid in authenticated state."); - } - - IMAP_Quota retVal = null; - - // Ensure that we send right separator to server, we accept both \ and /. - folder = folder.Replace('\\', PathSeparator).Replace('/', PathSeparator); - - string line = GetNextCmdTag() + " GETQUOTAROOT " + - TextUtils.QuoteString(Core.Encode_IMAP_UTF7_String(folder)); - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - // Must get lines with * and cmdTag + OK or cmdTag BAD/NO. - while (true) - { - line = ReadLine(); - - if (line.StartsWith("*")) - { - // Get rid of * - line = line.Substring(1).Trim(); - - if (line.ToUpper().StartsWith("QUOTAROOT")) - { - // Skip QUOTAROOT - } - else if (line.ToUpper().StartsWith("QUOTA")) - { - StringReader r = new StringReader(line); - // Skip QUOTA word - r.ReadWord(); - - string qoutaRootName = r.ReadWord(); - long storage = -1; - long maxStorage = -1; - long messages = -1; - long maxMessages = -1; - - string limits = r.ReadParenthesized(); - r = new StringReader(limits); - while (r.Available > 0) - { - string limitName = r.ReadWord(); - // STORAGE usedBytes maximumAllowedBytes - if (limitName.ToUpper() == "STORAGE") - { - storage = Convert.ToInt64(r.ReadWord()); - maxStorage = Convert.ToInt64(r.ReadWord()); - } - // STORAGE messagesCount maximumAllowedMessages - else if (limitName.ToUpper() == "MESSAGE") - { - messages = Convert.ToInt64(r.ReadWord()); - maxMessages = Convert.ToInt64(r.ReadWord()); - } - } - - retVal = new IMAP_Quota(qoutaRootName, messages, maxMessages, storage, maxStorage); - } - } - else - { - break; - } - } - - line = RemoveCmdTag(line); - if (!line.ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - - return retVal; - } - - /// - /// Gets IMAP server namespaces info. - /// - /// Returns user namespaces. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when IMAP client is not connected and authenticated. - public IMAP_NamespacesInfo GetNamespacesInfo() - { - /* RFC 2342 5. NAMESPACE Command. - Arguments: none - - Response: an untagged NAMESPACE response that contains the prefix - and hierarchy delimiter to the server's Personal - Namespace(s), Other Users' Namespace(s), and Shared - Namespace(s) that the server wishes to expose. The - response will contain a NIL for any namespace class - that is not available. Namespace_Response_Extensions - MAY be included in the response. - Namespace_Response_Extensions which are not on the IETF - standards track, MUST be prefixed with an "X-". - - Result: OK - Command completed - NO - Error: Can't complete command - BAD - argument invalid - - - Example: - < A server that supports a single personal namespace. No leading - prefix is used on personal mailboxes and "/" is the hierarchy - delimiter.> - - C: A001 NAMESPACE - S: * NAMESPACE (("" "/")) NIL NIL - S: A001 OK NAMESPACE command completed - */ - - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException( - "The NAMESPACE command is only valid in authenticated state."); - } - - string line = GetNextCmdTag() + " NAMESPACE"; - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - IMAP_NamespacesInfo namespacesInfo = new IMAP_NamespacesInfo(null, null, null); - while (true) - { - line = ReadLine(); - - if (line.StartsWith("*")) - { - line = RemoveCmdTag(line); - - if (line.ToUpper().StartsWith("NAMESPACE")) - { - namespacesInfo = IMAP_NamespacesInfo.Parse(line); - } - } - else - { - break; - } - } - - line = RemoveCmdTag(line); - if (!line.ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - - return namespacesInfo; - } - - /// - /// Gets specified folder ACL entries. - /// - /// Folder which ACL entries to get. - /// Returns specified folder ACL entries. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when IMAP client is not connected and authenticated. - public IMAP_Acl[] GetFolderACL(string folderName) - { - /* RFC 2086 4.3. GETACL - Arguments: mailbox name - - Data: untagged responses: ACL - - Result: OK - getacl completed - NO - getacl failure: can't get acl - BAD - command unknown or arguments invalid - - The GETACL command returns the access control list for mailbox in - an untagged ACL reply. - - Example: C: A002 GETACL INBOX - S: * ACL INBOX Fred rwipslda - S: A002 OK Getacl complete - - .... Multiple users - S: * ACL INBOX Fred rwipslda test rwipslda - - .... No acl flags for Fred - S: * ACL INBOX Fred "" test rwipslda - - */ - - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException("The GETACL command is only valid in authenticated state."); - } - - // Ensure that we send right separator to server, we accept both \ and /. - folderName = folderName.Replace('\\', PathSeparator).Replace('/', PathSeparator); - - string line = GetNextCmdTag() + " GETACL " + - TextUtils.QuoteString(Core.Encode_IMAP_UTF7_String(folderName)); - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - List retVal = new List(); - while (true) - { - line = ReadLine(); - - if (line.StartsWith("*")) - { - line = RemoveCmdTag(line); - - if (line.ToUpper().StartsWith("ACL")) - { - retVal.Add(IMAP_Acl.Parse(line)); - } - } - else - { - break; - } - } - - line = RemoveCmdTag(line); - if (!line.ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - - return retVal.ToArray(); - } - - /// - /// Sets specified user ACL permissions for specified folder. - /// - /// Folder name which ACL to set. - /// User name who's ACL to set. - /// ACL permissions to set. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when IMAP client is not connected and authenticated. - public void SetFolderACL(string folderName, string userName, IMAP_ACL_Flags acl) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException("The SETACL command is only valid in authenticated state."); - } - - // Ensure that we send right separator to server, we accept both \ and /. - folderName = folderName.Replace('\\', PathSeparator).Replace('/', PathSeparator); - - string line = GetNextCmdTag() + " SETACL " + - TextUtils.QuoteString(Core.Encode_IMAP_UTF7_String(folderName)) + " " + - TextUtils.QuoteString(userName) + " " + IMAP_Utils.ACL_to_String(acl); - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - line = ReadLine(); - line = RemoveCmdTag(line); - if (!line.ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - } - - /// - /// Deletes specified user access to specified folder. - /// - /// Folder which ACL to remove. - /// User name who's ACL to remove. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when IMAP client is not connected and authenticated. - public void DeleteFolderACL(string folderName, string userName) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException( - "The DELETEACL command is only valid in authenticated state."); - } - - // Ensure that we send right separator to server, we accept both \ and /. - folderName = folderName.Replace('\\', PathSeparator).Replace('/', PathSeparator); - - string line = GetNextCmdTag() + " DELETEACL " + - TextUtils.QuoteString(Core.Encode_IMAP_UTF7_String(folderName)) + " " + - TextUtils.QuoteString(userName); - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - line = ReadLine(); - line = RemoveCmdTag(line); - if (!line.ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - } - - /// - /// Gets myrights to specified folder. - /// - /// Folder which my rifgts to get. - /// Returns myrights to specified folder. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when IMAP client is not connected and authenticated. - public IMAP_ACL_Flags GetFolderMyrights(string folderName) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException( - "The MYRIGHTS command is only valid in authenticated state."); - } - - // Ensure that we send right separator to server, we accept both \ and /. - folderName = folderName.Replace('\\', PathSeparator).Replace('/', PathSeparator); - - string line = GetNextCmdTag() + " MYRIGHTS " + - TextUtils.QuoteString(Core.Encode_IMAP_UTF7_String(folderName)); - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - IMAP_ACL_Flags aclFlags = IMAP_ACL_Flags.None; - - // Must get lines with * and cmdTag + OK or cmdTag BAD/NO. - while (true) - { - line = ReadLine(); - - if (line.StartsWith("*")) - { - line = RemoveCmdTag(line); - - if (line.ToUpper().IndexOf("MYRIGHTS") > -1) - { - aclFlags = IMAP_Utils.ACL_From_String(line.Substring(0, line.IndexOf(" ")).Trim()); - } - } - else - { - break; - } - } - - line = RemoveCmdTag(line); - if (!line.ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - - return aclFlags; - } - - /// - /// Copies specified messages to specified folder. - /// - /// IMAP sequence-set. - /// Destination folder name. - /// Specifies if UID COPY or COPY. - /// For UID COPY all sequence_set numers must be message UID values and for normal COPY message numbers. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when IMAP client is not connected,not authenticated and folder not selected. - public void CopyMessages(IMAP_SequenceSet sequence_set, string destFolder, bool uidCopy) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException("The COPY command is only valid in authenticated state."); - } - if (m_SelectedFolder.Length == 0) - { - throw new InvalidOperationException("The COPY command is only valid in selected state."); - } - - // Ensure that we send right separator to server, we accept both \ and /. - destFolder = destFolder.Replace('\\', PathSeparator).Replace('/', PathSeparator); - - string line = ""; - if (uidCopy) - { - line = GetNextCmdTag() + " UID COPY " + sequence_set.ToSequenceSetString() + " " + - TextUtils.QuoteString(Core.Encode_IMAP_UTF7_String(destFolder)); - } - else - { - line = GetNextCmdTag() + " COPY " + sequence_set.ToSequenceSetString() + " " + - TextUtils.QuoteString(Core.Encode_IMAP_UTF7_String(destFolder)); - } - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - // Read un-tagged response lines while we get final response line. - while (true) - { - line = ReadLine(); - - // We have un-tagged resposne. - if (line.StartsWith("*")) - { - ProcessStatusResponse(line); - } - else - { - break; - } - } - - if (!RemoveCmdTag(line).ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - } - - /// - /// Moves specified messages to specified folder. - /// - /// IMAP sequence-set. - /// Folder where to copy messages. - /// Specifies if sequence-set contains message UIDs or message numbers. - public void MoveMessages(IMAP_SequenceSet sequence_set, string destFolder, bool uidMove) - { - if (!IsConnected) - { - throw new Exception("You must connect first !"); - } - if (!IsAuthenticated) - { - throw new Exception("You must authenticate first !"); - } - if (m_SelectedFolder.Length == 0) - { - throw new Exception("You must select folder first !"); - } - - CopyMessages(sequence_set, destFolder, uidMove); - DeleteMessages(sequence_set, uidMove); - } - - /// - /// Deletes specified messages. - /// - /// IMAP sequence-set. - /// Specifies if sequence-set contains message UIDs or message numbers. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when IMAP client is not connected,not authenticated and folder not selected. - public void DeleteMessages(IMAP_SequenceSet sequence_set, bool uidDelete) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException("The command is only valid in authenticated state."); - } - if (m_SelectedFolder.Length == 0) - { - throw new InvalidOperationException("The command is only valid in selected state."); - } - - // 1) Set deleted flag - // 2) Delete messages with EXPUNGE command - - string line = ""; - if (uidDelete) - { - line = GetNextCmdTag() + " UID STORE " + sequence_set.ToSequenceSetString() + " " + - "+FLAGS.SILENT (\\Deleted)"; - } - else - { - line = GetNextCmdTag() + " STORE " + sequence_set.ToSequenceSetString() + " " + - "+FLAGS.SILENT (\\Deleted)"; - } - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - // Read un-tagged response lines while we get final response line. - while (true) - { - line = ReadLine(); - - // We have un-tagged resposne. - if (line.StartsWith("*")) - { - ProcessStatusResponse(line); - } - else - { - break; - } - } - - if (!RemoveCmdTag(line).ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - - line = GetNextCmdTag() + " EXPUNGE"; - countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - // Read un-tagged response lines while we get final response line. - while (true) - { - line = ReadLine(); - - // We have un-tagged resposne. - if (line.StartsWith("*")) - { - ProcessStatusResponse(line); - } - else - { - break; - } - } - - if (!RemoveCmdTag(line).ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - } - - /// - /// Stores message to specified folder. - /// - /// Folder where to store message. - /// Message data which to store. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when IMAP client is not connected,not authenticated and folder not selected. - public void StoreMessage(string folderName, byte[] data) - { - StoreMessage(folderName, IMAP_MessageFlags.Seen, DateTime.Now, data); - } - - /// - /// Stores message to specified folder. - /// - /// Folder where to store message. - /// Message flags what are stored for message. - /// Internal date value what are stored for message. - /// Message data which to store. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when IMAP client is not connected,not authenticated and folder not selected. - public void StoreMessage(string folderName, - IMAP_MessageFlags messageFlags, - DateTime inernalDate, - byte[] data) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException("The command is only valid in authenticated state."); - } - - // Ensure that we send right separator to server, we accept both \ and /. - folderName = folderName.Replace('\\', PathSeparator).Replace('/', PathSeparator); - - string line = GetNextCmdTag() + " APPEND " + - TextUtils.QuoteString(Core.Encode_IMAP_UTF7_String(folderName)) + " (" + - IMAP_Utils.MessageFlagsToString(messageFlags) + ") \"" + - IMAP_Utils.DateTimeToString(inernalDate) + "\" {" + data.Length + "}"; - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - // Read un-tagged response lines while we get final response line or + send data. - while (true) - { - line = ReadLine(); - - // We have un-tagged resposne. - if (line.StartsWith("*")) - { - ProcessStatusResponse(line); - } - // Send data. - else if (line.StartsWith("+")) - { - // Send message. - TcpStream.Write(data, 0, data.Length); - LogAddWrite(data.Length, "Wrote " + data.Length + " bytes."); - - // Send CRLF, ends splitted command line. - TcpStream.Write(new[] {(byte) '\r', (byte) '\n'}, 0, 2); - LogAddWrite(data.Length, "Wrote CRLF."); - } - else - { - break; - } - } - - if (!RemoveCmdTag(line).ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - } - - - public int[] Search(string searchPattern, bool uidSearch) - { - if (!IsConnected) - { - throw new Exception("You must connect first !"); - } - if (!IsAuthenticated) - { - throw new Exception("You must authenticate first !"); - } - if (m_SelectedFolder.Length == 0) - { - throw new Exception("You must select folder first !"); - } - - string line = GetNextCmdTag() + (uidSearch ? " UID" : "") + " SEARCH " + - Core.Encode_IMAP_UTF7_String(searchPattern); - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - List messageIDs = new List(); - // Read un-tagged response lines while we get final response line or + send data. - while (true) - { - line = ReadLine(); - - // We have un-tagged resposne. - if (line.StartsWith("*")) - { - ProcessSearchResponse(line, messageIDs); - } - else - { - break; - } - } - - if (!RemoveCmdTag(line).ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - - return messageIDs.ToArray(); - } - - public int[] SearchText(string textToFind, bool uidSearch, Encoding charset) - { - if (!IsConnected) - { - throw new Exception("You must connect first !"); - } - if (!IsAuthenticated) - { - throw new Exception("You must authenticate first !"); - } - if (m_SelectedFolder.Length == 0) - { - throw new Exception("You must select folder first !"); - } - - string line = GetNextCmdTag() + (uidSearch ? " UID" : "") + " SEARCH " + (charset==null?"":string.Format("CHARSET {0} ", charset.WebName.ToUpper()))+string.Format("TEXT {0}", - (charset==null?string.Format("\"{0}\"",Core.Encode_IMAP_UTF7_String(textToFind)):string.Format("{{{0}}}", textToFind.Length))); - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - if (charset != null) - { - line = ReadLine(); - if (line.StartsWith("+")) - { - countWritten = TcpStream.WriteLine(textToFind); - LogAddWrite(countWritten, textToFind); - } - } - - List messageIDs = new List(); - // Read un-tagged response lines while we get final response line or + send data. - while (true) - { - line = ReadLine(); - - // We have un-tagged resposne. - if (line.StartsWith("*")) - { - ProcessSearchResponse(line, messageIDs); - } - else - { - break; - } - } - - if (!RemoveCmdTag(line).ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - - return messageIDs.ToArray(); - } - - /// - /// Fetches specifes messages specified fetch items. - /// - /// IMAP sequence-set. - /// Specifies what data to fetch from IMAP server. - /// If true message seen flag is setted. - /// Specifies if sequence-set contains message UIDs or message numbers. - /// Returns requested fetch items. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when IMAP client is not connected,not authenticated and folder not selected. - public IMAP_FetchItem[] FetchMessages(IMAP_SequenceSet sequence_set, - IMAP_FetchItem_Flags fetchFlags, - bool setSeenFlag, - bool uidFetch) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException("The command is only valid in authenticated state."); - } - if (m_SelectedFolder.Length == 0) - { - throw new InvalidOperationException("The command is only valid in selected state."); - } - - List fetchItems = new List(); - - //--- Construct FETCH command line -----------------------------------------------------------------------// - string fetchCmdLine = GetNextCmdTag(); - if (uidFetch) - { - fetchCmdLine += " UID"; - } - fetchCmdLine += " FETCH " + sequence_set.ToSequenceSetString() + " (UID"; - - // FLAGS - if ((fetchFlags & IMAP_FetchItem_Flags.MessageFlags) != 0) - { - fetchCmdLine += " FLAGS"; - } - // RFC822.SIZE - if ((fetchFlags & IMAP_FetchItem_Flags.Size) != 0) - { - fetchCmdLine += " RFC822.SIZE"; - } - // INTERNALDATE - if ((fetchFlags & IMAP_FetchItem_Flags.InternalDate) != 0) - { - fetchCmdLine += " INTERNALDATE"; - } - // ENVELOPE - if ((fetchFlags & IMAP_FetchItem_Flags.Envelope) != 0) - { - fetchCmdLine += " ENVELOPE"; - } - // BODYSTRUCTURE - if ((fetchFlags & IMAP_FetchItem_Flags.BodyStructure) != 0) - { - fetchCmdLine += " BODYSTRUCTURE"; - } - // BODY[] or BODY.PEEK[] - if ((fetchFlags & IMAP_FetchItem_Flags.Message) != 0) - { - if (setSeenFlag) - { - fetchCmdLine += " BODY[]"; - } - else - { - fetchCmdLine += " BODY.PEEK[]"; - } - } - // BODY[HEADER] or BODY.PEEK[HEADER] ---> This needed only if full message isn't requested. - if ((fetchFlags & IMAP_FetchItem_Flags.Message) == 0 && - (fetchFlags & IMAP_FetchItem_Flags.Header) != 0) - { - if (setSeenFlag) - { - fetchCmdLine += " BODY[HEADER]"; - } - else - { - fetchCmdLine += " BODY.PEEK[HEADER]"; - } - } - //--------------------------------------------------------------------------------------------------------// - - fetchCmdLine += ")"; - - // Send fetch command line to server. - int countWritten = TcpStream.WriteLine(fetchCmdLine); - LogAddWrite(countWritten, fetchCmdLine); - - // Read un-tagged response lines while we get final response line. - byte[] lineBuffer = new byte[Workaround.Definitions.MaxStreamLineLength]; - string line = ""; - while (true) - { - SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(lineBuffer, - SizeExceededAction. - JunkAndThrowException); - TcpStream.ReadLine(args, false); - if (args.Error != null) - { - throw args.Error; - } - line = args.LineUtf8; - LogAddRead(args.BytesInBuffer, line); - - // We have un-tagged resposne. - if (line.StartsWith("*")) - { - if (IsStatusResponse(line)) - { - ProcessStatusResponse(line); - } - else - { - int no = 0; - int uid = 0; - int size = 0; - byte[] data = null; - IMAP_MessageFlags flags = IMAP_MessageFlags.Recent; - string envelope = ""; - string bodystructure = ""; - string internalDate = ""; - - // Remove * - line = RemoveCmdTag(line); - - // Get message number - no = Convert.ToInt32(line.Substring(0, line.IndexOf(" "))); - - // Get rid of FETCH and parse params. Reply:* 1 FETCH (UID 12 BODY[] ...) - line = line.Substring(line.IndexOf("FETCH (") + 7); - - StringReader r = new StringReader(line); - // Loop fetch result fields - while (r.Available > 0) - { - r.ReadToFirstChar(); - - // Fetch command closing ) parenthesis - if (r.SourceString == ")") - { - break; - } - - #region UID - - // UID - else if (r.StartsWith("UID", false)) - { - // Remove UID word from reply - r.ReadSpecifiedLength("UID".Length); - r.ReadToFirstChar(); - - // Read - string word = r.ReadWord(); - if (word == null) - { - throw new Exception("IMAP server didn't return UID !"); - } - else - { - uid = Convert.ToInt32(word); - } - } - - #endregion - - #region RFC822.SIZE - - // RFC822.SIZE - else if (r.StartsWith("RFC822.SIZE", false)) - { - // Remove RFC822.SIZE word from reply - r.ReadSpecifiedLength("RFC822.SIZE".Length); - r.ReadToFirstChar(); - - // Read - string word = r.ReadWord(); - if (word == null) - { - throw new Exception("IMAP server didn't return RFC822.SIZE !"); - } - else - { - try - { - size = Convert.ToInt32(word); - } - catch - { - throw new Exception( - "IMAP server returned invalid RFC822.SIZE '" + word + - "' !"); - } - } - } - - #endregion - - #region INTERNALDATE - - // INTERNALDATE - else if (r.StartsWith("INTERNALDATE", false)) - { - // Remove INTERNALDATE word from reply - r.ReadSpecifiedLength("INTERNALDATE".Length); - r.ReadToFirstChar(); - - // Read - string word = r.ReadWord(); - if (word == null) - { - throw new Exception("IMAP server didn't return INTERNALDATE !"); - } - else - { - internalDate = word; - } - } - - #endregion - - #region ENVELOPE () - - else if (r.StartsWith("ENVELOPE", false)) - { - // Remove ENVELOPE word from reply - r.ReadSpecifiedLength("ENVELOPE".Length); - r.ReadToFirstChar(); - - /* - Handle string literals {count-to-read}data(length = count-to-read). - (string can be quoted string or literal) - Loop while get envelope,invalid response or timeout. - */ - - while (true) - { - try - { - envelope = r.ReadParenthesized(); - break; - } - catch (Exception x) - { - string s = r.ReadToEnd(); - - /* partial_envelope {count-to-read} - Example: ENVELOPE ("Mon, 03 Apr 2006 10:10:10 GMT" {35} - */ - if (s.EndsWith("}")) - { - // Get partial envelope and append it back to reader - r.AppenString(s.Substring(0, s.LastIndexOf('{'))); - - // Read remaining envelope and append it to reader. - int countToRead = - Convert.ToInt32(s.Substring(s.LastIndexOf('{') + 1, - s.LastIndexOf('}') - - s.LastIndexOf('{') - 1)); - string reply = TcpStream.ReadFixedCountString(countToRead); - LogAddRead(countToRead, reply); - r.AppenString(TextUtils.QuoteString(reply)); - - // Read fetch continuing line. - TcpStream.ReadLine(args, false); - if (args.Error != null) - { - throw args.Error; - } - line = args.LineUtf8; - LogAddRead(args.BytesInBuffer, line); - r.AppenString(line); - } - // Unexpected response - else - { - throw x; - } - } - } - } - - #endregion - - #region BODYSTRUCTURE () - - else if (r.StartsWith("BODYSTRUCTURE", false)) - { - // Remove BODYSTRUCTURE word from reply - r.ReadSpecifiedLength("BODYSTRUCTURE".Length); - r.ReadToFirstChar(); - - bodystructure = r.ReadParenthesized(); - } - - #endregion - - #region BODY[] or BODY[HEADER] - - // BODY[] or BODY[HEADER] - else if (r.StartsWith("BODY", false)) - { - if (r.StartsWith("BODY[]", false)) - { - // Remove BODY[] - r.ReadSpecifiedLength("BODY[]".Length); - } - else if (r.StartsWith("BODY[HEADER]", false)) - { - // Remove BODY[HEADER] - r.ReadSpecifiedLength("BODY[HEADER]".Length); - } - else - { - throw new Exception("Invalid FETCH response: " + r.SourceString); - } - r.ReadToFirstChar(); - - // We must now have {}, or there is error - if (!r.StartsWith("{")) - { - throw new Exception("Invalid FETCH BODY[] or BODY[HEADER] response: " + - r.SourceString); - } - // Read - int dataLength = Convert.ToInt32(r.ReadParenthesized()); - - // Read data - MemoryStream storeStrm = new MemoryStream(dataLength); - TcpStream.ReadFixedCount(storeStrm, dataLength); - LogAddRead(dataLength, "Readed " + dataLength + " bytes."); - data = storeStrm.ToArray(); - - // Read fetch continuing line. - TcpStream.ReadLine(args, false); - if (args.Error != null) - { - throw args.Error; - } - line = args.LineUtf8; - LogAddRead(args.BytesInBuffer, line); - r.AppenString(line); - } - - #endregion - - #region FLAGS () - - // FLAGS () - else if (r.StartsWith("FLAGS", false)) - { - // Remove FLAGS word from reply - r.ReadSpecifiedLength("FLAGS".Length); - r.ReadToFirstChar(); - - // Read () - string flagsList = r.ReadParenthesized(); - if (flagsList == null) - { - throw new Exception("IMAP server didn't return FLAGS () !"); - } - else - { - flags = IMAP_Utils.ParseMessageFlags(flagsList); - } - } - - #endregion - - else - { - throw new Exception("Not supported fetch reply: " + r.SourceString); - } - } - - fetchItems.Add(new IMAP_FetchItem(no, - uid, - size, - data, - flags, - internalDate, - envelope, - bodystructure, - fetchFlags)); - } - } - else - { - break; - } - } - - if (!RemoveCmdTag(line).ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - - return fetchItems.ToArray(); - } - - /// - /// Gets specified message from server and stores to specified stream. - /// - /// Message UID which to get. - /// Stream where to store message. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when IMAP client is not connected,not authenticated and folder not selected. - public void FetchMessage(int uid, Stream storeStream) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException("The command is only valid in authenticated state."); - } - if (m_SelectedFolder.Length == 0) - { - throw new InvalidOperationException("The command is only valid in selected state."); - } - - // Send fetch command line to server. - string line = GetNextCmdTag() + " UID FETCH " + uid + " BODY[]"; - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - // Read un-tagged response lines while we get final response line. - while (true) - { - line = ReadLine(); - - // We have un-tagged resposne. - if (line.StartsWith("*")) - { - if (IsStatusResponse(line)) - { - ProcessStatusResponse(line); - } - else if (line.ToUpper().IndexOf("BODY[") > - 1) - { - if (line.IndexOf('{') > -1) - { - StringReader r = new StringReader(line); - while (r.Available > 0 && !r.StartsWith("{")) - { - r.ReadSpecifiedLength(1); - } - int sizeOfData = Convert.ToInt32(r.ReadParenthesized()); - TcpStream.ReadFixedCount(storeStream, sizeOfData); - LogAddRead(sizeOfData, "Readed " + sizeOfData + " bytes."); - line = ReadLine(); - } - } - } - else - { - break; - } - } - - if (!RemoveCmdTag(line).ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - } - - /// - /// Stores specified message flags to specified messages. - /// - /// IMAP sequence-set. - /// Message flags. - /// Specifies if UID STORE or STORE. - /// For UID STORE all sequence_set numers must be message UID values and for normal STORE message numbers. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when IMAP client is not connected,not authenticated and folder not selected. - public void StoreMessageFlags(IMAP_SequenceSet sequence_set, IMAP_MessageFlags msgFlags, bool uidStore) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException("The command is only valid in authenticated state."); - } - if (m_SelectedFolder.Length == 0) - { - throw new InvalidOperationException("The command is only valid in selected state."); - } - - string line = ""; - if (uidStore) - { - line = GetNextCmdTag() + " UID STORE " + sequence_set.ToSequenceSetString() + " FLAGS (" + - IMAP_Utils.MessageFlagsToString(msgFlags) + ")"; - } - else - { - line = GetNextCmdTag() + " STORE " + sequence_set.ToSequenceSetString() + " FLAGS (" + - IMAP_Utils.MessageFlagsToString(msgFlags) + ")"; - } - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - // Read un-tagged response lines while we get final response line. - while (true) - { - line = ReadLine(); - - // We have un-tagged resposne. - if (line.StartsWith("*")) - { - ProcessStatusResponse(line); - } - else - { - break; - } - } - - if (!RemoveCmdTag(line).ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - } - - public void AddMessageFlags(IMAP_SequenceSet sequence_set, IMAP_MessageFlags msgFlags, bool uidStore) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException("The command is only valid in authenticated state."); - } - if (m_SelectedFolder.Length == 0) - { - throw new InvalidOperationException("The command is only valid in selected state."); - } - - string line = ""; - if (uidStore) - { - line = GetNextCmdTag() + " UID STORE " + sequence_set.ToSequenceSetString() + - " +FLAGS.SILENT (" + IMAP_Utils.MessageFlagsToString(msgFlags) + ")"; - } - else - { - line = GetNextCmdTag() + " STORE " + sequence_set.ToSequenceSetString() + " +FLAGS.SILENT (" + - IMAP_Utils.MessageFlagsToString(msgFlags) + ")"; - } - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - // Read un-tagged response lines while we get final response line. - while (true) - { - line = ReadLine(); - - // We have un-tagged resposne. - if (line.StartsWith("*")) - { - ProcessStatusResponse(line); - } - else - { - break; - } - } - - if (!RemoveCmdTag(line).ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - } - - public void RemoveMessageFlags(IMAP_SequenceSet sequence_set, - IMAP_MessageFlags msgFlags, - bool uidStore) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException("The command is only valid in authenticated state."); - } - if (m_SelectedFolder.Length == 0) - { - throw new InvalidOperationException("The command is only valid in selected state."); - } - - string line = ""; - if (uidStore) - { - line = GetNextCmdTag() + " UID STORE " + sequence_set.ToSequenceSetString() + - " -FLAGS.SILENT (" + IMAP_Utils.MessageFlagsToString(msgFlags) + ")"; - } - else - { - line = GetNextCmdTag() + " STORE " + sequence_set.ToSequenceSetString() + " -FLAGS.SILENT (" + - IMAP_Utils.MessageFlagsToString(msgFlags) + ")"; - } - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - // Read un-tagged response lines while we get final response line. - while (true) - { - line = ReadLine(); - - // We have un-tagged resposne. - if (line.StartsWith("*")) - { - ProcessStatusResponse(line); - } - else - { - break; - } - } - - if (!RemoveCmdTag(line).ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - } - - /// - /// Gets messages total size in selected folder. - /// - /// Returns messages total size in selected folder. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when IMAP client is not connected,not authenticated and folder not selected. - public int GetMessagesTotalSize() - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException("The command is only valid in authenticated state."); - } - if (m_SelectedFolder.Length == 0) - { - throw new InvalidOperationException("The command is only valid in selected state."); - } - - int totalSize = 0; - - string line = GetNextCmdTag() + " FETCH 1:* (RFC822.SIZE)"; - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - // Read un-tagged response lines while we get final response line. - while (true) - { - line = ReadLine(); - - // We have un-tagged resposne. - if (line.StartsWith("*")) - { - if (IsStatusResponse(line)) - { - ProcessStatusResponse(line); - } - else - { - // Get rid of * 1 FETCH and parse params. Reply:* 1 FETCH (UID 12 BODY[] ...) - line = line.Substring(line.IndexOf("FETCH (") + 7); - - // RFC822.SIZE field - if (line.ToUpper().StartsWith("RFC822.SIZE")) - { - line = line.Substring(11).Trim(); // Remove RFC822.SIZE word from reply - - totalSize += Convert.ToInt32(line.Substring(0, line.Length - 1).Trim()); - // Remove ending ')' - } - } - } - else - { - break; - } - } - - if (!RemoveCmdTag(line).ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - - return totalSize; - } - - /// - /// Gets unseen messages count in selected folder. - /// - /// Returns number of unseen messages. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when IMAP client is not connected,not authenticated and folder not selected. - public int GetUnseenMessagesCount() - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException("The command is only valid in authenticated state."); - } - if (m_SelectedFolder.Length == 0) - { - throw new InvalidOperationException("The command is only valid in selected state."); - } - - int count = 0; - - string line = GetNextCmdTag() + " FETCH 1:* (FLAGS)"; - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - // Read un-tagged response lines while we get final response line. - while (true) - { - line = ReadLine(); - - // We have un-tagged resposne. - if (line.StartsWith("*")) - { - if (IsStatusResponse(line)) - { - ProcessStatusResponse(line); - } - else - { - // Get rid of * 1 FETCH and parse params. Reply:* 1 FETCH (UID 12 BODY[] ...) - line = line.Substring(line.IndexOf("FETCH (") + 7); - - if (line.ToUpper().IndexOf("\\SEEN") == -1) - { - count++; - } - } - } - else - { - break; - } - } - - if (!RemoveCmdTag(line).ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - - return count; - } - - /// - /// Gets IMAP server folder separator char. - /// - /// Returns IMAP server folder separator char. - public string GetFolderSeparator() - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException("The command is only valid in authenticated state."); - } - - string folderSeparator = ""; - - string line = GetNextCmdTag() + " LIST \"\" \"\""; - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - // Read un-tagged response lines while we get final response line. - while (true) - { - line = ReadLine(); - - // We have un-tagged resposne. - if (line.StartsWith("*")) - { - if (IsStatusResponse(line)) - { - ProcessStatusResponse(line); - } - else - { - line = line.Substring(line.IndexOf(")") + 1).Trim(); // Remove * LIST(..) - - // get folder separator - folderSeparator = line.Substring(0, line.IndexOf(" ")).Trim(); - } - } - else - { - break; - } - } - - if (!RemoveCmdTag(line).ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - - return folderSeparator.Replace("\"", ""); - } - - #endregion - - #region Overrides - - /// - /// This method is called after TCP client has sucessfully connected. - /// - protected override void OnConnected() - { - SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength], - SizeExceededAction. - JunkAndThrowException); - TcpStream.ReadLine(args, false); - if (args.Error != null) - { - throw args.Error; - } - string line = args.LineUtf8; - LogAddRead(args.BytesInBuffer, line); - line = RemoveCmdTag(line); - if (line.ToUpper().StartsWith("OK")) - { - // Clear path separator, so next access will get it. - m_PathSeparator = '\0'; - } - else - { - throw new Exception("Server returned: " + line); - } - GetCapabilities(); - if (Capabilities.Contains("STARTTLS") && Capabilities.Contains("LOGINDISABLED")) - { - //Switch to ssl - StartTLS(); - //get caps again - GetCapabilities(); - } - } - - #endregion - - #region Event handlers - - private void Timer_Elapsed(object sender, ElapsedEventArgs e) - { - if (IsConnected && IsAuthenticated && !IsDisposed) - { - if ((DateTime.Now - LastActivity) > TimeSpan.FromMilliseconds(NoopInterval)) - { - //Send noop - //SendNoop();//No noop - } - } - } - - #endregion - - #region Utility methods - - private DateTime lastStatus; - - public void SendNoop() - { - SendNoop(false); - } - - public void SendNoop(bool bForce) - { - if (IsConnected && IsAuthenticated && !IsDisposed) - { - if (((DateTime.Now - lastStatus).TotalMilliseconds > NoopDelay)||bForce) - { - string cmdTag = GetNextCmdTag(); - string line = cmdTag + " NOOP"; - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - while (true) - { - line = ReadLine(); - - if (line.StartsWith("*")) - { - ProcessStatusResponse(line); - } - else - { - break; - } - } - - line = RemoveCmdTag(line); - if (!line.ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - lastStatus = DateTime.Now; - } - } - } - - private void ProcessSearchResponse(string line, List ds) - { - if (line.IndexOf("SEARCH") > -1 && !line.Equals("* SEARCH")/*empty responce*/) - { - string[] idsLine = line.Substring(line.IndexOf(' ', line.IndexOf("SEARCH"))).Split( - new[] {' '}, StringSplitOptions.RemoveEmptyEntries); - foreach (string num in idsLine) - { - int number; - if (int.TryParse(num, NumberStyles.Integer, CultureInfo.InvariantCulture, out number)) - { - ds.Add(number); - } - } - } - } - - /// - /// Removes command tag from response line. - /// - /// Response line with command tag. - /// - private string RemoveCmdTag(string responseLine) - { - return responseLine.Substring(responseLine.IndexOf(" ")).Trim(); - } - - /// - /// Processes IMAP STATUS response and updates this class status info. - /// - /// IMAP STATUS response line. - private void ProcessStatusResponse(string statusResponse) - { - /* RFC 3501 7.3.1. EXISTS Response - Example: S: * 23 EXISTS - */ - - /* RFC 3501 7.3.2. RECENT Response - Example: S: * 5 RECENT - */ - - statusResponse = statusResponse.ToUpper(); - - // Get rid of * - statusResponse = statusResponse.Substring(1).Trim(); - - if (statusResponse.IndexOf("EXISTS") > -1 && statusResponse.IndexOf("FLAGS") == -1) - { - MsgCount = Convert.ToInt32(statusResponse.Substring(0, statusResponse.IndexOf(" ")).Trim()); - lastStatus = DateTime.Now; - } - else if (statusResponse.IndexOf("RECENT") > -1 && statusResponse.IndexOf("FLAGS") == -1) - { - NewMsgCount = Convert.ToInt32(statusResponse.Substring(0, statusResponse.IndexOf(" ")).Trim()); - lastStatus = DateTime.Now; - } - else if (statusResponse.IndexOf("EXPUNGE") > -1 && statusResponse.IndexOf("FLAGS") == -1) - { - //Dec number by 1 - MsgCount--; - lastStatus = DateTime.Now; - } - } - - /// - /// Gets if specified line is STATUS response. - /// - /// - /// - private bool IsStatusResponse(string line) - { - if (!line.StartsWith("*")) - { - return false; - } - - // Remove * - line = line.Substring(1).TrimStart(); - - // RFC 3951 7.1.2. NO Response (untagged) - if (line.ToUpper().StartsWith("NO")) - { - return true; - } - // RFC 3951 7.1.3. BAD Response (untagged) - else if (line.ToUpper().StartsWith("BAD")) - { - return true; - } - - // * 1 EXISTS - // * 1 RECENT - // * 1 EXPUNGE - if (line.ToLower().IndexOf("exists") > -1) - { - return true; - } - if (line.ToLower().IndexOf("recent") > -1 && line.ToLower().IndexOf("flags") == -1) - { - return true; - } - else if (line.ToLower().IndexOf("expunge") > -1) - { - return true; - } - - return false; - } - - /// - /// Gets next command tag. - /// - /// Returns next command tag. - private string GetNextCmdTag() - { - m_CmdNumber++; - - return "ls" + m_CmdNumber; - } - - #endregion - - public byte[] GetFilters() - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException("The command is only valid in authenticated state."); - } - if (m_SelectedFolder.Length == 0) - { - throw new InvalidOperationException("The command is only valid in selected state."); - } - - // Send fetch command line to server. - string line = GetNextCmdTag() + " X-GET-FILTER "; - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - using (MemoryStream store = new MemoryStream()) - { - // Read un-tagged response lines while we get final response line. - while (true) - { - line = ReadLine(); - - // We have un-tagged resposne. - if (line.StartsWith("*")) - { - if (IsStatusResponse(line)) - { - ProcessStatusResponse(line); - } - else if (line.ToUpper().IndexOf("FILTER") > -1) - { - if (line.IndexOf('{') > -1) - { - StringReader r = new StringReader(line); - while (r.Available > 0 && !r.StartsWith("{")) - { - r.ReadSpecifiedLength(1); - } - int sizeOfData = Convert.ToInt32(r.ReadParenthesized()); - TcpStream.ReadFixedCount(store, sizeOfData); - LogAddRead(sizeOfData, "Readed " + sizeOfData + " bytes."); - line = ReadLine(); - } - } - } - else - { - break; - } - } - - if (!RemoveCmdTag(line).ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - byte[] buffer = store.GetBuffer(); - if (buffer.Length>0) - { - return Convert.FromBase64String(Encoding.UTF8.GetString(buffer)); - } - else - { - return null; - } - } - - } - - public void SetFilters(byte[] bufferRaw) - { - if (bufferRaw == null) - { - throw new ArgumentNullException("bufferRaw"); - } - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException("The command is only valid in authenticated state."); - } - - // Ensure that we send right separator to server, we accept both \ and /. - - byte[] buffer = Encoding.UTF8.GetBytes(Convert.ToBase64String(bufferRaw)); - string line = string.Format("{0} X-SET-FILTER {{{1}}}", GetNextCmdTag(), buffer.Length); - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - - // Read un-tagged response lines while we get final response line or + send data. - while (true) - { - line = ReadLine(); - - // We have un-tagged resposne. - if (line.StartsWith("*")) - { - ProcessStatusResponse(line); - } - // Send data. - else if (line.StartsWith("+")) - { - // Send message. - TcpStream.Write(buffer, 0, buffer.Length); - LogAddWrite(buffer.Length, "Wrote " + buffer.Length + " bytes."); - - // Send CRLF, ends splitted command line. - TcpStream.Write(new[] { (byte)'\r', (byte)'\n' }, 0, 2); - LogAddWrite(buffer.Length, "Wrote CRLF."); - } - else - { - break; - } - } - - if (!RemoveCmdTag(line).ToUpper().StartsWith("OK")) - { - throw new IMAP_ClientException(line); - } - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_ClientException.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_ClientException.cs deleted file mode 100644 index b8ba4c59b..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_ClientException.cs +++ /dev/null @@ -1,83 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Client -{ - #region usings - - using System; - - #endregion - - /// - /// IMAP client exception. - /// - public class IMAP_ClientException : Exception - { - #region Members - - private readonly string m_ResponseText = ""; - private readonly string m_StatusCode = ""; - - #endregion - - #region Properties - - /// - /// Gets IMAP server error status code. - /// - public string StatusCode - { - get { return m_StatusCode; } - } - - /// - /// Gets IMAP server response text after status code. - /// - public string ResponseText - { - get { return m_ResponseText; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// IMAP server response line. - /// Is raised when responseLine is null. - public IMAP_ClientException(string responseLine) : base(responseLine) - { - if (responseLine == null) - { - throw new ArgumentNullException("responseLine"); - } - - // SP - string[] code_text = responseLine.Split(new char[] {}, 2); - m_StatusCode = code_text[0]; - if (code_text.Length == 2) - { - m_ResponseText = code_text[1]; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_FetchItem.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_FetchItem.cs deleted file mode 100644 index d044ef7f9..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_FetchItem.cs +++ /dev/null @@ -1,317 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Client -{ - #region usings - - using System; - using System.IO; - using System.Text; - using Mime; - using Server; - - #endregion - - /// - /// IMAP fetch item. - /// - public class IMAP_FetchItem - { - #region Members - - private readonly string m_BodyStructure = ""; - private readonly byte[] m_Data; - private readonly string m_Envelope = ""; - private readonly IMAP_FetchItem_Flags m_FetchFlags = IMAP_FetchItem_Flags.MessageFlags; - private readonly IMAP_MessageFlags m_Flags = IMAP_MessageFlags.Recent; - private readonly string m_InternalDate = ""; - private readonly int m_No; - private readonly int m_Size; - private readonly int m_UID; - - #endregion - - #region Properties - - /// - /// Specifies what data this IMAP_FetchItem contains. This is flagged value and can contain multiple values. - /// - public IMAP_FetchItem_Flags FetchFlags - { - get { return m_FetchFlags; } - } - - /// - /// Gets number of message in folder. - /// - public int MessageNumber - { - get { return m_No; } - } - - /// - /// Gets message UID. This property is available only if IMAP_FetchItem_Flags.UID was specified, - /// otherwise throws exception. - /// - public int UID - { - get - { - if ((m_FetchFlags & IMAP_FetchItem_Flags.UID) == 0) - { - throw new Exception( - "IMAP_FetchItem_Flags.UID wasn't specified in FetchMessages command, becuse of it this property is unavailable."); - } - - return m_UID; - } - } - - /// - /// Gets message size. This property is available only if IMAP_FetchItem_Flags.Size was specified, - /// otherwise throws exception. - /// - public int Size - { - get - { - if ((m_FetchFlags & IMAP_FetchItem_Flags.Size) == 0) - { - throw new Exception( - "IMAP_FetchItem_Flags.Size wasn't specified in FetchMessages command, becuse of it this property is unavailable."); - } - - return m_Size; - } - } - - /// - /// Gets message IMAP server INTERNAL date. This property is available only if IMAP_FetchItem_Flags.InternalDate was specified, - /// otherwise throws exception. - /// - public DateTime InternalDate - { - get - { - if ((m_FetchFlags & IMAP_FetchItem_Flags.InternalDate) == 0) - { - throw new Exception( - "IMAP_FetchItem_Flags.InternalDate wasn't specified in FetchMessages command, becuse of it this property is unavailable."); - } - - return IMAP_Utils.ParseDate(m_InternalDate); - } - } - - /// - /// Gets message flags. This property is available only if IMAP_FetchItem_Flags.MessageFlags was specified, - /// otherwise throws exception. - /// - public IMAP_MessageFlags MessageFlags - { - get - { - if ((m_FetchFlags & IMAP_FetchItem_Flags.MessageFlags) == 0) - { - throw new Exception( - "IMAP_FetchItem_Flags.MessageFlags wasn't specified in FetchMessages command, becuse of it this property is unavailable."); - } - - return m_Flags; - } - } - - /// - /// Gets message IMAP ENVELOPE. This property is available only if IMAP_FetchItem_Flags.Envelope was specified, - /// otherwise throws exception. - /// - public IMAP_Envelope Envelope - { - get - { - if ((m_FetchFlags & IMAP_FetchItem_Flags.Envelope) == 0) - { - throw new Exception( - "IMAP_FetchItem_Flags.Envelope wasn't specified in FetchMessages command, becuse of it this property is unavailable."); - } - - IMAP_Envelope envelope = new IMAP_Envelope(); - envelope.Parse(m_Envelope); - return envelope; - } - } - - /// - /// Gets message IMAP BODYSTRUCTURE. This property is available only if IMAP_FetchItem_Flags.BodyStructure was specified, - /// otherwise throws exception. - /// - public IMAP_BODY BodyStructure - { - get - { - if ((m_FetchFlags & IMAP_FetchItem_Flags.BodyStructure) == 0) - { - throw new Exception( - "IMAP_FetchItem_Flags.BodyStructure wasn't specified in FetchMessages command, becuse of it this property is unavailable."); - } - - IMAP_BODY bodyStructure = new IMAP_BODY(); - bodyStructure.Parse(m_BodyStructure); - return bodyStructure; - } - } - - /// - /// Gets message header data. This property is available only if IMAP_FetchItem_Flags.Header was specified, - /// otherwise throws exception. - /// - public byte[] HeaderData - { - get - { - if ( - !((m_FetchFlags & IMAP_FetchItem_Flags.Header) != 0 || - (m_FetchFlags & IMAP_FetchItem_Flags.Message) != 0)) - { - throw new Exception( - "IMAP_FetchItem_Flags.Header wasn't specified in FetchMessages command, becuse of it this property is unavailable."); - } - - if ((m_FetchFlags & IMAP_FetchItem_Flags.Message) != 0) - { - return Encoding.ASCII.GetBytes(MimeUtils.ParseHeaders(new MemoryStream(m_Data))); - } - else - { - return m_Data; - } - } - } - - /// - /// Gets message data. This property is available only if IMAP_FetchItem_Flags.Message was specified, - /// otherwise throws exception. - /// - public byte[] MessageData - { - get - { - if ((m_FetchFlags & IMAP_FetchItem_Flags.Message) == 0) - { - throw new Exception( - "IMAP_FetchItem_Flags.Message wasn't specified in FetchMessages command, becuse of it this property is unavailable."); - } - - return m_Data; - } - } - - /// - /// Gets if message is unseen. This property is available only if IMAP_FetchItem_Flags.MessageFlags was specified, - /// otherwise throws exception. - /// - public bool IsNewMessage - { - get - { - if ((m_FetchFlags & IMAP_FetchItem_Flags.MessageFlags) == 0) - { - throw new Exception( - "IMAP_FetchItem_Flags.MessageFlags wasn't specified in FetchMessages command, becuse of it this property is unavailable."); - } - - return (m_Flags & IMAP_MessageFlags.Seen) == 0; - } - } - - /// - /// Gets if message is answered. This property is available only if IMAP_FetchItem_Flags.MessageFlags was specified, - /// otherwise throws exception. - /// - public bool IsAnswered - { - get - { - if ((m_FetchFlags & IMAP_FetchItem_Flags.MessageFlags) == 0) - { - throw new Exception( - "IMAP_FetchItem_Flags.MessageFlags wasn't specified in FetchMessages command, becuse of it this property is unavailable."); - } - - return (m_Flags & IMAP_MessageFlags.Answered) != 0; - } - } - - /// - /// Gets message data(headers or full message), it depends on HeadersOnly property. - /// - [Obsolete("Use HeaderData or MessageData data instead.")] - public byte[] Data - { - get { return m_Data; } - } - - /// - /// Gets if headers or full message requested in fetch. - /// - [Obsolete("Use FetchFlags property instead !")] - public bool HeadersOnly - { - get { return (m_FetchFlags & IMAP_FetchItem_Flags.Header) != 0; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Number of message in folder. - /// Message UID. - /// Message size. - /// Message data. - /// Message flags. - /// Message INTERNALDATE. - /// Envelope string. - /// BODYSTRUCTURE string. - /// Specifies what data fetched from IMAP server. - internal IMAP_FetchItem(int no, - int uid, - int size, - byte[] data, - IMAP_MessageFlags flags, - string internalDate, - string envelope, - string bodyStructure, - IMAP_FetchItem_Flags fetchFlags) - { - m_No = no; - m_UID = uid; - m_Size = size; - m_Data = data; - m_Flags = flags; - m_InternalDate = internalDate; - m_Envelope = envelope; - m_FetchFlags = fetchFlags; - m_BodyStructure = bodyStructure; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_FetchItem_Flags.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_FetchItem_Flags.cs deleted file mode 100644 index 1c03724bf..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_FetchItem_Flags.cs +++ /dev/null @@ -1,71 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Client -{ - /// - /// Specifies what data is requested from IMAP server FETCH command. - /// Fetch items are flags and can be combined. For example: IMAP_FetchItem_Flags.MessageFlags | IMAP_FetchItem_Flags.Header. - /// - public enum IMAP_FetchItem_Flags - { - /// - /// Message UID value. - /// - UID = 1, - - /// - /// Message size in bytes. - /// - Size = 2, - - /// - /// Message IMAP server INTERNALDATE. - /// - InternalDate = 4, - - /// - /// Fetches message flags. (\SEEN \ANSWERED ...) - /// - MessageFlags = 8, - - /// - /// Fetches message header. - /// - Header = 16, - - /// - /// Fetches full message. - /// - Message = 32, - - /// - /// Fetches message ENVELOPE structure. - /// - Envelope = 64, - - /// - /// Fetches message BODYSTRUCTURE structure. - /// - BodyStructure = 128, - - /// - /// Fetches all info. - /// - All = 0xFFFF - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_Namespace.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_Namespace.cs deleted file mode 100644 index 76b55e3c5..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_Namespace.cs +++ /dev/null @@ -1,67 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Client -{ - /// - /// IMAP namespace. Defined in RFC 2342. - /// - public class IMAP_Namespace - { - #region Members - - private readonly string m_Delimiter = ""; - private readonly string m_Name = ""; - - #endregion - - #region Properties - - /// - /// Gets namespace name. - /// - public string Name - { - get { return m_Name; } - } - - /// - /// Gets namespace hierarchy delimiter. - /// - public string Delimiter - { - get { return m_Delimiter; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Namespace name. - /// Namespace hierarchy delimiter. - public IMAP_Namespace(string name, string delimiter) - { - m_Name = name; - m_Delimiter = delimiter; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_NamespacesInfo.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_NamespacesInfo.cs deleted file mode 100644 index 953caec75..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_NamespacesInfo.cs +++ /dev/null @@ -1,185 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Client -{ - #region usings - - using System.Collections.Generic; - - #endregion - - /// - /// IMAP namespaces info. Defined in RFC 2342. - /// - public class IMAP_NamespacesInfo - { - #region Members - - private readonly IMAP_Namespace[] m_pOtherUsersNamespaces; - private readonly IMAP_Namespace[] m_pPersonalNamespaces; - private readonly IMAP_Namespace[] m_pSharedNamespaces; - - #endregion - - #region Properties - - /// - /// Gets IMAP server "Personal Namespaces". Returns null if namespace not defined. - /// - public IMAP_Namespace[] PersonalNamespaces - { - get { return m_pPersonalNamespaces; } - } - - /// - /// Gets IMAP server "Other Users Namespaces". Returns null if namespace not defined. - /// - public IMAP_Namespace[] OtherUsersNamespaces - { - get { return m_pOtherUsersNamespaces; } - } - - /// - /// Gets IMAP server "Shared Namespaces". Returns null if namespace not defined. - /// - public IMAP_Namespace[] SharedNamespaces - { - get { return m_pSharedNamespaces; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// IMAP server "Personal Namespaces". - /// IMAP server "Other Users Namespaces". - /// IMAP server "Shared Namespaces". - public IMAP_NamespacesInfo(IMAP_Namespace[] personalNamespaces, - IMAP_Namespace[] otherUsersNamespaces, - IMAP_Namespace[] sharedNamespaces) - { - m_pPersonalNamespaces = personalNamespaces; - m_pOtherUsersNamespaces = otherUsersNamespaces; - m_pSharedNamespaces = sharedNamespaces; - } - - #endregion - - #region Utility methods - - private static IMAP_Namespace[] ParseNamespaces(string val) - { - StringReader r = new StringReader(val); - r.ReadToFirstChar(); - - List namespaces = new List(); - while (r.StartsWith("(")) - { - namespaces.Add(ParseNamespace(r.ReadParenthesized())); - } - - return namespaces.ToArray(); - } - - private static IMAP_Namespace ParseNamespace(string val) - { - string[] parts = TextUtils.SplitQuotedString(val, ' ', true); - string name = ""; - if (parts.Length > 0) - { - name = parts[0]; - } - string delimiter = ""; - if (parts.Length > 1) - { - delimiter = parts[1]; - } - - // Remove delimiter from end, if it's there. - if (name.EndsWith(delimiter)) - { - name = name.Substring(0, name.Length - delimiter.Length); - } - - return new IMAP_Namespace(name, delimiter); - } - - #endregion - - #region Internal methods - - /// - /// Parses namespace info from IMAP NAMESPACE response string. - /// - /// IMAP NAMESPACE response string. - /// - internal static IMAP_NamespacesInfo Parse(string namespaceString) - { - StringReader r = new StringReader(namespaceString); - // Skip NAMESPACE - r.ReadWord(); - - IMAP_Namespace[] personalNamespaces = null; - IMAP_Namespace[] otherUsersNamespaces = null; - IMAP_Namespace[] sharedNamespaces = null; - - // Personal namespace - r.ReadToFirstChar(); - if (r.StartsWith("(")) - { - personalNamespaces = ParseNamespaces(r.ReadParenthesized()); - } - // NIL, skip it. - else - { - r.ReadWord(); - } - - // Users Shared namespace - r.ReadToFirstChar(); - if (r.StartsWith("(")) - { - otherUsersNamespaces = ParseNamespaces(r.ReadParenthesized()); - } - // NIL, skip it. - else - { - r.ReadWord(); - } - - // Shared namespace - r.ReadToFirstChar(); - if (r.StartsWith("(")) - { - sharedNamespaces = ParseNamespaces(r.ReadParenthesized()); - } - // NIL, skip it. - else - { - r.ReadWord(); - } - - return new IMAP_NamespacesInfo(personalNamespaces, otherUsersNamespaces, sharedNamespaces); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_Quota.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_Quota.cs deleted file mode 100644 index 52a1614ad..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Client/IMAP_Quota.cs +++ /dev/null @@ -1,100 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Client -{ - /// - /// IMAP quota entry. Defined in RFC 2087. - /// - public class IMAP_Quota - { - #region Members - - private readonly long m_MaxMessages = -1; - private readonly long m_MaxStorage = -1; - private readonly long m_Messages = -1; - private readonly string m_QuotaRootName = ""; - private readonly long m_Storage = -1; - - #endregion - - #region Properties - - /// - /// Gets quota root name. - /// - public string QuotaRootName - { - get { return m_QuotaRootName; } - } - - /// - /// Gets current messages count. Returns -1 if messages and maximum messages quota is not defined. - /// - public long Messages - { - get { return m_Messages; } - } - - /// - /// Gets maximum allowed messages count. Returns -1 if messages and maximum messages quota is not defined. - /// - public long MaximumMessages - { - get { return m_MaxMessages; } - } - - /// - /// Gets current storage in bytes. Returns -1 if storage and maximum storage quota is not defined. - /// - public long Storage - { - get { return m_Storage; } - } - - /// - /// Gets maximum allowed storage in bytes. Returns -1 if storage and maximum storage quota is not defined. - /// - public long MaximumStorage - { - get { return m_MaxStorage; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Quota root name. - /// Number of current messages. - /// Number of maximum allowed messages. - /// Current storage bytes. - /// Maximum allowed storage bytes. - public IMAP_Quota(string quotaRootName, long messages, long maxMessages, long storage, long maxStorage) - { - m_QuotaRootName = quotaRootName; - m_Messages = messages; - m_MaxMessages = maxMessages; - m_Storage = storage; - m_MaxStorage = maxStorage; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/IMAP_ACL_Flags.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/IMAP_ACL_Flags.cs deleted file mode 100644 index fb6a623a2..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/IMAP_ACL_Flags.cs +++ /dev/null @@ -1,70 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP -{ - /// - /// IMAP ACL(access control list) rights. - /// - public enum IMAP_ACL_Flags - { - /// - /// No permissions at all. - /// - None = 0, - /// - /// Lookup (mailbox is visible to LIST/LSUB commands). - /// - l = 1, - /// - /// Read (SELECT the mailbox, perform CHECK, FETCH, PARTIAL,SEARCH, COPY from mailbox). - /// - r = 2, - /// - /// Keep seen/unseen information across sessions (STORE SEEN flag). - /// - s = 4, - /// - /// Write (STORE flags other than SEEN and DELETED). - /// - w = 8, - /// - /// Insert (perform APPEND, COPY into mailbox). - /// - i = 16, - /// - /// Post (send mail to submission address for mailbox,not enforced by IMAP4 itself). - /// - p = 32, - /// - /// Create (CREATE new sub-mailboxes in any implementation-defined hierarchy). - /// - c = 64, - /// - /// Delete (STORE DELETED flag, perform EXPUNGE). - /// - d = 128, - /// - /// Administer (perform SETACL). - /// - a = 256, - /// - /// All permissions - /// - All = 0xFFFF, - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/IMAP_BODY.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/IMAP_BODY.cs deleted file mode 100644 index f67ca8728..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/IMAP_BODY.cs +++ /dev/null @@ -1,513 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP -{ - #region usings - - using System.Collections.Generic; - using System.IO; - using System.Text; - using Mime; - - #endregion - - /// - /// IMAP BODYSTRUCTURE. Defined in RFC 3501 7.4.2. - /// - public class IMAP_BODY - { - #region Members - - private IMAP_BODY_Entity m_pMainEntity; - - #endregion - - #region Properties - - /// - /// Gets main entity. - /// - public IMAP_BODY_Entity MainEntity - { - get { return m_pMainEntity; } - } - - /// - /// Gets all entities contained in BODYSTRUCTURE, including child entities. - /// - public IMAP_BODY_Entity[] Entities - { - get - { - List allEntities = new List(); - allEntities.Add(m_pMainEntity); - GetEntities(m_pMainEntity.ChildEntities, allEntities); - - return allEntities.ToArray(); - } - } - - /// - /// Gets attachment entities. Entity is considered as attachmnet if:

    - /// *) Content-Type: name = "" is specified (old RFC 822 message)

    - ///

    - public IMAP_BODY_Entity[] Attachmnets - { - // Cant use these at moment, we need to use extended BODYSTRUCTURE fo that - // *) Content-Disposition: attachment (RFC 2822 message)

    - // *) Content-Disposition: filename = "" is specified (RFC 2822 message)

    - - get - { - List attachments = new List(); - IMAP_BODY_Entity[] entities = Entities; - foreach (IMAP_BODY_Entity entity in entities) - { - if (entity.ContentType_Paramters != null) - { - foreach (HeaderFieldParameter parameter in entity.ContentType_Paramters) - { - if (parameter.Name.ToLower() == "name") - { - attachments.Add(entity); - break; - } - } - } - } - - return attachments.ToArray(); - } - } - - #endregion - - #region Constructor - - ///

    - /// Default constructor. - /// - public IMAP_BODY() - { - m_pMainEntity = new IMAP_BODY_Entity(); - } - - #endregion - - #region Methods - - /// - /// Constructs FETCH BODY and BODYSTRUCTURE response. - /// - /// Mime message. - /// Specifies if to construct BODY or BODYSTRUCTURE. - /// - public static string ConstructBodyStructure(Mime mime, bool bodystructure) - { - if (bodystructure) - { - return "BODYSTRUCTURE " + ConstructParts(mime.MainEntity, bodystructure); - } - else - { - return "BODY " + ConstructParts(mime.MainEntity, bodystructure); - } - } - - /// - /// Parses IMAP BODYSTRUCTURE from body structure string. - /// - /// Body structure string - public void Parse(string bodyStructureString) - { - m_pMainEntity = new IMAP_BODY_Entity(); - m_pMainEntity.Parse(bodyStructureString); - } - - #endregion - - #region Utility methods - - /// - /// Constructs specified entity and it's childentities bodystructure string. - /// - /// Mime entity. - /// Specifies if to construct BODY or BODYSTRUCTURE. - /// - private static string ConstructParts(MimeEntity entity, bool bodystructure) - { - /* RFC 3501 7.4.2 BODYSTRUCTURE - BODY A form of BODYSTRUCTURE without extension data. - - A parenthesized list that describes the [MIME-IMB] body - structure of a message. This is computed by the server by - parsing the [MIME-IMB] header fields, defaulting various fields - as necessary. - - For example, a simple text message of 48 lines and 2279 octets - can have a body structure of: ("TEXT" "PLAIN" ("CHARSET" - "US-ASCII") NIL NIL "7BIT" 2279 48) - - Multiple parts are indicated by parenthesis nesting. Instead - of a body type as the first element of the parenthesized list, - there is a sequence of one or more nested body structures. The - second element of the parenthesized list is the multipart - subtype (mixed, digest, parallel, alternative, etc.). - - For example, a two part message consisting of a text and a - BASE64-encoded text attachment can have a body structure of: - (("TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 1152 - 23)("TEXT" "PLAIN" ("CHARSET" "US-ASCII" "NAME" "cc.diff") - "<960723163407.20117h@cac.washington.edu>" "Compiler diff" - "BASE64" 4554 73) "MIXED") - - Extension data follows the multipart subtype. Extension data - is never returned with the BODY fetch, but can be returned with - a BODYSTRUCTURE fetch. Extension data, if present, MUST be in - the defined order. The extension data of a multipart body part - are in the following order: - - body parameter parenthesized list - A parenthesized list of attribute/value pairs [e.g., ("foo" - "bar" "baz" "rag") where "bar" is the value of "foo", and - "rag" is the value of "baz"] as defined in [MIME-IMB]. - - body disposition - A parenthesized list, consisting of a disposition type - string, followed by a parenthesized list of disposition - attribute/value pairs as defined in [DISPOSITION]. - - body language - A string or parenthesized list giving the body language - value as defined in [LANGUAGE-TAGS]. - - body location - A string list giving the body content URI as defined in [LOCATION]. - - Any following extension data are not yet defined in this - version of the protocol. Such extension data can consist of - zero or more NILs, strings, numbers, or potentially nested - parenthesized lists of such data. Client implementations that - do a BODYSTRUCTURE fetch MUST be prepared to accept such - extension data. Server implementations MUST NOT send such - extension data until it has been defined by a revision of this - protocol. - - The basic fields of a non-multipart body part are in the - following order: - - body type - A string giving the content media type name as defined in [MIME-IMB]. - - body subtype - A string giving the content subtype name as defined in [MIME-IMB]. - - body parameter parenthesized list - A parenthesized list of attribute/value pairs [e.g., ("foo" - "bar" "baz" "rag") where "bar" is the value of "foo" and - "rag" is the value of "baz"] as defined in [MIME-IMB]. - - body id - A string giving the content id as defined in [MIME-IMB]. - - body description - A string giving the content description as defined in [MIME-IMB]. - - body encoding - A string giving the content transfer encoding as defined in [MIME-IMB]. - - body size - A number giving the size of the body in octets. Note that - this size is the size in its transfer encoding and not the - resulting size after any decoding. - - A body type of type MESSAGE and subtype RFC822 contains, - immediately after the basic fields, the envelope structure, - body structure, and size in text lines of the encapsulated - message. - - A body type of type TEXT contains, immediately after the basic - fields, the size of the body in text lines. Note that this - size is the size in its content transfer encoding and not the - resulting size after any decoding. - - Extension data follows the basic fields and the type-specific - fields listed above. Extension data is never returned with the - BODY fetch, but can be returned with a BODYSTRUCTURE fetch. - Extension data, if present, MUST be in the defined order. - - The extension data of a non-multipart body part are in the - following order: - - body MD5 - A string giving the body MD5 value as defined in [MD5]. - - body disposition - A parenthesized list with the same content and function as - the body disposition for a multipart body part. - - body language - A string or parenthesized list giving the body language - value as defined in [LANGUAGE-TAGS]. - - body location - A string list giving the body content URI as defined in [LOCATION]. - - Any following extension data are not yet defined in this - version of the protocol, and would be as described above under - multipart extension data. - - - // We don't construct extention fields like rfc says: - Server implementations MUST NOT send such - extension data until it has been defined by a revision of this - protocol. - - - contentTypeMainMediaType - Example: 'TEXT' - contentTypeSubMediaType - Example: 'PLAIN' - conentTypeParameters - Example: '("CHARSET" "iso-8859-1" ...)' - contentID - Content-ID: header field value. - contentDescription - Content-Description: header field value. - contentEncoding - Content-Transfer-Encoding: header field value. - contentSize - mimeEntity ENCODED data size - [envelope] - NOTE: included only if contentType = "message" !!! - [contentLines] - number of ENCODED data lines. NOTE: included only if contentType = "text" !!! - - // Basic fields for multipart - (nestedMimeEntries) contentTypeSubMediaType - - // Basic fields for non-multipart - contentTypeMainMediaType contentTypeSubMediaType (conentTypeParameters) contentID contentDescription contentEncoding contentSize [envelope] [contentLine] - - */ - - StringBuilder retVal = new StringBuilder(); - // Multipart message - if ((entity.ContentType & MediaType_enum.Multipart) != 0) - { - retVal.Append("("); - - // Construct child entities. - foreach (MimeEntity childEntity in entity.ChildEntities) - { - // Construct child entity. This can be multipart or non multipart. - retVal.Append(ConstructParts(childEntity, bodystructure)); - } - - // Add contentTypeSubMediaType - string contentType = entity.ContentTypeString.Split(';')[0]; - if (contentType.Split('/').Length == 2) - { - retVal.Append(" \"" + contentType.Split('/')[1].Replace(";", "") + "\""); - } - else - { - retVal.Append(" NIL"); - } - - retVal.Append(")"); - } - // Single part message - else - { - retVal.Append("("); - - // NOTE: all header fields and parameters must in ENCODED form !!! - - // Add contentTypeMainMediaType - if (entity.ContentTypeString != null) - { - string contentType = entity.ContentTypeString.Split(';')[0]; - if (contentType.Split('/').Length == 2) - { - retVal.Append("\"" + entity.ContentTypeString.Split('/')[0] + "\""); - } - else - { - retVal.Append("NIL"); - } - } - else - { - retVal.Append("NIL"); - } - - // contentTypeSubMediaType - if (entity.ContentTypeString != null) - { - string contentType = entity.ContentTypeString.Split(';')[0]; - if (contentType.Split('/').Length == 2) - { - retVal.Append(" \"" + contentType.Split('/')[1].Replace(";", "") + "\""); - } - else - { - retVal.Append(" NIL"); - } - } - else - { - retVal.Append(" NIL"); - } - - // conentTypeParameters - Syntax: {("name" SP "value" *(SP "name" SP "value"))} - if (entity.ContentTypeString != null) - { - ParametizedHeaderField contentTypeParameters = - new ParametizedHeaderField(entity.Header.GetFirst("Content-Type:")); - if (contentTypeParameters.Parameters.Count > 0) - { - retVal.Append(" ("); - - bool first = true; - foreach (HeaderFieldParameter param in contentTypeParameters.Parameters) - { - // For first item, don't add SP - if (!first) - { - retVal.Append(" "); - } - else - { - // Clear first flag - first = false; - } - - retVal.Append("\"" + param.Name + "\" \"" + - MimeUtils.EncodeHeaderField(param.Value) + "\""); - } - - retVal.Append(")"); - } - else - { - retVal.Append(" NIL"); - } - } - else - { - retVal.Append(" NIL"); - } - - // contentID - string contentID = entity.ContentID; - if (contentID != null) - { - retVal.Append(" \"" + MimeUtils.EncodeHeaderField(contentID) + "\""); - } - else - { - retVal.Append(" NIL"); - } - - // contentDescription - string contentDescription = entity.ContentDescription; - if (contentDescription != null) - { - retVal.Append(" \"" + MimeUtils.EncodeHeaderField(contentDescription) + "\""); - } - else - { - retVal.Append(" NIL"); - } - - // contentEncoding - HeaderField contentEncoding = entity.Header.GetFirst("Content-Transfer-Encoding:"); - if (contentEncoding != null) - { - retVal.Append(" \"" + MimeUtils.EncodeHeaderField(contentEncoding.Value) + "\""); - } - else - { - // If not specified, then must be 7bit. - retVal.Append(" \"7bit\""); - } - - // contentSize - if (entity.DataEncoded != null) - { - retVal.Append(" " + entity.DataEncoded.Length); - } - else - { - retVal.Append(" 0"); - } - - // envelope ---> FOR ContentType: message/rfc822 ONLY ### - if ((entity.ContentType & MediaType_enum.Message_rfc822) != 0) - { - retVal.Append(" " + IMAP_Envelope.ConstructEnvelope(entity)); - - // TODO: BODYSTRUCTURE,LINES - } - - // contentLines ---> FOR ContentType: text/xxx ONLY ### - if ((entity.ContentType & MediaType_enum.Text) != 0) - { - if (entity.DataEncoded != null) - { - long lineCount = 0; - StreamLineReader r = new StreamLineReader(new MemoryStream(entity.DataEncoded)); - byte[] line = r.ReadLine(); - while (line != null) - { - lineCount++; - - line = r.ReadLine(); - } - - retVal.Append(" " + lineCount); - } - else - { - retVal.Append(" 0"); - } - } - - retVal.Append(")"); - } - - return retVal.ToString(); - } - - /// - /// Gets mime entities, including nested entries. - /// - /// - /// - private void GetEntities(IMAP_BODY_Entity[] entities, List allEntries) - { - if (entities != null) - { - foreach (IMAP_BODY_Entity ent in entities) - { - allEntries.Add(ent); - - // Add child entities, if any - if (ent.ChildEntities.Length > 0) - { - GetEntities(ent.ChildEntities, allEntries); - } - } - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/IMAP_BODY_Entity.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/IMAP_BODY_Entity.cs deleted file mode 100644 index 395133160..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/IMAP_BODY_Entity.cs +++ /dev/null @@ -1,314 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP -{ - #region usings - - using System; - using System.Collections.Generic; - using Mime; - - #endregion - - /// - /// IMAP BODY mime entity info. - /// - public class IMAP_BODY_Entity - { - #region Members - - private readonly List m_pChildEntities; - private string m_ContentDescription; - private ContentTransferEncoding_enum m_ContentEncoding = ContentTransferEncoding_enum._7bit; - private string m_ContentID; - private int m_ContentLines; - private int m_ContentSize; - private MediaType_enum m_pContentType = MediaType_enum.Text_plain; - private HeaderFieldParameter[] m_pContentTypeParams; - private IMAP_Envelope m_pEnvelope; - private IMAP_BODY_Entity m_pParentEntity; - - #endregion - - #region Properties - - /// - /// Gets parent entity of this entity. If this entity is top level, then this property returns null. - /// - public IMAP_BODY_Entity ParentEntity - { - get { return m_pParentEntity; } - } - - /// - /// Gets child entities. This property is available only if ContentType = multipart/... . - /// - public IMAP_BODY_Entity[] ChildEntities - { - get - { - // if((this.ContentType & MediaType_enum.Multipart) == 0){ - // throw new Exception("NOTE: ChildEntities property is available only for non-multipart contentype !"); - // } - - return m_pChildEntities.ToArray(); - } - } - - /// - /// Gets header field "Content-Type:" value. - /// - public MediaType_enum ContentType - { - get { return m_pContentType; } - } - - /// - /// Gets header field "Content-Type:" prameters. This value is null if no parameters. - /// - public HeaderFieldParameter[] ContentType_Paramters - { - get { return m_pContentTypeParams; } - } - - /// - /// Gets header field "Content-ID:" value. Returns null if value isn't set. - /// - public string ContentID - { - get { return m_ContentID; } - } - - /// - /// Gets header field "Content-Description:" value. Returns null if value isn't set. - /// - public string ContentDescription - { - get { return m_ContentDescription; } - } - - /// - /// Gets header field "Content-Transfer-Encoding:" value. - /// - public ContentTransferEncoding_enum ContentTransferEncoding - { - get { return m_ContentEncoding; } - } - - /// - /// Gets content encoded data size. NOTE: This property is available only for non-multipart contentype ! - /// - public int ContentSize - { - get - { - if ((ContentType & MediaType_enum.Multipart) != 0) - { - throw new Exception( - "NOTE: ContentSize property is available only for non-multipart contentype !"); - } - - return m_ContentSize; - } - } - - /// - /// Gets content envelope. NOTE: This property is available only for message/xxx content type ! - /// Yhis value can be also null if no ENVELOPE provided by server. - /// - public IMAP_Envelope Envelope - { - get - { - if ((ContentType & MediaType_enum.Message) == 0) - { - throw new Exception( - "NOTE: Envelope property is available only for non-multipart contentype !"); - } - - return null; - } - } - - /// - /// Gets content encoded data lines. NOTE: This property is available only for text/xxx content type ! - /// - public int ContentLines - { - get - { - if ((ContentType & MediaType_enum.Text) == 0) - { - throw new Exception( - "NOTE: ContentLines property is available only for text/xxx content type !"); - } - - return m_ContentSize; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - internal IMAP_BODY_Entity() - { - m_pChildEntities = new List(); - } - - #endregion - - #region Internal methods - - /// - /// Parses entity and it's child entities. - /// - internal void Parse(string text) - { - StringReader r = new StringReader(text); - r.ReadToFirstChar(); - - // If starts with ( then multipart/xxx, otherwise normal single part entity - if (r.StartsWith("(")) - { - // Entities are (entity1)(entity2)(...) ContentTypeSubType - while (r.StartsWith("(")) - { - IMAP_BODY_Entity entity = new IMAP_BODY_Entity(); - entity.Parse(r.ReadParenthesized()); - entity.m_pParentEntity = this; - m_pChildEntities.Add(entity); - - r.ReadToFirstChar(); - } - - // Read multipart values. (nestedMimeEntries) contentTypeSubMediaType - string contentTypeSubMediaType = r.ReadWord(); - - m_pContentType = MimeUtils.ParseMediaType("multipart/" + contentTypeSubMediaType); - } - else - { - // Basic fields for non-multipart - // contentTypeMainMediaType contentTypeSubMediaType (conentTypeParameters) contentID contentDescription contentEncoding contentSize [envelope] [contentLine] - - // Content-Type - string contentTypeMainMediaType = r.ReadWord(); - string contentTypeSubMediaType = r.ReadWord(); - if (contentTypeMainMediaType.ToUpper() != "NIL" && contentTypeSubMediaType.ToUpper() != "NIL") - { - m_pContentType = - MimeUtils.ParseMediaType(contentTypeMainMediaType + "/" + contentTypeSubMediaType); - } - - // Content-Type header field parameters - // Parameters syntax: "name" "value" "name" "value" ... . - r.ReadToFirstChar(); - string conentTypeParameters = "NIL"; - if (r.StartsWith("(")) - { - conentTypeParameters = r.ReadParenthesized(); - - StringReader contentTypeParamReader = - new StringReader(MimeUtils.DecodeWords(conentTypeParameters)); - List parameters = new List(); - while (contentTypeParamReader.Available > 0) - { - string parameterName = contentTypeParamReader.ReadWord(); - string parameterValue = contentTypeParamReader.ReadWord(); - - parameters.Add(new HeaderFieldParameter(parameterName, parameterValue)); - } - m_pContentTypeParams = parameters.ToArray(); - } - else - { - // Skip NIL - r.ReadWord(); - } - - // Content-ID: - string contentID = r.ReadWord(); - if (contentID.ToUpper() != "NIL") - { - m_ContentID = contentID; - } - - // Content-Description: - string contentDescription = r.ReadWord(); - if (contentDescription.ToUpper() != "NIL") - { - m_ContentDescription = contentDescription; - } - - // Content-Transfer-Encoding: - string contentEncoding = r.ReadWord(); - if (contentEncoding.ToUpper() != "NIL") - { - m_ContentEncoding = MimeUtils.ParseContentTransferEncoding(contentEncoding); - } - - // Content Encoded data size in bytes - string contentSize = r.ReadWord(); - if (contentSize.ToUpper() != "NIL") - { - m_ContentSize = Convert.ToInt32(contentSize); - } - - // Only for ContentType message/rfc822 - if (ContentType == MediaType_enum.Message_rfc822) - { - r.ReadToFirstChar(); - - // envelope - if (r.StartsWith("(")) - { - m_pEnvelope = new IMAP_Envelope(); - m_pEnvelope.Parse(r.ReadParenthesized()); - } - else - { - // Skip NIL, ENVELOPE wasn't included - r.ReadWord(); - } - - // TODO: - // BODYSTRUCTURE - - // contentLine - } - - // Only for ContentType text/xxx - if (contentTypeMainMediaType.ToLower() == "text") - { - // contentLine - string contentLines = r.ReadWord(); - if (contentLines.ToUpper() != "NIL") - { - m_ContentLines = Convert.ToInt32(contentLines); - } - } - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/IMAP_Envelope.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/IMAP_Envelope.cs deleted file mode 100644 index 6f7831c34..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/IMAP_Envelope.cs +++ /dev/null @@ -1,650 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP -{ - #region usings - - using System; - using System.Collections; - using System.Text; - using Mime; - using MIME; - - #endregion - - /// - /// IMAP ENVELOPE STRUCTURE (date, subject, from, sender, reply-to, to, cc, bcc, in-reply-to, and message-id). - /// Defined in RFC 3501 7.4.2. - /// - public class IMAP_Envelope - { - #region Members - - private MailboxAddress[] m_Bcc; - private MailboxAddress[] m_Cc; - private DateTime m_Date = DateTime.MinValue; - private MailboxAddress[] m_From; - private string m_InReplyTo; - private string m_MessageID; - private MailboxAddress[] m_ReplyTo; - private MailboxAddress m_Sender; - private string m_Subject; - private MailboxAddress[] m_To; - - #endregion - - #region Properties - - /// - /// Gets header field "Date:" value. Returns DateTime.MinValue if no date or date parsing fails. - /// - public DateTime Date - { - get { return m_Date; } - } - - /// - /// Gets header field "Subject:" value. Returns null if value isn't set. - /// - public string Subject - { - get { return m_Subject; } - } - - /// - /// Gets header field "From:" value. Returns null if value isn't set. - /// - public MailboxAddress[] From - { - get { return m_From; } - } - - /// - /// Gets header field "Sender:" value. Returns null if value isn't set. - /// - public MailboxAddress Sender - { - get { return m_Sender; } - } - - /// - /// Gets header field "Reply-To:" value. Returns null if value isn't set. - /// - public MailboxAddress[] ReplyTo - { - get { return m_ReplyTo; } - } - - /// - /// Gets header field "To:" value. Returns null if value isn't set. - /// - public MailboxAddress[] To - { - get { return m_To; } - } - - /// - /// Gets header field "Cc:" value. Returns null if value isn't set. - /// - public MailboxAddress[] Cc - { - get { return m_Cc; } - } - - /// - /// Gets header field "Bcc:" value. Returns null if value isn't set. - /// - public MailboxAddress[] Bcc - { - get { return m_Bcc; } - } - - /// - /// Gets header field "In-Reply-To:" value. Returns null if value isn't set. - /// - public string InReplyTo - { - get { return m_InReplyTo; } - } - - /// - /// Gets header field "Message-ID:" value. Returns null if value isn't set. - /// - public string MessageID - { - get { return m_MessageID; } - } - - #endregion - - #region Methods - - /// - /// Construct secified mime entity ENVELOPE string. - /// - /// Mime entity. - /// - public static string ConstructEnvelope(MimeEntity entity) - { - /* RFC 3501 7.4.2 - ENVELOPE - A parenthesized list that describes the envelope structure of a - message. This is computed by the server by parsing the - [RFC-2822] header into the component parts, defaulting various - fields as necessary. - - The fields of the envelope structure are in the following - order: date, subject, from, sender, reply-to, to, cc, bcc, - in-reply-to, and message-id. The date, subject, in-reply-to, - and message-id fields are strings. The from, sender, reply-to, - to, cc, and bcc fields are parenthesized lists of address - structures. - - An address structure is a parenthesized list that describes an - electronic mail address. The fields of an address structure - are in the following order: personal name, [SMTP] - at-domain-list (source route), mailbox name, and host name. - - [RFC-2822] group syntax is indicated by a special form of - address structure in which the host name field is NIL. If the - mailbox name field is also NIL, this is an end of group marker - (semi-colon in RFC 822 syntax). If the mailbox name field is - non-NIL, this is a start of group marker, and the mailbox name - field holds the group name phrase. - - If the Date, Subject, In-Reply-To, and Message-ID header lines - are absent in the [RFC-2822] header, the corresponding member - of the envelope is NIL; if these header lines are present but - empty the corresponding member of the envelope is the empty - string. - - Note: some servers may return a NIL envelope member in the - "present but empty" case. Clients SHOULD treat NIL and - empty string as identical. - - Note: [RFC-2822] requires that all messages have a valid - Date header. Therefore, the date member in the envelope can - not be NIL or the empty string. - - Note: [RFC-2822] requires that the In-Reply-To and - Message-ID headers, if present, have non-empty content. - Therefore, the in-reply-to and message-id members in the - envelope can not be the empty string. - - If the From, To, cc, and bcc header lines are absent in the - [RFC-2822] header, or are present but empty, the corresponding - member of the envelope is NIL. - - If the Sender or Reply-To lines are absent in the [RFC-2822] - header, or are present but empty, the server sets the - corresponding member of the envelope to be the same value as - the from member (the client is not expected to know to do - this). - - Note: [RFC-2822] requires that all messages have a valid - From header. Therefore, the from, sender, and reply-to - members in the envelope can not be NIL. - - ENVELOPE ("date" "subject" from sender reply-to to cc bcc "in-reply-to" "messageID") - */ - - // NOTE: all header fields and parameters must in ENCODED form !!! - - StringBuilder retVal = new StringBuilder(); - retVal.Append("("); - - // date - if (entity.Header.Contains("Date:")) - { - retVal.Append(TextUtils.QuoteString(MimeUtils.DateTimeToRfc2822(entity.Date))); - } - else - { - retVal.Append("NIL"); - } - - // subject - if (entity.Subject != null) - { - retVal.Append(" " + TextUtils.QuoteString(MimeUtils.EncodeHeaderField(entity.Subject))); - } - else - { - retVal.Append(" NIL"); - } - - // from - if (entity.From != null && entity.From.Count > 0) - { - retVal.Append(" " + ConstructAddresses(entity.From)); - } - else - { - retVal.Append(" NIL"); - } - - // sender - // NOTE: There is confusing part, according rfc 2822 Sender: is MailboxAddress and not AddressList. - if (entity.Sender != null) - { - retVal.Append(" ("); - - retVal.Append(ConstructAddress(entity.Sender)); - - retVal.Append(")"); - } - else if (entity.From != null) - { - retVal.Append(" " + ConstructAddresses(entity.From)); - } - else - { - retVal.Append(" NIL"); - } - - // reply-to - if (entity.ReplyTo != null) - { - retVal.Append(" " + ConstructAddresses(entity.ReplyTo)); - } - else if (entity.From != null) - { - retVal.Append(" " + ConstructAddresses(entity.From)); - } - else - { - retVal.Append(" NIL"); - } - - // to - if (entity.To != null && entity.To.Count > 0) - { - retVal.Append(" " + ConstructAddresses(entity.To)); - } - else - { - retVal.Append(" NIL"); - } - - // cc - if (entity.Cc != null && entity.Cc.Count > 0) - { - retVal.Append(" " + ConstructAddresses(entity.Cc)); - } - else - { - retVal.Append(" NIL"); - } - - // bcc - if (entity.Bcc != null && entity.Bcc.Count > 0) - { - retVal.Append(" " + ConstructAddresses(entity.Bcc)); - } - else - { - retVal.Append(" NIL"); - } - - // in-reply-to - if (entity.InReplyTo != null) - { - retVal.Append(" " + TextUtils.QuoteString(MimeUtils.EncodeHeaderField(entity.InReplyTo))); - } - else - { - retVal.Append(" NIL"); - } - - // message-id - if (entity.MessageID != null) - { - retVal.Append(" " + TextUtils.QuoteString(MimeUtils.EncodeHeaderField(entity.MessageID))); - } - else - { - retVal.Append(" NIL"); - } - - retVal.Append(")"); - - return retVal.ToString(); - } - - /// - /// Parses ENVELOPE from IMAP envelope string. - /// - /// Envelope string. - public void Parse(string envelopeString) - { - if (envelopeString.StartsWith("(")) - { - envelopeString = envelopeString.Substring(1); - } - if (envelopeString.EndsWith(")")) - { - envelopeString = envelopeString.Substring(0, envelopeString.Length - 1); - } - - string word = ""; - StringReader r = new StringReader(envelopeString); - - #region Date - - // Date - word = r.ReadWord(); - if (word == null) - { - throw new Exception("Invalid IMAP ENVELOPE structure !"); - } - if (word.ToUpper() == "NIL") - { - m_Date = DateTime.MinValue; - } - else - { - try - { - m_Date = MimeUtils.ParseDate(word); - } - catch - { - // Failed to parse date, return minimum. - m_Date = DateTime.MinValue; - } - } - - #endregion - - #region Subject - - // Subject - word = r.ReadWord(); - if (word == null) - { - throw new Exception("Invalid IMAP ENVELOPE structure !"); - } - if (word.ToUpper() == "NIL") - { - m_Subject = null; - } - else - { - m_Subject = MIME_Encoding_EncodedWord.DecodeS(word); - } - - #endregion - - #region From - - // From - m_From = ParseAddresses(r); - - #endregion - - #region Sender - - // Sender - // NOTE: There is confusing part, according rfc 2822 Sender: is MailboxAddress and not AddressList. - MailboxAddress[] sender = ParseAddresses(r); - if (sender != null && sender.Length > 0) - { - m_Sender = sender[0]; - } - else - { - m_Sender = null; - } - - #endregion - - #region ReplyTo - - // ReplyTo - m_ReplyTo = ParseAddresses(r); - - #endregion - - #region To - - // To - m_To = ParseAddresses(r); - - #endregion - - #region Cc - - // Cc - m_Cc = ParseAddresses(r); - - #endregion - - #region Bcc - - // Bcc - m_Bcc = ParseAddresses(r); - - #endregion - - #region InReplyTo - - // InReplyTo - r.ReadToFirstChar(); - word = r.ReadWord(); - if (word == null) - { - throw new Exception("Invalid IMAP ENVELOPE structure !"); - } - if (word.ToUpper() == "NIL") - { - m_InReplyTo = null; - } - else - { - m_InReplyTo = word; - } - - #endregion - - #region MessageID - - // MessageID - r.ReadToFirstChar(); - word = r.ReadWord(); - if (word == null) - { - throw new Exception("Invalid IMAP ENVELOPE structure !"); - } - if (word.ToUpper() == "NIL") - { - m_MessageID = null; - } - else - { - m_MessageID = word; - } - - #endregion - } - - #endregion - - #region Utility methods - - /// - /// Constructs ENVELOPE addresses structure. - /// - /// Address list. - /// - private static string ConstructAddresses(AddressList addressList) - { - StringBuilder retVal = new StringBuilder(); - retVal.Append("("); - - foreach (MailboxAddress address in addressList.Mailboxes) - { - retVal.Append(ConstructAddress(address)); - } - - retVal.Append(")"); - - return retVal.ToString(); - } - - /// - /// Constructs ENVELOPE address structure. - /// - /// Mailbox address. - /// - private static string ConstructAddress(MailboxAddress address) - { - /* An address structure is a parenthesized list that describes an - electronic mail address. The fields of an address structure - are in the following order: personal name, [SMTP] - at-domain-list (source route), mailbox name, and host name. - */ - - // NOTE: all header fields and parameters must in ENCODED form !!! - - StringBuilder retVal = new StringBuilder(); - retVal.Append("("); - - // personal name - retVal.Append(TextUtils.QuoteString(MimeUtils.EncodeHeaderField(address.DisplayName))); - - // source route, always NIL (not used nowdays) - retVal.Append(" NIL"); - - // mailbox name - retVal.Append(" " + TextUtils.QuoteString(MimeUtils.EncodeHeaderField(address.LocalPart))); - - // host name - retVal.Append(" " + TextUtils.QuoteString(MimeUtils.EncodeHeaderField(address.Domain))); - - retVal.Append(")"); - - return retVal.ToString(); - } - - /// - /// Parses addresses from IMAP ENVELOPE addresses structure. - /// - /// - /// - private MailboxAddress[] ParseAddresses(StringReader r) - { - r.ReadToFirstChar(); - if (r.StartsWith("NIL", false)) - { - // Remove NIL - r.ReadSpecifiedLength("NIL".Length); - - return null; - } - else - { - r.ReadToFirstChar(); - - // This must be ((address)[*(address)]) - if (!r.StartsWith("(")) - { - throw new Exception("Invalid IMAP ENVELOPE structure !"); - } - else - { - // Read addresses - string addressesString = r.ReadParenthesized(); - - ArrayList addresses = new ArrayList(); - StringReader rAddresses = new StringReader(addressesString.Trim()); - // Now we have (address)[*(address)], read addresses - while (rAddresses.StartsWith("(")) - { - addresses.Add(ParseAddress(rAddresses.ReadParenthesized())); - - rAddresses.ReadToFirstChar(); - } - - MailboxAddress[] retVal = new MailboxAddress[addresses.Count]; - addresses.CopyTo(retVal); - - return retVal; - } - } - } - - /// - /// Parses address from IMAP ENVELOPE address structure. - /// - /// Address structure string. - /// - private MailboxAddress ParseAddress(string addressString) - { - /* RFC 3501 7.4.2 ENVELOPE - An address structure is a parenthesized list that describes an - electronic mail address. The fields of an address structure - are in the following order: personal name, [SMTP] - at-domain-list (source route), mailbox name, and host name. - */ - - StringReader r = new StringReader(addressString.Trim()); - string personalName = ""; - string emailAddress = ""; - - // personal name - if (r.StartsWith("NIL", false)) - { - // Remove NIL - r.ReadSpecifiedLength("NIL".Length); - } - else - { - personalName = MIME_Encoding_EncodedWord.DecodeS(r.ReadWord()); - } - - // source route, always NIL (not used nowdays) - r.ReadWord(); - - // mailbox name - if (r.StartsWith("NIL", false)) - { - // Remove NIL - r.ReadSpecifiedLength("NIL".Length); - } - else - { - emailAddress = r.ReadWord() + "@"; - } - - // host name - if (r.StartsWith("NIL", false)) - { - // Remove NIL - r.ReadSpecifiedLength("NIL".Length); - } - else - { - emailAddress += r.ReadWord(); - } - - return new MailboxAddress(personalName, emailAddress); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/IMAP_Flags_SetType.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/IMAP_Flags_SetType.cs deleted file mode 100644 index edcd33703..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/IMAP_Flags_SetType.cs +++ /dev/null @@ -1,38 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Server -{ - /// - /// IMAP flags store type. - /// - public enum IMAP_Flags_SetType - { - /// - /// Flags are added to existing ones. - /// - Add = 1, - /// - /// Flags are removed from existing ones. - /// - Remove = 3, - /// - /// Flags are replaced. - /// - Replace = 4, - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/IMAP_MessageFlags.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/IMAP_MessageFlags.cs deleted file mode 100644 index 59aa488e4..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/IMAP_MessageFlags.cs +++ /dev/null @@ -1,63 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP -{ - using System; - - /// - /// IMAP message flags. - /// - [Flags] - public enum IMAP_MessageFlags - { - /// - /// No flags defined. - /// - None = 0, - - /// - /// Message has been read. - /// - Seen = 2, - - /// - /// Message has been answered. - /// - Answered = 4, - - /// - /// Message is "flagged" for urgent/special attention. - /// - Flagged = 8, - - /// - /// Message is "deleted" for removal by later EXPUNGE. - /// - Deleted = 16, - - /// - /// Message has not completed composition. - /// - Draft = 32, - - /// - /// Message is "recently" arrived in this mailbox. - /// - Recent = 64, - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/IMAP_SequenceSet.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/IMAP_SequenceSet.cs deleted file mode 100644 index 1a5f5b31d..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/IMAP_SequenceSet.cs +++ /dev/null @@ -1,281 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP -{ - #region usings - - using System; - using System.Collections.Generic; - - #endregion - - /// - /// IMAP sequence-set. RFC 3501. - /// - /// Examples: - /// 2 -> seq-number (2) - /// 2:4 -> seq-range (from 2 - 4) - /// 2:* -> seq-range (from 2 to last) - /// 2,3,10:* -> sequence-set (seq-number,seq-number,seq-range) - /// (2,3, 10 - last) - /// - /// NOTES: - /// *) comma separates sequence parts - /// *) * means maximum value. - /// - /// - public class IMAP_SequenceSet - { - #region Nested type: Range - - /// - /// Implements range. - /// - private class Range - { - #region Members - - private readonly long m_End; - private readonly long m_Start; - - #endregion - - #region Properties - - /// - /// Gets range start. - /// - public long Start - { - get { return m_Start; } - } - - /// - /// Gets range end. - /// - public long End - { - get { return m_End; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Range value. - public Range(long value) - { - m_Start = value; - m_End = value; - } - - /// - /// Default constructor. - /// - /// Range start. - /// Range end. - public Range(long start, long end) - { - m_Start = start; - m_End = end; - } - - #endregion - } - - #endregion - - #region Members - - private readonly List m_pSequenceParts; - private string m_SequenceString = ""; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public IMAP_SequenceSet() - { - m_pSequenceParts = new List(); - } - - #endregion - - #region Methods - - /// - /// Parses sequence-set from specified string. Throws exception if invalid sequnce-set value. - /// - /// Sequence-set string. - public void Parse(string sequenceSetString) - { - Parse(sequenceSetString, long.MaxValue); - } - - /// - /// Parses sequence-set from specified string. Throws exception if invalid sequnce-set value. - /// - /// Sequence-set string. - /// Maximum value. This if for replacement of * value. - public void Parse(string sequenceSetString, long seqMaxValue) - { - /* RFC 3501 - seq-number = nz-number / "*" - ; message sequence number (COPY, FETCH, STORE - ; commands) or unique identifier (UID COPY, - ; UID FETCH, UID STORE commands). - ; * represents the largest number in use. In - ; the case of message sequence numbers, it is - ; the number of messages in a non-empty mailbox. - ; In the case of unique identifiers, it is the - ; unique identifier of the last message in the - ; mailbox or, if the mailbox is empty, the - ; mailbox's current UIDNEXT value. - ; The server should respond with a tagged BAD - ; response to a command that uses a message - ; sequence number greater than the number of - ; messages in the selected mailbox. This - ; includes "*" if the selected mailbox is empty. - - seq-range = seq-number ":" seq-number - ; two seq-number values and all values between - ; these two regardless of order. - ; Example: 2:4 and 4:2 are equivalent and indicate - ; values 2, 3, and 4. - ; Example: a unique identifier sequence range of - ; 3291:* includes the UID of the last message in - ; the mailbox, even if that value is less than 3291. - - sequence-set = (seq-number / seq-range) *("," sequence-set) - ; set of seq-number values, regardless of order. - ; Servers MAY coalesce overlaps and/or execute the - ; sequence in any order. - ; Example: a message sequence number set of - ; 2,4:7,9,12:* for a mailbox with 15 messages is - ; equivalent to 2,4,5,6,7,9,12,13,14,15 - ; Example: a message sequence number set of *:4,5:7 - ; for a mailbox with 10 messages is equivalent to - ; 10,9,8,7,6,5,4,5,6,7 and MAY be reordered and - ; overlap coalesced to be 4,5,6,7,8,9,10. - */ - - //--- Validate sequence-set --------------------------------------------------------// - string[] sequenceSets = sequenceSetString.Trim().Split(','); - foreach (string sequenceSet in sequenceSets) - { - // seq-range - if (sequenceSet.IndexOf(":") > -1) - { - string[] rangeParts = sequenceSet.Split(':'); - if (rangeParts.Length == 2) - { - long start = Parse_Seq_Number(rangeParts[0], seqMaxValue); - long end = Parse_Seq_Number(rangeParts[1], seqMaxValue); - if (start <= end) - { - m_pSequenceParts.Add(new Range(start, end)); - } - else - { - m_pSequenceParts.Add(new Range(end, start)); - } - } - else - { - throw new Exception("Invalid '" + sequenceSet + "' value !"); - } - } - // seq-number - else - { - m_pSequenceParts.Add(new Range(Parse_Seq_Number(sequenceSet, seqMaxValue))); - } - } - //-----------------------------------------------------------------------------------// - - m_SequenceString = sequenceSetString; - } - - /// - /// Gets if sequence set contains specified number. - /// - /// Number to check. - public bool Contains(long seqNumber) - { - foreach (Range range in m_pSequenceParts) - { - if (seqNumber >= range.Start && seqNumber <= range.End) - { - return true; - } - } - - return false; - } - - /// - /// Converts IMAP_SequenceSet to IMAP sequence-set string. - /// - /// - public string ToSequenceSetString() - { - return m_SequenceString; - } - - #endregion - - #region Utility methods - - /// - /// Parses seq-number from specified value. Throws exception if invalid seq-number value. - /// - /// Integer number or *. - /// Maximum value. This if for replacement of * value. - private long Parse_Seq_Number(string seqNumberValue, long seqMaxValue) - { - seqNumberValue = seqNumberValue.Trim(); - - // * max value - if (seqNumberValue == "*") - { - // Replace it with max value - return seqMaxValue; - } - // Number - else - { - try - { - return Convert.ToInt64(seqNumberValue); - } - catch - { - throw new Exception("Invalid '" + seqNumberValue + "' value !"); - } - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/AuthUser_EventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/AuthUser_EventArgs.cs deleted file mode 100644 index e2c663b2f..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/AuthUser_EventArgs.cs +++ /dev/null @@ -1,71 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Server -{ - using POP3.Server; - - /// - /// Provides data for the AuthUser event for IMAP_Server. - /// - public class AuthUser_EventArgs : AuthUser_EventArgsBase - { - #region Members - - private readonly IMAP_Session m_pSession; - - #endregion - - #region Properties - - /// - /// Gets reference to smtp session. - /// - public IMAP_Session Session - { - get { return m_pSession; } - } - - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Reference to IMAP session. - /// Username. - /// Password data. - /// Authentication specific data(as tag). - /// Authentication type. - public AuthUser_EventArgs(IMAP_Session session, - string userName, - string passwData, - string data, - AuthType authType) - { - m_pSession = session; - m_UserName = userName; - m_PasswData = passwData; - m_Data = data; - m_AuthType = authType; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/Folder_EventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/Folder_EventArgs.cs deleted file mode 100644 index 4c1ded1a7..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/Folder_EventArgs.cs +++ /dev/null @@ -1,81 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Server -{ - /// - /// Provides data for IMAP events. - /// - public class Mailbox_EventArgs - { - #region Members - - private readonly string m_Folder = ""; - private readonly string m_NewFolder = ""; - - #endregion - - #region Properties - - /// - /// Gets folder. - /// - public string Folder - { - get { return m_Folder; } - } - - /// - /// Gets new folder name, this is available for rename only. - /// - public string NewFolder - { - get { return m_NewFolder; } - } - - /// - /// Gets or sets custom error text, which is returned to client. - /// - public string ErrorText { get; set; } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// - public Mailbox_EventArgs(string folder) - { - m_Folder = folder; - } - - /// - /// Folder rename constructor. - /// - /// - /// - public Mailbox_EventArgs(string folder, string newFolder) - { - m_Folder = folder; - m_NewFolder = newFolder; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_Folder.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_Folder.cs deleted file mode 100644 index 94fed274c..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_Folder.cs +++ /dev/null @@ -1,69 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Server -{ - /// - /// IMAP folder. - /// - public class IMAP_Folder - { - #region Members - - private readonly string m_Folder = ""; - private bool m_Selectable = true; - - #endregion - - #region Properties - - /// - /// Gets IMAP folder name. Eg. Inbox, Inbox/myFolder, ... . - /// - public string Folder - { - get { return m_Folder; } - } - - /// - /// Gets or sets if folder is selectable (SELECT command can select this folder). - /// - public bool Selectable - { - get { return m_Selectable; } - - set { m_Selectable = value; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Full path to folder, path separator = '/'. Eg. Inbox/myFolder . - /// Gets or sets if folder is selectable(SELECT command can select this folder). - public IMAP_Folder(string folder, bool selectable) - { - m_Folder = folder; - m_Selectable = selectable; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_Folders.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_Folders.cs deleted file mode 100644 index c48b33bb9..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_Folders.cs +++ /dev/null @@ -1,232 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Server -{ - #region usings - - using System.Collections; - - #endregion - - /// - /// IMAP folders collection. - /// - public class IMAP_Folders - { - #region Members - - private readonly string m_Mailbox = ""; - private readonly ArrayList m_Mailboxes; - private readonly IMAP_Session m_pSession; - private readonly string m_RefName = ""; - - #endregion - - #region Properties - - /// - /// Gets current IMAP session. - /// - public IMAP_Session Session - { - get { return m_pSession; } - } - - /// - /// Gest list of IMAP folders. - /// - public IMAP_Folder[] Folders - { - get - { - IMAP_Folder[] retVal = new IMAP_Folder[m_Mailboxes.Count]; - m_Mailboxes.CopyTo(retVal); - return retVal; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner IMAP session. - /// Folder Path. Eg. Inbox\. - /// Folder name. - public IMAP_Folders(IMAP_Session session, string referenceName, string folder) - { - m_pSession = session; - m_Mailboxes = new ArrayList(); - m_RefName = referenceName; - m_Mailbox = folder.Replace("\\", "/"); - } - - #endregion - - #region Methods - - /// - /// Adds folder to folders list. - /// - /// Full path to folder, path separator = '/'. Eg. Inbox/myFolder . - /// Gets or sets if folder is selectable(SELECT command can select this folder). - public void Add(string folder, bool selectable) - { - folder = folder.Replace("\\", "/"); - - string folderPattern = m_RefName + m_Mailbox; - if (m_RefName != "" && !m_RefName.EndsWith("/") && !m_Mailbox.StartsWith("/")) - { - folderPattern = m_RefName + "/" + m_Mailbox; - } - - if (FolderMatches(folderPattern, Core.Decode_IMAP_UTF7_String(folder))) - { - m_Mailboxes.Add(new IMAP_Folder(folder, selectable)); - } - } - - // TODO: move to some global utility method - - /// - /// Checks if specified text matches to specified asteric pattern. - /// - /// Asteric pattern. Foe example: *xxx,*xxx*,xx*aa*xx, ... . - /// Text to match. - /// - public bool AstericMatch(string pattern, string text) - { - pattern = pattern.ToLower(); - text = text.ToLower(); - - if (pattern == "") - { - pattern = "*"; - } - - while (pattern.Length > 0) - { - // *xxx[*xxx...] - if (pattern.StartsWith("*")) - { - // *xxx*xxx - if (pattern.IndexOf("*", 1) > -1) - { - string indexOfPart = pattern.Substring(1, pattern.IndexOf("*", 1) - 1); - if (text.IndexOf(indexOfPart) == -1) - { - return false; - } - - text = text.Substring(text.IndexOf(indexOfPart) + indexOfPart.Length + 1); - pattern = pattern.Substring(pattern.IndexOf("*", 1) + 1); - } - // *xxx This is last pattern - else - { - return text.EndsWith(pattern.Substring(1)); - } - } - // xxx*[xxx...] - else if (pattern.IndexOfAny(new[] {'*'}) > -1) - { - string startPart = pattern.Substring(0, pattern.IndexOfAny(new[] {'*'})); - - // Text must startwith - if (!text.StartsWith(startPart)) - { - return false; - } - - text = text.Substring(text.IndexOf(startPart) + startPart.Length); - pattern = pattern.Substring(pattern.IndexOfAny(new[] {'*'})); - } - // xxx - else - { - return text == pattern; - } - } - - return true; - } - - #endregion - - #region Utility methods - - /// - /// Gets if folder matches to specified folder pattern. - /// - /// Folder pattern. * and % between path separators have same meaning (asteric pattern). - /// If % is at the end, then matches only last folder child folders and not child folder child folders. - /// Folder name with full path. - /// - private bool FolderMatches(string folderPattern, string folder) - { - folderPattern = folderPattern.ToLower(); - folder = folder.ToLower(); - - string[] folderParts = folder.Split('/'); - string[] patternParts = folderPattern.Split('/'); - - // pattern is more nested than folder - if (folderParts.Length < patternParts.Length) - { - return false; - } - // This can happen only if * at end - else if (folderParts.Length > patternParts.Length && !folderPattern.EndsWith("*")) - { - return false; - } - else - { - // Loop patterns - for (int i = 0; i < patternParts.Length; i++) - { - string patternPart = patternParts[i].Replace("%", "*"); - - // This is asteric pattern - if (patternPart.IndexOf('*') > -1) - { - if (!AstericMatch(patternPart, folderParts[i])) - { - return false; - } - // else process next pattern - } - // No *, this must be exact match - else - { - if (folderParts[i] != patternPart) - { - return false; - } - } - } - } - - return true; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_Message.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_Message.cs deleted file mode 100644 index 9b5b13dd0..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_Message.cs +++ /dev/null @@ -1,148 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Server -{ - #region usings - - using System; - - #endregion - - /// - /// IMAP message info. - /// - public class IMAP_Message:IDisposable - { - #region Members - - private readonly string m_ID = ""; - private readonly DateTime m_InternalDate = DateTime.Now; - private readonly IMAP_MessageCollection m_pOwner; - private readonly long m_Size; - private readonly long m_UID; - private IMAP_MessageFlags m_Flags = IMAP_MessageFlags.None; - - #endregion - - #region Properties - - /// - /// Gets message 1 based sequence number in the collection. This property is slow, use with care, never use in big for loops ! - /// - public int SequenceNo - { - get { return m_pOwner.IndexOf(this) + 1; } - } - - /// - /// Gets message ID. - /// - public string ID - { - get { return m_ID; } - } - - /// - /// Gets message IMAP UID value. - /// - public long UID - { - get { return m_UID; } - } - - /// - /// Gets message store date. - /// - public DateTime InternalDate - { - get { return m_InternalDate; } - } - - /// - /// Gets message size in bytes. - /// - public long Size - { - get { return m_Size; } - } - - /// - /// Gets message flags. - /// - public IMAP_MessageFlags Flags - { - get { return m_Flags; } - } - - /// - /// Gets message flags string. For example: "\DELETES \SEEN". - /// - public string FlagsString - { - get { return IMAP_Utils.MessageFlagsToString(m_Flags); } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner collection. - /// Message ID. - /// Message IMAP UID value. - /// Message store date. - /// Message size in bytes. - /// Message flags. - internal IMAP_Message(IMAP_MessageCollection onwer, - string id, - long uid, - DateTime internalDate, - long size, - IMAP_MessageFlags flags) - { - m_pOwner = onwer; - m_ID = id; - m_UID = uid; - m_InternalDate = internalDate; - m_Size = size; - m_Flags = flags; - } - - #endregion - - #region Internal methods - - /// - /// Sets message flags. - /// - /// Message flags. - internal void SetFlags(IMAP_MessageFlags flags) - { - m_Flags = flags; - } - - #endregion - - public void Dispose() - { - - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_MessageCollection.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_MessageCollection.cs deleted file mode 100644 index fbbe7adfb..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_MessageCollection.cs +++ /dev/null @@ -1,179 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Server -{ - #region usings - - using System; - using System.Collections; - using System.Collections.Generic; - - #endregion - - /// - /// IMAP messages info collection. - /// - public class IMAP_MessageCollection : IEnumerable,IDisposable - { - #region Members - - private readonly SortedList m_pMessages; - - #endregion - - #region Properties - - /// - /// Gets number of messages in the collection. - /// - public int Count - { - get { return m_pMessages.Count; } - } - - /// - /// Gets a IMAP_Message object in the collection by index number. - /// - /// An Int32 value that specifies the position of the IMAP_Message object in the IMAP_MessageCollection collection. - public IMAP_Message this[int index] - { - get { return m_pMessages.Values[index]; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public IMAP_MessageCollection() - { - m_pMessages = new SortedList(); - } - - #endregion - - #region Methods - - /// - /// Adds new message info to the collection. - /// - /// Message ID. - /// Message IMAP UID value. - /// Message store date. - /// Message size in bytes. - /// Message flags. - /// Returns added IMAp message info. - public IMAP_Message Add(string id, long uid, DateTime internalDate, long size, IMAP_MessageFlags flags) - { - if (uid < 1) - { - throw new ArgumentException("Message UID value must be > 0 !"); - } - - IMAP_Message message = new IMAP_Message(this, id, uid, internalDate, size, flags); - m_pMessages.Add(uid, message); - - return message; - } - - /// - /// Removes specified IMAP message from the collection. - /// - /// IMAP message to remove. - public void Remove(IMAP_Message message) - { - m_pMessages.Remove(message.UID); - } - - /// - /// Gets collection contains specified message with specified UID. - /// - /// Message UID. - /// - public bool ContainsUID(long uid) - { - return m_pMessages.ContainsKey(uid); - } - - /// - /// Gets index of specified message in the collection. - /// - /// Message indesx to get. - /// Returns index of specified message in the collection or -1 if message doesn't belong to this collection. - public int IndexOf(IMAP_Message message) - { - return m_pMessages.IndexOfKey(message.UID); - } - - /// - /// Removes all messages from the collection. - /// - public void Clear() - { - m_pMessages.Clear(); - } - - /// - /// Gets messages which has specified flags set. - /// - /// Flags to match. - /// - public IMAP_Message[] GetWithFlags(IMAP_MessageFlags flags) - { - List retVal = new List(); - foreach (IMAP_Message message in m_pMessages.Values) - { - if ((message.Flags & flags) != 0) - { - retVal.Add(message); - } - } - return retVal.ToArray(); - } - - /// - /// Gets enumerator. - /// - /// - public IEnumerator GetEnumerator() - { - return m_pMessages.Values.GetEnumerator(); - } - - private bool isDisposed = false; - - public void Dispose() - { - if (!isDisposed) - { - isDisposed = true; - if (m_pMessages!=null) - { - foreach (var imapMessage in m_pMessages) - { - imapMessage.Value.Dispose(); - } - } - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_MessageItems_enum.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_MessageItems_enum.cs deleted file mode 100644 index 5f2e277e2..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_MessageItems_enum.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Server -{ - /// - /// Specifies message itmes. - /// - public enum IMAP_MessageItems_enum - { - /// - /// None. - /// - None = 0, - - /// - /// Message main header. - /// - Header = 2, - - /// - /// IMAP ENVELOPE structure. - /// - Envelope = 4, - - /// - /// IMAP BODYSTRUCTURE structure. - /// - BodyStructure = 8, - - /// - /// Full message. - /// - Message = 16, - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_SearchGroup.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_SearchGroup.cs deleted file mode 100644 index ad3f4046f..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_SearchGroup.cs +++ /dev/null @@ -1,265 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Server -{ - #region usings - - using System; - using System.Collections; - using Mime; - - #endregion - - /// - /// IMAP search command grouped(parenthesized) search-key collection. - /// - internal class SearchGroup - { - #region Members - - private readonly ArrayList m_pSearchKeys; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public SearchGroup() - { - m_pSearchKeys = new ArrayList(); - } - - #endregion - - #region Methods - - /// - /// Parses search key from current position. - /// - /// - public void Parse(StringReader reader) - { - //Remove spaces from string start - reader.ReadToFirstChar(); - - if (reader.StartsWith("(")) - { - reader = new StringReader(reader.ReadParenthesized().Trim()); - } - - //--- Start parsing search keys --------------// - while (reader.Available > 0) - { - object searchKey = ParseSearchKey(reader); - if (searchKey != null) - { - m_pSearchKeys.Add(searchKey); - } - } - //--------------------------------------------// - } - - /// - /// Gets if message Header is needed for matching. - /// - /// - public bool IsHeaderNeeded() - { - foreach (object searchKey in m_pSearchKeys) - { - if (IsHeaderNeededForKey(searchKey)) - { - return true; - } - } - - return false; - } - - /// - /// Gets if message body text is needed for matching. - /// - /// - public bool IsBodyTextNeeded() - { - foreach (object searchKey in m_pSearchKeys) - { - if (IsBodyTextNeededForKey(searchKey)) - { - return true; - } - } - - return false; - } - - /// - /// Gets if specified message matches with this class search-key. - /// - /// IMAP message sequence number. - /// IMAP message UID. - /// IMAP message size in bytes. - /// IMAP message INTERNALDATE (dateTime when server stored message). - /// IMAP message flags. - /// Mime message main header only. - /// Message body text. - /// - public bool Match(long no, - long uid, - long size, - DateTime internalDate, - IMAP_MessageFlags flags, - Mime mime, - string bodyText) - { - // We must match all keys, if one fails, no need to check others - - foreach (object searckKey in m_pSearchKeys) - { - if (!Match_Key_Value(searckKey, no, uid, size, internalDate, flags, mime, bodyText)) - { - return false; - } - } - - return true; - } - - #endregion - - #region Internal methods - - /// - /// Parses SearchGroup or SearchItem from reader. If reader starts with (, then parses searchGroup, otherwise SearchItem. - /// - /// - /// - internal static object ParseSearchKey(StringReader reader) - { - //Remove spaces from string start - reader.ReadToFirstChar(); - - // SearchGroup - if (reader.StartsWith("(")) - { - SearchGroup searchGroup = new SearchGroup(); - searchGroup.Parse(reader); - - return searchGroup; - } - // SearchItem - else - { - return SearchKey.Parse(reader); - } - } - - /// - /// Gets if specified message matches to specified search key. - /// - /// SearchKey or SearchGroup. - /// IMAP message sequence number. - /// IMAP message UID. - /// IMAP message size in bytes. - /// IMAP message INTERNALDATE (dateTime when server stored message). - /// IMAP message flags. - /// Mime message main header only. - /// Message body text. - /// - internal static bool Match_Key_Value(object searchKey, - long no, - long uid, - long size, - DateTime internalDate, - IMAP_MessageFlags flags, - Mime mime, - string bodyText) - { - if (searchKey.GetType() == typeof (SearchKey)) - { - if (!((SearchKey) searchKey).Match(no, uid, size, internalDate, flags, mime, bodyText)) - { - return false; - } - } - else if (searchKey.GetType() == typeof (SearchGroup)) - { - if (!((SearchGroup) searchKey).Match(no, uid, size, internalDate, flags, mime, bodyText)) - { - return false; - } - } - - return true; - } - - /// - /// Gets if message header is needed for matching. - /// - /// - /// - internal static bool IsHeaderNeededForKey(object searchKey) - { - if (searchKey.GetType() == typeof (SearchKey)) - { - if (((SearchKey) searchKey).IsHeaderNeeded()) - { - return true; - } - } - else if (searchKey.GetType() == typeof (SearchGroup)) - { - if (((SearchGroup) searchKey).IsHeaderNeeded()) - { - return true; - } - } - - return false; - } - - /// - /// Gets if message body text is needed for matching. - /// - /// - /// - internal static bool IsBodyTextNeededForKey(object searchKey) - { - if (searchKey.GetType() == typeof (SearchKey)) - { - if (((SearchKey) searchKey).IsBodyTextNeeded()) - { - return true; - } - } - else if (searchKey.GetType() == typeof (SearchGroup)) - { - if (((SearchGroup) searchKey).IsBodyTextNeeded()) - { - return true; - } - } - - return false; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_SearchKey.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_SearchKey.cs deleted file mode 100644 index 248c89d21..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_SearchKey.cs +++ /dev/null @@ -1,1324 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Server -{ - #region usings - - using System; - using Mime; - - #endregion - - /// - /// IMAP search key (RFC 3501 6.4.4 SEARCH Command). - /// - internal class SearchKey - { - #region Members - - private readonly string m_SearchKeyName = ""; - private readonly object m_SearchKeyValue; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public SearchKey(string searchKeyName, object value) - { - m_SearchKeyName = searchKeyName; - m_SearchKeyValue = value; - } - - #endregion - - #region Methods - - /// - /// Parses one search key from current position. Returns null if there isn't any search key left. - /// - /// - public static SearchKey Parse(StringReader reader) - { - string searchKeyName = ""; - object searchKeyValue = null; - - //Remove spaces from string start - reader.ReadToFirstChar(); - - // Search keyname is always 1 word - string word = reader.ReadWord(); - if (word == null) - { - return null; - } - word = word.ToUpper().Trim(); - - //Remove spaces from string start - reader.ReadToFirstChar(); - - #region ALL - - // ALL - // All messages in the mailbox; the default initial key for ANDing. - if (word == "ALL") - { - searchKeyName = "ALL"; - } - - #endregion - - #region ANSWERED - - // ANSWERED - // Messages with the \Answered flag set. - else if (word == "ANSWERED") - { - // We internally use KEYWORD ANSWERED - searchKeyName = "KEYWORD"; - searchKeyValue = "ANSWERED"; - } - - #endregion - - #region BCC - - // BCC - // Messages that contain the specified string in the envelope structure's BCC field. - else if (word == "BCC") - { - // We internally use HEADER "BCC:" "value" - searchKeyName = "HEADER"; - - // Read - string val = ReadString(reader); - if (val != null) - { - searchKeyValue = new[] {"BCC:", TextUtils.UnQuoteString(val)}; - } - else - { - throw new Exception("BCC value is missing !"); - } - } - - #endregion - - #region BEFORE - - // BEFORE - // Messages whose internal date (disregarding time and timezone) is earlier than the specified date. - else if (word == "BEFORE") - { - searchKeyName = "BEFORE"; - - // Read - string val = reader.QuotedReadToDelimiter(' '); - if (val != null) - { - // Parse date - try - { - searchKeyValue = IMAP_Utils.ParseDate(TextUtils.UnQuoteString(val)); - } - // Invalid date - catch - { - throw new Exception("Invalid BEFORE value '" + val + - "', valid date syntax: {dd-MMM-yyyy} !"); - } - } - else - { - throw new Exception("BEFORE value is missing !"); - } - } - - #endregion - - #region BODY - - // BODY - // Messages that contain the specified string in the body of the message. - else if (word == "BODY") - { - searchKeyName = "BODY"; - - string val = ReadString(reader); - if (val != null) - { - searchKeyValue = val; - } - else - { - throw new Exception("BODY value is missing !"); - } - } - - #endregion - - #region CC - - // CC - // Messages that contain the specified string in the envelope structure's CC field. - else if (word == "CC") - { - // We internally use HEADER "CC:" "value" - searchKeyName = "HEADER"; - - // Read - string val = ReadString(reader); - if (val != null) - { - searchKeyValue = new[] {"CC:", TextUtils.UnQuoteString(val)}; - } - else - { - throw new Exception("CC value is missing !"); - } - } - - #endregion - - #region DELETED - - // DELETED - // Messages with the \Deleted flag set. - else if (word == "DELETED") - { - // We internally use KEYWORD DELETED - searchKeyName = "KEYWORD"; - searchKeyValue = "DELETED"; - } - - #endregion - - #region DRAFT - - // DRAFT - // Messages with the \Draft flag set. - else if (word == "DRAFT") - { - // We internally use KEYWORD DRAFT - searchKeyName = "KEYWORD"; - searchKeyValue = "DRAFT"; - } - - #endregion - - #region FLAGGED - - // FLAGGED - // Messages with the \Flagged flag set. - else if (word == "FLAGGED") - { - // We internally use KEYWORD FLAGGED - searchKeyName = "KEYWORD"; - searchKeyValue = "FLAGGED"; - } - - #endregion - - #region FROM - - // FROM - // Messages that contain the specified string in the envelope structure's FROM field. - else if (word == "FROM") - { - // We internally use HEADER "FROM:" "value" - searchKeyName = "HEADER"; - - // Read - string val = ReadString(reader); - if (val != null) - { - searchKeyValue = new[] {"FROM:", TextUtils.UnQuoteString(val)}; - } - else - { - throw new Exception("FROM value is missing !"); - } - } - - #endregion - - #region HEADER - - // HEADER - // Messages that have a header with the specified field-name (as - // defined in [RFC-2822]) and that contains the specified string - // in the text of the header (what comes after the colon). If the - // string to search is zero-length, this matches all messages that - // have a header line with the specified field-name regardless of - // the contents. - else if (word == "HEADER") - { - searchKeyName = "HEADER"; - - // Read - string fieldName = ReadString(reader); - if (fieldName != null) - { - fieldName = TextUtils.UnQuoteString(fieldName); - } - else - { - throw new Exception("HEADER value is missing !"); - } - - // Read - string val = ReadString(reader); - if (val != null) - { - searchKeyValue = new[] {fieldName, TextUtils.UnQuoteString(val)}; - } - else - { - throw new Exception("(HEADER ) value is missing !"); - } - } - - #endregion - - #region KEYWORD - - // KEYWORD - // Messages with the specified keyword flag set. - else if (word == "KEYWORD") - { - searchKeyName = "KEYWORD"; - - // Read - string val = reader.QuotedReadToDelimiter(' '); - if (val != null) - { - searchKeyValue = TextUtils.UnQuoteString(val); - } - else - { - throw new Exception("KEYWORD value is missing !"); - } - } - - #endregion - - #region LARGER - - // LARGER - // Messages with an [RFC-2822] size larger than the specified number of octets. - else if (word == "LARGER") - { - searchKeyName = "LARGER"; - - // Read - string val = reader.QuotedReadToDelimiter(' '); - if (val != null) - { - // Parse - must be integer value - try - { - searchKeyValue = Convert.ToInt64(TextUtils.UnQuoteString(val)); - } - // Invalid - catch - { - throw new Exception("Invalid LARGER value '" + val + - "', it must be numeric value !"); - } - } - else - { - throw new Exception("LARGER value is missing !"); - } - } - - #endregion - - #region NEW - - // NEW - // Messages that have the \Recent flag set but not the \Seen flag. - // This is functionally equivalent to "(RECENT UNSEEN)". - else if (word == "NEW") - { - // We internally use KEYWORD RECENT - searchKeyName = "KEYWORD"; - searchKeyValue = "RECENT"; - } - - #endregion - - #region NOT - - // NOT or ( ...)(SearchGroup) - // Messages that do not match the specified search key. - else if (word == "NOT") - { - searchKeyName = "NOT"; - - object searchItem = SearchGroup.ParseSearchKey(reader); - if (searchItem != null) - { - searchKeyValue = searchItem; - } - else - { - throw new Exception("Required NOT isn't specified !"); - } - } - - #endregion - - #region OLD - - // OLD - // Messages that do not have the \Recent flag set. This is - // functionally equivalent to "NOT RECENT" (as opposed to "NOT NEW"). - else if (word == "OLD") - { - // We internally use UNKEYWORD RECENT - searchKeyName = "UNKEYWORD"; - searchKeyValue = "RECENT"; - } - - #endregion - - #region ON - - // ON - // Messages whose internal date (disregarding time and timezone) is within the specified date. - else if (word == "ON") - { - searchKeyName = "ON"; - - // Read - string val = reader.QuotedReadToDelimiter(' '); - if (val != null) - { - // Parse date - try - { - searchKeyValue = IMAP_Utils.ParseDate(TextUtils.UnQuoteString(val)); - } - // Invalid date - catch - { - throw new Exception("Invalid ON value '" + val + - "', valid date syntax: {dd-MMM-yyyy} !"); - } - } - else - { - throw new Exception("ON value is missing !"); - } - } - - #endregion - - #region OR - - // OR - SearckKey can be parenthesis list of keys ! - // Messages that match either search key. - else if (word == "OR") - { - searchKeyName = "OR"; - - //--- ----------------------------------------------------// - object searchKey1 = SearchGroup.ParseSearchKey(reader); - if (searchKey1 == null) - { - throw new Exception("Required OR isn't specified !"); - } - //----------------------------------------------------------------------// - - //--- ----------------------------------------------------// - object searchKey2 = SearchGroup.ParseSearchKey(reader); - if (searchKey2 == null) - { - throw new Exception("Required (OR ) isn't specified !"); - } - //-----------------------------------------------------------------------// - - searchKeyValue = new[] {searchKey1, searchKey2}; - } - - #endregion - - #region RECENT - - // RECENT - // Messages that have the \Recent flag set. - else if (word == "RECENT") - { - // We internally use KEYWORD RECENT - searchKeyName = "KEYWORD"; - searchKeyValue = "RECENT"; - } - - #endregion - - #region SEEN - - // SEEN - // Messages that have the \Seen flag set. - else if (word == "SEEN") - { - // We internally use KEYWORD SEEN - searchKeyName = "KEYWORD"; - searchKeyValue = "SEEN"; - } - - #endregion - - #region SENTBEFORE - - // SENTBEFORE - // Messages whose [RFC-2822] Date: header (disregarding time and - // timezone) is earlier than the specified date. - else if (word == "SENTBEFORE") - { - searchKeyName = "SENTBEFORE"; - - // Read - string val = reader.QuotedReadToDelimiter(' '); - if (val != null) - { - // Parse date - try - { - searchKeyValue = IMAP_Utils.ParseDate(TextUtils.UnQuoteString(val)); - } - // Invalid date - catch - { - throw new Exception("Invalid SENTBEFORE value '" + val + - "', valid date syntax: {dd-MMM-yyyy} !"); - } - } - else - { - throw new Exception("SENTBEFORE value is missing !"); - } - } - - #endregion - - #region SENTON - - // SENTON - // Messages whose [RFC-2822] Date: header (disregarding time and - // timezone) is within the specified date. - else if (word == "SENTON") - { - searchKeyName = "SENTON"; - - // Read - string val = reader.QuotedReadToDelimiter(' '); - if (val != null) - { - // Parse date - try - { - searchKeyValue = IMAP_Utils.ParseDate(TextUtils.UnQuoteString(val)); - } - // Invalid date - catch - { - throw new Exception("Invalid SENTON value '" + val + - "', valid date syntax: {dd-MMM-yyyy} !"); - } - } - else - { - throw new Exception("SENTON value is missing !"); - } - } - - #endregion - - #region SENTSINCE - - // SENTSINCE - // Messages whose [RFC-2822] Date: header (disregarding time and - // timezone) is within or later than the specified date. - else if (word == "SENTSINCE") - { - searchKeyName = "SENTSINCE"; - - // Read - string val = reader.QuotedReadToDelimiter(' '); - if (val != null) - { - // Parse date - try - { - searchKeyValue = IMAP_Utils.ParseDate(TextUtils.UnQuoteString(val)); - } - // Invalid date - catch - { - throw new Exception("Invalid SENTSINCE value '" + val + - "', valid date syntax: {dd-MMM-yyyy} !"); - } - } - else - { - throw new Exception("SENTSINCE value is missing !"); - } - } - - #endregion - - #region SINCE - - // SINCE - // Messages whose internal date (disregarding time and timezone) - // is within or later than the specified date. - else if (word == "SINCE") - { - searchKeyName = "SINCE"; - - // Read - string val = reader.ReadWord(); - if (val != null) - { - // Parse date - try - { - searchKeyValue = IMAP_Utils.ParseDate(TextUtils.UnQuoteString(val)); - } - // Invalid date - catch - { - throw new Exception("Invalid SINCE value '" + val + - "', valid date syntax: {dd-MMM-yyyy} !"); - } - } - else - { - throw new Exception("SINCE value is missing !"); - } - } - - #endregion - - #region SMALLER - - // SMALLER - // Messages with an [RFC-2822] size smaller than the specified number of octets. - else if (word == "SMALLER") - { - searchKeyName = "SMALLER"; - - // Read - string val = reader.QuotedReadToDelimiter(' '); - if (val != null) - { - val = TextUtils.UnQuoteString(val); - - // Parse - must be integer value - try - { - searchKeyValue = Convert.ToInt64(val); - } - // Invalid - catch - { - throw new Exception("Invalid SMALLER value '" + val + - "', it must be numeric value !"); - } - } - else - { - throw new Exception("SMALLER value is missing !"); - } - } - - #endregion - - #region SUBJECT - - // SUBJECT - // Messages that contain the specified string in the envelope structure's SUBJECT field. - else if (word == "SUBJECT") - { - // We internally use HEADER "SUBJECT:" "value" - searchKeyName = "HEADER"; - - // Read - string val = ReadString(reader); - if (val != null) - { - searchKeyValue = new[] {"SUBJECT:", TextUtils.UnQuoteString(val)}; - } - else - { - throw new Exception("SUBJECT value is missing !"); - } - } - - #endregion - - #region TEXT - - // TEXT - // Messages that contain the specified string in the header or body of the message. - else if (word == "TEXT") - { - searchKeyName = "TEXT"; - - string val = ReadString(reader); - if (val != null) - { - searchKeyValue = val; - } - else - { - throw new Exception("TEXT value is missing !"); - } - } - - #endregion - - #region TO - - // TO - // Messages that contain the specified string in the envelope structure's TO field. - else if (word == "TO") - { - // We internally use HEADER "TO:" "value" - searchKeyName = "HEADER"; - - // Read - string val = ReadString(reader); - if (val != null) - { - searchKeyValue = new[] {"TO:", TextUtils.UnQuoteString(val)}; - } - else - { - throw new Exception("TO value is missing !"); - } - } - - #endregion - - #region UID - - // UID - // Messages with unique identifiers corresponding to the specified - // unique identifier set. Sequence set ranges are permitted. - else if (word == "UID") - { - searchKeyName = "UID"; - - // Read - string val = reader.QuotedReadToDelimiter(' '); - - if (val != null) - { - try - { - IMAP_SequenceSet sequenceSet = new IMAP_SequenceSet(); - sequenceSet.Parse(TextUtils.UnQuoteString(val), long.MaxValue); - - searchKeyValue = sequenceSet; - } - catch - { - throw new Exception("Invalid UID value '" + val + "' !"); - } - } - else - { - throw new Exception("UID value is missing !"); - } - } - - #endregion - - #region UNANSWERED - - // UNANSWERED - // Messages that do not have the \Answered flag set. - else if (word == "UNANSWERED") - { - // We internally use UNKEYWORD SEEN - searchKeyName = "UNKEYWORD"; - searchKeyValue = "ANSWERED"; - } - - #endregion - - #region UNDELETED - - // UNDELETED - // Messages that do not have the \Deleted flag set. - else if (word == "UNDELETED") - { - // We internally use UNKEYWORD UNDELETED - searchKeyName = "UNKEYWORD"; - searchKeyValue = "DELETED"; - } - - #endregion - - #region UNDRAFT - - // UNDRAFT - // Messages that do not have the \Draft flag set. - else if (word == "UNDRAFT") - { - // We internally use UNKEYWORD UNDRAFT - searchKeyName = "UNKEYWORD"; - searchKeyValue = "DRAFT"; - } - - #endregion - - #region UNFLAGGED - - // UNFLAGGED - // Messages that do not have the \Flagged flag set. - else if (word == "UNFLAGGED") - { - // We internally use UNKEYWORD UNFLAGGED - searchKeyName = "UNKEYWORD"; - searchKeyValue = "FLAGGED"; - } - - #endregion - - #region UNKEYWORD - - // UNKEYWORD - // Messages that do not have the specified keyword flag set. - else if (word == "UNKEYWORD") - { - searchKeyName = "UNKEYWORD"; - - // Read - string val = reader.QuotedReadToDelimiter(' '); - if (val != null) - { - searchKeyValue = TextUtils.UnQuoteString(val); - } - else - { - throw new Exception("UNKEYWORD value is missing !"); - } - } - - #endregion - - #region UNSEEN - - // UNSEEN - // Messages that do not have the \Seen flag set. - else if (word == "UNSEEN") - { - // We internally use UNKEYWORD UNSEEN - searchKeyName = "UNKEYWORD"; - searchKeyValue = "SEEN"; - } - - #endregion - - #region Unknown or SEQUENCESET - - // Unkown keyword or - else - { - // DUMMY palce(bad design) in IMAP. - // Active keyword can be or bad keyword, there is now way to distinguish what is meant. - // Why they don't key work SEQUENCESET ? - - // - // Messages with message sequence numbers corresponding to the - // specified message sequence number set. - - // Just try if it can be parsed as sequence-set - try - { - IMAP_SequenceSet sequenceSet = new IMAP_SequenceSet(); - sequenceSet.Parse(word, long.MaxValue); - - searchKeyName = "SEQUENCESET"; - searchKeyValue = sequenceSet; - } - // This isn't vaild sequnce-set value - catch - { - throw new Exception("Invalid search key or value '" + word + "' !"); - } - } - - #endregion - - // REMOVE ME: - // Console.WriteLine(searchKeyName + " : " + Convert.ToString(searchKeyValue)); - - return new SearchKey(searchKeyName, searchKeyValue); - } - - // TODO: We have envelope, see if Header is needed or can use envelope for it - - /// - /// Gets if message Header is needed for matching. - /// - /// - public bool IsHeaderNeeded() - { - if (m_SearchKeyName == "HEADER") - { - return true; - } - else if (m_SearchKeyName == "NOT") - { - return SearchGroup.IsHeaderNeededForKey(m_SearchKeyValue); - } - else if (m_SearchKeyName == "OR") - { - object serachKey1 = ((object[]) m_SearchKeyValue)[0]; - object serachKey2 = ((object[]) m_SearchKeyValue)[1]; - - if (SearchGroup.IsHeaderNeededForKey(serachKey1) || - SearchGroup.IsHeaderNeededForKey(serachKey2)) - { - return true; - } - } - else if (m_SearchKeyName == "SENTBEFORE") - { - return true; - } - else if (m_SearchKeyName == "SENTON") - { - return true; - } - else if (m_SearchKeyName == "SENTSINCE") - { - return true; - } - else if (m_SearchKeyName == "TEXT") - { - return true; - } - - return false; - } - - /// - /// Gets if message body text is needed for matching. - /// - /// - public bool IsBodyTextNeeded() - { - if (m_SearchKeyName == "BODY") - { - return true; - } - else if (m_SearchKeyName == "NOT") - { - return SearchGroup.IsBodyTextNeededForKey(m_SearchKeyValue); - } - else if (m_SearchKeyName == "OR") - { - object serachKey1 = ((object[]) m_SearchKeyValue)[0]; - object serachKey2 = ((object[]) m_SearchKeyValue)[1]; - - if (SearchGroup.IsBodyTextNeededForKey(serachKey1) || - SearchGroup.IsBodyTextNeededForKey(serachKey2)) - { - return true; - } - } - else if (m_SearchKeyName == "TEXT") - { - return true; - } - - return false; - } - - /// - /// Gets if specified message matches with this class search-key. - /// - /// IMAP message sequence number. - /// IMAP message UID. - /// IMAP message size in bytes. - /// IMAP message INTERNALDATE (dateTime when server stored message). - /// IMAP message flags. - /// Mime message main header only. - /// Message body text. - /// - public bool Match(long no, - long uid, - long size, - DateTime internalDate, - IMAP_MessageFlags flags, - Mime mime, - string bodyText) - { - #region ALL - - // ALL - // All messages in the mailbox; the default initial key for ANDing. - if (m_SearchKeyName == "ALL") - { - return true; - } - - #endregion - - #region BEFORE - - // BEFORE - // Messages whose internal date (disregarding time and timezone) - // is earlier than the specified date. - else if (m_SearchKeyName == "BEFORE") - { - if (internalDate.Date < (DateTime) m_SearchKeyValue) - { - return true; - } - } - - #endregion - - #region BODY - - // BODY - // Messages that contain the specified string in the body of the message. - // - // NOTE: Compare must be done on decoded header and decoded body of message. - // In all search keys that use strings, a message matches the key if - // the string is a substring of the field. The matching is case-insensitive. - else if (m_SearchKeyName == "BODY") - { - string val = bodyText; - if (val != null && val.ToLower().IndexOf(((string) m_SearchKeyValue).ToLower()) > -1) - { - return true; - } - } - - #endregion - - #region HEADER - - // HEADER - // Messages that have a header with the specified field-name (as - // defined in [RFC-2822]) and that contains the specified string - // in the text of the header (what comes after the colon). If the - // string to search is zero-length, this matches all messages that - // have a header line with the specified field-name regardless of - // the contents. - // - // NOTE: Compare must be done on decoded header field value. - // In all search keys that use strings, a message matches the key if - // the string is a substring of the field. The matching is case-insensitive. - else if (m_SearchKeyName == "HEADER") - { - string[] headerField_value = (string[]) m_SearchKeyValue; - - // If header field name won't end with :, add it - if (!headerField_value[0].EndsWith(":")) - { - headerField_value[0] = headerField_value[0] + ":"; - } - - if (mime.MainEntity.Header.Contains(headerField_value[0])) - { - if (headerField_value[1].Length == 0) - { - return true; - } - else if ( - mime.MainEntity.Header.GetFirst(headerField_value[0]).Value.ToLower().IndexOf( - headerField_value[1].ToLower()) > -1) - { - return true; - } - } - } - - #endregion - - #region KEYWORD - - // KEYWORD - // Messages with the specified keyword flag set. - else if (m_SearchKeyName == "KEYWORD") - { - if ((flags & IMAP_Utils.ParseMessageFlags((string) m_SearchKeyValue)) != 0) - { - return true; - } - } - - #endregion - - #region LARGER - - // LARGER - // Messages with an [RFC-2822] size larger than the specified number of octets. - else if (m_SearchKeyName == "LARGER") - { - if (size > (long) m_SearchKeyValue) - { - return true; - } - } - - #endregion - - #region NOT - - // NOT or ( ...)(SearchGroup) - // Messages that do not match the specified search key. - else if (m_SearchKeyName == "NOT") - { - return - !SearchGroup.Match_Key_Value(m_SearchKeyValue, - no, - uid, - size, - internalDate, - flags, - mime, - bodyText); - } - - #endregion - - #region ON - - // ON - // Messages whose internal date (disregarding time and timezone) - // is within the specified date. - else if (m_SearchKeyName == "ON") - { - if (internalDate.Date == (DateTime) m_SearchKeyValue) - { - return true; - } - } - - #endregion - - #region OR - - // OR - SearckKey can be parenthesis list of keys ! - // Messages that match either search key. - else if (m_SearchKeyName == "OR") - { - object serachKey1 = ((object[]) m_SearchKeyValue)[0]; - object serachKey2 = ((object[]) m_SearchKeyValue)[1]; - - if ( - SearchGroup.Match_Key_Value(serachKey1, no, uid, size, internalDate, flags, mime, bodyText) || - SearchGroup.Match_Key_Value(serachKey2, no, uid, size, internalDate, flags, mime, bodyText)) - { - return true; - } - } - - #endregion - - #region SENTBEFORE - - // SENTBEFORE - // Messages whose [RFC-2822] Date: header (disregarding time and - // timezone) is earlier than the specified date. - else if (m_SearchKeyName == "SENTBEFORE") - { - if (mime.MainEntity.Date.Date < (DateTime) m_SearchKeyValue) - { - return true; - } - } - - #endregion - - #region SENTON - - // SENTON - // Messages whose [RFC-2822] Date: header (disregarding time and - // timezone) is within the specified date. - else if (m_SearchKeyName == "SENTON") - { - if (mime.MainEntity.Date.Date == (DateTime) m_SearchKeyValue) - { - return true; - } - } - - #endregion - - #region SENTSINCE - - // SENTSINCE - // Messages whose [RFC-2822] Date: header (disregarding time and - // timezone) is within or later than the specified date. - else if (m_SearchKeyName == "SENTSINCE") - { - if (mime.MainEntity.Date.Date >= (DateTime) m_SearchKeyValue) - { - return true; - } - } - - #endregion - - #region SINCE - - // SINCE - // Messages whose internal date (disregarding time and timezone) - // is within or later than the specified date. - else if (m_SearchKeyName == "SINCE") - { - if (internalDate.Date >= (DateTime) m_SearchKeyValue) - { - return true; - } - } - - #endregion - - #region SMALLER - - // SMALLER - // Messages with an [RFC-2822] size smaller than the specified number of octets. - else if (m_SearchKeyName == "SMALLER") - { - if (size < (long) m_SearchKeyValue) - { - return true; - } - } - - #endregion - - #region TEXT - - // TEXT - // Messages that contain the specified string in the header or body of the message. - // - // NOTE: Compare must be done on decoded header and decoded body of message. - // In all search keys that use strings, a message matches the key if - // the string is a substring of the field. The matching is case-insensitive. - else if (m_SearchKeyName == "TEXT") - { - // See body first - string val = bodyText; - if (val != null && val.ToLower().IndexOf(((string) m_SearchKeyValue).ToLower()) > -1) - { - return true; - } - - // If we reach so far, that means body won't contain specified text and we need to check header. - foreach (HeaderField headerField in mime.MainEntity.Header) - { - if (headerField.Value.ToLower().IndexOf(((string) m_SearchKeyValue).ToLower()) > -1) - { - return true; - } - } - } - - #endregion - - #region UID - - // UID - // Messages with unique identifiers corresponding to the specified - // unique identifier set. Sequence set ranges are permitted. - else if (m_SearchKeyName == "UID") - { - return ((IMAP_SequenceSet) m_SearchKeyValue).Contains(uid); - } - - #endregion - - #region UNKEYWORD - - // UNKEYWORD - // Messages that do not have the specified keyword flag set. - else if (m_SearchKeyName == "UNKEYWORD") - { - if ((flags & IMAP_Utils.ParseMessageFlags((string) m_SearchKeyValue)) == 0) - { - return true; - } - } - - #endregion - - #region SEQUENCESET - - // - // Messages with message sequence numbers corresponding to the - // specified message sequence number set. - else if (m_SearchKeyName == "SEQUENCESET") - { - return ((IMAP_SequenceSet) m_SearchKeyValue).Contains(no); - } - - #endregion - - return false; - } - - #endregion - - #region Utility methods - - /// - /// Reads search-key <string> value. - /// - /// - /// - private static string ReadString(StringReader reader) - { - //Remove spaces from string start - reader.ReadToFirstChar(); - - // We must support: - // word - // "text" - // {string_length}data(string_length) - - // {string_length}data(string_length) - if (reader.StartsWith("{")) - { - // Remove { - reader.ReadSpecifiedLength("{".Length); - - int dataLength = Convert.ToInt32(reader.QuotedReadToDelimiter('}')); - return reader.ReadSpecifiedLength(dataLength); - } - - return TextUtils.UnQuoteString(reader.QuotedReadToDelimiter(' ')); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_SearchMatcher.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_SearchMatcher.cs deleted file mode 100644 index d61b543b9..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_SearchMatcher.cs +++ /dev/null @@ -1,105 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Server -{ - #region usings - - using System; - using Mime; - - #endregion - - /// - /// IMAP SEARCH message matcher. You can use this class to see if message values match to search criteria. - /// - public class IMAP_SearchMatcher - { - #region Members - - private readonly SearchGroup m_pSearchCriteria; - - #endregion - - #region Properties - - /// - /// Gets if header is needed for matching. - /// - public bool IsHeaderNeeded - { - get { return m_pSearchCriteria.IsHeaderNeeded(); } - } - - /// - /// Gets if body text is needed for matching. - /// - public bool IsBodyTextNeeded - { - get { return m_pSearchCriteria.IsBodyTextNeeded(); } - } - - #endregion - - #region Constructor - - /// - /// Deault constuctor. - /// - /// SEARCH command main search group. - internal IMAP_SearchMatcher(SearchGroup mainSearchGroup) - { - m_pSearchCriteria = mainSearchGroup; - } - - #endregion - - #region Methods - - /// - /// Gets if specified values match search criteria. - /// - /// Message sequence number. - /// Message UID. - /// Message size in bytes. - /// Message INTERNALDATE (dateTime when server stored message). - /// Message flags. - /// Message header. This is only needed if this.IsHeaderNeeded is true. - /// Message body text (must be decoded unicode text). This is only needed if this.IsBodyTextNeeded is true. - /// - public bool Matches(int no, - int uid, - int size, - DateTime internalDate, - IMAP_MessageFlags flags, - string header, - string bodyText) - { - // Parse header only if it's needed - Mime m = null; - if (m_pSearchCriteria.IsHeaderNeeded()) - { - m = new Mime(); - m.MainEntity.Header.Parse(header); - } - - return m_pSearchCriteria.Match(no, uid, size, internalDate, flags, m, bodyText); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_SelectedFolder.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_SelectedFolder.cs deleted file mode 100644 index bc7cca93b..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_SelectedFolder.cs +++ /dev/null @@ -1,249 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; - -namespace ASC.Mail.Net.IMAP.Server -{ - #region usings - - using System.Text; - - #endregion - - /// - /// Holds IMAP selected folder info. - /// - public class IMAP_SelectedFolder:IDisposable - { - #region Members - - private readonly string m_Folder = ""; - private readonly IMAP_MessageCollection m_pMessages; - - #endregion - - #region Properties - - /// - /// Gets selected folder name. - /// - public string Folder - { - get { return m_Folder; } - } - - /// - /// Gets folder UID(UIDVADILITY) value. - /// - public long FolderUID { get; set; } - - /// - /// Gets or sets if folder is read only. - /// - public bool ReadOnly { get; set; } - - /// - /// Gets selected folder messages info. - /// - public IMAP_MessageCollection Messages - { - get { return m_pMessages; } - } - - /// - /// Gets number of messages with \UNSEEN flags in the collection. - /// - public int UnSeenCount - { - get - { - int count = 0; - foreach (IMAP_Message message in m_pMessages) - { - if ((message.Flags & IMAP_MessageFlags.Seen) == 0) - { - count++; - } - } - return count; - } - } - - /// - /// Gets number of messages with \RECENT flags in the collection. - /// - public int RecentCount - { - get - { - int count = 0; - foreach (IMAP_Message message in m_pMessages) - { - if ((message.Flags & IMAP_MessageFlags.Recent) != 0) - { - count++; - } - } - return count; - } - } - - /// - /// Gets number of messages with \DELETED flags in the collection. - /// - public int DeletedCount - { - get - { - int count = 0; - foreach (IMAP_Message message in m_pMessages) - { - if ((message.Flags & IMAP_MessageFlags.Deleted) != 0) - { - count++; - } - } - return count; - } - } - - /// - /// Gets first message index in the collection which has not \SEEN flag set. - /// - public int FirstUnseen - { - get - { - int index = 1; - foreach (IMAP_Message message in m_pMessages) - { - if ((message.Flags & IMAP_MessageFlags.Seen) == 0) - { - return index; - } - index++; - } - - return 0; - } - } - - /// - /// Gets next new message predicted UID. - /// - public long MessageUidNext - { - get - { - if (m_pMessages.Count > 0) - { - return m_pMessages[m_pMessages.Count - 1].UID + 1; - } - else - { - return 1; - } - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Folder name. - internal IMAP_SelectedFolder(string folder) - { - m_Folder = folder; - m_pMessages = new IMAP_MessageCollection(); - } - - #endregion - - #region Internal methods - - /// - /// Updates current folder messages info with new messages info. - /// - /// - /// - internal string Update(IMAP_SelectedFolder folder) - { - StringBuilder retVal = new StringBuilder(); - long maxUID = MessageUidNext - 1; - - long countExists = Messages.Count; - long countRecent = RecentCount; - - // Add new messages - for (int i = folder.Messages.Count - 1; i >= 0; i--)//FIX - { - IMAP_Message message = folder.Messages[i]; - // New message - if (message.UID > maxUID) - { - m_pMessages.Add(message.ID, message.UID, message.InternalDate, message.Size, message.Flags); - } - // New messages ended - else - { - break; - } - } - - // Remove deleted messages - for (int i = 0; i < m_pMessages.Count; i++) - { - IMAP_Message message = m_pMessages[i]; - - if (!folder.m_pMessages.ContainsUID(message.UID)) - { - retVal.Append("* " + message.SequenceNo + " EXPUNGE\r\n"); - m_pMessages.Remove(message); - i--; - } - } - - if (countExists != Messages.Count) - { - retVal.Append("* " + Messages.Count + " EXISTS\r\n"); - } - if (countRecent != RecentCount) - { - retVal.Append("* " + RecentCount + " RECENT\r\n"); - } - - return retVal.ToString(); - } - - #endregion - - private bool isDisposed = false; - - public void Dispose() - { - if (!isDisposed) - { - isDisposed = true; - m_pMessages.Dispose(); - } - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_Server.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_Server.cs deleted file mode 100644 index 50715cb24..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_Server.cs +++ /dev/null @@ -1,782 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using ASC.Mail.Net.TCP; - -namespace ASC.Mail.Net.IMAP.Server -{ - #region usings - - using System; - using System.Net; - using System.Net.Sockets; - using System.Text; - using AUTH; - - #endregion - - #region Event delegates - - /// - /// Represents the method that will handle the AuthUser event for SMTP_Server. - /// - /// The source of the event. - /// A AuthUser_EventArgs that contains the event data. - public delegate void AuthUserEventHandler(object sender, AuthUser_EventArgs e); - - /// - /// - /// - public delegate void FolderEventHandler(object sender, Mailbox_EventArgs e); - - /// - /// - /// - public delegate void FoldersEventHandler(object sender, IMAP_Folders e); - - /// - /// - /// - public delegate void MessagesEventHandler(object sender, IMAP_eArgs_GetMessagesInfo e); - - /// - /// - /// - public delegate void MessagesItemsEventHandler(object sender, IMAP_eArgs_MessageItems e); - - public delegate byte[] FiletGetDelegate(IMAP_Session session); - public delegate void FiletSetDelegate(IMAP_Session session, byte[] buffer); - /// - /// - /// - public delegate void MessageEventHandler(object sender, Message_EventArgs e); - - /* - /// - /// - /// - public delegate void SearchEventHandler(object sender,IMAP_eArgs_Search e);*/ - - /// - /// - /// - public delegate void SharedRootFoldersEventHandler(object sender, SharedRootFolders_EventArgs e); - - /// - /// - /// - public delegate void GetFolderACLEventHandler(object sender, IMAP_GETACL_eArgs e); - - /// - /// - /// - public delegate void DeleteFolderACLEventHandler(object sender, IMAP_DELETEACL_eArgs e); - - /// - /// - /// - public delegate void SetFolderACLEventHandler(object sender, IMAP_SETACL_eArgs e); - - /// - /// - /// - public delegate void GetUserACLEventHandler(object sender, IMAP_GetUserACL_eArgs e); - - /// - /// - /// - public delegate void GetUserQuotaHandler(object sender, IMAP_eArgs_GetQuota e); - - #endregion - - /// - /// IMAP server componet. - /// - public class IMAP_Server : TCP_Server - { - #region Events - - /// - /// Occurs when connected user tryes to authenticate. - /// - public event AuthUserEventHandler AuthUser = null; - - /// - /// Occurs when server requests to copy message to new location. - /// - public event MessageEventHandler CopyMessage = null; - - /// - /// Occurs when server requests to create folder. - /// - public event FolderEventHandler CreateFolder = null; - - /// - /// Occurs when server requests to delete folder. - /// - public event FolderEventHandler DeleteFolder = null; - - /// - /// Occurs when IMAP server requests to delete folder ACL. - /// - public event DeleteFolderACLEventHandler DeleteFolderACL = null; - - /// - /// Occurs when server requests to delete message. - /// - public event MessageEventHandler DeleteMessage = null; - - /// - /// Occurs when IMAP server requests folder ACL. - /// - public event GetFolderACLEventHandler GetFolderACL = null; - - /// - /// Occurs when server requests all available folders. - /// - public event FoldersEventHandler GetFolders = null; - - /// - /// Occurs when server requests to get message items. - /// - public event MessagesItemsEventHandler GetMessageItems = null; - - /// - /// Occurs when server requests to folder messages info. - /// - public event MessagesEventHandler GetMessagesInfo = null; - - /// - /// Occurs when IMAP server requests shared root folders info. - /// - public event SharedRootFoldersEventHandler GetSharedRootFolders = null; - - /// - /// Occurs when server requests subscribed folders. - /// - public event FoldersEventHandler GetSubscribedFolders = null; - - /// - /// Occurs when IMAP server requests to get user ACL for specified folder. - /// - public event GetUserACLEventHandler GetUserACL = null; - - /// - /// Occurs when IMAP server requests to get user quota. - /// - public event GetUserQuotaHandler GetUserQuota = null; - - /// - /// Occurs when server requests to rename folder. - /// - public event FolderEventHandler RenameFolder = null; - - /// - /// Occurs when IMAP session has finished and session log is available. - /// - public event LogEventHandler SessionLog = null; - - /// - /// Occurs when IMAP server requests to set folder ACL. - /// - public event SetFolderACLEventHandler SetFolderACL = null; - - /// - /// Occurs when server requests to store message. - /// - public event MessageEventHandler StoreMessage = null; - - /* - /// - /// Occurs when server requests to search specified folder messages. - /// - public event SearchEventHandler Search = null;*/ - - /// - /// Occurs when server requests to store message flags. - /// - public event MessageEventHandler StoreMessageFlags = null; - - /// - /// Occurs when server requests to subscribe folder. - /// - public event FolderEventHandler SubscribeFolder = null; - - /// - /// Occurs when server requests to unsubscribe folder. - /// - public event FolderEventHandler UnSubscribeFolder = null; - - /// - /// Occurs when new computer connected to IMAP server. - /// - public event ValidateIPHandler ValidateIPAddress = null; - - internal delegate void FolderChangedHandler(string folderName, string username); - - internal event FolderChangedHandler FolderChanged = null; - - - public void OnFolderChanged(string folderName, string username) - { - if (FolderChanged!=null) - { - FolderChanged(folderName, username); - } - } - - #endregion - - #region Members - - private string m_GreetingText = ""; - private int m_MaxConnectionsPerIP; - private int m_MaxMessageSize = 1000000; - private SaslAuthTypes m_SupportedAuth = SaslAuthTypes.All; - - #endregion - - #region Properties - - /// - /// Gets or sets server supported authentication types. - /// - public SaslAuthTypes SupportedAuthentications - { - get { return m_SupportedAuth; } - - set { m_SupportedAuth = value; } - } - - /// - /// Gets or sets server greeting text. - /// - public string GreetingText - { - get { return m_GreetingText; } - - set { m_GreetingText = value; } - } - - /// - /// Gets or sets maximum allowed conncurent connections from 1 IP address. Value 0 means unlimited connections. - /// - public new int MaxConnectionsPerIP - { - get { return m_MaxConnectionsPerIP; } - - set { m_MaxConnectionsPerIP = value; } - } - - /// - /// Maximum message size. - /// - public int MaxMessageSize - { - get { return m_MaxMessageSize; } - - set { m_MaxMessageSize = value; } - } - - - public int MaxBadCommands { get; set; } - - #endregion - - #region Constructor - - - #endregion - - #region Overrides - - protected override void OnMaxConnectionsPerIPExceeded(IMAP_Session session) - { - base.OnMaxConnectionsPerIPExceeded(session); - session.TcpStream.WriteLine("* NO Maximum connections from your IP address is exceeded, try again later !\r\n"); - } - - protected override void OnMaxConnectionsExceeded(IMAP_Session session) - { - session.TcpStream.WriteLine("* NO Maximum connections is exceeded"); - } - - /// - /// Initialize and start new session here. Session isn't added to session list automatically, - /// session must add itself to server session list by calling AddSession(). - /// - /// Connected client socket. - /// BindInfo what accepted socket. - //protected override void InitNewSession(Socket socket, IPBindInfo bindInfo) - //{ - // // Check maximum conncurent connections from 1 IP. - // if (m_MaxConnectionsPerIP > 0) - // { - // lock (Sessions) - // { - // int nSessions = 0; - // foreach (SocketServerSession s in Sessions) - // { - // IPEndPoint ipEndpoint = s.RemoteEndPoint; - // if (ipEndpoint != null) - // { - // if (ipEndpoint.Address.Equals(((IPEndPoint) socket.RemoteEndPoint).Address)) - // { - // nSessions++; - // } - // } - - // // Maimum allowed exceeded - // if (nSessions >= m_MaxConnectionsPerIP) - // { - // socket.Send( - // Encoding.ASCII.GetBytes( - // "* NO Maximum connections from your IP address is exceeded, try again later !\r\n")); - // socket.Shutdown(SocketShutdown.Both); - // socket.Close(); - // return; - // } - // } - // } - // } - - // string sessionID = Guid.NewGuid().ToString(); - // SocketEx socketEx = new SocketEx(socket); - // if (LogCommands) - // { - // socketEx.Logger = new SocketLogger(socket, SessionLog); - // socketEx.Logger.SessionID = sessionID; - // } - // IMAP_Session session = new IMAP_Session(sessionID, socketEx, bindInfo, this); - // FolderChanged += session.OnFolderChanged; - //} - - #endregion - - #region Internal methods - - /// - /// Raises event ValidateIP event. - /// - /// Server IP. - /// Connected client IP. - /// Returns true if connection allowed. - internal bool OnValidate_IpAddress(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint) - { - ValidateIP_EventArgs oArg = new ValidateIP_EventArgs(localEndPoint, remoteEndPoint); - if (ValidateIPAddress != null) - { - ValidateIPAddress(this, oArg); - } - - return oArg.Validated; - } - - /// - /// Raises event AuthUser. - /// - /// Reference to current IMAP session. - /// User name. - /// Password compare data,it depends of authentication type. - /// For md5 eg. md5 calculation hash.It depends of authentication type. - /// Authentication type. - /// Returns true if user is authenticated ok. - internal AuthUser_EventArgs OnAuthUser(IMAP_Session session, - string userName, - string passwordData, - string data, - AuthType authType) - { - AuthUser_EventArgs oArgs = new AuthUser_EventArgs(session, userName, passwordData, data, authType); - if (AuthUser != null) - { - AuthUser(this, oArgs); - } - - return oArgs; - } - - /// - /// Raises event 'SubscribeMailbox'. - /// - /// Reference to IMAP session. - /// Mailbox which to subscribe. - /// - internal string OnSubscribeMailbox(IMAP_Session session, string mailbox) - { - if (SubscribeFolder != null) - { - Mailbox_EventArgs eArgs = new Mailbox_EventArgs(mailbox); - SubscribeFolder(session, eArgs); - - return eArgs.ErrorText; - } - - return null; - } - - /// - /// Raises event 'UnSubscribeMailbox'. - /// - /// Reference to IMAP session. - /// Mailbox which to unsubscribe. - /// - internal string OnUnSubscribeMailbox(IMAP_Session session, string mailbox) - { - if (UnSubscribeFolder != null) - { - Mailbox_EventArgs eArgs = new Mailbox_EventArgs(mailbox); - UnSubscribeFolder(session, eArgs); - - return eArgs.ErrorText; - } - - return null; - } - - /// - /// Raises event 'GetSubscribedMailboxes'. - /// - /// Reference to IMAP session. - /// Mailbox reference. - /// Mailbox search pattern or mailbox. - /// - internal IMAP_Folders OnGetSubscribedMailboxes(IMAP_Session session, - string referenceName, - string mailBox) - { - IMAP_Folders retVal = new IMAP_Folders(session, referenceName, mailBox); - if (GetSubscribedFolders != null) - { - GetSubscribedFolders(session, retVal); - } - - return retVal; - } - - /// - /// Raises event 'GetMailboxes'. - /// - /// Reference to IMAP session. - /// Mailbox reference. - /// Mailbox search pattern or mailbox. - /// - internal IMAP_Folders OnGetMailboxes(IMAP_Session session, string referenceName, string mailBox) - { - IMAP_Folders retVal = new IMAP_Folders(session, referenceName, mailBox); - if (GetFolders != null) - { - GetFolders(session, retVal); - } - - return retVal; - } - - /// - /// Raises event 'CreateMailbox'. - /// - /// Reference to IMAP session. - /// Mailbox to create. - /// - internal string OnCreateMailbox(IMAP_Session session, string mailbox) - { - if (CreateFolder != null) - { - Mailbox_EventArgs eArgs = new Mailbox_EventArgs(mailbox); - CreateFolder(session, eArgs); - - return eArgs.ErrorText; - } - - return null; - } - - /// - /// Raises event 'DeleteMailbox'. - /// - /// Reference to IMAP session. - /// Mailbox which to delete. - /// - internal string OnDeleteMailbox(IMAP_Session session, string mailbox) - { - if (DeleteFolder != null) - { - Mailbox_EventArgs eArgs = new Mailbox_EventArgs(mailbox); - DeleteFolder(session, eArgs); - - return eArgs.ErrorText; - } - - return null; - } - - /// - /// Raises event 'RenameMailbox'. - /// - /// Reference to IMAP session. - /// Mailbox which to rename. - /// New mailbox name. - /// - internal string OnRenameMailbox(IMAP_Session session, string mailbox, string newMailboxName) - { - if (RenameFolder != null) - { - Mailbox_EventArgs eArgs = new Mailbox_EventArgs(mailbox, newMailboxName); - RenameFolder(session, eArgs); - - return eArgs.ErrorText; - } - - return null; - } - - /// - /// Raises event 'GetMessagesInfo'. - /// - /// Reference to IMAP session. - /// Folder which messages info to get. - /// - internal IMAP_eArgs_GetMessagesInfo OnGetMessagesInfo(IMAP_Session session, IMAP_SelectedFolder folder) - { - IMAP_eArgs_GetMessagesInfo eArgs = new IMAP_eArgs_GetMessagesInfo(session, folder); - if (GetMessagesInfo != null) - { - GetMessagesInfo(session, eArgs); - } - - return eArgs; - } - - /// - /// Raises event 'DeleteMessage'. - /// - /// Reference to IMAP session. - /// Message which to delete. - /// - internal string OnDeleteMessage(IMAP_Session session, IMAP_Message message) - { - Message_EventArgs eArgs = - new Message_EventArgs(Core.Decode_IMAP_UTF7_String(session.SelectedMailbox), message); - if (DeleteMessage != null) - { - DeleteMessage(session, eArgs); - } - - return eArgs.ErrorText; - } - - /// - /// Raises event 'CopyMessage'. - /// - /// Reference to IMAP session. - /// Message which to copy. - /// New message location. - /// - internal string OnCopyMessage(IMAP_Session session, IMAP_Message msg, string location) - { - Message_EventArgs eArgs = - new Message_EventArgs(Core.Decode_IMAP_UTF7_String(session.SelectedMailbox), msg, location); - if (CopyMessage != null) - { - CopyMessage(session, eArgs); - } - - return eArgs.ErrorText; - } - - /// - /// Raises event 'StoreMessage'. - /// - /// Reference to IMAP session. - /// Folder where to store. - /// Message which to store. - /// Message data which to store. - /// - internal string OnStoreMessage(IMAP_Session session, - string folder, - IMAP_Message msg, - byte[] messageData) - { - Message_EventArgs eArgs = new Message_EventArgs(folder, msg); - eArgs.MessageData = messageData; - if (StoreMessage != null) - { - StoreMessage(session, eArgs); - } - - return eArgs.ErrorText; - } - - /* - /// - /// Raises event 'Search'. - /// - /// IMAP session what calls this search. - /// Folder what messages to search. - /// Matcher what must be used to check if message matches searching criterial. - /// - internal IMAP_eArgs_Search OnSearch(IMAP_Session session,string folder,IMAP_SearchMatcher matcher) - { - IMAP_eArgs_Search eArgs = new IMAP_eArgs_Search(session,folder,matcher); - if(this.Search != null){ - this.Search(session,eArgs); - } - - return eArgs; - }*/ - - /// - /// Raises event 'StoreMessageFlags'. - /// - /// Reference to IMAP session. - /// Message which flags to store. - /// - internal string OnStoreMessageFlags(IMAP_Session session, IMAP_Message msg) - { - Message_EventArgs eArgs = - new Message_EventArgs(Core.Decode_IMAP_UTF7_String(session.SelectedMailbox), msg); - if (StoreMessageFlags != null) - { - StoreMessageFlags(session, eArgs); - } - - return eArgs.ErrorText; - } - - internal SharedRootFolders_EventArgs OnGetSharedRootFolders(IMAP_Session session) - { - SharedRootFolders_EventArgs eArgs = new SharedRootFolders_EventArgs(session); - if (GetSharedRootFolders != null) - { - GetSharedRootFolders(session, eArgs); - } - - return eArgs; - } - - internal IMAP_GETACL_eArgs OnGetFolderACL(IMAP_Session session, string folderName) - { - IMAP_GETACL_eArgs eArgs = new IMAP_GETACL_eArgs(session, folderName); - if (GetFolderACL != null) - { - GetFolderACL(session, eArgs); - } - - return eArgs; - } - - internal IMAP_SETACL_eArgs OnSetFolderACL(IMAP_Session session, - string folderName, - string userName, - IMAP_Flags_SetType flagsSetType, - IMAP_ACL_Flags aclFlags) - { - IMAP_SETACL_eArgs eArgs = new IMAP_SETACL_eArgs(session, - folderName, - userName, - flagsSetType, - aclFlags); - if (SetFolderACL != null) - { - SetFolderACL(session, eArgs); - } - - return eArgs; - } - - internal IMAP_DELETEACL_eArgs OnDeleteFolderACL(IMAP_Session session, - string folderName, - string userName) - { - IMAP_DELETEACL_eArgs eArgs = new IMAP_DELETEACL_eArgs(session, folderName, userName); - if (DeleteFolderACL != null) - { - DeleteFolderACL(session, eArgs); - } - - return eArgs; - } - - internal IMAP_GetUserACL_eArgs OnGetUserACL(IMAP_Session session, string folderName, string userName) - { - IMAP_GetUserACL_eArgs eArgs = new IMAP_GetUserACL_eArgs(session, folderName, userName); - if (GetUserACL != null) - { - GetUserACL(session, eArgs); - } - - return eArgs; - } - - internal IMAP_eArgs_GetQuota OnGetUserQuota(IMAP_Session session) - { - IMAP_eArgs_GetQuota eArgs = new IMAP_eArgs_GetQuota(session); - if (GetUserQuota != null) - { - GetUserQuota(session, eArgs); - } - - return eArgs; - } - - #endregion - - /// - /// Raises event GetMessageItems. - /// - /// Reference to IMAP session. - /// Message info what message items to get. - /// Specifies message items what must be filled. - /// - protected internal IMAP_eArgs_MessageItems OnGetMessageItems(IMAP_Session session, - IMAP_Message messageInfo, - IMAP_MessageItems_enum messageItems) - { - IMAP_eArgs_MessageItems eArgs = new IMAP_eArgs_MessageItems(session, messageInfo, messageItems); - if (GetMessageItems != null) - { - GetMessageItems(session, eArgs); - } - return eArgs; - } - - public event FiletSetDelegate SetFilterSet = null; - public event FiletGetDelegate GetFilterSet = null; - - public byte[] GetFilter(IMAP_Session session) - { - if (GetFilterSet != null) - { - return GetFilterSet(session); - } - return null; - } - - public void SetFilter(IMAP_Session session, byte[] filter) - { - if (SetFilterSet != null) - { - SetFilterSet(session, filter); - } - } - - public void OnSysError(string text, Exception exception) - { - OnError(exception); - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_Session.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_Session.cs deleted file mode 100644 index 0c1b6feb0..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_Session.cs +++ /dev/null @@ -1,4775 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System.Collections.Generic; -using System.Security.Principal; -using ASC.Mail.Net.IO; - -namespace ASC.Mail.Net.IMAP.Server -{ - #region usings - - using System; - using System.Collections; - using System.IO; - using System.Net.Sockets; - using System.Text; - using System.Timers; - using AUTH; - using Mime; - using StringReader = StringReader; - using ASC.Mail.Net.TCP; - - #endregion - - /// - /// IMAP session. - /// - public class IMAP_Session : TCP_ServerSession - { - #region Nested type: Command_IDLE - - /// - /// This class implements IDLE command. Defined in RFC 2177. - /// - private class Command_IDLE: IDisposable - { - #region Members - - private readonly string m_CmdTag = ""; - private readonly string folder; - private bool m_IsDisposed; - private IMAP_Session m_pSession; - private TimerEx m_pTimer; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner IMAP session. - /// IDLE command command-tag. - /// Is raised when session or cmdTag is null reference. - /// Is raised when any of the arguments has invalid value. - public Command_IDLE(IMAP_Session session, string cmdTag) : this(session, cmdTag, session.SelectedMailbox) { } - - /// - /// Default constructor. - /// - /// Owner IMAP session. - /// IDLE command command-tag. - /// Is raised when session or cmdTag is null reference. - /// Is raised when any of the arguments has invalid value. - public Command_IDLE(IMAP_Session session, string cmdTag, string folder) - { - if (session == null) - { - throw new ArgumentNullException("session"); - } - if (cmdTag == null) - { - throw new ArgumentNullException("cmdTag"); - } - if (cmdTag == string.Empty) - { - throw new ArgumentException("Argument 'cmdTag' value must be specified.", "cmdTag"); - } - - try - { - m_pSession = session; - m_CmdTag = cmdTag; - this.folder = folder; - Start(); - } - catch (Exception x) - { - m_pSession.OnError(x); - } - } - - void m_pSession_FolderChanged(string folderName, string username) - { - //tada - if (!m_IsDisposed) - m_pSession.ProcessMailboxChanges(folder); - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - /// - public void Dispose() - { - if (m_IsDisposed) - { - return; - } - m_IsDisposed = true; - try - { - m_pTimer.Stop(); - m_pSession.FolderChanged -= m_pSession_FolderChanged; - m_pSession.m_pIDLE = null; - m_pSession = null; - m_pTimer.Dispose(); - m_pTimer = null; - - } - catch (Exception) - { - - } - } - - #endregion - - #region Event handlers - - private void m_pTimer_Elapsed(object sender, ElapsedEventArgs e) - { - // If mailbox changes, report them to connected client. - if (!m_IsDisposed) - m_pSession.ProcessMailboxChanges(folder); - } - - #endregion - - #region Utility methods - - private void ReadLineCompleted(object sender, EventArgs eventArgs) - { - try - { - // Accoridng RFC, we should get only "DONE" here. - if (m_IsDisposed) - return; - - if (eventArgs.Value.IsCompleted && eventArgs.Value.Error == null) - { - if (eventArgs.Value.LineUtf8 == - "DONE") - { - // Send "cmd-tag OK IDLE terminated" to connected client. - m_pSession.WriteLine(m_CmdTag + " OK IDLE terminated"); - - m_pSession.BeginRecieveCmd(); - - Dispose(); - } - // Connected client send illegal stuff us, end session. - else - { - m_pSession.EndSession(); - - Dispose(); - } - } - // Receive errors, probably TCP connection broken. - else - { - m_pSession.EndSession(); - - Dispose(); - } - - } - catch (Exception) - { - - } - } - - /// - /// Starts IDLE command processing. - /// - private void Start() - { - /* RFC 2177 IDLE command example. - C: A004 IDLE - S: * 2 EXPUNGE - S: * 3 EXISTS - S: + idling - ...time passes; another client expunges message 3... - S: * 3 EXPUNGE - S: * 2 EXISTS - ...time passes; new mail arrives... - S: * 3 EXISTS - C: DONE - S: A004 OK IDLE terminated - */ - - // Send status reponse to connected client if any. - m_pSession.ProcessMailboxChanges(folder); - - // Send "+ idling" to connected client. - m_pSession.WriteLine("+ idling"); - - - // Start timer to poll mailbox changes. - m_pSession.FolderChanged += m_pSession_FolderChanged; - - m_pTimer = new TimerEx(5000, true); - m_pTimer.Elapsed += m_pTimer_Elapsed; - m_pTimer.Enabled = true; - - // Start waiting DONE command from connected client. - m_pSession.ReadAsync(ReadLineCompleted); - } - - #endregion - } - - #endregion - - #region Members - - private IMAP_Server ImapServer { get { return Server as IMAP_Server; } } - private Command_IDLE m_pIDLE; - private IMAP_SelectedFolder m_pSelectedFolder; - private IMAP_SelectedFolder m_pAdditionalFolder; - private string m_StatusedMailbox = ""; - private int m_BadCmdCount; - private GenericIdentity m_pUser; - - public IMAP_Session() - { - SelectedMailbox = ""; - } - - public override void Dispose() - { - base.Dispose(); - if (!IsDisposed) - { - if (m_pIDLE!=null) - { - m_pIDLE.Dispose(); - } - if (m_pSelectedFolder!=null) - { - m_pSelectedFolder.Dispose(); - } - if (m_pAdditionalFolder != null) - { - m_pAdditionalFolder.Dispose(); - } - } - } - - internal event IMAP_Server.FolderChangedHandler FolderChanged = null; - - public void OnFolderChanged(string folderName, string username) - { - if (FolderChanged != null) - { - FolderChanged(folderName, username); - } - } - - #endregion - - #region Properties - - /// - /// Gets selected mailbox. - /// - public string SelectedMailbox { get; private set; } - - #endregion - - #region Constructor - - - - - #endregion - - #region Methods - - - #endregion - - #region Overrides - - protected override void OnTimeout() - { - base.OnTimeout(); - WriteLine("* BYE Session timeout"); - TcpStream.Flush(); - } - - - #endregion - - #region Utility methods - - - protected internal override void Start() - { - base.Start(); - try - { - ImapServer.FolderChanged += OnFolderChanged; - // Check if ip is allowed to connect this computer - if (ImapServer.OnValidate_IpAddress(LocalEndPoint, RemoteEndPoint)) - { - WriteLine(string.Format("* OK {0} IMAP Server ready", LocalHostName)); - BeginRecieveCmd(); - } - else - { - EndSession(); - } - } - catch (Exception x) - { - OnError(x); - } - } - - /// - /// Ends session, closes socket. - /// - private void EndSession() - { - Disconnect(); - } - - /// - /// Is called when error occures. - /// - /// - private new void OnError(Exception x) - { - // Session disposed, so we don't care about terminating errors any more. - if (IsDisposed) - { - return; - } - try - { - ImapServer.OnSysError("ending session", x); - EndSession(); - } - catch (Exception) - { - - } - } - - - private void AddLog(string message) - { - // Log - if (ImapServer.Logger != null) - { - ImapServer.Logger.AddText(ID, message); - } - } - - private void AddReadEntry(string cmd) - { - // Log - if (ImapServer.Logger != null) - { - ImapServer.Logger.AddRead(ID, AuthenticatedUserIdentity, cmd.Length, cmd, LocalEndPoint, RemoteEndPoint); - } - } - - private void AddWriteEntry(string cmd) - { - // Log - if (ImapServer.Logger != null) - { - ImapServer.Logger.AddWrite(ID, AuthenticatedUserIdentity, cmd.Length, cmd, LocalEndPoint, RemoteEndPoint); - } - } - - /// - /// Starts recieveing command. - /// - private void BeginRecieveCmd() - { - SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength], - SizeExceededAction. - JunkAndThrowException); - - args.Completed += new EventHandler>(ReciveCmdComleted); - TcpStream.ReadLine(args, true); - } - - internal void ReadAsync(EventHandler> completed) - { - SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength], - SizeExceededAction. - JunkAndThrowException); - - args.Completed += completed; - TcpStream.ReadLine(args, true); - } - - protected void WriteLine(string line) - { - try - { - AddWriteEntry(line); - TcpStream.WriteLine(line); - } - catch (Exception x) - { - OnError(x); - } - } - - - private void ReciveCmdComleted(object sender, EventArgs e) - { - if (e.Value.IsCompleted && e.Value.Error == null) - { - //Call - bool sessionEnd = false; - try - { - string cmdLine = e.Value.LineUtf8; - AddReadEntry(cmdLine); - // Exceute command - TcpStream.MemoryBuffer = true; - sessionEnd = SwitchCommand(cmdLine); - TcpStream.Flush(); - if (sessionEnd) - { - // Session end, close session - EndSession(); - } - } - catch (Exception ex) - { - OnError(ex); - } - } - else if (e.Value.Error != null) - { - OnError(e.Value.Error); - } - } - - private void EndSession(string text) - { - Disconnect(text); - } - - /// - /// Executes IMAP command. - /// - /// Original command text. - /// Returns true if must end session(command loop). - private bool SwitchCommand(string IMAP_commandTxt) - { - // Parse commandTag + comand + args - // eg. C: a100 SELECT INBOX - // a100 - commandTag - // SELECT - command - // INBOX - arg - - //---- Parse command --------------------------------------------------// - string[] cmdParts = IMAP_commandTxt.TrimStart().Split(new[] { ' ' }); - // For bad command, just return empty cmdTag and command name - if (cmdParts.Length < 2) - { - cmdParts = new[] { "", "" }; - } - string commandTag = cmdParts[0].Trim().Trim(); - string command = cmdParts[1].ToUpper().Trim(); - string argsText = Core.GetArgsText(IMAP_commandTxt, cmdParts[0] + " " + cmdParts[1]); - //---------------------------------------------------------------------// - - bool getNextCmd = true; - - switch (command) - { - //--- Non-IsAuthenticated State - case "STARTTLS": - STARTTLS(commandTag, argsText); - break; - - case "AUTHENTICATE": - Authenticate(commandTag, argsText); - break; - - case "LOGIN": - LogIn(commandTag, argsText); - break; - //--- End of non-IsAuthenticated - - //--- IsAuthenticated State - case "SELECT": - Select(commandTag, argsText); - break; - - case "EXAMINE": - Examine(commandTag, argsText); - break; - - case "CREATE": - Create(commandTag, argsText); - break; - - case "DELETE": - Delete(commandTag, argsText); - break; - - case "RENAME": - Rename(commandTag, argsText); - break; - - case "SUBSCRIBE": - Suscribe(commandTag, argsText); - break; - - case "UNSUBSCRIBE": - UnSuscribe(commandTag, argsText); - break; - - case "LIST": - List(commandTag, argsText); - break; - - case "LSUB": - LSub(commandTag, argsText); - break; - - case "STATUS": - Status(commandTag, argsText); - break; - - case "APPEND": - getNextCmd = BeginAppendCmd(commandTag, argsText); - break; - - case "NAMESPACE": - Namespace(commandTag, argsText); - break; - - case "GETACL": - GETACL(commandTag, argsText); - break; - - case "SETACL": - SETACL(commandTag, argsText); - break; - - case "DELETEACL": - DELETEACL(commandTag, argsText); - break; - - case "LISTRIGHTS": - LISTRIGHTS(commandTag, argsText); - break; - - case "MYRIGHTS": - MYRIGHTS(commandTag, argsText); - break; - - case "GETQUOTA": - GETQUOTA(commandTag, argsText); - break; - - case "GETQUOTAROOT": - GETQUOTAROOT(commandTag, argsText); - break; - //--- End of IsAuthenticated - - //--- Selected State - case "CHECK": - Check(commandTag); - break; - - case "CLOSE": - Close(commandTag); - break; - - case "EXPUNGE": - Expunge(commandTag); - break; - - case "SEARCH": - Search(commandTag, argsText, false); - break; - - case "FETCH": - Fetch(commandTag, argsText, false); - break; - - case "STORE": - Store(commandTag, argsText, false); - break; - - case "COPY": - Copy(commandTag, argsText, false); - break; - - case "UID": - Uid(commandTag, argsText); - break; - - case "IDLE": - getNextCmd = !Idle(commandTag, argsText); - break; - //--- End of Selected - - //--- Any State - case "CAPABILITY": - Capability(commandTag); - break; - case "NOOP": - Noop(commandTag); - break; - - case "LOGOUT": - LogOut(commandTag); - return true; - //--- End of Any - - default: - WriteLine(commandTag + " BAD command unrecognized"); - - //---- Check that maximum bad commands count isn't exceeded ---------------// - if (m_BadCmdCount > ImapServer.MaxBadCommands - 1) - { - WriteLine("* BAD Too many bad commands, closing transmission channel"); - return true; - } - m_BadCmdCount++; - //-------------------------------------------------------------------------// - break; - } - - if (getNextCmd) - { - BeginRecieveCmd(); - } - - return false; - } - - - - - //--- Non-IsAuthenticated State ------ - - private void STARTTLS(string cmdTag, string argsText) - { - /* RFC 2595 3. IMAP STARTTLS extension. - 3. IMAP STARTTLS extension - - When the TLS extension is present in IMAP, "STARTTLS" is listed as a - capability in response to the CAPABILITY command. This extension - adds a single command, "STARTTLS" to the IMAP protocol which is used - to begin a TLS negotiation. - - 3.1. STARTTLS Command - - Arguments: none - - Responses: no specific responses for this command - - Result: OK - begin TLS negotiation - BAD - command unknown or arguments invalid - - A TLS negotiation begins immediately after the CRLF at the end of - the tagged OK response from the server. Once a client issues a - STARTTLS command, it MUST NOT issue further commands until a - server response is seen and the TLS negotiation is complete. - - The STARTTLS command is only valid in non-authenticated state. - The server remains in non-authenticated state, even if client - credentials are supplied during the TLS negotiation. The SASL - [SASL] EXTERNAL mechanism MAY be used to authenticate once TLS - client credentials are successfully exchanged, but servers - supporting the STARTTLS command are not required to support the - EXTERNAL mechanism. - - Once TLS has been started, the client MUST discard cached - information about server capabilities and SHOULD re-issue the - CAPABILITY command. This is necessary to protect against - man-in-the-middle attacks which alter the capabilities list prior - to STARTTLS. The server MAY advertise different capabilities - after STARTTLS. - - The formal syntax for IMAP is amended as follows: - - command_any =/ "STARTTLS" - - Example: C: a001 CAPABILITY - S: * CAPABILITY IMAP4rev1 STARTTLS LOGINDISABLED - S: a001 OK CAPABILITY completed - C: a002 STARTTLS - S: a002 OK Begin TLS negotiation now - - C: a003 CAPABILITY - S: * CAPABILITY IMAP4rev1 AUTH=EXTERNAL - S: a003 OK CAPABILITY completed - C: a004 LOGIN joe password - S: a004 OK LOGIN completed - */ - - if (IsAuthenticated) - { - WriteLine(cmdTag + - " NO The STARTTLS command is only valid in non-authenticated state !"); - return; - } - if (IsSecureConnection) - { - WriteLine(cmdTag + " NO The STARTTLS already started !"); - return; - } - if (Certificate == null) - { - WriteLine(cmdTag + " NO TLS not available, SSL certificate isn't specified !"); - return; - } - - WriteLine(cmdTag + " OK Ready to start TLS"); - - try - { - SwitchToSecure(); - AddLog("TLS negotiation completed successfully."); - } - catch (Exception x) - { - WriteLine(cmdTag + " NO TLS handshake failed ! " + x.Message); - } - } - - private void Authenticate(string cmdTag, string argsText) - { - /* Rfc 3501 6.2.2. AUTHENTICATE Command - - Arguments: authentication mechanism name - - Responses: continuation data can be requested - - Result: OK - authenticate completed, now in authenticated state - NO - authenticate failure: unsupported authentication - mechanism, credentials rejected - BAD - command unknown or arguments invalid, - authentication exchange cancelled - */ - if (IsAuthenticated) - { - WriteLine(string.Format("{0} NO AUTH you are already logged in", cmdTag)); - return; - } - - string userName = ""; - // string password = ""; - AuthUser_EventArgs aArgs = null; - - switch (argsText.ToUpper()) - { - case "CRAM-MD5": - - #region CRAM-MDD5 authentication - - /* Cram-M5 - C: A0001 AUTH CRAM-MD5 - S: + - C: base64(decoded:username password_hash) - S: A0001 OK CRAM authentication successful - */ - - string md5Hash = string.Format("<{0}>", Guid.NewGuid().ToString().ToLower()); - WriteLine(string.Format("+ {0}", Convert.ToBase64String(Encoding.ASCII.GetBytes(md5Hash)))); - - string reply = ReadLine(); - reply = Encoding.Default.GetString(Convert.FromBase64String(reply)); - string[] replyArgs = reply.Split(' '); - userName = replyArgs[0]; - - aArgs = ImapServer.OnAuthUser(this, userName, replyArgs[1], md5Hash, AuthType.CRAM_MD5); - - // There is custom error, return it - if (aArgs.ErrorText != null) - { - WriteLine(string.Format("{0} NO {1}", cmdTag, aArgs.ErrorText)); - return; - } - - if (aArgs.Validated) - { - WriteLine(string.Format("{0} OK Authentication successful.", cmdTag)); - - SetUserName(userName); - } - else - { - WriteLine(string.Format("{0} NO Authentication failed", cmdTag)); - } - - #endregion - - break; - - case "DIGEST-MD5": - - #region DIGEST-MD5 authentication - - /* RFC 2831 AUTH DIGEST-MD5 - * - * Example: - * - * C: AUTH DIGEST-MD5 - * S: + base64(realm="elwood.innosoft.com",nonce="OA6MG9tEQGm2hh",qop="auth",algorithm=md5-sess) - * C: base64(username="chris",realm="elwood.innosoft.com",nonce="OA6MG9tEQGm2hh", - * nc=00000001,cnonce="OA6MHXh6VqTrRk",digest-uri="imap/elwood.innosoft.com", - * response=d388dad90d4bbd760a152321f2143af7,qop=auth) - * S: + base64(rspauth=ea40f60335c427b5527b84dbabcdfffd) - * C: - * S: A0001 OK Authentication successful. - */ - - string realm = LocalHostName; - string nonce = AuthHelper.GenerateNonce(); - - WriteLine("+ " + - AuthHelper.Base64en(AuthHelper.Create_Digest_Md5_ServerResponse(realm, - nonce))); - - string clientResponse = AuthHelper.Base64de(ReadLine()); - // Check that realm and nonce in client response are same as we specified - if (clientResponse.IndexOf(string.Format("realm=\"{0}\"", realm)) > -1 && - clientResponse.IndexOf(string.Format("nonce=\"{0}\"", nonce)) > -1) - { - // Parse user name and password compare value - // string userName = ""; - string passwData = ""; - string cnonce = ""; - foreach (string clntRespParam in clientResponse.Split(',')) - { - if (clntRespParam.StartsWith("username=")) - { - userName = clntRespParam.Split(new[] { '=' }, 2)[1].Replace("\"", ""); - } - else if (clntRespParam.StartsWith("response=")) - { - passwData = clntRespParam.Split(new[] { '=' }, 2)[1]; - } - else if (clntRespParam.StartsWith("cnonce=")) - { - cnonce = clntRespParam.Split(new[] { '=' }, 2)[1].Replace("\"", ""); - } - } - - aArgs = ImapServer.OnAuthUser(this, - userName, - passwData, - clientResponse, - AuthType.DIGEST_MD5); - - // There is custom error, return it - if (aArgs.ErrorText != null) - { - WriteLine(string.Format("{0} NO {1}", cmdTag, aArgs.ErrorText)); - return; - } - - if (aArgs.Validated) - { - // Send server computed password hash - WriteLine("+ " + AuthHelper.Base64en("rspauth=" + aArgs.ReturnData)); - - // We must got empty line here - clientResponse = ReadLine(); - if (clientResponse == "") - { - WriteLine(string.Format("{0} OK Authentication successful.", cmdTag)); - - SetUserName(userName); - } - else - { - WriteLine(string.Format("{0} NO Authentication failed", cmdTag)); - } - } - else - { - WriteLine(string.Format("{0} NO Authentication failed", cmdTag)); - } - } - else - { - WriteLine(string.Format("{0} NO Authentication failed", cmdTag)); - } - - #endregion - - break; - - default: - WriteLine(string.Format("{0} NO unsupported authentication mechanism", cmdTag)); - break; - } - } - - private void LogIn(string cmdTag, string argsText) - { - /* RFC 3501 6.2.3 LOGIN Command - - Arguments: user name - password - - Responses: no specific responses for this command - - Result: OK - login completed, now in authenticated state - NO - login failure: user name or password rejected - BAD - command unknown or arguments invalid - - The LOGIN command identifies the client to the server and carries - the plaintext password authenticating this user. - - Example: C: a001 LOGIN SMITH SESAME - S: a001 OK LOGIN completed - - //---- - C: a001 LOGIN "SMITH" "SESAME" - S: a001 OK LOGIN completed - - */ - /*if (IsAuthenticated) - { - WriteLine(cmdTag + " NO Reauthentication error, you are already logged in"); - return; - }*/ - - string[] args = TextUtils.SplitQuotedString(argsText, ' ', true); - if (args.Length != 2) - { - WriteLine(string.Format("{0} BAD Invalid arguments, syntax: {{ LOGIN \"\" \"\"}}", cmdTag)); - return; - } - - string userName = args[0]; - string password = args[1]; - - // Store start time - long startTime = DateTime.Now.Ticks; - - AuthUser_EventArgs aArgs = ImapServer.OnAuthUser(this, userName, password, "", AuthType.Plain); - // There is custom error, return it - if (aArgs.ErrorText != null) - { - WriteLine(string.Format("{0} NO {1}", cmdTag, aArgs.ErrorText)); - return; - } - - if (aArgs.Validated) - { - WriteLine(string.Format("{0} OK LOGIN Completed in {1} seconds", cmdTag, ((DateTime.Now.Ticks - startTime) / (decimal)10000000).ToString("f2"))); - - SetUserName(userName); - } - else - { - WriteLine(string.Format("{0} NO LOGIN failed", cmdTag)); - } - } - - public override GenericIdentity AuthenticatedUserIdentity - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pUser; - } - } - - private void SetUserName(string userName) - { - UserName = userName; - m_pUser = new GenericIdentity(UserName); - - } - - public string UserName { get; set; } - - //--- End of non-IsAuthenticated State - - //--- IsAuthenticated State ------ - - private void Select(string cmdTag, string argsText) - { - /* Rfc 3501 6.3.1 SELECT Command - - Arguments: mailbox name - - Responses: REQUIRED untagged responses: FLAGS, EXISTS, RECENT - REQUIRED OK untagged responses: UNSEEN, PERMANENTFLAGS, - UIDNEXT, UIDVALIDITY - - Result: OK - select completed, now in selected state - NO - select failure, now in authenticated state: no - such mailbox, can't access mailbox - BAD - command unknown or arguments invalid - - The SELECT command selects a mailbox so that messages in the - mailbox can be accessed. Before returning an OK to the client, - the server MUST send the following untagged data to the client. - Note that earlier versions of this protocol only required the - FLAGS, EXISTS, and RECENT untagged data; consequently, client - implementations SHOULD implement default behavior for missing data - as discussed with the individual item. - - FLAGS Defined flags in the mailbox. See the description - of the FLAGS response for more detail. - - EXISTS The number of messages in the mailbox. See the - description of the EXISTS response for more detail. - - RECENT The number of messages with the \Recent flag set. - See the description of the RECENT response for more - detail. - - OK [UNSEEN ] - The message sequence number of the first unseen - message in the mailbox. If this is missing, the - client can not make any assumptions about the first - unseen message in the mailbox, and needs to issue a - SEARCH command if it wants to find it. - - OK [PERMANENTFLAGS ()] - A list of message flags that the client can change - permanently. If this is missing, the client should - assume that all flags can be changed permanently. - - OK [UIDNEXT ] - The next unique identifier value. Refer to section - 2.3.1.1 for more information. If this is missing, - the client can not make any assumptions about the - next unique identifier value. - - OK [UIDVALIDITY ] - The unique identifier validity value. Refer to - section 2.3.1.1 for more information. If this is - missing, the server does not support unique - identifiers. - - Only one mailbox can be selected at a time in a connection; - simultaneous access to multiple mailboxes requires multiple - connections. The SELECT command automatically deselects any - currently selected mailbox before attempting the new selection. - Consequently, if a mailbox is selected and a SELECT command that - fails is attempted, no mailbox is selected. - - If the client is permitted to modify the mailbox, the server - SHOULD prefix the text of the tagged OK response with the - "[READ-WRITE]" response code. - - If the client is not permitted to modify the mailbox but is - permitted read access, the mailbox is selected as read-only, and - the server MUST prefix the text of the tagged OK response to - SELECT with the "[READ-ONLY]" response code. Read-only access - through SELECT differs from the EXAMINE command in that certain - read-only mailboxes MAY permit the change of permanent state on a - per-user (as opposed to global) basis. Netnews messages marked in - a server-based .newsrc file are an example of such per-user - permanent state that can be modified with read-only mailboxes. - - Example: C: A142 SELECT INBOX - S: * 172 EXISTS - S: * 1 RECENT - S: * OK [UNSEEN 12] Message 12 is first unseen - S: * OK [UIDVALIDITY 3857529045] UIDs valid - S: * OK [UIDNEXT 4392] Predicted next UID - S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft) - S: * OK [PERMANENTFLAGS (\Deleted \Seen \*)] Limited - S: A142 OK [READ-WRITE] SELECT completed - - */ - if (!IsAuthenticated) - { - WriteLine(string.Format("{0} NO Authenticate first !", cmdTag)); - return; - } - - string[] args = TextUtils.SplitQuotedString(argsText, ' ', true); - if (args.Length != 1) - { - WriteLine(string.Format("{0} BAD SELECT invalid arguments. Syntax: {{ SELECT \"mailboxName\"}}", cmdTag)); - return; - } - - // Store start time - long startTime = DateTime.Now.Ticks; - - IMAP_SelectedFolder selectedFolder = new IMAP_SelectedFolder(Core.Decode_IMAP_UTF7_String(args[0])); - IMAP_eArgs_GetMessagesInfo eArgs = ImapServer.OnGetMessagesInfo(this, selectedFolder); - if (eArgs.ErrorText == null) - { - m_pSelectedFolder = selectedFolder; - SelectedMailbox = Core.Decode_IMAP_UTF7_String(args[0]); - - string response = ""; - response += "* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)\r\n"; - response += string.Format("* {0} EXISTS\r\n", m_pSelectedFolder.Messages.Count); - response += string.Format("* {0} RECENT\r\n", m_pSelectedFolder.RecentCount); - response += string.Format("* OK [UNSEEN {0}] Message {1} is first unseen\r\n", m_pSelectedFolder.FirstUnseen, m_pSelectedFolder.FirstUnseen); - response += string.Format("* OK [UIDVALIDITY {0}] Folder UID\r\n", m_pSelectedFolder.FolderUID); - response += string.Format("* OK [UIDNEXT {0}] Predicted next message UID\r\n", m_pSelectedFolder.MessageUidNext); - response += - "* OK [PERMANENTFLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)] Available permanent flags\r\n"; - response += string.Format("{0} OK [{1}] SELECT Completed in {2} seconds\r\n", cmdTag, (m_pSelectedFolder.ReadOnly ? "READ-ONLY" : "READ-WRITE"), ((DateTime.Now.Ticks - startTime) / (decimal)10000000).ToString("f2")); - - TcpStream.Write(response); - } - else - { - WriteLine(string.Format("{0} NO {1}", cmdTag, eArgs.ErrorText)); - } - } - - private void Examine(string cmdTag, string argsText) - { - /* Rfc 3501 6.3.2 EXAMINE Command - - Arguments: mailbox name - - Responses: REQUIRED untagged responses: FLAGS, EXISTS, RECENT - REQUIRED OK untagged responses: UNSEEN, PERMANENTFLAGS, - UIDNEXT, UIDVALIDITY - - Result: OK - examine completed, now in selected state - NO - examine failure, now in authenticated state: no - such mailbox, can't access mailbox - BAD - command unknown or arguments invalid - - The EXAMINE command is identical to SELECT and returns the same - output; however, the selected mailbox is identified as read-only. - No changes to the permanent state of the mailbox, including - per-user state, are permitted; in particular, EXAMINE MUST NOT - cause messages to lose the \Recent flag. - - The text of the tagged OK response to the EXAMINE command MUST - begin with the "[READ-ONLY]" response code. - - Example: C: A932 EXAMINE blurdybloop - S: * 17 EXISTS - S: * 2 RECENT - S: * OK [UNSEEN 8] Message 8 is first unseen - S: * OK [UIDVALIDITY 3857529045] UIDs valid - S: * OK [UIDNEXT 4392] Predicted next UID - S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft) - S: * OK [PERMANENTFLAGS ()] No permanent flags permitted - S: A932 OK [READ-ONLY] EXAMINE completed - */ - if (!IsAuthenticated) - { - WriteLine(string.Format("{0} NO Authenticate first !", cmdTag)); - return; - } - - string[] args = TextUtils.SplitQuotedString(argsText, ' ', true); - if (args.Length != 1) - { - WriteLine(string.Format("{0} BAD EXAMINE invalid arguments. Syntax: {{ EXAMINE \"mailboxName\"}}", cmdTag)); - return; - } - - // Store start time - long startTime = DateTime.Now.Ticks; - - IMAP_SelectedFolder selectedFolder = new IMAP_SelectedFolder(Core.Decode_IMAP_UTF7_String(args[0])); - IMAP_eArgs_GetMessagesInfo eArgs = ImapServer.OnGetMessagesInfo(this, selectedFolder); - if (eArgs.ErrorText == null) - { - m_pSelectedFolder = selectedFolder; - m_pSelectedFolder.ReadOnly = true; - SelectedMailbox = Core.Decode_IMAP_UTF7_String(args[0]); - - string response = ""; - response += "* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)\r\n"; - response += string.Format("* {0} EXISTS\r\n", m_pSelectedFolder.Messages.Count); - response += string.Format("* {0} RECENT\r\n", m_pSelectedFolder.RecentCount); - response += string.Format("* OK [UNSEEN {0}] Message {1} is first unseen\r\n", m_pSelectedFolder.FirstUnseen, m_pSelectedFolder.FirstUnseen); - response += string.Format("* OK [UIDVALIDITY {0}] UIDs valid\r\n", m_pSelectedFolder.FolderUID); - response += string.Format("* OK [UIDNEXT {0}] Predicted next UID\r\n", m_pSelectedFolder.MessageUidNext); - response += - "* OK [PERMANENTFLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)] Available permanent falgs\r\n"; - response += string.Format("{0} OK [READ-ONLY] EXAMINE Completed in {1} seconds\r\n", cmdTag, ((DateTime.Now.Ticks - startTime) / (decimal)10000000).ToString("f2")); - - TcpStream.Write(response); - } - else - { - WriteLine(string.Format("{0} NO {1}", cmdTag, eArgs.ErrorText)); - } - } - - private void Create(string cmdTag, string argsText) - { - /* RFC 3501 6.3.3 - - Arguments: mailbox name - - Responses: no specific responses for this command - - Result: OK - create completed - NO - create failure: can't create mailbox with that name - BAD - command unknown or arguments invalid - - The CREATE command creates a mailbox with the given name. An OK - response is returned only if a new mailbox with that name has been - created. It is an error to attempt to create INBOX or a mailbox - with a name that refers to an extant mailbox. Any error in - creation will return a tagged NO response. - - If the mailbox name is suffixed with the server's hierarchy - separator character (as returned from the server by a LIST - command), this is a declaration that the client intends to create - mailbox names under this name in the hierarchy. Server - implementations that do not require this declaration MUST ignore - it. - - If the server's hierarchy separator character appears elsewhere in - the name, the server SHOULD create any superior hierarchical names - that are needed for the CREATE command to complete successfully. - In other words, an attempt to create "foo/bar/zap" on a server in - which "/" is the hierarchy separator character SHOULD create foo/ - and foo/bar/ if they do not already exist. - - If a new mailbox is created with the same name as a mailbox which - was deleted, its unique identifiers MUST be greater than any - unique identifiers used in the previous incarnation of the mailbox - UNLESS the new incarnation has a different unique identifier - validity value. See the description of the UID command for more - detail. - - Example: C: A003 CREATE owatagusiam/ - S: A003 OK CREATE completed - C: A004 CREATE owatagusiam/blurdybloop - S: A004 OK CREATE completed - - */ - if (!IsAuthenticated) - { - WriteLine(string.Format("{0} NO Authenticate first !", cmdTag)); - return; - } - - string[] args = TextUtils.SplitQuotedString(argsText, ' ', true); - if (args.Length != 1) - { - WriteLine(string.Format("{0} BAD CREATE invalid arguments. Syntax: {{ CREATE \"mailboxName\"}}", cmdTag)); - return; - } - - // Store start time - long startTime = DateTime.Now.Ticks; - - string errorText = ImapServer.OnCreateMailbox(this, Core.Decode_IMAP_UTF7_String(args[0])); - if (errorText == null) - { - WriteLine(string.Format("{0} OK CREATE Completed in {1} seconds", cmdTag, ((DateTime.Now.Ticks - startTime) / (decimal)10000000).ToString("f2"))); - } - else - { - WriteLine(string.Format("{0} NO {1}", cmdTag, errorText)); - } - } - - private void Delete(string cmdTag, string argsText) - { - /* RFC 3501 6.3.4 DELETE Command - - Arguments: mailbox name - - Responses: no specific responses for this command - - Result: OK - create completed - NO - create failure: can't create mailbox with that name - BAD - command unknown or arguments invalid - - The DELETE command permanently removes the mailbox with the given - name. A tagged OK response is returned only if the mailbox has - been deleted. It is an error to attempt to delete INBOX or a - mailbox name that does not exist. - - The DELETE command MUST NOT remove inferior hierarchical names. - For example, if a mailbox "foo" has an inferior "foo.bar" - (assuming "." is the hierarchy delimiter character), removing - "foo" MUST NOT remove "foo.bar". It is an error to attempt to - delete a name that has inferior hierarchical names and also has - the \Noselect mailbox name attribute (see the description of the - LIST response for more details). - - It is permitted to delete a name that has inferior hierarchical - names and does not have the \Noselect mailbox name attribute. In - this case, all messages in that mailbox are removed, and the name - will acquire the \Noselect mailbox name attribute. - - The value of the highest-used unique identifier of the deleted - mailbox MUST be preserved so that a new mailbox created with the - same name will not reuse the identifiers of the former - incarnation, UNLESS the new incarnation has a different unique - identifier validity value. See the description of the UID command - for more detail. - - Examples: C: A682 LIST "" * - S: * LIST () "/" blurdybloop - S: * LIST (\Noselect) "/" foo - S: * LIST () "/" foo/bar - S: A682 OK LIST completed - C: A683 DELETE blurdybloop - S: A683 OK DELETE completed - C: A684 DELETE foo - S: A684 NO Name "foo" has inferior hierarchical names - C: A685 DELETE foo/bar - S: A685 OK DELETE Completed - C: A686 LIST "" * - S: * LIST (\Noselect) "/" foo - S: A686 OK LIST completed - C: A687 DELETE foo - S: A687 OK DELETE Completed - - */ - if (!IsAuthenticated) - { - WriteLine(string.Format("{0} NO Authenticate first !", cmdTag)); - return; - } - - string[] args = TextUtils.SplitQuotedString(argsText, ' ', true); - if (args.Length != 1) - { - WriteLine(string.Format("{0} BAD DELETE invalid arguments. Syntax: {{ DELETE \"mailboxName\"}}", cmdTag)); - return; - } - - string errorText = ImapServer.OnDeleteMailbox(this, Core.Decode_IMAP_UTF7_String(args[0])); - if (errorText == null) - { - WriteLine(string.Format("{0} OK DELETE Completed", cmdTag)); - } - else - { - WriteLine(string.Format("{0} NO {1}", cmdTag, errorText)); - } - } - - private void Rename(string cmdTag, string argsText) - { - /* RFC 3501 6.3.5 RENAME Command - - Arguments: existing mailbox name - new mailbox name - - Responses: no specific responses for this command - - Result: OK - rename completed - NO - rename failure: can't rename mailbox with that name, - can't rename to mailbox with that name - BAD - command unknown or arguments invalid - - The RENAME command changes the name of a mailbox. A tagged OK - response is returned only if the mailbox has been renamed. It is - an error to attempt to rename from a mailbox name that does not - exist or to a mailbox name that already exists. Any error in - renaming will return a tagged NO response. - - If the name has inferior hierarchical names, then the inferior - hierarchical names MUST also be renamed. For example, a rename of - "foo" to "zap" will rename "foo/bar" (assuming "/" is the - hierarchy delimiter character) to "zap/bar". - - The value of the highest-used unique identifier of the old mailbox - name MUST be preserved so that a new mailbox created with the same - name will not reuse the identifiers of the former incarnation, - UNLESS the new incarnation has a different unique identifier - validity value. See the description of the UID command for more - detail. - - Renaming INBOX is permitted, and has special behavior. It moves - all messages in INBOX to a new mailbox with the given name, - leaving INBOX empty. If the server implementation supports - inferior hierarchical names of INBOX, these are unaffected by a - rename of INBOX. - - Examples: C: A682 LIST "" * - S: * LIST () "/" blurdybloop - S: * LIST (\Noselect) "/" foo - S: * LIST () "/" foo/bar - S: A682 OK LIST completed - C: A683 RENAME blurdybloop sarasoop - S: A683 OK RENAME completed - C: A684 RENAME foo zowie - S: A684 OK RENAME Completed - C: A685 LIST "" * - S: * LIST () "/" sarasoop - S: * LIST (\Noselect) "/" zowie - S: * LIST () "/" zowie/bar - S: A685 OK LIST completed - */ - if (!IsAuthenticated) - { - WriteLine(string.Format("{0} NO Authenticate first !", cmdTag)); - return; - } - - string[] args = TextUtils.SplitQuotedString(argsText, ' ', true); - if (args.Length != 2) - { - WriteLine(string.Format("{0} BAD RENAME invalid arguments. Syntax: {{ RENAME \"mailboxName\" \"newMailboxName\"}}", cmdTag)); - return; - } - - string mailbox = Core.Decode_IMAP_UTF7_String(args[0]); - string newMailbox = Core.Decode_IMAP_UTF7_String(args[1]); - - string errorText = ImapServer.OnRenameMailbox(this, mailbox, newMailbox); - if (errorText == null) - { - WriteLine(string.Format("{0} OK RENAME Completed", cmdTag)); - } - else - { - WriteLine(string.Format("{0} NO {1}", cmdTag, errorText)); - } - } - - private void Suscribe(string cmdTag, string argsText) - { - /* RFC 3501 6.3.6 SUBSCRIBE Commmand - - Arguments: mailbox - - Responses: no specific responses for this command - - Result: OK - subscribe completed - NO - subscribe failure: can't subscribe to that name - BAD - command unknown or arguments invalid - - The SUBSCRIBE command adds the specified mailbox name to the - server's set of "active" or "subscribed" mailboxes as returned by - the LSUB command. This command returns a tagged OK response only - if the subscription is successful. - - A server MAY validate the mailbox argument to SUBSCRIBE to verify - that it exists. However, it MUST NOT unilaterally remove an - existing mailbox name from the subscription list even if a mailbox - by that name no longer exists. - - Note: this requirement is because some server sites may routinely - remove a mailbox with a well-known name (e.g. "system-alerts") - after its contents expire, with the intention of recreating it - when new contents are appropriate. - - Example: C: A002 SUBSCRIBE #news.comp.mail.mime - S: A002 OK SUBSCRIBE completed - - */ - if (!IsAuthenticated) - { - WriteLine(string.Format("{0} NO Authenticate first !", cmdTag)); - return; - } - - string[] args = TextUtils.SplitQuotedString(argsText, ' ', true); - if (args.Length != 1) - { - WriteLine(string.Format("{0} BAD SUBSCRIBE invalid arguments. Syntax: {{ SUBSCRIBE \"mailboxName\"}}", cmdTag)); - return; - } - string errorText = ImapServer.OnSubscribeMailbox(this, Core.Decode_IMAP_UTF7_String(args[0])); - if (errorText == null) - { - AdditionalSelect(Core.Decode_IMAP_UTF7_String(args[0])); - WriteLine(string.Format("{0} OK SUBSCRIBE completed", cmdTag)); - } - else - { - WriteLine(string.Format("{0} NO {1}", cmdTag, errorText)); - } - } - - - - private void AdditionalSelect(string folder) - { - m_StatusedMailbox = folder; - m_pAdditionalFolder = new IMAP_SelectedFolder(folder); - IMAP_eArgs_GetMessagesInfo eArgs = ImapServer.OnGetMessagesInfo(this, m_pAdditionalFolder); - } - - private void UnSuscribe(string cmdTag, string argsText) - { - /* RFC 3501 6.3.7 UNSUBSCRIBE Command - - Arguments: mailbox - - Responses: no specific responses for this command - - Result: OK - subscribe completed - NO - subscribe failure: can't subscribe to that name - BAD - command unknown or arguments invalid - - The UNSUBSCRIBE command removes the specified mailbox name from - the server's set of "active" or "subscribed" mailboxes as returned - by the LSUB command. This command returns a tagged OK response - only if the unsubscription is successful. - - Example: C: A002 UNSUBSCRIBE #news.comp.mail.mime - S: A002 OK UNSUBSCRIBE completed - - */ - if (!IsAuthenticated) - { - WriteLine(string.Format("{0} NO Authenticate first !", cmdTag)); - return; - } - - string[] args = TextUtils.SplitQuotedString(argsText, ' ', true); - if (args.Length != 1) - { - WriteLine(string.Format("{0} BAD UNSUBSCRIBE invalid arguments. Syntax: {{ UNSUBSCRIBE \"mailboxName\"}}", cmdTag)); - return; - } - - string errorText = ImapServer.OnUnSubscribeMailbox(this, Core.Decode_IMAP_UTF7_String(args[0])); - - if (errorText == null) - { - AdditionalSelect(Core.Decode_IMAP_UTF7_String(args[0])); - WriteLine(string.Format("{0} OK UNSUBSCRIBE completed", cmdTag)); - } - else - { - WriteLine(string.Format("{0} NO {1}", cmdTag, errorText)); - } - } - - private void List(string cmdTag, string argsText) - { - /* Rc 3501 6.3.8 LIST Command - - Arguments: reference name - mailbox name with possible wildcards - - Responses: untagged responses: LIST - - Result: OK - list completed - NO - list failure: can't list that reference or name - BAD - command unknown or arguments invalid - - The LIST command returns a subset of names from the complete set - of all names available to the client. Zero or more untagged LIST - replies are returned, containing the name attributes, hierarchy - delimiter, and name; see the description of the LIST reply for - more detail. - - An empty ("" string) reference name argument indicates that the - mailbox name is interpreted as by SELECT. The returned mailbox - names MUST match the supplied mailbox name pattern. A non-empty - reference name argument is the name of a mailbox or a level of - mailbox hierarchy, and indicates a context in which the mailbox - name is interpreted in an implementation-defined manner. - - An empty ("" string) mailbox name argument is a special request to - return the hierarchy delimiter and the root name of the name given - in the reference. The value returned as the root MAY be null if - the reference is non-rooted or is null. In all cases, the - hierarchy delimiter is returned. This permits a client to get the - hierarchy delimiter even when no mailboxes by that name currently - exist. - - The character "*" is a wildcard, and matches zero or more - characters at this position. The character "%" is similar to "*", - but it does not match a hierarchy delimiter. If the "%" wildcard - is the last character of a mailbox name argument, matching levels - of hierarchy are also returned. - - */ - if (!IsAuthenticated) - { - WriteLine(string.Format("{0} NO Authenticate first !", cmdTag)); - return; - } - - string[] args = TextUtils.SplitQuotedString(argsText, ' ', true); - if (args.Length != 2) - { - WriteLine(string.Format("{0} BAD Invalid LIST arguments. Syntax: {{ LIST \"\" \"\"}}", cmdTag)); - return; - } - - // Store start time - long startTime = DateTime.Now.Ticks; - - string refName = Core.Decode_IMAP_UTF7_String(args[0]); - string mailbox = Core.Decode_IMAP_UTF7_String(args[1]); - - string reply = ""; - - // Folder separator wanted - if (mailbox.Length == 0) - { - reply += "* LIST (\\Noselect) \"/\" \"\"\r\n"; - } - // List mailboxes - else - { - IMAP_Folders mailboxes = ImapServer.OnGetMailboxes(this, refName, mailbox); - foreach (IMAP_Folder mBox in mailboxes.Folders) - { - if (mBox.Selectable) - { - reply += string.Format("* LIST () \"/\" \"{0}\" \r\n", Core.Encode_IMAP_UTF7_String(mBox.Folder)); - } - else - { - reply += string.Format("* LIST (\\Noselect) \"/\" \"{0}\" \r\n", Core.Encode_IMAP_UTF7_String(mBox.Folder)); - } - } - } - - reply += string.Format("{0} OK LIST Completed in {1} seconds\r\n", cmdTag, ((DateTime.Now.Ticks - startTime) / (decimal)10000000).ToString("f2")); - TcpStream.Write(reply); - } - - private void LSub(string cmdTag, string argsText) - { - /* RFC 3501 6.3.9 LSUB Command - - Arguments: reference name - mailbox name with possible wildcards - - Responses: untagged responses: LSUB - - Result: OK - lsub completed - NO - lsub failure: can't list that reference or name - BAD - command unknown or arguments invalid - - The LSUB command returns a subset of names from the set of names - that the user has declared as being "active" or "subscribed". - Zero or more untagged LSUB replies are returned. The arguments to - LSUB are in the same form as those for LIST. - - The returned untagged LSUB response MAY contain different mailbox - flags from a LIST untagged response. If this should happen, the - flags in the untagged LIST are considered more authoritative. - - A special situation occurs when using LSUB with the % wildcard. - Consider what happens if "foo/bar" (with a hierarchy delimiter of - "/") is subscribed but "foo" is not. A "%" wildcard to LSUB must - return foo, not foo/bar, in the LSUB response, and it MUST be - flagged with the \Noselect attribute. - - The server MUST NOT unilaterally remove an existing mailbox name - from the subscription list even if a mailbox by that name no - longer exists. - - Example: C: A002 LSUB "#news." "comp.mail.*" - S: * LSUB () "." #news.comp.mail.mime - S: * LSUB () "." #news.comp.mail.misc - S: A002 OK LSUB completed - - */ - if (!IsAuthenticated) - { - WriteLine(string.Format("{0} NO Authenticate first !", cmdTag)); - return; - } - - string[] args = TextUtils.SplitQuotedString(argsText, ' ', true); - if (args.Length != 2) - { - WriteLine(string.Format("{0} BAD LSUB invalid arguments", cmdTag)); - return; - } - - string refName = Core.Decode_IMAP_UTF7_String(args[0]); - string mailbox = Core.Decode_IMAP_UTF7_String(args[1]); - - string reply = ""; - - IMAP_Folders mailboxes = ImapServer.OnGetSubscribedMailboxes(this, refName, mailbox); - foreach (IMAP_Folder mBox in mailboxes.Folders) - { - reply += string.Format("* LSUB () \"/\" \"{0}\" \r\n", Core.Encode_IMAP_UTF7_String(mBox.Folder)); - } - - reply += string.Format("{0} OK LSUB Completed\r\n", cmdTag); - TcpStream.Write(reply); - } - - private void Status(string cmdTag, string argsText) - { - /* RFC 3501 6.3.10 STATUS Command - - Arguments: mailbox name - status data item names - - Responses: untagged responses: STATUS - - Result: OK - status completed - NO - status failure: no status for that name - BAD - command unknown or arguments invalid - - The STATUS command requests the status of the indicated mailbox. - It does not change the currently selected mailbox, nor does it - affect the state of any messages in the queried mailbox (in - particular, STATUS MUST NOT cause messages to lose the \Recent - flag). - - The STATUS command provides an alternative to opening a second - IMAP4rev1 connection and doing an EXAMINE command on a mailbox to - query that mailbox's status without deselecting the current - mailbox in the first IMAP4rev1 connection. - - Unlike the LIST command, the STATUS command is not guaranteed to - be fast in its response. In some implementations, the server is - obliged to open the mailbox read-only internally to obtain certain - status information. Also unlike the LIST command, the STATUS - command does not accept wildcards. - - The currently defined status data items that can be requested are: - - MESSAGES - The number of messages in the mailbox. - - RECENT - The number of messages with the \Recent flag set. - - UIDNEXT - The next unique identifier value of the mailbox. Refer to - section 2.3.1.1 for more information. - - UIDVALIDITY - The unique identifier validity value of the mailbox. Refer to - section 2.3.1.1 for more information. - - UNSEEN - The number of messages which do not have the \Seen flag set. - - - Example: C: A042 STATUS blurdybloop (UIDNEXT MESSAGES) - S: * STATUS blurdybloop (MESSAGES 231 UIDNEXT 44292) - S: A042 OK STATUS completed - - */ - if (!IsAuthenticated) - { - WriteLine(string.Format("{0} NO Authenticate first !", cmdTag)); - return; - } - - string[] args = ParseParams(argsText); - if (args.Length != 2) - { - WriteLine(string.Format("{0} BAD Invalid STATUS arguments. Syntax: {{ STATUS \"\" \"(status-data-items)\"}}", cmdTag)); - return; - } - - string folder = Core.Decode_IMAP_UTF7_String(args[0]); - string wantedItems = args[1].ToUpper(); - - // See wanted items are valid. - if ( - wantedItems.Replace("MESSAGES", "").Replace("RECENT", "").Replace("UIDNEXT", "").Replace( - "UIDVALIDITY", "").Replace("UNSEEN", "").Trim().Length > 0) - { - WriteLine(string.Format("{0} BAD STATUS invalid arguments", cmdTag)); - return; - } - - AdditionalSelect(folder); - IMAP_SelectedFolder selectedFolder = new IMAP_SelectedFolder(folder); - IMAP_eArgs_GetMessagesInfo eArgs = ImapServer.OnGetMessagesInfo(this, selectedFolder); - if (eArgs.ErrorText == null) - { - string itemsReply = ""; - if (wantedItems.IndexOf("MESSAGES") > -1) - { - itemsReply += string.Format(" MESSAGES {0}", selectedFolder.Messages.Count); - } - if (wantedItems.IndexOf("RECENT") > -1) - { - itemsReply += string.Format(" RECENT {0}", selectedFolder.RecentCount); - } - if (wantedItems.IndexOf("UNSEEN") > -1) - { - itemsReply += string.Format(" UNSEEN {0}", selectedFolder.UnSeenCount); - } - if (wantedItems.IndexOf("UIDVALIDITY") > -1) - { - itemsReply += string.Format(" UIDVALIDITY {0}", selectedFolder.FolderUID); - } - if (wantedItems.IndexOf("UIDNEXT") > -1) - { - itemsReply += string.Format(" UIDNEXT {0}", selectedFolder.MessageUidNext); - } - itemsReply = itemsReply.Trim(); - - WriteLine(string.Format("* STATUS {0} ({1})", args[0], itemsReply)); - WriteLine(string.Format("{0} OK STATUS completed", cmdTag)); - } - else - { - WriteLine(string.Format("{0} NO {1}", cmdTag, eArgs.ErrorText)); - } - } - - /// - /// Returns true if command ended syncronously. - /// - private bool BeginAppendCmd(string cmdTag, string argsText) - { - /* Rfc 3501 6.3.11 APPEND Command - - Arguments: mailbox name - OPTIONAL flag parenthesized list - OPTIONAL date/time string - message literal - - Responses: no specific responses for this command - - Result: OK - append completed - NO - append error: can't append to that mailbox, error - in flags or date/time or message text - BAD - command unknown or arguments invalid - - The APPEND command appends the literal argument as a new message - to the end of the specified destination mailbox. This argument - SHOULD be in the format of an [RFC-2822] message. 8-bit - characters are permitted in the message. A server implementation - that is unable to preserve 8-bit data properly MUST be able to - reversibly convert 8-bit APPEND data to 7-bit using a [MIME-IMB] - content transfer encoding. - - If a flag parenthesized list is specified, the flags SHOULD be set - in the resulting message; otherwise, the flag list of the - resulting message is set to empty by default. In either case, the - Recent flag is also set. - - If a date-time is specified, the internal date SHOULD be set in - the resulting message; otherwise, the internal date of the - resulting message is set to the current date and time by default. - - If the append is unsuccessful for any reason, the mailbox MUST be - restored to its state before the APPEND attempt; no partial - appending is permitted. - - If the destination mailbox does not exist, a server MUST return an - error, and MUST NOT automatically create the mailbox. Unless it - is certain that the destination mailbox can not be created, the - server MUST send the response code "[TRYCREATE]" as the prefix of - the text of the tagged NO response. This gives a hint to the - client that it can attempt a CREATE command and retry the APPEND - if the CREATE is successful. - - Example: C: A003 APPEND saved-messages (\Seen) {310} - S: + Ready for literal data - C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST) - C: From: Fred Foobar - C: Subject: afternoon meeting - C: To: mooch@owatagu.siam.edu - C: Message-Id: - C: MIME-Version: 1.0 - C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII - C: - C: Hello Joe, do you think we can meet at 3:30 tomorrow? - C: - S: A003 OK APPEND completed - - */ - if (!IsAuthenticated) - { - WriteLine(string.Format("{0} NO Authenticate first !", cmdTag)); - return true; - } - - string[] args = ParseParams(argsText); - if (args.Length < 2 || args.Length > 4) - { - WriteLine(string.Format("{0} BAD APPEND Invalid arguments", cmdTag)); - return true; - } - - string mailbox = Core.Decode_IMAP_UTF7_String(args[0]); - IMAP_MessageFlags mFlags = 0; - DateTime date = DateTime.Now; - long msgLen = Convert.ToInt64(args[args.Length - 1].Replace("{", "").Replace("}", "")); - - if (args.Length == 4) - { - //--- Parse flags, see if valid ---------------- - string flags = args[1].ToUpper(); - if ( - flags.Replace("\\ANSWERED", "").Replace("\\FLAGGED", "").Replace("\\DELETED", "").Replace( - "\\SEEN", "").Replace("\\DRAFT", "").Trim().Length > 0) - { - WriteLine(string.Format("{0} BAD arguments invalid", cmdTag)); - return false; - } - - mFlags = IMAP_Utils.ParseMessageFlags(flags); - date = MimeUtils.ParseDate(args[2]); - } - else if (args.Length == 3) - { - // See if date or flags specified, try date first - try - { - date = MimeUtils.ParseDate(args[1]); - } - catch - { - //--- Parse flags, see if valid ---------------- - string flags = args[1].ToUpper(); - if ( - flags.Replace("\\ANSWERED", "").Replace("\\FLAGGED", "").Replace("\\DELETED", ""). - Replace("\\SEEN", "").Replace("\\DRAFT", "").Trim().Length > 0) - { - WriteLine(string.Format("{0} BAD arguments invalid", cmdTag)); - return false; - } - - mFlags = IMAP_Utils.ParseMessageFlags(flags); - } - } - - // Request data - WriteLine("+ Ready for literal data"); - - MemoryStream strm = new MemoryStream(); - Hashtable param = new Hashtable(); - param.Add("cmdTag", cmdTag); - param.Add("mailbox", mailbox); - param.Add("mFlags", mFlags); - param.Add("date", date); - param.Add("strm", strm); - - // Begin recieving data Why needed msgLen+2 ??? - TcpStream.BeginReadFixedCount(strm, (int)msgLen + 2, EndAppendCmd, param); - - return false; - } - - /// - /// Is called when DATA command is finnished. - /// - /// - /// - /// - /// - private void EndAppendCmd(IAsyncResult ar) - { - try - { - TcpStream.EndReadFixedCount(ar); - if (ar.IsCompleted) - { - - Hashtable param = (Hashtable)ar.AsyncState; - string cmdTag = (string)param["cmdTag"]; - string mailbox = (string)param["mailbox"]; - IMAP_MessageFlags mFlags = (IMAP_MessageFlags)param["mFlags"]; - DateTime date = (DateTime)param["date"]; - MemoryStream strm = (MemoryStream)param["strm"]; - - IMAP_Message msg = new IMAP_Message(null, "", 0, date, 0, mFlags); - string errotText = ImapServer.OnStoreMessage(this, mailbox, msg, strm.ToArray()); - if (errotText == null) - { - WriteLine(string.Format("{0} OK APPEND completed, recieved {1} bytes", cmdTag, strm.Length)); - } - else - { - WriteLine(string.Format("{0} NO {1}", cmdTag, errotText)); - } - } - - // Command completed ok, get next command - BeginRecieveCmd(); - } - catch (Exception x) - { - OnError(x); - } - } - - private void Namespace(string cmdTag, string argsText) - { - /* Rfc 2342 5. NAMESPACE Command. - - Arguments: none - - Response: an untagged NAMESPACE response that contains the prefix - and hierarchy delimiter to the server's Personal - Namespace(s), Other Users' Namespace(s), and Shared - Namespace(s) that the server wishes to expose. The - response will contain a NIL for any namespace class - that is not available. Namespace_Response_Extensions - MAY be included in the response. - Namespace_Response_Extensions which are not on the IETF - standards track, MUST be prefixed with an "X-". - - Result: OK - Command completed - NO - Error: Can't complete command - BAD - argument invalid - - Example: < A server that contains a Personal Namespace and a single Shared - Namespace. > - - C: A001 NAMESPACE - S: * NAMESPACE (("" "/")) NIL (("Public Folders/" "/")) - S: A001 OK NAMESPACE command completed - */ - - if (!IsAuthenticated) - { - WriteLine(string.Format("{0} NO Authenticate first !", cmdTag)); - return; - } - - SharedRootFolders_EventArgs eArgs = ImapServer.OnGetSharedRootFolders(this); - string publicRootFolders = "NIL"; - if (eArgs.PublicRootFolders != null && eArgs.PublicRootFolders.Length > 0) - { - publicRootFolders = "("; - foreach (string publicRootFolder in eArgs.PublicRootFolders) - { - publicRootFolders += string.Format("(\"{0}/\" \"/\")", publicRootFolder); - } - publicRootFolders += ")"; - } - string sharedRootFolders = "NIL"; - if (eArgs.SharedRootFolders != null && eArgs.SharedRootFolders.Length > 0) - { - sharedRootFolders = "("; - foreach (string sharedRootFolder in eArgs.SharedRootFolders) - { - sharedRootFolders += string.Format("(\"{0}/\" \"/\")", sharedRootFolder); - } - sharedRootFolders += ")"; - } - - string response = string.Format("* NAMESPACE ((\"\" \"/\")) {0} {1}\r\n", sharedRootFolders, publicRootFolders); - response += string.Format("{0} OK NAMESPACE completed", cmdTag); - - WriteLine(response); - } - - private void GETACL(string cmdTag, string argsText) - { - /* RFC 2086 4.3. GETACL - Arguments: mailbox name - - Data: untagged responses: ACL - - Result: OK - getacl completed - NO - getacl failure: can't get acl - BAD - command unknown or arguments invalid - - The GETACL command returns the access control list for mailbox in - an untagged ACL reply. - - Example: C: A002 GETACL INBOX - S: * ACL INBOX Fred rwipslda - S: A002 OK Getacl complete - - .... Multiple users - S: * ACL INBOX Fred rwipslda test rwipslda - - .... No acl flags for Fred - S: * ACL INBOX Fred "" test rwipslda - - */ - - if (!IsAuthenticated) - { - WriteLine(string.Format("{0} NO Authenticate first !", cmdTag)); - return; - } - - string[] args = TextUtils.SplitQuotedString(argsText, ' ', true); - if (args.Length != 1) - { - WriteLine(string.Format("{0} BAD GETACL invalid arguments. Syntax: GETACLFolderName", cmdTag)); - return; - } - - IMAP_GETACL_eArgs eArgs = ImapServer.OnGetFolderACL(this, - Core.Decode_IMAP_UTF7_String( - IMAP_Utils.NormalizeFolder(args[0]))); - if (eArgs.ErrorText.Length > 0) - { - WriteLine(string.Format("{0} NO GETACL {1}", cmdTag, eArgs.ErrorText)); - } - else - { - string response = ""; - if (eArgs.ACL.Count > 0) - { - response += string.Format("* ACL \"{0}\"", args[0]); - foreach (DictionaryEntry ent in eArgs.ACL) - { - string aclFalgs = IMAP_Utils.ACL_to_String((IMAP_ACL_Flags)ent.Value); - if (aclFalgs.Length == 0) - { - aclFalgs = "\"\""; - } - response += string.Format(" \"{0}\" {1}", ent.Key, aclFalgs); - } - response += "\r\n"; - } - response += string.Format("{0} OK Getacl complete\r\n", cmdTag); - - TcpStream.Write(response); - } - } - - private void SETACL(string cmdTag, string argsText) - { - /* RFC 2086 4.1. SETACL - Arguments: mailbox name - authentication identifier - access right modification - - Data: no specific data for this command - - Result: OK - setacl completed - NO - setacl failure: can't set acl - BAD - command unknown or arguments invalid - - The SETACL command changes the access control list on the - specified mailbox so that the specified identifier is granted - permissions as specified in the third argument. - - The third argument is a string containing an optional plus ("+") - or minus ("-") prefix, followed by zero or more rights characters. - If the string starts with a plus, the following rights are added - to any existing rights for the identifier. If the string starts - with a minus, the following rights are removed from any existing - rights for the identifier. If the string does not start with a - plus or minus, the rights replace any existing rights for the - identifier. - */ - - if (!IsAuthenticated) - { - WriteLine(string.Format("{0} NO Authenticate first !", cmdTag)); - return; - } - - string[] args = ParseParams(argsText); - if (args.Length != 3) - { - WriteLine(string.Format("{0} BAD GETACL invalid arguments. Syntax: SETACLFolderNameUserNameACL_Flags", cmdTag)); - return; - } - - string aclFlags = args[2]; - IMAP_Flags_SetType setType = IMAP_Flags_SetType.Replace; - if (aclFlags.StartsWith("+")) - { - setType = IMAP_Flags_SetType.Add; - } - else if (aclFlags.StartsWith("-")) - { - setType = IMAP_Flags_SetType.Remove; - } - - IMAP_SETACL_eArgs eArgs = ImapServer.OnSetFolderACL(this, - IMAP_Utils.NormalizeFolder(args[0]), - args[1], - setType, - IMAP_Utils.ACL_From_String(aclFlags)); - if (eArgs.ErrorText.Length > 0) - { - WriteLine(string.Format("{0} NO SETACL {1}", cmdTag, eArgs.ErrorText)); - } - else - { - WriteLine(string.Format("{0} OK SETACL completed", cmdTag)); - } - } - - private void DELETEACL(string cmdTag, string argsText) - { - /* RFC 2086 4.2. DELETEACL - Arguments: mailbox name - authentication identifier - - Data: no specific data for this command - - Result: OK - deleteacl completed - NO - deleteacl failure: can't delete acl - BAD - command unknown or arguments invalid - - The DELETEACL command removes any pair for the - specified identifier from the access control list for the specified - mailbox. - - Example: C: A002 DELETEACL INBOX test - S: A002 OK DELETEACL completed - */ - - if (!IsAuthenticated) - { - WriteLine(string.Format("{0} NO Authenticate first !", cmdTag)); - return; - } - - string[] args = TextUtils.SplitQuotedString(argsText, ' ', true); - if (args.Length != 2) - { - WriteLine(string.Format("{0} BAD GETACL invalid arguments. Syntax: DELETEACLFolderNameUserName", cmdTag)); - return; - } - - IMAP_DELETEACL_eArgs eArgs = ImapServer.OnDeleteFolderACL(this, - IMAP_Utils.NormalizeFolder(args[0]), - args[1]); - if (eArgs.ErrorText.Length > 0) - { - WriteLine(string.Format("{0} NO DELETEACL {1}", cmdTag, eArgs.ErrorText)); - } - else - { - WriteLine(string.Format("{0} OK DELETEACL completed", cmdTag)); - } - } - - private void LISTRIGHTS(string cmdTag, string argsText) - { - /* RFC 2086 4.4. LISTRIGHTS - Arguments: mailbox name - authentication identifier - - Data: untagged responses: LISTRIGHTS - - Result: OK - listrights completed - NO - listrights failure: can't get rights list - BAD - command unknown or arguments invalid - - The LISTRIGHTS command takes a mailbox name and an identifier and - returns information about what rights may be granted to the identifier - in the ACL for the mailbox. - - Example: C: a001 LISTRIGHTS ~/Mail/saved smith - S: * LISTRIGHTS ~/Mail/saved "smith" la r swicd - S: a001 OK Listrights completed - - - C: a005 LISTRIGHTS archive.imap anyone - S: * LISTRIGHTS archive.imap "anyone" "" l r s w i p c d a - 0 1 2 3 4 5 6 7 8 9 - - */ - - if (!IsAuthenticated) - { - WriteLine(string.Format("{0} NO Authenticate first !", cmdTag)); - return; - } - - string[] args = TextUtils.SplitQuotedString(argsText, ' ', true); - if (args.Length != 2) - { - WriteLine(string.Format("{0} BAD GETACL invalid arguments. Syntax: LISTRIGHTSFolderNameUserName", cmdTag)); - return; - } - - string response = string.Format("* LISTRIGHTS \"{0}\" \"{1}\" l r s w i p c d a\r\n", args[0], args[1]); - response += string.Format("{0} OK MYRIGHTS Completed\r\n", cmdTag); - - TcpStream.Write(response); - } - - private void MYRIGHTS(string cmdTag, string argsText) - { - /* RFC 2086 4.5. MYRIGHTS - Arguments: mailbox name - - Data: untagged responses: MYRIGHTS - - Result: OK - myrights completed - NO - myrights failure: can't get rights - BAD - command unknown or arguments invalid - - The MYRIGHTS command returns the set of rights that the user has - to mailbox in an untagged MYRIGHTS reply. - - Example: C: A003 MYRIGHTS INBOX - S: * MYRIGHTS INBOX rwipslda - S: A003 OK Myrights complete - - */ - - if (!IsAuthenticated) - { - WriteLine(string.Format("{0} NO Authenticate first !", cmdTag)); - return; - } - - string[] args = TextUtils.SplitQuotedString(argsText, ' ', true); - if (args.Length != 1) - { - WriteLine(string.Format("{0} BAD GETACL invalid arguments. Syntax: MYRIGHTSFolderName", cmdTag)); - return; - } - - IMAP_GetUserACL_eArgs eArgs = ImapServer.OnGetUserACL(this, - IMAP_Utils.NormalizeFolder(args[0]), - UserName); - if (eArgs.ErrorText.Length > 0) - { - WriteLine(string.Format("{0} NO MYRIGHTS {1}", cmdTag, eArgs.ErrorText)); - } - else - { - string aclFlags = IMAP_Utils.ACL_to_String(eArgs.ACL); - if (aclFlags.Length == 0) - { - aclFlags = "\"\""; - } - string response = string.Format("* MYRIGHTS \"{0}\" {1}\r\n", args[0], aclFlags); - response += string.Format("{0} OK MYRIGHTS Completed\r\n", cmdTag); - - TcpStream.Write(response); - } - } - - private void GETQUOTA(string cmdTag, string argsText) - { - /* RFC 2087 4.2. GETQUOTA - Arguments: quota root - - Data: untagged responses: QUOTA - - Result: OK - getquota completed - NO - getquota error: no such quota root, permission denied - BAD - command unknown or arguments invalid - - The GETQUOTA command takes the name of a quota root and returns the - quota root's resource usage and limits in an untagged QUOTA response. - - Example: C: A003 GETQUOTA "" - S: * QUOTA "" (STORAGE 10 512) - S: A003 OK Getquota completed - - */ - - if (!IsAuthenticated) - { - WriteLine(string.Format("{0} NO Authenticate first !", cmdTag)); - return; - } - - string[] args = TextUtils.SplitQuotedString(argsText, ' ', true); - if (args.Length != 1) - { - WriteLine(string.Format("{0} BAD GETQUOTA invalid arguments. Syntax: GETQUOTA \"quota_root\"", cmdTag)); - return; - } - - IMAP_eArgs_GetQuota eArgs = ImapServer.OnGetUserQuota(this); - string reply = string.Format("* QUOTA \"\" (STORAGE {0} {1})\r\n", eArgs.MailboxSize, eArgs.MaxMailboxSize); - reply += string.Format("{0} OK GETQUOTA completed\r\n", cmdTag); - TcpStream.Write(reply); - } - - private void GETQUOTAROOT(string cmdTag, string argsText) - { - /* RFC 2087 4.3. GETQUOTAROOT - Arguments: mailbox name - - Data: untagged responses: QUOTAROOT, QUOTA - - Result: OK - getquota completed - NO - getquota error: no such mailbox, permission denied - BAD - command unknown or arguments invalid - - The GETQUOTAROOT command takes the name of a mailbox and returns the - list of quota roots for the mailbox in an untagged QUOTAROOT - response. For each listed quota root, it also returns the quota - root's resource usage and limits in an untagged QUOTA response. - - Example: C: A003 GETQUOTAROOT INBOX - S: * QUOTAROOT INBOX "" - S: * QUOTA "" (STORAGE 10 512) - S: A003 OK Getquota completed - - */ - - if (!IsAuthenticated) - { - WriteLine(string.Format("{0} NO Authenticate first !", cmdTag)); - return; - } - - string[] args = TextUtils.SplitQuotedString(argsText, ' ', true); - if (args.Length != 1) - { - WriteLine(string.Format("{0} BAD GETQUOTAROOT invalid arguments. Syntax: GETQUOTAROOT \"folder\"", cmdTag)); - return; - } - - IMAP_eArgs_GetQuota eArgs = ImapServer.OnGetUserQuota(this); - string reply = string.Format("* QUOTAROOT \"{0}\" \"\"\r\n", args[0]); - reply += string.Format("* QUOTA \"\" (STORAGE {0} {1})\r\n", eArgs.MailboxSize, eArgs.MaxMailboxSize); - reply += string.Format("{0} OK GETQUOTAROOT completed\r\n", cmdTag); - TcpStream.Write(reply); - } - - //--- End of IsAuthenticated State - - //--- Selected State ------ - - private void Check(string cmdTag) - { - /* RFC 3501 6.4.1 CHECK Command - - Arguments: none - - Responses: no specific responses for this command - - Result: OK - check completed - BAD - command unknown or arguments invalid - - The CHECK command requests a checkpoint of the currently selected - mailbox. A checkpoint refers to any implementation-dependent - housekeeping associated with the mailbox (e.g. resolving the - server's in-memory state of the mailbox with the state on its - disk) that is not normally executed as part of each command. A - checkpoint MAY take a non-instantaneous amount of real time to - complete. If a server implementation has no such housekeeping - considerations, CHECK is equivalent to NOOP. - - There is no guarantee that an EXISTS untagged response will happen - as a result of CHECK. NOOP, not CHECK, SHOULD be used for new - mail polling. - - Example: C: FXXZ CHECK - S: FXXZ OK CHECK Completed - - */ - if (!IsAuthenticated) - { - WriteLine(string.Format("{0} NO Authenticate first !", cmdTag)); - return; - } - if (SelectedMailbox.Length == 0) - { - WriteLine(string.Format("{0} NO Select mailbox first !", cmdTag)); - return; - } - - WriteLine(string.Format("{0} OK CHECK completed", cmdTag)); - } - - private void Close(string cmdTag) - { - /* RFC 3501 6.4.2 CLOSE Command - - Arguments: none - - Responses: no specific responses for this command - - Result: OK - close completed, now in authenticated state - BAD - command unknown or arguments invalid - - The CLOSE command permanently removes from the currently selected - mailbox all messages that have the \Deleted flag set, and returns - to authenticated state from selected state. No untagged EXPUNGE - responses are sent. - - No messages are removed, and no error is given, if the mailbox is - selected by an EXAMINE command or is otherwise selected read-only. - - Even if a mailbox is selected, a SELECT, EXAMINE, or LOGOUT - command MAY be issued without previously issuing a CLOSE command. - The SELECT, EXAMINE, and LOGOUT commands implicitly close the - currently selected mailbox without doing an expunge. However, - when many messages are deleted, a CLOSE-LOGOUT or CLOSE-SELECT - sequence is considerably faster than an EXPUNGE-LOGOUT or - EXPUNGE-SELECT because no untagged EXPUNGE responses (which the - client would probably ignore) are sent. - - Example: C: A341 CLOSE - S: A341 OK CLOSE completed - - */ - if (!IsAuthenticated) - { - WriteLine(string.Format("{0} NO Authenticate first !", cmdTag)); - return; - } - if (SelectedMailbox.Length == 0) - { - WriteLine(string.Format("{0} NO Select mailbox first !", cmdTag)); - return; - } - - if (!m_pSelectedFolder.ReadOnly) - { - IMAP_Message[] messages = m_pSelectedFolder.Messages.GetWithFlags(IMAP_MessageFlags.Deleted); - foreach (IMAP_Message msg in messages) - { - ImapServer.OnDeleteMessage(this, msg); - } - } - - SelectedMailbox = ""; - m_pSelectedFolder = null; - - WriteLine(string.Format("{0} OK CLOSE completed", cmdTag)); - //EndSession(); - } - - private void Expunge(string cmdTag) - { - /* RFC 3501 6.4.3 EXPUNGE Command - - Arguments: none - - Responses: untagged responses: EXPUNGE - - Result: OK - expunge completed - NO - expunge failure: can't expunge (e.g., permission - denied) - BAD - command unknown or arguments invalid - - The EXPUNGE command permanently removes all messages that have the - \Deleted flag set from the currently selected mailbox. Before - returning an OK to the client, an untagged EXPUNGE response is - sent for each message that is removed. - - The EXPUNGE response reports that the specified message sequence - number has been permanently removed from the mailbox. The message - sequence number for each successive message in the mailbox is - IMMEDIATELY DECREMENTED BY 1, and this decrement is reflected in - message sequence numbers in subsequent responses (including other - untagged EXPUNGE responses). - - - Example: C: A202 EXPUNGE - S: * 3 EXPUNGE - S: * 3 EXPUNGE - S: * 5 EXPUNGE - S: * 8 EXPUNGE - S: A202 OK EXPUNGE completed - - Note: In this example, messages 3, 4, 7, and 11 had the - \Deleted flag set. See the description of the EXPUNGE - response for further explanation. - - */ - if (!IsAuthenticated) - { - WriteLine(string.Format("{0} NO Authenticate first !", cmdTag)); - return; - } - if (SelectedMailbox.Length == 0) - { - WriteLine(string.Format("{0} NO Select mailbox first !", cmdTag)); - return; - } - - IMAP_Message[] messages = m_pSelectedFolder.Messages.GetWithFlags(IMAP_MessageFlags.Deleted); - for (int i = 0; i < messages.Length; i++) - { - IMAP_Message msg = messages[i]; - - string errorText = ImapServer.OnDeleteMessage(this, msg); - if (errorText == null) - { - WriteLine(string.Format("* {0} EXPUNGE", msg.SequenceNo)); - m_pSelectedFolder.Messages.Remove(msg); - } - else - { - WriteLine(string.Format("{0} NO {1}", cmdTag, errorText)); - return; - } - } - - WriteLine(string.Format("{0} OK EXPUNGE completed", cmdTag)); - } - - private void Search(string cmdTag, string argsText, bool uidSearch) - { - /* RFC 3501 6.4.4 SEARCH Command - - Arguments: OPTIONAL [CHARSET] specification - searching criteria (one or more) - - Responses: REQUIRED untagged response: SEARCH - - Result: OK - search completed - NO - search error: can't search that [CHARSET] or - criteria - BAD - command unknown or arguments invalid - - The SEARCH command searches the mailbox for messages that match - the given searching criteria. Searching criteria consist of one - or more search keys. The untagged SEARCH response from the server - contains a listing of message sequence numbers corresponding to - those messages that match the searching criteria. - - When multiple keys are specified, the result is the intersection - (AND function) of all the messages that match those keys. For - example, the criteria DELETED FROM "SMITH" SINCE 1-Feb-1994 refers - to all deleted messages from Smith that were placed in the mailbox - since February 1, 1994. A search key can also be a parenthesized - list of one or more search keys (e.g., for use with the OR and NOT - keys). - - Server implementations MAY exclude [MIME-IMB] body parts with - terminal content media types other than TEXT and MESSAGE from - consideration in SEARCH matching. - - The OPTIONAL [CHARSET] specification consists of the word - "CHARSET" followed by a registered [CHARSET]. It indicates the - [CHARSET] of the strings that appear in the search criteria. - [MIME-IMB] content transfer encodings, and [MIME-HDRS] strings in - [RFC-2822]/[MIME-IMB] headers, MUST be decoded before comparing - text in a [CHARSET] other than US-ASCII. US-ASCII MUST be - supported; other [CHARSET]s MAY be supported. - - If the server does not support the specified [CHARSET], it MUST - return a tagged NO response (not a BAD). This response SHOULD - contain the BADCHARSET response code, which MAY list the - [CHARSET]s supported by the server. - - In all search keys that use strings, a message matches the key if - the string is a substring of the field. The matching is - case-insensitive. - - The defined search keys are as follows. Refer to the Formal - Syntax section for the precise syntactic definitions of the - arguments. - - - Messages with message sequence numbers corresponding to the - specified message sequence number set. - - ALL - All messages in the mailbox; the default initial key for - ANDing. - - ANSWERED - Messages with the \Answered flag set. - - BCC - Messages that contain the specified string in the envelope - structure's BCC field. - - BEFORE - Messages whose internal date (disregarding time and timezone) - is earlier than the specified date. - - BODY - Messages that contain the specified string in the body of the - message. - - CC - Messages that contain the specified string in the envelope - structure's CC field. - - DELETED - Messages with the \Deleted flag set. - - DRAFT - Messages with the \Draft flag set. - - FLAGGED - Messages with the \Flagged flag set. - - FROM - Messages that contain the specified string in the envelope - structure's FROM field. - - HEADER - Messages that have a header with the specified field-name (as - defined in [RFC-2822]) and that contains the specified string - in the text of the header (what comes after the colon). If the - string to search is zero-length, this matches all messages that - have a header line with the specified field-name regardless of - the contents. - - KEYWORD - Messages with the specified keyword flag set. - - LARGER - Messages with an [RFC-2822] size larger than the specified - number of octets. - - NEW - Messages that have the \Recent flag set but not the \Seen flag. - This is functionally equivalent to "(RECENT UNSEEN)". - - NOT - Messages that do not match the specified search key. - - OLD - Messages that do not have the \Recent flag set. This is - functionally equivalent to "NOT RECENT" (as opposed to "NOT - NEW"). - - ON - Messages whose internal date (disregarding time and timezone) - is within the specified date. - - OR - Messages that match either search key. - - RECENT - Messages that have the \Recent flag set. - - SEEN - Messages that have the \Seen flag set. - - SENTBEFORE - Messages whose [RFC-2822] Date: header (disregarding time and - timezone) is earlier than the specified date. - - SENTON - Messages whose [RFC-2822] Date: header (disregarding time and - timezone) is within the specified date. - - SENTSINCE - Messages whose [RFC-2822] Date: header (disregarding time and - timezone) is within or later than the specified date. - - SINCE - Messages whose internal date (disregarding time and timezone) - is within or later than the specified date. - - SMALLER - Messages with an [RFC-2822] size smaller than the specified - number of octets. - - SUBJECT - Messages that contain the specified string in the envelope - structure's SUBJECT field. - - TEXT - Messages that contain the specified string in the header or - body of the message. - - TO - Messages that contain the specified string in the envelope - structure's TO field. - - UID - Messages with unique identifiers corresponding to the specified - unique identifier set. Sequence set ranges are permitted. - - UNANSWERED - Messages that do not have the \Answered flag set. - - UNDELETED - Messages that do not have the \Deleted flag set. - - UNDRAFT - Messages that do not have the \Draft flag set. - - UNFLAGGED - Messages that do not have the \Flagged flag set. - - UNKEYWORD - Messages that do not have the specified keyword flag set. - - UNSEEN - Messages that do not have the \Seen flag set. - - Example: - C: A282 SEARCH FLAGGED SINCE 1-Feb-1994 NOT FROM "Smith" - S: * SEARCH 2 84 882 - S: A282 OK SEARCH completed - C: A283 SEARCH TEXT "string not in mailbox" - S: * SEARCH - S: A283 OK SEARCH completed - C: A284 SEARCH CHARSET UTF-8 TEXT {6} - S: + Continue ### THIS IS UNDOCUMENTED !!! - C: XXXXXX[arg conitnue] - S: * SEARCH 43 - S: A284 OK SEARCH completed - - Note: Since this document is restricted to 7-bit ASCII - text, it is not possible to show actual UTF-8 data. The - "XXXXXX" is a placeholder for what would be 6 octets of - 8-bit data in an actual transaction. - */ - if (!IsAuthenticated) - { - WriteLine(string.Format("{0} NO Authenticate first !", cmdTag)); - return; - } - if (SelectedMailbox.Length == 0) - { - WriteLine(string.Format("{0} NO Select mailbox first !", cmdTag)); - return; - } - - // Store start time - long startTime = DateTime.Now.Ticks; - - //--- Get Optional charset, if specified -----------------------------------------------------------------// - string charset = "ASCII"; - // CHARSET charset - if (argsText.ToUpper().StartsWith("CHARSET")) - { - // Remove CHARSET from argsText - argsText = argsText.Substring(7).Trim(); - - string charsetValueString = IMAP_Utils.ParseQuotedParam(ref argsText); - - try - { - EncodingTools.GetEncodingByCodepageName_Throws(charsetValueString); - - charset = charsetValueString; - } - catch - { - WriteLine(string.Format("{0} NO [BADCHARSET UTF-8] {1} is not supported", cmdTag, charsetValueString)); - return; - } - } - //---------------------------------------------------------------------------------------------------------// - - /* If multiline command, read all lines - C: A284 SEARCH CHARSET UTF-8 TEXT {6} - S: + Continue ### THIS IS UNDOCUMENTED !!! - C: XXXXXX[arg conitnue] - */ - argsText = argsText.Trim(); - while (argsText.EndsWith("}") && argsText.IndexOf("{") > -1) - { - long dataLength = 0; - try - { - // Get data length from {xxx} - dataLength = - Convert.ToInt64(argsText.Substring(argsText.LastIndexOf("{") + 1, - argsText.Length - argsText.LastIndexOf("{") - 2)); - } - // There is no valid numeric value between {}, just skip and allow SearchGroup parser to handle this value - catch - { - break; - } - - MemoryStream dataStream = new MemoryStream(); - - WriteLine("+ Continue"); - ReadSpecifiedLength((int)dataLength, dataStream); - string argsContinueLine = ReadLine(); - - // Append readed data + [args conitnue] line - argsText += EncodingTools.GetEncodingByCodepageName_Throws(charset).GetString(dataStream.ToArray()) + argsContinueLine; - - // There is no more argumets, stop getting. - // We must check this because if length = 0 and no args returned, last line ends with {0}, - // leaves this into endless loop. - if (argsContinueLine == "") - { - break; - } - } - - //--- Parse search criteria ------------------------// - SearchGroup searchCriteria = new SearchGroup(); - try - { - searchCriteria.Parse(new StringReader(argsText)); - } - catch (Exception x) - { - WriteLine(cmdTag + " NO " + x.Message); - return; - } - //--------------------------------------------------// - /* - string searchResponse = "* SEARCH"; - - // No header and body text needed, can do search on internal messages info data - if(!searchCriteria.IsHeaderNeeded() && !searchCriteria.IsBodyTextNeeded()){ - // Loop internal messages info, see what messages match - for(int i=0;i -1){ - searchResponse += " " + no.ToString(); - } - } - } - } - - searchResponse += "\r\n"; - searchResponse += cmdTag + " OK SEARCH completed in " + ((DateTime.Now.Ticks - startTime) / (decimal)10000000).ToString("f2") + " seconds\r\n"; -*/ - ProcessMailboxChanges(); - - // Just loop messages headers or full messages (depends on search type) - // string searchResponse = "* SEARCH"; - TcpStream.Write("* SEARCH"); - string searchResponse = ""; - - IMAP_MessageItems_enum messageItems = IMAP_MessageItems_enum.None; - if (searchCriteria.IsBodyTextNeeded()) - { - messageItems |= IMAP_MessageItems_enum.Message; - } - else if (searchCriteria.IsHeaderNeeded()) - { - messageItems |= IMAP_MessageItems_enum.Header; - } - for (int i = 0; i < m_pSelectedFolder.Messages.Count; i++) - { - IMAP_Message msg = m_pSelectedFolder.Messages[i]; - - //-- Get message only if matching needs it ------------------------// - Mime parser = null; - if ((messageItems & IMAP_MessageItems_enum.Message) != 0 || - (messageItems & IMAP_MessageItems_enum.Header) != 0) - { - // Raise event GetMessageItems, get requested message items. - IMAP_eArgs_MessageItems eArgs = ImapServer.OnGetMessageItems(this, msg, messageItems); - - // Message data is null if no such message available, just skip that message - if (!eArgs.MessageExists) - { - continue; - } - try - { - // Ensure that all requested items were provided. - eArgs.Validate(); - } - catch (Exception x) - { - ImapServer.OnSysError(x.Message, x); - WriteLine(cmdTag + " NO Internal IMAP server component error: " + x.Message); - return; - } - - try - { - if (eArgs.MessageStream != null) - { - parser = Mime.Parse(eArgs.MessageStream); - } - else - { - parser = Mime.Parse(eArgs.Header); - } - } - // Message parsing failed, bad message. Just make new warning message. - catch (Exception x) - { - parser = Mime.CreateSimple(new AddressList(), - new AddressList(), - "[BAD MESSAGE] Bad message, message parsing failed !", - "NOTE: Bad message, message parsing failed !\r\n\r\n" + - x.Message, - ""); - } - } - //-----------------------------------------------------------------// - - string bodyText = ""; - if (searchCriteria.IsBodyTextNeeded()) - { - bodyText = parser.BodyText; - } - - // See if message matches to search criteria - if (searchCriteria.Match(i, - msg.UID, - (int)msg.Size, - msg.InternalDate, - msg.Flags, - parser, - bodyText)) - { - if (uidSearch) - { - TcpStream.Write(" " + msg.UID); - - // searchResponse += " " + msg.MessageUID.ToString(); - } - else - { - TcpStream.Write(" " + (i + 1)); - - // searchResponse += " " + i.ToString(); - } - } - } - - searchResponse += "\r\n"; - searchResponse += string.Format("{0} OK SEARCH completed in {1} seconds\r\n", cmdTag, ((DateTime.Now.Ticks - startTime) / (decimal)10000000).ToString("f2")); - - // Send search server response - TcpStream.Write(searchResponse); - } - - private void ReadSpecifiedLength(int dataLength, MemoryStream dataStream) - { - TcpStream.ReadFixedCount(dataStream, dataLength); - } - - private string ReadLine() - { - SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength], - SizeExceededAction. - JunkAndThrowException); - if (TcpStream.ReadLine(args, false)) - { - return args.LineUtf8; - } - return string.Empty; - } - - - private void Fetch(string cmdTag, string argsText, bool uidFetch) - { - /* Rfc 3501 6.4.5 FETCH Command - - Arguments: message set - message data item names - - Responses: untagged responses: FETCH - - Result: OK - fetch completed - NO - fetch error: can't fetch that data - BAD - command unknown or arguments invalid - - The FETCH command retrieves data associated with a message in the - mailbox. The data items to be fetched can be either a single atom - or a parenthesized list. - - Most data items, identified in the formal syntax under the - msg-att-static rule, are static and MUST NOT change for any - particular message. Other data items, identified in the formal - syntax under the msg-att-dynamic rule, MAY change, either as a - result of a STORE command or due to external events. - - For example, if a client receives an ENVELOPE for a - message when it already knows the envelope, it can - safely ignore the newly transmitted envelope. - - There are three macros which specify commonly-used sets of data - items, and can be used instead of data items. A macro must be - used by itself, and not in conjunction with other macros or data - items. - - ALL - Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE) - - FAST - Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE) - - FULL - Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE - BODY) - - The currently defined data items that can be fetched are: - - BODY - Non-extensible form of BODYSTRUCTURE. - - BODY[
    ]<> - The text of a particular body section. The section - specification is a set of zero or more part specifiers - delimited by periods. A part specifier is either a part number - or one of the following: HEADER, HEADER.FIELDS, - HEADER.FIELDS.NOT, MIME, and TEXT. An empty section - specification refers to the entire message, including the - header. - - Every message has at least one part number. Non-[MIME-IMB] - messages, and non-multipart [MIME-IMB] messages with no - encapsulated message, only have a part 1. - - Multipart messages are assigned consecutive part numbers, as - they occur in the message. If a particular part is of type - message or multipart, its parts MUST be indicated by a period - followed by the part number within that nested multipart part. - - A part of type MESSAGE/RFC822 also has nested part numbers, - referring to parts of the MESSAGE part's body. - - The HEADER, HEADER.FIELDS, HEADER.FIELDS.NOT, and TEXT part - specifiers can be the sole part specifier or can be prefixed by - one or more numeric part specifiers, provided that the numeric - part specifier refers to a part of type MESSAGE/RFC822. The - MIME part specifier MUST be prefixed by one or more numeric - part specifiers. - - The HEADER, HEADER.FIELDS, and HEADER.FIELDS.NOT part - specifiers refer to the [RFC-2822] header of the message or of - an encapsulated [MIME-IMT] MESSAGE/RFC822 message. - HEADER.FIELDS and HEADER.FIELDS.NOT are followed by a list of - field-name (as defined in [RFC-2822]) names, and return a - subset of the header. The subset returned by HEADER.FIELDS - contains only those header fields with a field-name that - matches one of the names in the list; similarly, the subset - returned by HEADER.FIELDS.NOT contains only the header fields - with a non-matching field-name. The field-matching is - case-insensitive but otherwise exact. Subsetting does not - exclude the [RFC-2822] delimiting blank line between the header - and the body; the blank line is included in all header fetches, - except in the case of a message which has no body and no blank - line. - - The MIME part specifier refers to the [MIME-IMB] header for - this part. - - The TEXT part specifier refers to the text body of the message, - omitting the [RFC-2822] header. - - Here is an example of a complex message with some of its - part specifiers: - - HEADER ([RFC-2822] header of the message) - TEXT ([RFC-2822] text body of the message) MULTIPART/MIXED - 1 TEXT/PLAIN - 2 APPLICATION/OCTET-STREAM - 3 MESSAGE/RFC822 - 3.HEADER ([RFC-2822] header of the message) - 3.TEXT ([RFC-2822] text body of the message) MULTIPART/MIXED - 3.1 TEXT/PLAIN - 3.2 APPLICATION/OCTET-STREAM - 4 MULTIPART/MIXED - 4.1 IMAGE/GIF - 4.1.MIME ([MIME-IMB] header for the IMAGE/GIF) - 4.2 MESSAGE/RFC822 - 4.2.HEADER ([RFC-2822] header of the message) - 4.2.TEXT ([RFC-2822] text body of the message) MULTIPART/MIXED - 4.2.1 TEXT/PLAIN - 4.2.2 MULTIPART/ALTERNATIVE - 4.2.2.1 TEXT/PLAIN - 4.2.2.2 TEXT/RICHTEXT - - - It is possible to fetch a substring of the designated text. - This is done by appending an open angle bracket ("<"), the - octet position of the first desired octet, a period, the - maximum number of octets desired, and a close angle bracket - (">") to the part specifier. If the starting octet is beyond - the end of the text, an empty string is returned. - - Any partial fetch that attempts to read beyond the end of the - text is truncated as appropriate. A partial fetch that starts - at octet 0 is returned as a partial fetch, even if this - truncation happened. - - Note: This means that BODY[]<0.2048> of a 1500-octet message - will return BODY[]<0> with a literal of size 1500, not - BODY[]. - - Note: A substring fetch of a HEADER.FIELDS or - HEADER.FIELDS.NOT part specifier is calculated after - subsetting the header. - - The \Seen flag is implicitly set; if this causes the flags to - change, they SHOULD be included as part of the FETCH responses. - - BODY.PEEK[
    ]<> - An alternate form of BODY[
    ] that does not implicitly - set the \Seen flag. - - BODYSTRUCTURE - The [MIME-IMB] body structure of the message. This is computed - by the server by parsing the [MIME-IMB] header fields in the - [RFC-2822] header and [MIME-IMB] headers. - - ENVELOPE - The envelope structure of the message. This is computed by the - server by parsing the [RFC-2822] header into the component - parts, defaulting various fields as necessary. - - FLAGS - The flags that are set for this message. - - INTERNALDATE - The internal date of the message. - - RFC822 - Functionally equivalent to BODY[], differing in the syntax of - the resulting untagged FETCH data (RFC822 is returned). - - RFC822.HEADER - Functionally equivalent to BODY.PEEK[HEADER], differing in the - syntax of the resulting untagged FETCH data (RFC822.HEADER is - returned). - - RFC822.SIZE - The [RFC-2822] size of the message. - - RFC822.TEXT - Functionally equivalent to BODY[TEXT], differing in the syntax - of the resulting untagged FETCH data (RFC822.TEXT is returned). - - UID - The unique identifier for the message. - - - Example: C: A654 FETCH 2:4 (FLAGS BODY[HEADER.FIELDS (DATE FROM)]) - S: * 2 FETCH .... - S: * 3 FETCH .... - S: * 4 FETCH .... - S: A654 OK FETCH completed - - */ - if (!this.IsAuthenticated) - { - TcpStream.WriteLine(string.Format("{0} NO Authenticate first !", cmdTag)); - return; - } - if (SelectedMailbox.Length == 0) - { - this.TcpStream.WriteLine(string.Format("{0} NO Select mailbox first !", cmdTag)); - return; - } - - TcpStream.MemoryBuffer = true; - - // Store start time - long startTime = DateTime.Now.Ticks; - - IMAP_MessageItems_enum messageItems = IMAP_MessageItems_enum.None; - - #region Parse parameters - - string[] args = ParseParams(argsText); - if (args.Length != 2) - { - this.TcpStream.WriteLine(string.Format("{0} BAD Invalid arguments", cmdTag)); - return; - } - - IMAP_SequenceSet sequenceSet = new IMAP_SequenceSet(); - // Just try if it can be parsed as sequence-set - try - { - if (uidFetch) - { - if (m_pSelectedFolder.Messages.Count > 0) - { - sequenceSet.Parse(args[0], m_pSelectedFolder.Messages[m_pSelectedFolder.Messages.Count - 1].UID); - } - } - else - { - sequenceSet.Parse(args[0], m_pSelectedFolder.Messages.Count); - } - } - // This isn't valid sequnce-set value - catch - { - this.TcpStream.WriteLine(string.Format("{0} BAD Invalid value '{1}' Syntax: {{ FETCH ()}}!", cmdTag, args[0])); - return; - } - - // Replace macros - string fetchItems = args[1].ToUpper(); - fetchItems = fetchItems.Replace("ALL", "FLAGS INTERNALDATE RFC822.SIZE ENVELOPE"); - fetchItems = fetchItems.Replace("FAST", "FLAGS INTERNALDATE RFC822.SIZE"); - fetchItems = fetchItems.Replace("FULL", "FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY"); - - // If UID FETCH and no UID, we must implicity add it, it's required - if (uidFetch && fetchItems.ToUpper().IndexOf("UID") == -1) - { - fetchItems += " UID"; - } - - // Start parm parsing from left to end in while loop while params parsed or bad param found - ArrayList fetchFlags = new ArrayList(); - StringReader argsReader = new StringReader(fetchItems.Trim()); - while (argsReader.Available > 0) - { - argsReader.ReadToFirstChar(); - - #region BODYSTRUCTURE - - // BODYSTRUCTURE - if (argsReader.StartsWith("BODYSTRUCTURE")) - { - argsReader.ReadSpecifiedLength("BODYSTRUCTURE".Length); - - fetchFlags.Add(new object[] { "BODYSTRUCTURE" }); - messageItems |= IMAP_MessageItems_enum.BodyStructure; - } - - #endregion - - #region BODY, BODY[
    ]<>, BODY.PEEK[
    ]<> - - // BODY, BODY[
    ]<>, BODY.PEEK[
    ]<> - else if (argsReader.StartsWith("BODY")) - { - // Remove BODY - argsReader.ReadSpecifiedLength("BODY".Length); - - bool peek = false; - // BODY.PEEK - if (argsReader.StartsWith(".PEEK")) - { - // Remove .PEEK - argsReader.ReadSpecifiedLength(".PEEK".Length); - - peek = true; - } - - // [
    ]<> - if (argsReader.StartsWith("[")) - { - // Read value between [] - string section = ""; - try - { - section = argsReader.ReadParenthesized(); - } - catch - { - this.TcpStream.WriteLine(cmdTag + " BAD Invalid BODY[], closing ] parenthesize is missing !"); - return; - } - - string originalSectionValue = section; - string mimePartsSpecifier = ""; - string sectionType = ""; - string sectionArgs = ""; - /* Validate
    - Section can be: - "" - entire message - [MimePartsSepcifier.]HEADER - message header - [MimePartsSepcifier.]HEADER.FIELDS (headerFields) - message header fields - [MimePartsSepcifier.]HEADER.FIELDS.NOT (headerFields) - message header fields except requested - [MimePartsSepcifier.]TEXT - message text - [MimePartsSepcifier.]MIME - same as header, different response - */ - if (section.Length > 0) - { - string[] section_args = section.Split(new char[] { ' ' }, 2); - section = section_args[0]; - if (section_args.Length == 2) - { - sectionArgs = section_args[1]; - } - - if (section.EndsWith("HEADER")) - { - // Remove HEADER from end - section = section.Substring(0, section.Length - "HEADER".Length); - - sectionType = "HEADER"; - messageItems |= IMAP_MessageItems_enum.Header; - } - else if (section.EndsWith("HEADER.FIELDS")) - { - // Remove HEADER.FIELDS from end - section = section.Substring(0, section.Length - "HEADER.FIELDS".Length); - - sectionType = "HEADER.FIELDS"; - messageItems |= IMAP_MessageItems_enum.Header; - } - else if (section.EndsWith("HEADER.FIELDS.NOT")) - { - // Remove HEADER.FIELDS.NOT from end - section = section.Substring(0, section.Length - "HEADER.FIELDS.NOT".Length); - - sectionType = "HEADER.FIELDS.NOT"; - messageItems |= IMAP_MessageItems_enum.Header; - } - else if (section.EndsWith("TEXT")) - { - // Remove TEXT from end - section = section.Substring(0, section.Length - "TEXT".Length); - - sectionType = "TEXT"; - messageItems |= IMAP_MessageItems_enum.Message; - } - else if (section.EndsWith("MIME")) - { - // Remove MIME from end - section = section.Substring(0, section.Length - "MIME".Length); - - sectionType = "MIME"; - messageItems = IMAP_MessageItems_enum.Header; - } - - // Remove last ., if there is any - if (section.EndsWith(".")) - { - section = section.Substring(0, section.Length - 1); - } - - // MimePartsSepcifier is specified, validate it. It can contain numbers only. - if (section.Length > 0) - { - // Now we certainly need full message, because nested mime parts wanted - messageItems |= IMAP_MessageItems_enum.Message; - - string[] sectionParts = section.Split('.'); - foreach (string sectionPart in sectionParts) - { - if (!Core.IsNumber(sectionPart)) - { - this.TcpStream.WriteLine(string.Format("{0} BAD Invalid BODY[
    ] argument. Invalid
    : {1}", cmdTag, section)); - return; - } - } - - mimePartsSpecifier = section; - } - } - else - { - messageItems |= IMAP_MessageItems_enum.Message; - } - - long startPosition = -1; - long length = -1; - // See if partial fetch - if (argsReader.StartsWith("<")) - { - /* syntax: - startPosition[.endPosition] - */ - - // Read partial value between <> - string partial = ""; - try - { - partial = argsReader.ReadParenthesized(); - } - catch - { - this.TcpStream.WriteLine(string.Format("{0} BAD Invalid BODY[], closing > parenthesize is missing !", cmdTag)); - return; - } - - string[] start_length = partial.Split('.'); - - // Validate - if (start_length.Length == 0 || start_length.Length > 2 || !Core.IsNumber(start_length[0]) || (start_length.Length == 2 && !Core.IsNumber(start_length[1]))) - { - this.TcpStream.WriteLine(string.Format("{0} BAD Invalid BODY[] argument. Invalid : {1}", cmdTag, partial)); - return; - } - - startPosition = Convert.ToInt64(start_length[0]); - if (start_length.Length == 2) - { - length = Convert.ToInt64(start_length[1]); - } - } - - // object[] structure for BODY[] - // fetchFlagName - // isPeek - // mimePartsSpecifier - // originalSectionValue - // sectionType - // sectionArgs - // startPosition - // length - fetchFlags.Add(new object[] { "BODY[]", peek, mimePartsSpecifier, originalSectionValue, sectionType, sectionArgs, startPosition, length }); - } - // BODY - else - { - fetchFlags.Add(new object[] { "BODY" }); - messageItems |= IMAP_MessageItems_enum.BodyStructure; - } - } - - #endregion - - #region ENVELOPE - - // ENVELOPE - else if (argsReader.StartsWith("ENVELOPE")) - { - argsReader.ReadSpecifiedLength("ENVELOPE".Length); - - fetchFlags.Add(new object[] { "ENVELOPE" }); - messageItems |= IMAP_MessageItems_enum.Envelope; - } - - #endregion - - #region FLAGS - - // FLAGS - // The flags that are set for this message. - else if (argsReader.StartsWith("FLAGS")) - { - argsReader.ReadSpecifiedLength("FLAGS".Length); - - fetchFlags.Add(new object[] { "FLAGS" }); - } - - #endregion - - #region INTERNALDATE - - // INTERNALDATE - else if (argsReader.StartsWith("INTERNALDATE")) - { - argsReader.ReadSpecifiedLength("INTERNALDATE".Length); - - fetchFlags.Add(new object[] { "INTERNALDATE" }); - } - - #endregion - - #region RFC822.HEADER - - // RFC822.HEADER - else if (argsReader.StartsWith("RFC822.HEADER")) - { - argsReader.ReadSpecifiedLength("RFC822.HEADER".Length); - - fetchFlags.Add(new object[] { "RFC822.HEADER" }); - messageItems |= IMAP_MessageItems_enum.Header; - } - - #endregion - - #region RFC822.SIZE - - // RFC822.SIZE - // The [RFC-2822] size of the message. - else if (argsReader.StartsWith("RFC822.SIZE")) - { - argsReader.ReadSpecifiedLength("RFC822.SIZE".Length); - - fetchFlags.Add(new object[] { "RFC822.SIZE" }); - } - - #endregion - - #region RFC822.TEXT - - // RFC822.TEXT - else if (argsReader.StartsWith("RFC822.TEXT")) - { - argsReader.ReadSpecifiedLength("RFC822.TEXT".Length); - - fetchFlags.Add(new object[] { "RFC822.TEXT" }); - messageItems |= IMAP_MessageItems_enum.Message; - } - - #endregion - - #region RFC822 - - // RFC822 NOTE: RFC822 must be below RFC822.xxx or is parsed wrong ! - else if (argsReader.StartsWith("RFC822")) - { - argsReader.ReadSpecifiedLength("RFC822".Length); - - fetchFlags.Add(new object[] { "RFC822" }); - messageItems |= IMAP_MessageItems_enum.Message; - } - - #endregion - - #region UID - - // UID - // The unique identifier for the message. - else if (argsReader.StartsWith("UID")) - { - argsReader.ReadSpecifiedLength("UID".Length); - - fetchFlags.Add(new object[] { "UID" }); - } - - #endregion - - // This must be unknown fetch flag - else - { - this.TcpStream.WriteLine(string.Format("{0} BAD Invalid fetch-items argument. Unkown part starts from: {1}", cmdTag, argsReader.SourceString)); - return; - } - } - - #endregion - - // ToDo: ??? But non of the servers do it ? - // The server should respond with a tagged BAD response to a command that uses a message - // sequence number greater than the number of messages in the selected mailbox. This - // includes "*" if the selected mailbox is empty. - // if(m_Messages.Count == 0 || ){ - // SendData(cmdTag + " BAD Sequence number greater than the number of messages in the selected mailbox !\r\n"); - // return; - // } - - // Create buffered writer, so we make less network calls. - - for (int i = 0; i < m_pSelectedFolder.Messages.Count; i++) - { - IMAP_Message msg = m_pSelectedFolder.Messages[i]; - - // For UID FETCH we must compare UIDs and for normal FETCH message numbers. - bool sequenceSetContains = false; - if (uidFetch) - { - sequenceSetContains = sequenceSet.Contains(msg.UID); - } - else - { - sequenceSetContains = sequenceSet.Contains(i + 1); - } - - if (sequenceSetContains) - { - IMAP_eArgs_MessageItems eArgs = null; - // Get message items only if they are needed. - if (messageItems != IMAP_MessageItems_enum.None) - { - // Raise event GetMessageItems to get all neccesary message itmes - eArgs = ImapServer.OnGetMessageItems(this, msg, messageItems); - - // Message doesn't exist any more, notify email client. - if (!eArgs.MessageExists) - { - TcpStream.Write("* " + msg.SequenceNo + " EXPUNGE"); - ImapServer.OnDeleteMessage(this, msg); - m_pSelectedFolder.Messages.Remove(msg); - i--; - continue; - } - try - { - // Ensure that all requested items were provided. - eArgs.Validate(); - } - catch (Exception x) - { - ImapServer.OnSysError(x.Message, x); - this.TcpStream.WriteLine(string.Format("{0} NO Internal IMAP server component error: {1}", cmdTag, x.Message)); - return; - } - } - - // Write fetch start data "* msgNo FETCH (" - TcpStream.Write("* " + (i + 1) + " FETCH ("); - - IMAP_MessageFlags msgFlagsOr = msg.Flags; - // Construct reply here, based on requested fetch items - int nCount = 0; - foreach (object[] fetchFlag in fetchFlags) - { - string fetchFlagName = (string)fetchFlag[0]; - - #region BODY - - // BODY - if (fetchFlagName == "BODY") - { - // Sets \seen flag - msg.SetFlags(msg.Flags | IMAP_MessageFlags.Seen); - - // BODY () - TcpStream.Write("BODY " + eArgs.BodyStructure); - } - - #endregion - - #region BODY[], BODY.PEEK[] - - // BODY[
    ]<>, BODY.PEEK[
    ]<> - else if (fetchFlagName == "BODY[]") - { - // Force to write all buffered data. - TcpStream.Flush(); - - // object[] structure for BODY[] - // fetchFlagName - // isPeek - // mimePartsSpecifier - // originalSectionValue - // sectionType - // sectionArgs - // startPosition - // length - bool isPeek = (bool)fetchFlag[1]; - string mimePartsSpecifier = (string)fetchFlag[2]; - string originalSectionValue = (string)fetchFlag[3]; - string sectionType = (string)fetchFlag[4]; - string sectionArgs = (string)fetchFlag[5]; - long startPosition = (long)fetchFlag[6]; - long length = (long)fetchFlag[7]; - - // Difference between BODY[] and BODY.PEEK[] is that .PEEK won't set seen flag - if (!isPeek) - { - // Sets \seen flag - msg.SetFlags(msg.Flags | IMAP_MessageFlags.Seen); - } - - /* Section value: - "" - entire message - HEADER - message header - HEADER.FIELDS - message header fields - HEADER.FIELDS.NOT - message header fields except requested - TEXT - message text - MIME - same as header, different response - */ - Stream dataStream = null; - if (sectionType == "" && mimePartsSpecifier == "") - { - dataStream = eArgs.MessageStream; - } - else - { - Mime parser = null; - try - { - if (eArgs.MessageStream == null) - { - parser = Mime.Parse(eArgs.Header); - } - else - { - parser = Mime.Parse(eArgs.MessageStream); - } - } - // Invalid message, parsing failed - catch - { - parser = Mime.CreateSimple(new AddressList(), new AddressList(), "BAD Message", "This is BAD message, mail server failed to parse it !", ""); - } - MimeEntity currentEntity = parser.MainEntity; - // Specific mime entity requested, get it - if (mimePartsSpecifier != "") - { - currentEntity = FetchHelper.GetMimeEntity(parser, mimePartsSpecifier); - } - - if (currentEntity != null) - { - if (sectionType == "HEADER") - { - dataStream = new MemoryStream(FetchHelper.GetMimeEntityHeader(currentEntity)); - } - else if (sectionType == "HEADER.FIELDS") - { - dataStream = new MemoryStream(FetchHelper.ParseHeaderFields(sectionArgs, currentEntity)); - } - else if (sectionType == "HEADER.FIELDS.NOT") - { - dataStream = new MemoryStream(FetchHelper.ParseHeaderFieldsNot(sectionArgs, currentEntity)); - } - else if (sectionType == "TEXT") - { - try - { - if (currentEntity.DataEncoded != null) - { - dataStream = new MemoryStream(currentEntity.DataEncoded); - } - } - catch - { // This probably multipart entity, data isn't available - } - } - else if (sectionType == "MIME") - { - dataStream = new MemoryStream(FetchHelper.GetMimeEntityHeader(currentEntity)); - } - else if (sectionType == "") - { - try - { - dataStream = new MemoryStream(currentEntity.DataEncoded); - } - catch - { // This probably multipart entity, data isn't available - } - } - } - } - - // Partial fetch. Reports in fetch reply. - if (startPosition > -1) - { - if (dataStream == null) - { - this.TcpStream.Write("BODY[" + originalSectionValue + "]<" + startPosition.ToString() + "> \"\"\r\n"); - } - else - { - long lengthToSend = length; - if (lengthToSend == -1) - { - lengthToSend = (dataStream.Length - dataStream.Position) - startPosition; - } - if ((lengthToSend + startPosition) > (dataStream.Length - dataStream.Position)) - { - lengthToSend = (dataStream.Length - dataStream.Position) - startPosition; - } - - if (startPosition >= (dataStream.Length - dataStream.Position)) - { - this.TcpStream.Write("BODY[" + originalSectionValue + "]<" + startPosition.ToString() + "> \"\"\r\n"); - } - else - { - this.TcpStream.Write("BODY[" + originalSectionValue + "]<" + startPosition.ToString() + "> {" + lengthToSend + "}\r\n"); - dataStream.Position += startPosition; - this.TcpStream.WriteStream(dataStream, lengthToSend); - } - } - } - // Normal fetch - else - { - if (dataStream == null) - { - this.TcpStream.Write("BODY[" + originalSectionValue + "] \"\"\r\n"); - } - else - { - this.TcpStream.Write("BODY[" + originalSectionValue + "] {" + (dataStream.Length - dataStream.Position) + "}\r\n"); - this.TcpStream.WriteStream(dataStream); - } - } - } - - #endregion - - #region BODYSTRUCTURE - - // BODYSTRUCTURE - else if (fetchFlagName == "BODYSTRUCTURE") - { - TcpStream.Write("BODYSTRUCTURE " + eArgs.BodyStructure); - } - - #endregion - - #region ENVELOPE - - // ENVELOPE - else if (fetchFlagName == "ENVELOPE") - { - TcpStream.Write("ENVELOPE " + eArgs.Envelope); - } - - #endregion - - #region FLAGS - - // FLAGS - else if (fetchFlagName == "FLAGS") - { - TcpStream.Write("FLAGS (" + msg.FlagsString + ")"); - } - - #endregion - - #region INTERNALDATE - - // INTERNALDATE - else if (fetchFlagName == "INTERNALDATE") - { - // INTERNALDATE "date" - TcpStream.Write("INTERNALDATE \"" + IMAP_Utils.DateTimeToString(msg.InternalDate) + "\""); - } - - #endregion - - #region RFC822 - - // RFC822 - else if (fetchFlagName == "RFC822") - { - // Force to write all buffered data. - TcpStream.Flush(); - - // Sets \seen flag - msg.SetFlags(msg.Flags | IMAP_MessageFlags.Seen); - - // RFC822 {size} - // msg data - this.TcpStream.Write("RFC822 {" + eArgs.MessageSize.ToString() + "}\r\n"); - this.TcpStream.WriteStream(eArgs.MessageStream); - } - - #endregion - - #region RFC822.HEADER - - // RFC822.HEADER - else if (fetchFlagName == "RFC822.HEADER") - { - // Force to write all buffered data. - TcpStream.Flush(); - - // RFC822.HEADER {size} - // msg header data - this.TcpStream.Write("RFC822.HEADER {" + eArgs.Header.Length + "}\r\n"); - this.TcpStream.Write(eArgs.Header); - } - - #endregion - - #region RFC822.SIZE - - // RFC822.SIZE - else if (fetchFlagName == "RFC822.SIZE") - { - // RFC822.SIZE size - TcpStream.Write("RFC822.SIZE " + msg.Size); - } - - #endregion - - #region RFC822.TEXT - - // RFC822.TEXT - else if (fetchFlagName == "RFC822.TEXT") - { - // Force to write all buffered data. - TcpStream.Flush(); - - // Sets \seen flag - msg.SetFlags(msg.Flags | IMAP_MessageFlags.Seen); - - //--- Find body text entity ------------------------------------// - Mime parser = Mime.Parse(eArgs.MessageStream); - MimeEntity bodyTextEntity = null; - if (parser.MainEntity.ContentType == MediaType_enum.NotSpecified) - { - if (parser.MainEntity.DataEncoded != null) - { - bodyTextEntity = parser.MainEntity; - } - } - else - { - MimeEntity[] entities = parser.MimeEntities; - foreach (MimeEntity entity in entities) - { - if (entity.ContentType == MediaType_enum.Text_plain) - { - bodyTextEntity = entity; - break; - } - } - } - //----------------------------------------------------------------// - - // RFC822.TEXT {size} - // msg text - byte[] data = null; - if (bodyTextEntity != null) - { - data = bodyTextEntity.DataEncoded; - } - else - { - data = System.Text.Encoding.ASCII.GetBytes(""); - } - - this.TcpStream.Write("RFC822.TEXT {" + data.Length + "}\r\n"); - this.TcpStream.Write(data); - } - - #endregion - - #region UID - - // UID - else if (fetchFlagName == "UID") - { - TcpStream.Write("UID " + msg.UID); - } - - #endregion - - nCount++; - - // Write fetch item separator data " " - // We don't write it for last item - if (nCount < fetchFlags.Count) - { - TcpStream.Write(" "); - } - } - - // Write fetch end data ")" - TcpStream.Write(")\r\n"); - - // Free event args, close message stream, ... . - if (eArgs != null) - { - eArgs.Dispose(); - } - - // Set message flags here if required or changed - if (((int)IMAP_MessageFlags.Recent & (int)msg.Flags) != 0 || msgFlagsOr != msg.Flags) - { - msg.SetFlags(msg.Flags & ~IMAP_MessageFlags.Recent); - - ImapServer.OnStoreMessageFlags(this, msg); - } - } - } - - // Force to write all buffered data. - TcpStream.Flush(); - - this.TcpStream.WriteLine(string.Format("{0} OK FETCH completed in {1} seconds", cmdTag, ((DateTime.Now.Ticks - startTime) / (decimal)10000000).ToString("f2"))); - } - - - - private void Store(string cmdTag, string argsText, bool uidStore) - { - /* Rfc 3501 6.4.6 STORE Command - - Arguments: message set - message data item name - value for message data item - - Responses: untagged responses: FETCH - - Result: OK - store completed - NO - store error: can't store that data - BAD - command unknown or arguments invalid - - The STORE command alters data associated with a message in the - mailbox. Normally, STORE will return the updated value of the - data with an untagged FETCH response. A suffix of ".SILENT" in - the data item name prevents the untagged FETCH, and the server - SHOULD assume that the client has determined the updated value - itself or does not care about the updated value. - - Note: regardless of whether or not the ".SILENT" suffix was - used, the server SHOULD send an untagged FETCH response if a - change to a message's flags from an external source is - observed. The intent is that the status of the flags is - determinate without a race condition. - - The currently defined data items that can be stored are: - - FLAGS - Replace the flags for the message (other than \Recent) with the - argument. The new value of the flags is returned as if a FETCH - of those flags was done. - - FLAGS.SILENT - Equivalent to FLAGS, but without returning a new value. - - +FLAGS - Add the argument to the flags for the message. The new value - of the flags is returned as if a FETCH of those flags was done. - - +FLAGS.SILENT - Equivalent to +FLAGS, but without returning a new value. - - -FLAGS - Remove the argument from the flags for the message. The new - value of the flags is returned as if a FETCH of those flags was - done. - - -FLAGS.SILENT - Equivalent to -FLAGS, but without returning a new value. - - - Example: C: A003 STORE 2:4 +FLAGS (\Deleted) - S: * 2 FETCH FLAGS (\Deleted \Seen) - S: * 3 FETCH FLAGS (\Deleted) - S: * 4 FETCH FLAGS (\Deleted \Flagged \Seen) - S: A003 OK STORE completed - - */ - if (!IsAuthenticated) - { - WriteLine(string.Format("{0} NO Authenticate first !", cmdTag)); - return; - } - if (SelectedMailbox.Length == 0) - { - WriteLine(string.Format("{0} NO Select mailbox first !", cmdTag)); - return; - } - if (m_pSelectedFolder.ReadOnly) - { - WriteLine(string.Format("{0} NO Mailbox is read-only", cmdTag)); - return; - } - - // Store start time - long startTime = DateTime.Now.Ticks; - - string[] args = ParseParams(argsText); - if (args.Length != 3) - { - WriteLine(string.Format("{0} BAD STORE invalid arguments. Syntax: {{ STORE ()}}", cmdTag)); - return; - } - - IMAP_SequenceSet sequenceSet = new IMAP_SequenceSet(); - // Just try if it can be parsed as sequence-set - try - { - if (uidStore) - { - if (m_pSelectedFolder.Messages.Count > 0) - { - sequenceSet.Parse(args[0], - m_pSelectedFolder.Messages[m_pSelectedFolder.Messages.Count - 1].UID); - } - } - else - { - sequenceSet.Parse(args[0], m_pSelectedFolder.Messages.Count); - } - } - // This isn't vaild sequnce-set value - catch - { - WriteLine(string.Format("{0}BAD Invalid value '{1}' Syntax: {{ STORE ()}}!", cmdTag, args[0])); - return; - } - - //--- Parse Flags behaviour ---------------// - string flagsAction = ""; - bool silent = false; - string flagsType = args[1].ToUpper(); - switch (flagsType) - { - case "FLAGS": - flagsAction = "REPLACE"; - break; - - case "FLAGS.SILENT": - flagsAction = "REPLACE"; - silent = true; - break; - - case "+FLAGS": - flagsAction = "ADD"; - break; - - case "+FLAGS.SILENT": - flagsAction = "ADD"; - silent = true; - break; - - case "-FLAGS": - flagsAction = "REMOVE"; - break; - - case "-FLAGS.SILENT": - flagsAction = "REMOVE"; - silent = true; - break; - - default: - WriteLine(cmdTag + " BAD arguments invalid"); - return; - } - //-------------------------------------------// - - //--- Parse flags, see if valid ---------------- - string flags = args[2].ToUpper(); - if ( - flags.Replace("\\ANSWERED", "").Replace("\\FLAGGED", "").Replace("\\DELETED", "").Replace( - "\\SEEN", "").Replace("\\DRAFT", "").Trim().Length > 0) - { - WriteLine(string.Format("{0} BAD arguments invalid", cmdTag)); - return; - } - - IMAP_MessageFlags mFlags = IMAP_Utils.ParseMessageFlags(flags); - - // Call OnStoreMessageFlags for each message in sequence set - // Calulate new flags(using old message flags + new flags) for message - // and request to store all flags to message, don't specify if add, remove or replace falgs. - - for (int i = 0; i < m_pSelectedFolder.Messages.Count; i++) - { - IMAP_Message msg = m_pSelectedFolder.Messages[i]; - - // For UID STORE we must compare UIDs and for normal STORE message numbers. - bool sequenceSetContains = false; - if (uidStore) - { - sequenceSetContains = sequenceSet.Contains(msg.UID); - } - else - { - sequenceSetContains = sequenceSet.Contains(i + 1); - } - - if (sequenceSetContains) - { - // Calculate new flags and set to msg - switch (flagsAction) - { - case "REPLACE": - msg.SetFlags(mFlags); - break; - - case "ADD": - msg.SetFlags(msg.Flags | mFlags); - break; - - case "REMOVE": - msg.SetFlags(msg.Flags & ~mFlags); - break; - } - - // ToDo: see if flags changed, if not don't call OnStoreMessageFlags - - string errorText = ImapServer.OnStoreMessageFlags(this, msg); - if (errorText == null) - { - if (!silent) - { - // Silent doesn't reply untagged lines - if (!uidStore) - { - WriteLine(string.Format("* {0} FETCH FLAGS ({1})", (i + 1), msg.FlagsString)); - } - // Called from UID command, need to add UID response - else - { - WriteLine(string.Format("* {0} FETCH (FLAGS ({1}) UID {2}))", (i + 1), msg.FlagsString, msg.UID)); - } - } - } - else - { - WriteLine(string.Format("{0} NO {1}", cmdTag, errorText)); - return; - } - } - } - - WriteLine(string.Format("{0} OK STORE completed in {1} seconds", cmdTag, ((DateTime.Now.Ticks - startTime) / (decimal)10000000).ToString("f2"))); - } - - private void Copy(string cmdTag, string argsText, bool uidCopy) - { - /* RFC 3501 6.4.7 COPY Command - - Arguments: message set - mailbox name - - Responses: no specific responses for this command - - Result: OK - copy completed - NO - copy error: can't copy those messages or to that - name - BAD - command unknown or arguments invalid - - The COPY command copies the specified message(s) to the end of the - specified destination mailbox. The flags and internal date of the - message(s) SHOULD be preserved in the copy. - - If the destination mailbox does not exist, a server SHOULD return - an error. It SHOULD NOT automatically create the mailbox. Unless - it is certain that the destination mailbox can not be created, the - server MUST send the response code "[TRYCREATE]" as the prefix of - the text of the tagged NO response. This gives a hint to the - client that it can attempt a CREATE command and retry the COPY if - - If the COPY command is unsuccessful for any reason, server - implementations MUST restore the destination mailbox to its state - before the COPY attempt. - - Example: C: A003 COPY 2:4 MEETING - S: A003 OK COPY completed - - */ - if (!IsAuthenticated) - { - WriteLine(string.Format("{0} NO Authenticate first !", cmdTag)); - return; - } - if (SelectedMailbox.Length == 0) - { - WriteLine(string.Format("{0} NO Select mailbox first !", cmdTag)); - return; - } - - string[] args = ParseParams(argsText); - if (args.Length != 2) - { - WriteLine(string.Format("{0} BAD Invalid arguments", cmdTag)); - return; - } - - IMAP_SequenceSet sequenceSet = new IMAP_SequenceSet(); - // Just try if it can be parsed as sequence-set - try - { - if (uidCopy) - { - if (m_pSelectedFolder.Messages.Count > 0) - { - sequenceSet.Parse(args[0], - m_pSelectedFolder.Messages[m_pSelectedFolder.Messages.Count - 1].UID); - } - } - else - { - sequenceSet.Parse(args[0], m_pSelectedFolder.Messages.Count); - } - } - // This isn't vaild sequnce-set value - catch - { - WriteLine(string.Format("{0}BAD Invalid value '{1}' Syntax: {{ COPY \"\"}}!", cmdTag, args[0])); - return; - } - - string errorText = ""; - for (int i = 0; i < m_pSelectedFolder.Messages.Count; i++) - { - IMAP_Message msg = m_pSelectedFolder.Messages[i]; - - // For UID COPY we must compare UIDs and for normal COPY message numbers. - bool sequenceSetContains = false; - if (uidCopy) - { - sequenceSetContains = sequenceSet.Contains(msg.UID); - } - else - { - sequenceSetContains = sequenceSet.Contains(i + 1); - } - - if (sequenceSetContains) - { - errorText = ImapServer.OnCopyMessage(this, msg, Core.Decode_IMAP_UTF7_String(args[1])); - if (errorText != null) - { - break; // Errors return error text, don't try to copy other messages - } - } - } - - if (errorText == null) - { - WriteLine(string.Format("{0} OK COPY completed", cmdTag)); - } - else - { - WriteLine(string.Format("{0} NO {1}", cmdTag, errorText)); - } - } - - private void Uid(string cmdTag, string argsText) - { - /* Rfc 3501 6.4.8 UID Command - - Arguments: command name - command arguments - - Responses: untagged responses: FETCH, SEARCH - - Result: OK - UID command completed - NO - UID command error - BAD - command unknown or arguments invalid - - The UID command has two forms. In the first form, it takes as its - arguments a COPY, FETCH, or STORE command with arguments - appropriate for the associated command. However, the numbers in - the message set argument are unique identifiers instead of message - sequence numbers. - - In the second form, the UID command takes a SEARCH command with - SEARCH command arguments. The interpretation of the arguments is - the same as with SEARCH; however, the numbers returned in a SEARCH - response for a UID SEARCH command are unique identifiers instead - of message sequence numbers. For example, the command UID SEARCH - 1:100 UID 443:557 returns the unique identifiers corresponding to - the intersection of the message sequence number set 1:100 and the - UID set 443:557. - - Message set ranges are permitted; however, there is no guarantee - that unique identifiers be contiguous. A non-existent unique - identifier within a message set range is ignored without any error - message generated. - - The number after the "*" in an untagged FETCH response is always a - message sequence number, not a unique identifier, even for a UID - command response. However, server implementations MUST implicitly - include the UID message data item as part of any FETCH response - caused by a UID command, regardless of whether a UID was specified - as a message data item to the FETCH. - - Example: C: A999 UID FETCH 4827313:4828442 FLAGS - S: * 23 FETCH (FLAGS (\Seen) UID 4827313) - S: * 24 FETCH (FLAGS (\Seen) UID 4827943) - S: * 25 FETCH (FLAGS (\Seen) UID 4828442) - S: A999 UID FETCH completed - */ - if (!IsAuthenticated) - { - WriteLine(string.Format("{0} NO Authenticate first !", cmdTag)); - return; - } - if (SelectedMailbox.Length == 0) - { - WriteLine(string.Format("{0} NO Select mailbox first !", cmdTag)); - return; - } - - string[] args = ParseParams(argsText); - if (args.Length < 2) - { - // We must have at least command and message-set or cmd args - WriteLine(string.Format("{0} BAD Invalid arguments", cmdTag)); - return; - } - - // Get commands args text, we just remove COMMAND - string cmdArgs = Core.GetArgsText(argsText, args[0]); - - // See if valid command specified with UID command - switch (args[0].ToUpper()) - { - case "COPY": - Copy(cmdTag, cmdArgs, true); - break; - - case "FETCH": - Fetch(cmdTag, cmdArgs, true); - break; - - case "STORE": - Store(cmdTag, cmdArgs, true); - break; - - case "SEARCH": - Search(cmdTag, cmdArgs, true); - break; - - default: - WriteLine(cmdTag + " BAD Invalid arguments"); - return; - } - } - - /// - /// Processes IDLE command. - /// - /// Command tag. - /// Command arguments text. - /// Returns true if IDLE comand accepted, otherwise false. - private bool Idle(string cmdTag, string argsText) - { - if (!IsAuthenticated) - { - WriteLine(string.Format("{0} NO Authenticate first !", cmdTag)); - return false; - } - if (SelectedMailbox.Length == 0 && m_StatusedMailbox.Length == 0) - { - WriteLine(string.Format("{0} BAD Select mailbox first !", cmdTag)); - return false; - } - if (m_pIDLE!=null) - { - m_pIDLE.Dispose(); - } - m_pIDLE = new Command_IDLE(this, cmdTag, SelectedMailbox.Length == 0 ? m_StatusedMailbox : SelectedMailbox); - - return true; - } - - //--- End of Selected State - - //--- Any State ------ - - private void Capability(string cmdTag) - { - /* RFC 3501 6.1.1 - - Arguments: none - - Responses: REQUIRED untagged response: CAPABILITY - - Result: OK - capability completed - BAD - command unknown or arguments invalid - - The CAPABILITY command requests a listing of capabilities that the - server supports. The server MUST send a single untagged - CAPABILITY response with "IMAP4rev1" as one of the listed - capabilities before the (tagged) OK response. - - A capability name which begins with "AUTH=" indicates that the - server supports that particular authentication mechanism. - - Example: C: abcd CAPABILITY - S: * CAPABILITY IMAP4rev1 STARTTLS AUTH=GSSAPI - LOGINDISABLED - S: abcd OK CAPABILITY completed - C: efgh STARTTLS - S: efgh OK STARTLS completed - - C: ijkl CAPABILITY - S: * CAPABILITY IMAP4rev1 AUTH=GSSAPI AUTH=PLAIN - S: ijkl OK CAPABILITY completed - */ - - string reply = "* CAPABILITY IMAP4rev1"; - if ((ImapServer.SupportedAuthentications & SaslAuthTypes.Digest_md5) != 0) - { - reply += " AUTH=DIGEST-MD5"; - } - if ((ImapServer.SupportedAuthentications & SaslAuthTypes.Cram_md5) != 0) - { - reply += " AUTH=CRAM-MD5"; - } - if (!IsSecureConnection && Certificate != null) - { - reply += " STARTTLS"; - } - reply += " NAMESPACE ACL QUOTA IDLE X-FILTER\r\n"; - reply += cmdTag + " OK CAPABILITY completed\r\n"; - - TcpStream.Write(reply); - } - - private void Noop(string cmdTag) - { - /* RFC 3501 6.1.2 NOOP Command - - Arguments: none - - Responses: no specific responses for this command (but see below) - - Result: OK - noop completed - BAD - command unknown or arguments invalid - - The NOOP command always succeeds. It does nothing. - Since any command can return a status update as untagged data, the - NOOP command can be used as a periodic poll for new messages or - message status updates during a period of inactivity. The NOOP - command can also be used to reset any inactivity autologout timer - on the server. - - Example: C: a002 NOOP - S: a002 OK NOOP completed - */ - - // Store start time - long startTime = DateTime.Now.Ticks; - - // If there is selected mailbox, see if messages status has changed - if (SelectedMailbox.Length > 0) - { - ProcessMailboxChanges(); - - WriteLine(cmdTag + " OK NOOP Completed in " + - ((DateTime.Now.Ticks - startTime) / (decimal)10000000).ToString("f2") + - " seconds\r\n"); - } - else - { - WriteLine(cmdTag + " OK NOOP Completed in " + - ((DateTime.Now.Ticks - startTime) / (decimal)10000000).ToString("f2") + - " seconds\r\n"); - } - } - - private void LogOut(string cmdTag) - { - /* RFC 3501 6.1.3 - - Arguments: none - - Responses: REQUIRED untagged response: BYE - - Result: OK - logout completed - BAD - command unknown or arguments invalid - - The LOGOUT command informs the server that the client is done with - the connection. The server MUST send a BYE untagged response - before the (tagged) OK response, and then close the network - connection. - - Example: C: A023 LOGOUT - S: * BYE IMAP4rev1 Server logging out - S: A023 OK LOGOUT completed - (Server and client then close the connection) - */ - - string reply = "* BYE IMAP4rev1 Server logging out\r\n"; - reply += cmdTag + " OK LOGOUT completed\r\n"; - - TcpStream.Write(reply); - TcpStream.Flush(); - } - - //--- End of Any State - - /// - /// Processes changes and sends status responses if there are changes in selected mailbox. - /// - private void ProcessMailboxChanges() - { - ProcessMailboxChanges(SelectedMailbox); - } - - /// - /// Processes changes and sends status responses if there are changes in selected mailbox. - /// - private void ProcessMailboxChanges(string mailBox) - { - //TODO: Many whelps! Handle it! - // Get status - IMAP_SelectedFolder folderInfo = new IMAP_SelectedFolder(mailBox); - IMAP_eArgs_GetMessagesInfo eArgs = ImapServer.OnGetMessagesInfo(this, folderInfo); - - // Join new info with exisiting - if (m_pSelectedFolder != null) - { - string statusResponse = m_pSelectedFolder.Update(folderInfo); - if (!string.IsNullOrEmpty(statusResponse)) - { - WriteLine(statusResponse); - - m_pSelectedFolder = folderInfo; - } - } - if (m_pAdditionalFolder != null) - { - string statusResponse = m_pAdditionalFolder.Update(folderInfo); - if (!string.IsNullOrEmpty(statusResponse)) - { - WriteLine(statusResponse); - - m_pAdditionalFolder = folderInfo; - } - } - } - - private string[] ParseParams(string argsText) - { - List p = new List(); - - try - { - while (argsText.Length > 0) - { - // Parameter is between "" - if (argsText.StartsWith("\"")) - { - p.Add(argsText.Substring(1, argsText.IndexOf("\"", 1) - 1)); - // Remove parsed param - argsText = argsText.Substring(argsText.IndexOf("\"", 1) + 1).Trim(); - } - else - { - // Parameter is between () - if (argsText.StartsWith("(")) - { - p.Add(argsText.Substring(1, argsText.LastIndexOf(")") - 1)); - // Remove parsed param - argsText = argsText.Substring(argsText.LastIndexOf(")") + 1).Trim(); - } - else - { - // Read parameter till " ", probably there is more params - // Note: If there is ({ before SP, cosider that it's last parameter. - // For example body[header.fields (from to)] - if (argsText.IndexOf(" ") > -1 && - argsText.IndexOfAny(new[] { '(', '[' }, 0, argsText.IndexOf(" ")) == -1) - { - p.Add(argsText.Substring(0, argsText.IndexOf(" "))); - // Remove parsed param - argsText = argsText.Substring(argsText.IndexOf(" ") + 1).Trim(); - } - // This is last param - else - { - p.Add(argsText); - argsText = ""; - } - } - } - } - } - catch { } - - return p.ToArray(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_Utils.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_Utils.cs deleted file mode 100644 index f3eca7b81..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_Utils.cs +++ /dev/null @@ -1,359 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Server -{ - #region usings - - using System; - using System.Globalization; - using MIME; - - #endregion - - /// - /// Provides utility methods for IMAP. - /// - public class IMAP_Utils - { - #region Methods - - /// - /// Parses message flags from string. - /// - /// Message flags string. - /// - public static IMAP_MessageFlags ParseMessageFlags(string flagsString) - { - IMAP_MessageFlags mFlags = 0; - - flagsString = flagsString.ToUpper(); - - if (flagsString.IndexOf("ANSWERED") > -1) - { - mFlags |= IMAP_MessageFlags.Answered; - } - if (flagsString.IndexOf("FLAGGED") > -1) - { - mFlags |= IMAP_MessageFlags.Flagged; - } - if (flagsString.IndexOf("DELETED") > -1) - { - mFlags |= IMAP_MessageFlags.Deleted; - } - if (flagsString.IndexOf("SEEN") > -1) - { - mFlags |= IMAP_MessageFlags.Seen; - } - if (flagsString.IndexOf("DRAFT") > -1) - { - mFlags |= IMAP_MessageFlags.Draft; - } - - return mFlags; - } - - /// - /// Converts message flags to string. Eg. \SEEN \DELETED . - /// - /// - public static string MessageFlagsToString(IMAP_MessageFlags msgFlags) - { - string retVal = ""; - if (((int) IMAP_MessageFlags.Answered & (int) msgFlags) != 0) - { - retVal += " \\ANSWERED"; - } - if (((int) IMAP_MessageFlags.Flagged & (int) msgFlags) != 0) - { - retVal += " \\FLAGGED"; - } - if (((int) IMAP_MessageFlags.Deleted & (int) msgFlags) != 0) - { - retVal += " \\DELETED"; - } - if (((int) IMAP_MessageFlags.Seen & (int) msgFlags) != 0) - { - retVal += " \\SEEN"; - } - if (((int) IMAP_MessageFlags.Draft & (int) msgFlags) != 0) - { - retVal += " \\DRAFT"; - } - - return retVal.Trim(); - } - - /// - /// Converts IMAP_ACL_Flags to string. - /// - /// Flags to convert. - /// - public static string ACL_to_String(IMAP_ACL_Flags flags) - { - string retVal = ""; - if ((flags & IMAP_ACL_Flags.l) != 0) - { - retVal += "l"; - } - if ((flags & IMAP_ACL_Flags.r) != 0) - { - retVal += "r"; - } - if ((flags & IMAP_ACL_Flags.s) != 0) - { - retVal += "s"; - } - if ((flags & IMAP_ACL_Flags.w) != 0) - { - retVal += "w"; - } - if ((flags & IMAP_ACL_Flags.i) != 0) - { - retVal += "i"; - } - if ((flags & IMAP_ACL_Flags.p) != 0) - { - retVal += "p"; - } - if ((flags & IMAP_ACL_Flags.c) != 0) - { - retVal += "c"; - } - if ((flags & IMAP_ACL_Flags.d) != 0) - { - retVal += "d"; - } - if ((flags & IMAP_ACL_Flags.a) != 0) - { - retVal += "a"; - } - - return retVal; - } - - /// - /// Parses IMAP_ACL_Flags from string. - /// - /// String from where to convert - /// - public static IMAP_ACL_Flags ACL_From_String(string aclString) - { - IMAP_ACL_Flags retVal = IMAP_ACL_Flags.None; - aclString = aclString.ToLower(); - if (aclString.IndexOf('l') > -1) - { - retVal |= IMAP_ACL_Flags.l; - } - if (aclString.IndexOf('r') > -1) - { - retVal |= IMAP_ACL_Flags.r; - } - if (aclString.IndexOf('s') > -1) - { - retVal |= IMAP_ACL_Flags.s; - } - if (aclString.IndexOf('w') > -1) - { - retVal |= IMAP_ACL_Flags.w; - } - if (aclString.IndexOf('i') > -1) - { - retVal |= IMAP_ACL_Flags.i; - } - if (aclString.IndexOf('p') > -1) - { - retVal |= IMAP_ACL_Flags.p; - } - if (aclString.IndexOf('c') > -1) - { - retVal |= IMAP_ACL_Flags.c; - } - if (aclString.IndexOf('d') > -1) - { - retVal |= IMAP_ACL_Flags.d; - } - if (aclString.IndexOf('a') > -1) - { - retVal |= IMAP_ACL_Flags.a; - } - - return retVal; - } - - /// - /// Parses IMAP date time from string. - /// - /// DateTime string. - /// Returns parsed date-time value. - /// Is raised when date is null reference. - public static DateTime ParseDate(string date) - { - if (date == null) - { - throw new ArgumentNullException("date"); - } - - return MIME_Utils.ParseRfc2822DateTime(date); - } - - /// - /// Converts date time to IMAP date time string. - /// - /// DateTime to convert. - /// - public static string DateTimeToString(DateTime date) - { - string retVal = ""; - retVal += date.ToString("dd-MMM-yyyy HH:mm:ss", CultureInfo.InvariantCulture); - retVal += " " + date.ToString("zzz", CultureInfo.InvariantCulture).Replace(":", ""); - - return retVal; - } - - /// - /// Normalizes folder path. Example: /Inbox/SubFolder/ will be Inbox/SubFolder. - /// - /// Folder path to normalize. - /// Returns normalized folder path. - public static string NormalizeFolder(string folder) - { - folder = folder.Replace("\\", "/"); - if (folder.StartsWith("/")) - { - folder = folder.Substring(1); - } - if (folder.EndsWith("/")) - { - folder = folder.Substring(0, folder.Length - 1); - } - - return folder; - } - - /// - /// Parses [quoted] parameter from args text. Parameter may be not quoted, then parameter is - /// terminated by SP. Example: argsText="string gdkga agkgs";argsText=stringValue 10. - /// - /// This method also removes parsed parameter from argsText. - /// - /// Arguments line from where to parse param. - /// - public static string ParseQuotedParam(ref string argsText) - { - string paramValue = ""; - - // Get value, it is between "" - if (argsText.StartsWith("\"")) - { - // Find next " not escaped " - char lastChar = ' '; - int qIndex = -1; - for (int i = 1; i < argsText.Length; i++) - { - if (argsText[i] == '\"' && lastChar != '\\') - { - qIndex = i; - break; - } - lastChar = argsText[i]; - } - - if (qIndex == -1) - { - throw new Exception("qouted-string doesn't have enclosing quote(\")"); - } - - paramValue = argsText.Substring(1, qIndex - 1).Replace("\\\"", "\""); - - // Remove value from argsText - argsText = argsText.Substring(qIndex + 1).Trim(); - } - else - { - paramValue = argsText.Split(' ')[0]; - - // Remove value from argsText - argsText = argsText.Substring(paramValue.Length).Trim(); - } - - return paramValue; - } - - /// - /// Parses bracket parameter from args text. Parameter may be not between (), then - /// then args text is considered as value. Example: (test test);test test. - /// - /// This method also removes parsed parameter from argsText. - /// - /// - /// - public static string ParseBracketParam(ref string argsText) - { - string paramValue = ""; - if (argsText.StartsWith("(")) - { - // Find matching ) - char lastChar = ' '; - int bIndex = -1; - int nestedBracketCount = 0; - for (int i = 1; i < argsText.Length; i++) - { - // There is nested () - if (argsText[i] == '(') - { - nestedBracketCount++; - } - else if (argsText[i] == ')') - { - if (nestedBracketCount == 0) - { - bIndex = i; - break; - } - // This was nested bracket ) - else - { - nestedBracketCount--; - } - } - lastChar = argsText[i]; - } - - if (bIndex == -1) - { - throw new Exception("bracket doesn't have enclosing bracket ')'"); - } - - paramValue = argsText.Substring(1, bIndex - 1); - - // Remove value from argsText - argsText = argsText.Substring(bIndex + 1).Trim(); - } - else - { - paramValue = argsText; - - argsText = ""; - } - - return paramValue; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_DELETEACL.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_DELETEACL.cs deleted file mode 100644 index 6699f2a53..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_DELETEACL.cs +++ /dev/null @@ -1,89 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Server -{ - /// - /// Provides data for DeleteFolderACL event. - /// - public class IMAP_DELETEACL_eArgs - { - #region Members - - private readonly string m_pFolderName = ""; - private readonly IMAP_Session m_pSession; - private readonly string m_UserName = ""; - private string m_ErrorText = ""; - - #endregion - - #region Properties - - /// - /// Gets current IMAP session. - /// - public IMAP_Session Session - { - get { return m_pSession; } - } - - /// - /// Gets folder name which ACL to delete. - /// - public string Folder - { - get { return m_pFolderName; } - } - - /// - /// Gets user name which ACL to delete. - /// - public string UserName - { - get { return m_UserName; } - } - - /// - /// Gets or sets error text returned to connected client. - /// - public string ErrorText - { - get { return m_ErrorText; } - - set { m_ErrorText = value; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner IMAP session. - /// Folder name which ACL to delete. - /// User name which ACL to delete. - public IMAP_DELETEACL_eArgs(IMAP_Session session, string folderName, string userName) - { - m_pSession = session; - m_pFolderName = folderName; - m_UserName = userName; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_GETACL.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_GETACL.cs deleted file mode 100644 index beb1dd088..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_GETACL.cs +++ /dev/null @@ -1,95 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Server -{ - #region usings - - using System.Collections; - - #endregion - - /// - /// Provides data for GetFolderACL event. - /// - public class IMAP_GETACL_eArgs - { - #region Members - - private readonly Hashtable m_ACLs; - private readonly string m_pFolderName = ""; - private readonly IMAP_Session m_pSession; - private string m_ErrorText = ""; - - #endregion - - #region Properties - - /// - /// Gets current IMAP session. - /// - public IMAP_Session Session - { - get { return m_pSession; } - } - - /// - /// Gets folder name which ACL to get. - /// - public string Folder - { - get { return m_pFolderName; } - } - - /// - /// Gets ACL collection. Key = userName, Value = IMAP_ACL_Flags. - /// - public Hashtable ACL - { - get { return m_ACLs; } - } - - /// - /// Gets or sets error text returned to connected client. - /// - public string ErrorText - { - get { return m_ErrorText; } - - set { m_ErrorText = value; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner IMAP session. - /// Folder name which ACL to get. - public IMAP_GETACL_eArgs(IMAP_Session session, string folderName) - { - m_pSession = session; - m_pFolderName = folderName; - - m_ACLs = new Hashtable(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_GetMessagesInfo.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_GetMessagesInfo.cs deleted file mode 100644 index 51b6d7d05..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_GetMessagesInfo.cs +++ /dev/null @@ -1,70 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Server -{ - /// - /// Provides data to event GetMessagesInfo. - /// - public class IMAP_eArgs_GetMessagesInfo - { - #region Members - - private readonly IMAP_SelectedFolder m_pFolderInfo; - private readonly IMAP_Session m_pSession; - - #endregion - - #region Properties - - /// - /// Gets current IMAP session. - /// - public IMAP_Session Session - { - get { return m_pSession; } - } - - /// - /// Gets folder info. - /// - public IMAP_SelectedFolder FolderInfo - { - get { return m_pFolderInfo; } - } - - /// - /// Gets or sets custom error text, which is returned to client. Null value means no error. - /// - public string ErrorText { get; set; } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public IMAP_eArgs_GetMessagesInfo(IMAP_Session session, IMAP_SelectedFolder folder) - { - m_pSession = session; - m_pFolderInfo = folder; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_GetQuota.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_GetQuota.cs deleted file mode 100644 index 2a04d857b..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_GetQuota.cs +++ /dev/null @@ -1,74 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Server -{ - /// - /// Provides data for GetUserQuota event. - /// - public class IMAP_eArgs_GetQuota - { - #region Members - - private readonly IMAP_Session m_pSession; - - #endregion - - #region Properties - - /// - /// Gets current IMAP session. - /// - public IMAP_Session Session - { - get { return m_pSession; } - } - - /// - /// Gets user name. - /// - public string UserName - { - get { return m_pSession.UserName; } - } - - /// - /// Gets or sets maximum mailbox size. - /// - public long MaxMailboxSize { get; set; } - - /// - /// Gets or sets current mailbox size. - /// - public long MailboxSize { get; set; } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner IMAP session. - public IMAP_eArgs_GetQuota(IMAP_Session session) - { - m_pSession = session; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_GetUserACL.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_GetUserACL.cs deleted file mode 100644 index d3c1aa417..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_GetUserACL.cs +++ /dev/null @@ -1,100 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Server -{ - /// - /// Provides data for GetUserACL event. - /// - public class IMAP_GetUserACL_eArgs - { - #region Members - - private readonly string m_pFolderName = ""; - private readonly IMAP_Session m_pSession; - private readonly string m_UserName = ""; - private IMAP_ACL_Flags m_ACL_Flags = 0; - private string m_ErrorText = ""; - - #endregion - - #region Properties - - /// - /// Gets current IMAP session. - /// - public IMAP_Session Session - { - get { return m_pSession; } - } - - /// - /// Gets folder name which ACL to get. - /// - public string Folder - { - get { return m_pFolderName; } - } - - /// - /// Gets user name which ACL to get. - /// - public string UserName - { - get { return m_UserName; } - } - - /// - /// Gets or sets user permissions(ACL) for specified folder. - /// - public IMAP_ACL_Flags ACL - { - get { return m_ACL_Flags; } - - set { m_ACL_Flags = value; } - } - - /// - /// Gets or sets error text returned to connected client. - /// - public string ErrorText - { - get { return m_ErrorText; } - - set { m_ErrorText = value; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner IMAP session. - /// Folder name which ACL to get. - /// User name which ACL to get. - public IMAP_GetUserACL_eArgs(IMAP_Session session, string folderName, string userName) - { - m_pSession = session; - m_pFolderName = folderName; - m_UserName = userName; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_MessageItems.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_MessageItems.cs deleted file mode 100644 index 511486649..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_MessageItems.cs +++ /dev/null @@ -1,277 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Server -{ - #region usings - - using System; - using System.IO; - - #endregion - - /// - /// Provides data to event GetMessageItems. - /// - public class IMAP_eArgs_MessageItems - { - #region Members - - private readonly IMAP_MessageItems_enum m_MessageItems = IMAP_MessageItems_enum.Message; - private readonly IMAP_Message m_pMessageInfo; - private readonly IMAP_Session m_pSession; - private string m_BodyStructure; - private bool m_CloseMessageStream = true; - private string m_Envelope; - private byte[] m_Header; - private bool m_MessageExists = true; - private long m_MessageStartOffset; - private Stream m_MessageStream; - - #endregion - - #region Properties - - /// - /// Gets reference to current IMAP session. - /// - public IMAP_Session Session - { - get { return m_pSession; } - } - - /// - /// Gets message info what message items to get. - /// - public IMAP_Message MessageInfo - { - get { return m_pMessageInfo; } - } - - /// - /// Gets what message items must be filled. - /// - public IMAP_MessageItems_enum MessageItems - { - get { return m_MessageItems; } - } - - /// - /// Gets or sets if message stream is closed automatically if all actions on it are completed. - /// Default value is true. - /// - public bool CloseMessageStream - { - get { return m_CloseMessageStream; } - - set { m_CloseMessageStream = value; } - } - - /// - /// Gets or sets message stream. When setting this property Stream position must be where message begins. - /// Fill this property only if IMAP_MessageItems_enum.Message flag is specified. - /// - public Stream MessageStream - { - get - { - if (m_MessageStream != null) - { - m_MessageStream.Position = m_MessageStartOffset; - } - return m_MessageStream; - } - - set - { - if (value == null) - { - throw new ArgumentNullException("Property MessageStream value can't be null !"); - } - if (!value.CanSeek) - { - throw new Exception("Stream must support seeking !"); - } - - m_MessageStream = value; - m_MessageStartOffset = m_MessageStream.Position; - } - } - - /// - /// Gets message size in bytes. - /// - public long MessageSize - { - get - { - if (m_MessageStream == null) - { - throw new Exception("You must set MessageStream property first to use this property !"); - } - else - { - return m_MessageStream.Length - m_MessageStream.Position; - } - } - } - - /// - /// Gets or sets message main header. - /// Fill this property only if IMAP_MessageItems_enum.Header flag is specified. - /// - public byte[] Header - { - get { return m_Header; } - - set - { - if (value == null) - { - throw new ArgumentNullException("Property Header value can't be null !"); - } - - m_Header = value; - } - } - - /// - /// Gets or sets IMAP ENVELOPE string. - /// Fill this property only if IMAP_MessageItems_enum.Envelope flag is specified. - /// - public string Envelope - { - get { return m_Envelope; } - - set - { - if (value == null) - { - throw new ArgumentNullException("Property Envelope value can't be null !"); - } - - m_Envelope = value; - } - } - - /// - /// Gets or sets IMAP BODYSTRUCTURE string. - /// Fill this property only if IMAP_MessageItems_enum.BodyStructure flag is specified. - /// - public string BodyStructure - { - get { return m_BodyStructure; } - - set - { - if (value == null) - { - throw new ArgumentNullException("Property BodyStructure value can't be null !"); - } - - m_BodyStructure = value; - } - } - - /// - /// Gets or sets if message exists. Set this false, if message actually doesn't exist any more. - /// - public bool MessageExists - { - get { return m_MessageExists; } - - set { m_MessageExists = value; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Reference to current IMAP session. - /// Message info what message items to get. - /// Specifies message items what must be filled. - public IMAP_eArgs_MessageItems(IMAP_Session session, - IMAP_Message messageInfo, - IMAP_MessageItems_enum messageItems) - { - m_pSession = session; - m_pMessageInfo = messageInfo; - m_MessageItems = messageItems; - } - - #endregion - - #region Methods - - /// - /// Clean up any resources being used. - /// - public void Dispose() - { - if (m_CloseMessageStream && m_MessageStream != null) - { - m_MessageStream.Dispose(); - m_MessageStream = null; - } - } - - #endregion - - #region Overrides - - /// - /// Default deconstructor. - /// - ~IMAP_eArgs_MessageItems() - { - Dispose(); - } - - #endregion - - #region Internal methods - - /// - /// Checks that all required data items are provided, if not throws exception. - /// - internal void Validate() - { - if ((m_MessageItems & IMAP_MessageItems_enum.BodyStructure) != 0 && m_BodyStructure == null) - { - throw new Exception( - "IMAP BODYSTRUCTURE is required, but not provided to IMAP server component !"); - } - if ((m_MessageItems & IMAP_MessageItems_enum.Envelope) != 0 && m_Envelope == null) - { - throw new Exception("IMAP ENVELOPE is required, but not provided to IMAP server component !"); - } - if ((m_MessageItems & IMAP_MessageItems_enum.Header) != 0 && m_Header == null) - { - throw new Exception("Message header is required, but not provided to IMAP server component !"); - } - if ((m_MessageItems & IMAP_MessageItems_enum.Message) != 0 && m_MessageStream == null) - { - throw new Exception("Full message is required, but not provided to IMAP server component !"); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_SETACL.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_SETACL.cs deleted file mode 100644 index e1d6097cd..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_SETACL.cs +++ /dev/null @@ -1,115 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Server -{ - /// - /// Provides data for SetFolderACL event. - /// - public class IMAP_SETACL_eArgs - { - #region Members - - private readonly IMAP_ACL_Flags m_ACL_Flags = IMAP_ACL_Flags.None; - private readonly IMAP_Flags_SetType m_FlagsSetType = IMAP_Flags_SetType.Replace; - private readonly string m_pFolderName = ""; - private readonly IMAP_Session m_pSession; - private readonly string m_UserName = ""; - private string m_ErrorText = ""; - - #endregion - - #region Properties - - /// - /// Gets current IMAP session. - /// - public IMAP_Session Session - { - get { return m_pSession; } - } - - /// - /// Gets folder name which ACL to set. - /// - public string Folder - { - get { return m_pFolderName; } - } - - /// - /// Gets user name which ACL to set. - /// - public string UserName - { - get { return m_UserName; } - } - - /// - /// Gets how ACL flags must be stored. - /// - public IMAP_Flags_SetType FlagsSetType - { - get { return m_FlagsSetType; } - } - - /// - /// Gets ACL flags. NOTE: See this.FlagsSetType how to store flags. - /// - public IMAP_ACL_Flags ACL - { - get { return m_ACL_Flags; } - } - - /// - /// Gets or sets error text returned to connected client. - /// - public string ErrorText - { - get { return m_ErrorText; } - - set { m_ErrorText = value; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner IMAP session. - /// Folder name which ACL to set. - /// User name which ACL to set. - /// Specifies how flags must be stored. - /// Flags which to store. - public IMAP_SETACL_eArgs(IMAP_Session session, - string folderName, - string userName, - IMAP_Flags_SetType flagsSetType, - IMAP_ACL_Flags aclFlags) - { - m_pSession = session; - m_pFolderName = folderName; - m_UserName = userName; - m_FlagsSetType = flagsSetType; - m_ACL_Flags = aclFlags; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_Search.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_Search.cs deleted file mode 100644 index 8d8192f5f..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/IMAP_eArgs_Search.cs +++ /dev/null @@ -1,85 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; - -namespace LumiSoft.Net.IMAP.Server -{ - /// - /// Provides data from IMAP Search command. - /// - public class IMAP_eArgs_Search - { - private IMAP_Session m_pSession = null; - private string m_Folder = ""; - private IMAP_Messages m_pMessages = null; - private IMAP_SearchMatcher m_pMatcher = null; - - /// - /// Default constructor. - /// - /// IMAP session what calls this search. - /// IMAP folder name which messages to search. - /// Matcher what must be used to check if message matches searching criterial. - public IMAP_eArgs_Search(IMAP_Session session,string folder,IMAP_SearchMatcher matcher) - { - m_pSession = session; - m_Folder = folder; - m_pMatcher = matcher; - - m_pMessages = new IMAP_Messages(folder); - } - - - #region Properties Implementation - - /// - /// Gets current session. - /// - public IMAP_Session Session - { - get{ return m_pSession; } - } - - /// - /// Gets folder name which messages to search. - /// - public string Folder - { - get{ return m_Folder; } - } - - /// - /// Gets or sets messges what match searching criterial. - /// - public IMAP_Messages MatchingMessages - { - get{ return m_pMessages; } - } - - /// - /// Gets matcher what must be used to check if message matches searching criterial. - /// - public IMAP_SearchMatcher Matcher - { - get{ return m_pMatcher; } - } - - #endregion - - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/Message_EventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/Message_EventArgs.cs deleted file mode 100644 index a8687ea7d..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/Message_EventArgs.cs +++ /dev/null @@ -1,121 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Server -{ - /// - /// Provides data for message related events. - /// - public class Message_EventArgs - { - #region Members - - private readonly string m_CopyLocation = ""; - private readonly string m_Folder = ""; - private readonly bool m_HeadersOnly; - private readonly IMAP_Message m_pMessage; - - #endregion - - #region Properties - - /// - /// Gets IMAP folder. - /// - public string Folder - { - get { return m_Folder; } - } - - /// - /// Gets IMAP message info. - /// - public IMAP_Message Message - { - get { return m_pMessage; } - } - - /// - /// Gets message new location. NOTE: this is available for copy command only. - /// - public string CopyLocation - { - get { return m_CopyLocation; } - } - - /// - /// Gets or sets message data. NOTE: this is available for GetMessage and StoreMessage event only. - /// - public byte[] MessageData { get; set; } - - /// - /// Gets if message headers or full message wanted. NOTE: this is available for GetMessage event only. - /// - public bool HeadersOnly - { - get { return m_HeadersOnly; } - } - - /// - /// Gets or sets custom error text, which is returned to client. - /// - public string ErrorText { get; set; } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// IMAP folder which message is. - /// - public Message_EventArgs(string folder, IMAP_Message msg) - { - m_Folder = folder; - m_pMessage = msg; - } - - /// - /// Copy constructor. - /// - /// IMAP folder which message is. - /// - /// - public Message_EventArgs(string folder, IMAP_Message msg, string copyLocation) - { - m_Folder = folder; - m_pMessage = msg; - m_CopyLocation = copyLocation; - } - - /// - /// GetMessage constructor. - /// - /// IMAP folder which message is. - /// - /// Specifies if messages headers or full message is needed. - public Message_EventArgs(string folder, IMAP_Message msg, bool headersOnly) - { - m_Folder = folder; - m_pMessage = msg; - m_HeadersOnly = headersOnly; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/SharedRootFolders_EventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/SharedRootFolders_EventArgs.cs deleted file mode 100644 index 7873ec193..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/SharedRootFolders_EventArgs.cs +++ /dev/null @@ -1,65 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Server -{ - /// - /// Summary description for SharedRootFolders_EventArgs. - /// - public class SharedRootFolders_EventArgs - { - #region Members - - private readonly IMAP_Session m_pSession; - - #endregion - - #region Properties - - /// - /// Gets reference to smtp session. - /// - public IMAP_Session Session - { - get { return m_pSession; } - } - - /// - /// Gets or sets users shared root folders. Ususaly there is only one root folder 'Shared Folders'. - /// - public string[] SharedRootFolders { get; set; } - - /// - /// Gets or sets public root folders. Ususaly there is only one root folder 'Public Folders'. - /// - public string[] PublicRootFolders { get; set; } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public SharedRootFolders_EventArgs(IMAP_Session session) - { - m_pSession = session; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/_FetchHelper.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/_FetchHelper.cs deleted file mode 100644 index e83e3d7a6..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IMAP/Server/_FetchHelper.cs +++ /dev/null @@ -1,320 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IMAP.Server -{ - #region usings - - using System; - using System.IO; - using System.Text; - using Mime; - - #endregion - - /// - /// FETCH command helper methods. - /// - internal class FetchHelper - { - #region Methods - - /// - /// Returns requested header fields lines. - /// Note: Header terminator blank line is included. - /// - /// Header fields to get. - /// Entity which header field lines to get. - /// - public static byte[] ParseHeaderFields(string fieldsStr, MimeEntity entity) - { - return ParseHeaderFields(fieldsStr, Encoding.Default.GetBytes(entity.HeaderString)); - } - - /// - /// Returns requested header fields lines. - /// Note: Header terminator blank line is included. - /// - /// Header fields to get. - /// Message data. - /// - public static byte[] ParseHeaderFields(string fieldsStr, byte[] data) - { - fieldsStr = fieldsStr.Trim(); - if (fieldsStr.StartsWith("(")) - { - fieldsStr = fieldsStr.Substring(1, fieldsStr.Length - 1); - } - if (fieldsStr.EndsWith(")")) - { - fieldsStr = fieldsStr.Substring(0, fieldsStr.Length - 1); - } - - string retVal = ""; - - string[] fields = fieldsStr.Split(' '); - using (MemoryStream mStrm = new MemoryStream(data)) - { - TextReader r = new StreamReader(mStrm); - string line = r.ReadLine(); - - bool fieldFound = false; - // Loop all header lines - while (line != null) - { - // End of header - if (line.Length == 0) - { - break; - } - - // Field continues - if (fieldFound && line.StartsWith("\t")) - { - retVal += line + "\r\n"; - } - else - { - fieldFound = false; - - // Check if wanted field - foreach (string field in fields) - { - if (line.Trim().ToLower().StartsWith(field.Trim().ToLower())) - { - retVal += line + "\r\n"; - fieldFound = true; - } - } - } - - line = r.ReadLine(); - } - } - - // Add header terminating blank line - retVal += "\r\n"; - - return Encoding.ASCII.GetBytes(retVal); - } - - /// - /// Returns header fields lines except requested. - /// Note: Header terminator blank line is included. - /// - /// Header fields to skip. - /// Entity which header field lines to get. - /// - public static byte[] ParseHeaderFieldsNot(string fieldsStr, MimeEntity entity) - { - return ParseHeaderFieldsNot(fieldsStr, Encoding.Default.GetBytes(entity.HeaderString)); - } - - /// - /// Returns header fields lines except requested. - /// Note: Header terminator blank line is included. - /// - /// Header fields to skip. - /// Message data. - /// - public static byte[] ParseHeaderFieldsNot(string fieldsStr, byte[] data) - { - fieldsStr = fieldsStr.Trim(); - if (fieldsStr.StartsWith("(")) - { - fieldsStr = fieldsStr.Substring(1, fieldsStr.Length - 1); - } - if (fieldsStr.EndsWith(")")) - { - fieldsStr = fieldsStr.Substring(0, fieldsStr.Length - 1); - } - - string retVal = ""; - - string[] fields = fieldsStr.Split(' '); - using (MemoryStream mStrm = new MemoryStream(data)) - { - TextReader r = new StreamReader(mStrm); - string line = r.ReadLine(); - - bool fieldFound = false; - // Loop all header lines - while (line != null) - { - // End of header - if (line.Length == 0) - { - break; - } - - // Filed continues - if (fieldFound && line.StartsWith("\t")) - { - retVal += line + "\r\n"; - } - else - { - fieldFound = false; - - // Check if wanted field - foreach (string field in fields) - { - if (line.Trim().ToLower().StartsWith(field.Trim().ToLower())) - { - fieldFound = true; - } - } - - if (!fieldFound) - { - retVal += line + "\r\n"; - } - } - - line = r.ReadLine(); - } - } - - return Encoding.ASCII.GetBytes(retVal); - } - - /// - /// Gets specified mime entity. Returns null if specified mime entity doesn't exist. - /// - /// Reference to mime parser. - /// Mime entity specifier. Nested mime entities are pointed by '.'. - /// For example: 1,1.1,2.1, ... . - /// - public static MimeEntity GetMimeEntity(Mime parser, string mimeEntitySpecifier) - { - // TODO: nested rfc 822 message - - // For single part message there is only one entity with value 1. - // Example: - // header - // entity -> 1 - - // For multipart message, entity counting starts from MainEntity.ChildEntities - // Example: - // header - // multipart/mixed - // entity1 -> 1 - // entity2 -> 2 - // ... - - // Single part - if ((parser.MainEntity.ContentType & MediaType_enum.Multipart) == 0) - { - if (mimeEntitySpecifier.Length == 1 && Convert.ToInt32(mimeEntitySpecifier) == 1) - { - return parser.MainEntity; - } - else - { - return null; - } - } - // multipart - else - { - MimeEntity entity = parser.MainEntity; - string[] parts = mimeEntitySpecifier.Split('.'); - foreach (string part in parts) - { - int mEntryNo = Convert.ToInt32(part) - 1; - // Enitites are zero base, mimeEntitySpecifier is 1 based. - if (mEntryNo > -1 && mEntryNo < entity.ChildEntities.Count) - { - entity = entity.ChildEntities[mEntryNo]; - } - else - { - return null; - } - } - - return entity; - } - } - - /// - /// Gets specified mime entity header. - /// Note: Header terminator blank line is included. - /// - /// Mime entity. - /// - public static byte[] GetMimeEntityHeader(MimeEntity entity) - { - return Encoding.ASCII.GetBytes(entity.HeaderString + "\r\n"); - } - - /// - /// Gets requested mime entity header. Returns null if specified mime entity doesn't exist. - /// Note: Header terminator blank line is included. - /// - /// Reference to mime parser. - /// Mime entity specifier. Nested mime entities are pointed by '.'. - /// For example: 1,1.1,2.1, ... . - /// Returns requested mime entity data or NULL if requested entry doesn't exist. - public static byte[] GetMimeEntityHeader(Mime parser, string mimeEntitySpecifier) - { - MimeEntity mEntry = GetMimeEntity(parser, mimeEntitySpecifier); - if (mEntry != null) - { - return GetMimeEntityHeader(mEntry); - } - else - { - return null; - } - } - - /// - /// Gets requested mime entity data. Returns null if specified mime entity doesn't exist. - /// - /// Reference to mime parser. - /// Mime entity specifier. Nested mime entities are pointed by '.'. - /// For example: 1,1.1,2.1, ... . - /// Returns requested mime entity data or NULL if requested entry doesn't exist. - public static byte[] GetMimeEntityData(Mime parser, string mimeEntitySpecifier) - { - MimeEntity entity = GetMimeEntity(parser, mimeEntitySpecifier); - if (entity != null) - { - return entity.DataEncoded; - } - else - { - return null; - } - } - - #endregion - - #region Utility methods - - private static string Escape(string text) - { - text = text.Replace("\\", "\\\\"); - text = text.Replace("\"", "\\\""); - - return text; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/Base64Stream.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/Base64Stream.cs deleted file mode 100644 index 2d6e5467d..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/Base64Stream.cs +++ /dev/null @@ -1,698 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IO -{ - #region usings - - using System; - using System.Collections.Generic; - using System.IO; - - #endregion - - /// - /// This class implements base64 encoder/decoder. Defined in RFC 4648. - /// - public class Base64Stream : Stream, IDisposable - { - #region Members - - private static readonly short[] BASE64_DECODE_TABLE = new short[] - { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - // 0 - 9 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - //10 - 19 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - //20 - 29 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - //30 - 39 - -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, - //40 - 49 - 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, - //50 - 59 - -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, - //60 - 69 - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - //70 - 79 - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - //80 - 89 - 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, - //90 - 99 - 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, - //100 - 109 - 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - //110 - 119 - 49, 50, 51, -1, -1, -1, -1, -1 //120 - 127 - }; - - private static readonly byte[] BASE64_ENCODE_TABLE = new[] - { - (byte) 'A', (byte) 'B', (byte) 'C', - (byte) 'D', (byte) 'E', (byte) 'F', - (byte) 'G', (byte) 'H', (byte) 'I', - (byte) 'J', (byte) 'K', (byte) 'L', - (byte) 'M', (byte) 'N', (byte) 'O', - (byte) 'P', (byte) 'Q', (byte) 'R', - (byte) 'S', (byte) 'T', (byte) 'U', - (byte) 'V', (byte) 'W', (byte) 'X', - (byte) 'Y', (byte) 'Z', (byte) 'a', - (byte) 'b', (byte) 'c', (byte) 'd', - (byte) 'e', (byte) 'f', (byte) 'g', - (byte) 'h', (byte) 'i', (byte) 'j', - (byte) 'k', (byte) 'l', (byte) 'm', - (byte) 'n', (byte) 'o', (byte) 'p', - (byte) 'q', (byte) 'r', (byte) 's', - (byte) 't', (byte) 'u', (byte) 'v', - (byte) 'w', (byte) 'x', (byte) 'y', - (byte) 'z', (byte) '0', (byte) '1', - (byte) '2', (byte) '3', (byte) '4', - (byte) '5', (byte) '6', (byte) '7', - (byte) '8', (byte) '9', (byte) '+', - (byte) '/' - }; - - private readonly FileAccess m_AccessMode = FileAccess.ReadWrite; - private readonly bool m_AddLineBreaks = true; - private readonly bool m_IsOwner; - private readonly Queue m_pDecodeReminder; - private readonly byte[] m_pEncode3x8Block = new byte[3]; - private readonly byte[] m_pEncodeBuffer = new byte[78]; - private readonly Stream m_pStream; - private int m_EncodeBufferOffset; - private bool m_IsDisposed; - private bool m_IsFinished; - private int m_OffsetInEncode3x8Block; - - private static string[] padding_tails = new string[4] { "", "===", "==", "=" }; - - #endregion - - #region Properties - - /// - /// Gets if this object is disposed. - /// - public bool IsDisposed - { - get { return m_IsDisposed; } - } - - /// - /// Gets a value indicating whether the current stream supports reading. - /// - /// Is raised when this object is disposed and this property is accessed. - public override bool CanRead - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - return true; - } - } - - /// - /// Gets a value indicating whether the current stream supports seeking. - /// - /// Is raised when this object is disposed and this property is accessed. - public override bool CanSeek - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - return false; - } - } - - /// - /// Gets a value indicating whether the current stream supports writing. - /// - /// Is raised when this object is disposed and this property is accessed. - public override bool CanWrite - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - return false; - } - } - - /// - /// Gets the length in bytes of the stream. This method is not supported and always throws a NotSupportedException. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when this property is accessed. - public override long Length - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - throw new NotSupportedException(); - } - } - - /// - /// Gets or sets the position within the current stream. This method is not supported and always throws a NotSupportedException. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when this property is accessed. - public override long Position - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - throw new NotSupportedException(); - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - throw new NotSupportedException(); - } - } - - #endregion - - private MemoryStream decodedDataStream = null; - #region Constructor - - /// - /// Default constructor. - /// - /// Stream which to encode/decode. - /// Specifies if Base64Stream is owner of stream. - /// Specifies if encoder inserts CRLF after each 76 bytes. - /// Is raised when stream is null reference. - public Base64Stream(Stream stream, bool owner, bool addLineBreaks) - : this(stream, owner, addLineBreaks, FileAccess.ReadWrite) {} - - /// - /// Default constructor. - /// - /// Stream which to encode/decode. - /// Specifies if Base64Stream is owner of stream. - /// Specifies if encoder inserts CRLF after each 76 bytes. - /// This stream access mode. - /// Is raised when stream is null reference. - public Base64Stream(Stream stream, bool owner, bool addLineBreaks, FileAccess access) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - // Parse all stream - if (stream.Length > 0) - { - stream.Seek(0, SeekOrigin.Begin); - decodedDataStream = new MemoryStream(); - using (StreamReader reader = new StreamReader(stream)) - { - List lines = new List(); - while (!reader.EndOfStream) - { - string next_line = reader.ReadLine(); - lines.Add(next_line); - int exc_bytes = next_line.Length & 0x03; - // If the line length is not multiple of 4 then we need to process possible wrong message content (it really happens) - if (exc_bytes != 0) - { - // Process even part of the line first - string even_part = next_line.Substring(0, next_line.Length - exc_bytes); - byte[] decoded = Convert.FromBase64String(even_part); - decodedDataStream.Write(decoded, 0, decoded.Length); - - // then try to recover the odd part by appending "=" to it - try - { - string rest = next_line.Substring(next_line.Length - exc_bytes, exc_bytes); - byte[] rest_decoded = Convert.FromBase64String(rest + padding_tails[exc_bytes]); - decodedDataStream.Write(decoded, 0, decoded.Length); - } - catch (System.FormatException) {} - } - else - { - byte[] decoded = Convert.FromBase64String(next_line); - decodedDataStream.Write(decoded, 0, decoded.Length); - } - } - decodedDataStream.Seek(0, SeekOrigin.Begin); - } - } - - m_pStream = stream; - m_IsOwner = owner; - m_AddLineBreaks = addLineBreaks; - m_AccessMode = access; - - m_pDecodeReminder = new Queue(); - } - - #endregion - - #region Methods - - /// - /// Celans up any resources being used. - /// - public new void Dispose() - { - if (m_IsDisposed) - { - return; - } - try - { - Finish(); - } - catch {} - m_IsDisposed = true; - - if (m_IsOwner) - { - m_pStream.Close(); - } - if (decodedDataStream!=null) - { - decodedDataStream.Close(); - decodedDataStream.Dispose(); - } - } - - /// - /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device. - /// - /// Is raised when this object is disposed and this method is accessed. - public override void Flush() - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("Base64Stream"); - } - } - - /// - /// Sets the position within the current stream. This method is not supported and always throws a NotSupportedException. - /// - /// A byte offset relative to the origin parameter. - /// A value of type SeekOrigin indicating the reference point used to obtain the new position. - /// The new position within the current stream. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when this method is accessed. - public override long Seek(long offset, SeekOrigin origin) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("Base64Stream"); - } - - throw new NotSupportedException(); - } - - /// - /// Sets the length of the current stream. This method is not supported and always throws a NotSupportedException. - /// - /// The desired length of the current stream in bytes. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when this method is accessed. - public override void SetLength(long value) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("Base64Stream"); - } - - throw new NotSupportedException(); - } - - /// - /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. - /// - /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source. - /// The zero-based byte offset in buffer at which to begin storing the data read from the current stream. - /// The maximum number of bytes to be read from the current stream. - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when buffer is null reference. - /// Is raised when reading not supported. - public override int Read(byte[] buffer, int offset, int count) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("Base64Stream"); - } - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if ((m_AccessMode & FileAccess.Read) == 0) - { - throw new NotSupportedException(); - } - - if (decodedDataStream!=null) - { - //read from it - return decodedDataStream.Read(buffer, offset, count); - } - - /* RFC 4648. - - Base64 is processed from left to right by 4 6-bit byte block, 4 6-bit byte block - are converted to 3 8-bit bytes. - If base64 4 byte block doesn't have 3 8-bit bytes, missing bytes are marked with =. - - Value Encoding Value Encoding Value Encoding Value Encoding - 0 A 17 R 34 i 51 z - 1 B 18 S 35 j 52 0 - 2 C 19 T 36 k 53 1 - 3 D 20 U 37 l 54 2 - 4 E 21 V 38 m 55 3 - 5 F 22 W 39 n 56 4 - 6 G 23 X 40 o 57 5 - 7 H 24 Y 41 p 58 6 - 8 I 25 Z 42 q 59 7 - 9 J 26 a 43 r 60 8 - 10 K 27 b 44 s 61 9 - 11 L 28 c 45 t 62 + - 12 M 29 d 46 u 63 / - 13 N 30 e 47 v - 14 O 31 f 48 w (pad) = - 15 P 32 g 49 x - 16 Q 33 h 50 y - - NOTE: 4 base64 6-bit bytes = 3 8-bit bytes - // | 6-bit | 6-bit | 6-bit | 6-bit | - // | 1 2 3 4 5 6 | 1 2 3 4 5 6 | 1 2 3 4 5 6 | 1 2 3 4 5 6 | - // | 8-bit | 8-bit | 8-bit | - */ - - int storedInBuffer = 0; - - // If we have decoded-buffered bytes, use them first. - while (m_pDecodeReminder.Count > 0) - { - buffer[offset++] = m_pDecodeReminder.Dequeue(); - storedInBuffer++; - count--; - - // We filled whole "buffer", no more room. - if (count == 0) - { - return storedInBuffer; - } - } - - // 1) Calculate as much we can decode to "buffer". !!! We need to read as 4x7-bit blocks. - int rawBytesToRead = (int) Math.Ceiling(count/3.0)*4; - - byte[] readBuffer = new byte[rawBytesToRead]; - short[] decodeBlock = new short[4]; - byte[] decodedBlock = new byte[3]; - int decodeBlockOffset = 0; - int paddedCount = 0; - // Decode while we have room in "buffer". - while (storedInBuffer < count) - { - int readedCount = m_pStream.Read(readBuffer, 0, rawBytesToRead); - // We reached end of stream, no more data. - if (readedCount == 0) - { - // We have last block without padding 1 char. - if (decodeBlockOffset == 3) - { - buffer[offset + storedInBuffer++] = (byte) (decodeBlock[0] << 2 | decodeBlock[1] >> 4); - // See if "buffer" can accomodate 2 byte. - if (storedInBuffer < count) - { - buffer[offset + storedInBuffer++] = - (byte) ((decodeBlock[1] & 0xF) << 4 | decodeBlock[2] >> 2); - } - else - { - m_pDecodeReminder.Enqueue( - (byte) ((decodeBlock[1] & 0xF) << 4 | decodeBlock[2] >> 2)); - } - } - // We have last block without padding 2 chars. - else if (decodeBlockOffset == 2) - { - buffer[offset + storedInBuffer++] = (byte) (decodeBlock[0] << 2 | decodeBlock[1] >> 4); - } - // We have invalid base64 data. - else if (decodeBlockOffset == 1) - { - throw new InvalidDataException("Incomplete base64 data.."); - } - - return storedInBuffer; - } - - // Process readed bytes. - for (int i = 0; i < readedCount; i++) - { - byte b = readBuffer[i]; - - // If padding char. - if (b == '=') - { - decodeBlock[decodeBlockOffset++] = (byte) '='; - paddedCount++; - rawBytesToRead--; - } - // If base64 char. - else if (BASE64_DECODE_TABLE[b] != -1) - { - decodeBlock[decodeBlockOffset++] = BASE64_DECODE_TABLE[b]; - rawBytesToRead--; - } - // Non-base64 char, skip it. - else {} - - // Decode block full, decode bytes. - if (decodeBlockOffset == 4) - { - // Decode 3x8-bit block. - decodedBlock[0] = (byte) (decodeBlock[0] << 2 | decodeBlock[1] >> 4); - decodedBlock[1] = (byte) ((decodeBlock[1] & 0xF) << 4 | decodeBlock[2] >> 2); - decodedBlock[2] = (byte) ((decodeBlock[2] & 0x3) << 6 | decodeBlock[3] >> 0); - - // Invalid base64 data. Base64 final quantum may have max 2 padding chars. - if (paddedCount > 2) - { - throw new InvalidDataException( - "Invalid base64 data, more than 2 padding chars(=)."); - } - - for (int n = 0; n < (3 - paddedCount); n++) - { - // We have room in "buffer", store byte there. - if (storedInBuffer < count) - { - buffer[offset + storedInBuffer++] = decodedBlock[n]; - } - //No room in "buffer", store reminder. - else - { - m_pDecodeReminder.Enqueue(decodedBlock[n]); - } - } - - decodeBlockOffset = 0; - paddedCount = 0; - } - } - } - - return storedInBuffer; - } - - /// - /// Encodes a sequence of bytes, writes to the current stream and advances the current position within this stream by the number of bytes written. - /// - /// An array of bytes. This method copies count bytes from buffer to the current stream. - /// The zero-based byte offset in buffer at which to begin copying bytes to the current stream. - /// The number of bytes to be written to the current stream. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when this.Finish has been called and this method is accessed. - /// Is raised when buffer is null reference. - /// Is raised when any of the arguments has invalid value. - /// Is raised when reading not supported. - public override void Write(byte[] buffer, int offset, int count) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (m_IsFinished) - { - throw new InvalidOperationException("Stream is marked as finished by calling Finish method."); - } - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (offset < 0 || offset > buffer.Length) - { - throw new ArgumentException("Invalid argument 'offset' value."); - } - if (count < 0 || count > (buffer.Length - offset)) - { - throw new ArgumentException("Invalid argument 'count' value."); - } - if ((m_AccessMode & FileAccess.Write) == 0) - { - throw new NotSupportedException(); - } - - /* RFC 4648. - - Base64 is processed from left to right by 4 6-bit byte block, 4 6-bit byte block - are converted to 3 8-bit bytes. - If base64 4 byte block doesn't have 3 8-bit bytes, missing bytes are marked with =. - - Value Encoding Value Encoding Value Encoding Value Encoding - 0 A 17 R 34 i 51 z - 1 B 18 S 35 j 52 0 - 2 C 19 T 36 k 53 1 - 3 D 20 U 37 l 54 2 - 4 E 21 V 38 m 55 3 - 5 F 22 W 39 n 56 4 - 6 G 23 X 40 o 57 5 - 7 H 24 Y 41 p 58 6 - 8 I 25 Z 42 q 59 7 - 9 J 26 a 43 r 60 8 - 10 K 27 b 44 s 61 9 - 11 L 28 c 45 t 62 + - 12 M 29 d 46 u 63 / - 13 N 30 e 47 v - 14 O 31 f 48 w (pad) = - 15 P 32 g 49 x - 16 Q 33 h 50 y - - NOTE: 4 base64 6-bit bytes = 3 8-bit bytes - // | 6-bit | 6-bit | 6-bit | 6-bit | - // | 1 2 3 4 5 6 | 1 2 3 4 5 6 | 1 2 3 4 5 6 | 1 2 3 4 5 6 | - // | 8-bit | 8-bit | 8-bit | - */ - - int encodeBufSize = m_pEncodeBuffer.Length; - - // Process all bytes. - for (int i = 0; i < count; i++) - { - m_pEncode3x8Block[m_OffsetInEncode3x8Block++] = buffer[offset + i]; - - // 3x8-bit encode block is full, encode it. - if (m_OffsetInEncode3x8Block == 3) - { - m_pEncodeBuffer[m_EncodeBufferOffset++] = BASE64_ENCODE_TABLE[m_pEncode3x8Block[0] >> 2]; - m_pEncodeBuffer[m_EncodeBufferOffset++] = - BASE64_ENCODE_TABLE[(m_pEncode3x8Block[0] & 0x03) << 4 | m_pEncode3x8Block[1] >> 4]; - m_pEncodeBuffer[m_EncodeBufferOffset++] = - BASE64_ENCODE_TABLE[(m_pEncode3x8Block[1] & 0x0F) << 2 | m_pEncode3x8Block[2] >> 6]; - m_pEncodeBuffer[m_EncodeBufferOffset++] = - BASE64_ENCODE_TABLE[(m_pEncode3x8Block[2] & 0x3F)]; - - // Encode buffer is full, write buffer to underlaying stream (we reserved 2 bytes for CRLF). - if (m_EncodeBufferOffset >= (encodeBufSize - 2)) - { - if (m_AddLineBreaks) - { - m_pEncodeBuffer[m_EncodeBufferOffset++] = (byte) '\r'; - m_pEncodeBuffer[m_EncodeBufferOffset++] = (byte) '\n'; - } - - m_pStream.Write(m_pEncodeBuffer, 0, m_EncodeBufferOffset); - m_EncodeBufferOffset = 0; - } - - m_OffsetInEncode3x8Block = 0; - } - } - } - - /// - /// Completes encoding. Call this method if all data has written and no more data. - /// - /// Is raised when this object is disposed and this method is accessed. - public void Finish() - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (m_IsFinished) - { - return; - } - m_IsFinished = true; - - // PADD left-over, if any. Write encode buffer to underlaying stream. - if (m_OffsetInEncode3x8Block == 1) - { - m_pEncodeBuffer[m_EncodeBufferOffset++] = BASE64_ENCODE_TABLE[m_pEncode3x8Block[0] >> 2]; - m_pEncodeBuffer[m_EncodeBufferOffset++] = - BASE64_ENCODE_TABLE[(m_pEncode3x8Block[0] & 0x03) << 4]; - m_pEncodeBuffer[m_EncodeBufferOffset++] = (byte) '='; - m_pEncodeBuffer[m_EncodeBufferOffset++] = (byte) '='; - } - else if (m_OffsetInEncode3x8Block == 2) - { - m_pEncodeBuffer[m_EncodeBufferOffset++] = BASE64_ENCODE_TABLE[m_pEncode3x8Block[0] >> 2]; - m_pEncodeBuffer[m_EncodeBufferOffset++] = - BASE64_ENCODE_TABLE[(m_pEncode3x8Block[0] & 0x03) << 4 | m_pEncode3x8Block[1] >> 4]; - m_pEncodeBuffer[m_EncodeBufferOffset++] = - BASE64_ENCODE_TABLE[(m_pEncode3x8Block[1] & 0x0F) << 2]; - m_pEncodeBuffer[m_EncodeBufferOffset++] = (byte) '='; - } - - if (m_EncodeBufferOffset > 0) - { - m_pStream.Write(m_pEncodeBuffer, 0, m_EncodeBufferOffset); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/DataSizeExceededException.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/DataSizeExceededException.cs deleted file mode 100644 index f9a69dd84..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/DataSizeExceededException.cs +++ /dev/null @@ -1,30 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IO -{ - #region usings - - using System; - - #endregion - - /// - /// The exception that is thrown when maximum allowed data size has exceeded. - /// - public class DataSizeExceededException : Exception {} -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/IncompleteDataException.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/IncompleteDataException.cs deleted file mode 100644 index a7546a0dd..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/IncompleteDataException.cs +++ /dev/null @@ -1,47 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IO -{ - #region usings - - using System; - - #endregion - - /// - /// The exception that is thrown when incomplete data received. - /// For example for ReadPeriodTerminated() method reaches end of stream before getting period terminator. - /// - public class IncompleteDataException : Exception - { - #region Constructor - - /// - /// Default constructor. - /// - public IncompleteDataException() {} - - /// - /// Default constructor. - /// - /// Exception message text. - public IncompleteDataException(string message) : base(message) {} - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/LineReader.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/LineReader.cs deleted file mode 100644 index 8f8a41e9a..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/LineReader.cs +++ /dev/null @@ -1,387 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IO -{ - #region usings - - using System; - using System.IO; - using System.Text; - - #endregion - - /// - /// This class implements "line" reader, LF and CRLF lines are supported. - /// - public class LineReader : IDisposable - { - #region Members - - private readonly int m_BufferSize = Workaround.Definitions.MaxStreamLineLength; - private readonly bool m_Owner; - private readonly byte[] m_pLineBuffer; - private readonly byte[] m_pReadBuffer; - private readonly Stream m_pSource; - private bool m_IsDisposed; - private int m_OffsetInBuffer; - private Encoding m_pCharset; - private int m_StoredInBuffer; - - #endregion - - #region Properties - - /// - /// Gets if this object is disposed. - /// - public bool IsDisposed - { - get { return m_IsDisposed; } - } - - /// - /// Gets source stream. - /// - /// Is raised when this object is disposed and this property is accessed. - public Stream Stream - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pSource; - } - } - - /// - /// Gets if line reader is Stream owner. - /// - /// Is raised when this object is disposed and this property is accessed. - public bool IsStreamOwner - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_Owner; - } - } - - /// - /// Gets or sets charset to us for deocoding bytes. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when null reference is passed. - public Encoding Charset - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pCharset; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (value == null) - { - throw new ArgumentNullException("value"); - } - - m_pCharset = value; - } - } - - /// - /// Gets number of bytes in read buffer. - /// - /// Is raised when this object is disposed and this property is accessed. - public int AvailableInBuffer - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_StoredInBuffer - m_OffsetInBuffer; - } - } - - /// - /// Gets if line reader can synchronize source stream to actual readed data position. - /// - public bool CanSyncStream - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pSource.CanSeek; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Stream from where to read lines. - /// Specifies if LineReader is owner of stream. - /// Read buffer size, value 1 means no buffering. - /// If this value is true, closing reader will close stream. - /// Is raised when stream is null. - public LineReader(Stream stream, bool owner, int bufferSize) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - if (bufferSize < 1) - { - throw new ArgumentException("Argument 'bufferSize' value must be >= 1."); - } - - m_pSource = stream; - m_Owner = owner; - m_BufferSize = bufferSize; - - m_pReadBuffer = new byte[bufferSize]; - m_pLineBuffer = new byte[Workaround.Definitions.MaxStreamLineLength]; - m_pCharset = Encoding.UTF8; - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public void Dispose() - { - if (m_IsDisposed) - { - return; - } - m_IsDisposed = true; - - if (m_Owner) - { - m_pSource.Dispose(); - } - } - - /// - /// Reads line from source stream. Returns null if end of stream(EOS) reached. - /// - /// Returns readed line or null if end of stream reached. - /// Is raised when this object is disposed and this method is accessed. - public string ReadLine() - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - int storedCount = ReadLine(m_pLineBuffer, - 0, - m_pLineBuffer.Length, - SizeExceededAction.ThrowException); - if (storedCount == -1) - { - return null; - } - else - { - return m_pCharset.GetString(m_pLineBuffer, 0, storedCount); - } - } - - /// - /// Reads binary line and stores it to the specified buffer. - /// - /// Buffer where to store line data. - /// Start offset in the buffer. - /// Maximum number of bytes store to the buffer. - /// Specifies how reader acts when line buffer too small. - /// Returns number of bytes stored to buffer or -1 if end of stream reached. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when buffer is null. - /// Is raised when any of the arguments has invalid value. - /// Is raised when line is bigger than buffer can store. - public int ReadLine(byte[] buffer, int offset, int count, SizeExceededAction exceededAction) - { - int rawBytesReaded = 0; - - return ReadLine(buffer, offset, count, exceededAction, out rawBytesReaded); - } - - /// - /// Reads binary line and stores it to the specified buffer. - /// - /// Buffer where to store line data. - /// Start offset in the buffer. - /// Maximum number of bytes store to the buffer. - /// Specifies how reader acts when line buffer too small. - /// Gets raw number of bytes readed from source. - /// Returns number of bytes stored to buffer or -1 if end of stream reached. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when buffer is null. - /// Is raised when any of the arguments has invalid value. - /// Is raised when line is bigger than buffer can store. - public virtual int ReadLine(byte[] buffer, - int offset, - int count, - SizeExceededAction exceededAction, - out int rawBytesReaded) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (offset < 0) - { - throw new ArgumentException("Argument 'offset' value must be >= 0."); - } - if (count < 0) - { - throw new ArgumentException("Argument 'count' value must be >= 0."); - } - if (buffer.Length < (count + offset)) - { - throw new ArgumentException( - "Argument 'count' value is bigger than specified 'buffer' can store."); - } - - int maxStoreCount = buffer.Length - offset; - int storedCount = 0; - rawBytesReaded = 0; - bool sizeExceeded = false; - while (true) - { - // No data in buffer, buffer next block. - if (m_StoredInBuffer == m_OffsetInBuffer) - { - m_OffsetInBuffer = 0; - m_StoredInBuffer = m_pSource.Read(m_pReadBuffer, 0, m_BufferSize); - // We reached end of stream, no more data. - if (m_StoredInBuffer == 0) - { - break; - } - } - - byte currentByte = m_pReadBuffer[m_OffsetInBuffer++]; - rawBytesReaded++; - // We have LF, we got a line. - if (currentByte == '\n') - { - break; - } - // We just skip CR, because CR must be with LF, otherwise it's invalid CR. - else if (currentByte == '\r') {} - // Normal byte. - else - { - // Line buffer full. - if (storedCount == maxStoreCount) - { - sizeExceeded = true; - - if (exceededAction == SizeExceededAction.ThrowException) - { - throw new LineSizeExceededException(); - } - } - else - { - buffer[offset + storedCount] = currentByte; - storedCount++; - } - } - } - - // Line buffer is not big enough to store whole line data. - if (sizeExceeded) - { - throw new LineSizeExceededException(); - } - // We haven't readed nothing, we are end of stream. - else if (rawBytesReaded == 0) - { - return -1; - } - else - { - return storedCount; - } - } - - /// - /// Sets stream position to the place we have consumed from stream and clears buffer data. - /// For example if we have 10 byets in buffer, stream position is actually +10 bigger than - /// we readed, the result is that stream.Position -= 10 and buffer is cleared. - /// - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when source stream won't support seeking. - public virtual void SyncStream() - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!m_pSource.CanSeek) - { - throw new InvalidOperationException("Source stream does not support seeking, can't sync."); - } - - if (AvailableInBuffer > 0) - { - m_pSource.Position -= AvailableInBuffer; - m_OffsetInBuffer = 0; - m_StoredInBuffer = 0; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/LineSizeExceededException.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/LineSizeExceededException.cs deleted file mode 100644 index 1e5b6a030..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/LineSizeExceededException.cs +++ /dev/null @@ -1,30 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IO -{ - #region usings - - using System; - - #endregion - - /// - /// The exception that is thrown when maximum allowed line size has exceeded. - /// - public class LineSizeExceededException : Exception {} -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/MultiStream.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/MultiStream.cs deleted file mode 100644 index fb5807496..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/MultiStream.cs +++ /dev/null @@ -1,306 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IO -{ - #region usings - - using System; - using System.Collections.Generic; - using System.IO; - - #endregion - - /// - /// This class combines multiple stream into one stream for reading. - /// The most common usage for that stream is when you need to insert some data to the beginning of some stream. - /// - public class MultiStream : Stream - { - #region Members - - private bool m_IsDisposed; - private Queue m_pStreams; - - #endregion - - #region Properties - - /// - /// Gets a value indicating whether the current stream supports reading. - /// - /// Is raised when this object is disposed and this property is accessed. - public override bool CanRead - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - return true; - } - } - - /// - /// Gets a value indicating whether the current stream supports seeking. - /// - /// Is raised when this object is disposed and this property is accessed. - public override bool CanSeek - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - return false; - } - } - - /// - /// Gets a value indicating whether the current stream supports writing. - /// - /// Is raised when this object is disposed and this property is accessed. - public override bool CanWrite - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - return false; - } - } - - /// - /// Gets the length in bytes of the stream. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when one of the source streams won't support Length property. - public override long Length - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - long length = 0; - foreach (Stream stream in m_pStreams.ToArray()) - { - length += stream.Length; - } - - return length; - } - } - - /// - /// Gets or sets the position within the current stream. This method is not supported and always throws a NotSupportedException. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when this property is accessed. - public override long Position - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - throw new NotSupportedException(); - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - throw new NotSupportedException(); - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public MultiStream() - { - m_pStreams = new Queue(); - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public new void Dispose() - { - if (m_IsDisposed) - { - return; - } - - m_IsDisposed = true; - m_pStreams = null; - - base.Dispose(); - } - - /// - /// Appends this stream to read queue. - /// - /// Stream to add. - /// Is raised when this object is disposed and and this method is accessed. - /// Is raised when stream is null. - public void AppendStream(Stream stream) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - m_pStreams.Enqueue(stream); - } - - /// - /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device. - /// - /// Is raised when this object is disposed and this method is accessed. - public override void Flush() - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - } - - /// - /// Sets the position within the current stream. This method is not supported and always throws a NotSupportedException. - /// - /// A byte offset relative to the origin parameter. - /// A value of type SeekOrigin indicating the reference point used to obtain the new position. - /// The new position within the current stream. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when this method is accessed. - public override long Seek(long offset, SeekOrigin origin) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - throw new NotSupportedException(); - } - - /// - /// Sets the length of the current stream. This method is not supported and always throws a NotSupportedException. - /// - /// The desired length of the current stream in bytes. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when this method is accessed. - public override void SetLength(long value) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - throw new NotSupportedException(); - } - - /// - /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. - /// - /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source. - /// The zero-based byte offset in buffer at which to begin storing the data read from the current stream. - /// The maximum number of bytes to be read from the current stream. - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// Is raised when this object is disposed and this method is accessed. - public override int Read(byte[] buffer, int offset, int count) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - while (true) - { - // We have readed all streams data, no data left. - if (m_pStreams.Count == 0) - { - return 0; - } - else - { - int readedCount = m_pStreams.Peek().Read(buffer, offset, count); - // We have readed all current stream data. - if (readedCount == 0) - { - // Move to next stream . - m_pStreams.Dequeue(); - - // Next while loop will process "read". - } - else - { - return readedCount; - } - } - } - } - - /// - /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. - /// This method is not supported and always throws a NotSupportedException. - /// - /// An array of bytes. This method copies count bytes from buffer to the current stream. - /// The zero-based byte offset in buffer at which to begin copying bytes to the current stream. - /// The number of bytes to be written to the current stream. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when this method is accessed. - public override void Write(byte[] buffer, int offset, int count) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - throw new NotSupportedException(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/PartialStream.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/PartialStream.cs deleted file mode 100644 index 1dd97b82d..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/PartialStream.cs +++ /dev/null @@ -1,300 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IO -{ - #region usings - - using System; - using System.IO; - - #endregion - - /// - /// Implements read-only stream what operates on specified range of source stream - /// - public class PartialStream : Stream - { - #region Members - - private readonly long m_Length; - private readonly Stream m_pStream; - private readonly long m_Start; - private bool m_IsDisposed; - private long m_Position; - - #endregion - - #region Properties - - /// - /// Gets a value indicating whether the current stream supports reading. - /// - /// Is raised when this object is disposed and this property is accessed. - public override bool CanRead - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - return true; - } - } - - /// - /// Gets a value indicating whether the current stream supports seeking. - /// - /// Is raised when this object is disposed and this property is accessed. - public override bool CanSeek - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - return true; - } - } - - /// - /// Gets a value indicating whether the current stream supports writing. - /// - /// Is raised when this object is disposed and this property is accessed. - public override bool CanWrite - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - return false; - } - } - - /// - /// Gets the length in bytes of the stream. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when this property is accessed. - public override long Length - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - return m_Length; - } - } - - /// - /// Gets or sets the position within the current stream. - /// - /// Is raised when this object is disposed and this property is accessed. - public override long Position - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - return m_Position; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - if (value < 0 || value > Length) - { - throw new ArgumentException("Property 'Position' value must be >= 0 and <= this.Length."); - } - - m_Position = value; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Source stream. - /// Zero based start positon in source stream. - /// Length of stream. - /// Is raised when stream is null. - /// Is raised when any of the arguments has invalid value. - public PartialStream(Stream stream, long start, long length) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - if (!stream.CanSeek) - { - throw new ArgumentException("Argument 'stream' does not support seeking."); - } - if (start < 0) - { - throw new ArgumentException("Argument 'start' value must be >= 0."); - } - if ((start + length) > stream.Length) - { - throw new ArgumentException("Argument 'length' value will exceed source stream length."); - } - - m_pStream = stream; - m_Start = start; - m_Length = length; - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public new void Dispose() - { - if (m_IsDisposed) - { - return; - } - - m_IsDisposed = true; - - base.Dispose(); - } - - /// - /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device. - /// - /// Is raised when this object is disposed and this method is accessed. - public override void Flush() - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - } - - /// - /// Sets the position within the current stream. - /// - /// A byte offset relative to the origin parameter. - /// A value of type SeekOrigin indicating the reference point used to obtain the new position. - /// The new position within the current stream. - /// Is raised when this object is disposed and this method is accessed. - public override long Seek(long offset, SeekOrigin origin) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - if (origin == SeekOrigin.Begin) - { - m_Position = 0; - } - else if (origin == SeekOrigin.Current) {} - else if (origin == SeekOrigin.End) - { - m_Position = m_Length; - } - - return m_Position; - } - - /// - /// Sets the length of the current stream. This method is not supported and always throws a NotSupportedException. - /// - /// The desired length of the current stream in bytes. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when this method is accessed. - public override void SetLength(long value) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - throw new NotSupportedException(); - } - - /// - /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. - /// - /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source. - /// The zero-based byte offset in buffer at which to begin storing the data read from the current stream. - /// The maximum number of bytes to be read from the current stream. - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// Is raised when this object is disposed and this method is accessed. - public override int Read(byte[] buffer, int offset, int count) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - if (m_pStream.Position != (m_Start + m_Position)) - { - m_pStream.Position = m_Start + m_Position; - } - int readedCount = m_pStream.Read(buffer, offset, Math.Min(count, (int) (Length - m_Position))); - m_Position += readedCount; - - return readedCount; - } - - /// - /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. - /// This method is not supported and always throws a NotSupportedException. - /// - /// An array of bytes. This method copies count bytes from buffer to the current stream. - /// The zero-based byte offset in buffer at which to begin copying bytes to the current stream. - /// The number of bytes to be written to the current stream. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when this method is accessed. - public override void Write(byte[] buffer, int offset, int count) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - throw new NotSupportedException(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/QuotedPrintableStream.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/QuotedPrintableStream.cs deleted file mode 100644 index 4660a7824..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/QuotedPrintableStream.cs +++ /dev/null @@ -1,347 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IO -{ - #region usings - - using System; - using System.Globalization; - using System.IO; - - #endregion - - /// - /// Implements RFC 2045 6.7. Quoted-Printable stream. - /// - public class QuotedPrintableStream : Stream - { - #region Members - - private readonly FileAccess m_AccessMode = FileAccess.ReadWrite; - private readonly byte[] m_pDecodedBuffer; - private readonly byte[] m_pEncodedBuffer; - private readonly SmartStream m_pStream; - private int m_DecodedCount; - private int m_DecodedOffset; - private int m_EncodedCount; - private byte[] line_buf; - - #endregion - - #region Properties - - /// - /// Gets a value indicating whether the current stream supports reading. - /// - /// Is raised when this object is disposed and this property is accessed. - public override bool CanRead - { - get { return (m_AccessMode & FileAccess.Read) != 0; } - } - - /// - /// Gets a value indicating whether the current stream supports seeking. - /// - /// Is raised when this object is disposed and this property is accessed. - public override bool CanSeek - { - get { return false; } - } - - /// - /// Gets a value indicating whether the current stream supports writing. - /// - /// Is raised when this object is disposed and this property is accessed. - public override bool CanWrite - { - get { return (m_AccessMode & FileAccess.Write) != 0; } - } - - /// - /// Gets the length in bytes of the stream. This method is not supported and always throws a NotSupportedException. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when this property is accessed. - public override long Length - { - get { throw new NotSupportedException(); } - } - - /// - /// Gets or sets the position within the current stream. This method is not supported and always throws a NotSupportedException. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when this property is accessed. - public override long Position - { - get { throw new NotSupportedException(); } - - set { throw new NotSupportedException(); } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Source stream. - /// Specifies stream access mode. - /// Is raised when stream is null reference. - public QuotedPrintableStream(SmartStream stream, FileAccess access) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - m_pStream = stream; - m_AccessMode = access; - - m_pDecodedBuffer = new byte[Workaround.Definitions.MaxStreamLineLength]; - m_pEncodedBuffer = new byte[78]; - line_buf = new byte[Workaround.Definitions.MaxStreamLineLength]; - } - - #endregion - - #region Methods - - /// - /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device. - /// - /// Is raised when this object is disposed and this method is accessed. - public override void Flush() - { - if (m_EncodedCount > 0) - { - m_pStream.Write(m_pEncodedBuffer, 0, m_EncodedCount); - m_EncodedCount = 0; - } - } - - /// - /// Sets the position within the current stream. This method is not supported and always throws a NotSupportedException. - /// - /// A byte offset relative to the origin parameter. - /// A value of type SeekOrigin indicating the reference point used to obtain the new position. - /// The new position within the current stream. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when this method is accessed. - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - /// - /// Sets the length of the current stream. This method is not supported and always throws a NotSupportedException. - /// - /// The desired length of the current stream in bytes. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when this method is accessed. - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - /// - /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. - /// - /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source. - /// The zero-based byte offset in buffer at which to begin storing the data read from the current stream. - /// The maximum number of bytes to be read from the current stream. - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// Is raised when buffer is null reference. - /// Is raised when any of the arguments has invalid value. - /// Is raised when reading not supported. - public override int Read(byte[] buffer, int offset, int count) - { - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (offset < 0 || offset > buffer.Length) - { - throw new ArgumentException("Invalid argument 'offset' value."); - } - if (offset + count > buffer.Length) - { - throw new ArgumentException("Invalid argument 'count' value."); - } - if ((m_AccessMode & FileAccess.Read) == 0) - { - throw new NotSupportedException(); - } - - while (true) - { - // Read next quoted-printable line and decode it. - if (m_DecodedOffset >= m_DecodedCount) - { - m_DecodedOffset = 0; - m_DecodedCount = 0; - SmartStream.ReadLineAsyncOP readLineOP = new SmartStream.ReadLineAsyncOP(line_buf, - SizeExceededAction - . - ThrowException); - m_pStream.ReadLine(readLineOP, false); - // IO error reading line. - if (readLineOP.Error != null) - { - throw readLineOP.Error; - } - // We reached end of stream. - else if (readLineOP.BytesInBuffer == 0) - { - return 0; - } - // Decode quoted-printable line. - else - { - // Process bytes. - bool softLineBreak = false; - int lineLength = readLineOP.LineBytesInBuffer; - for (int i = 0; i < readLineOP.LineBytesInBuffer; i++) - { - byte b = readLineOP.Buffer[i]; - // We have soft line-break. - if (b == '=' && i == (lineLength - 1)) - { - softLineBreak = true; - } - // We should have =XX char. - else if (b == '=') - { - byte b1 = readLineOP.Buffer[++i]; - byte b2 = readLineOP.Buffer[++i]; - - string b1b2 = ((char)b1).ToString() + (char)b2; - byte b1b2_num; - if (byte.TryParse(b1b2, NumberStyles.HexNumber, null, out b1b2_num)) - { - m_pDecodedBuffer[m_DecodedCount++] = b1b2_num; - } - else - { - m_pDecodedBuffer[m_DecodedCount++] = b; - m_pDecodedBuffer[m_DecodedCount++] = b1; - m_pDecodedBuffer[m_DecodedCount++] = b2; - } - } - // Normal char. - else - { - m_pDecodedBuffer[m_DecodedCount++] = b; - } - } - - if (!softLineBreak) - { - m_pDecodedBuffer[m_DecodedCount++] = (byte) '\r'; - m_pDecodedBuffer[m_DecodedCount++] = (byte) '\n'; - } - } - } - - // We some decoded data, return it. - if (m_DecodedOffset < m_DecodedCount) - { - int countToCopy = Math.Min(count, m_DecodedCount - m_DecodedOffset); - Array.Copy(m_pDecodedBuffer, m_DecodedOffset, buffer, offset, countToCopy); - m_DecodedOffset += countToCopy; - - return countToCopy; - } - } - } - - /// - /// Encodes a sequence of bytes, writes to the current stream and advances the current position within this stream by the number of bytes written. - /// - /// An array of bytes. This method copies count bytes from buffer to the current stream. - /// The zero-based byte offset in buffer at which to begin copying bytes to the current stream. - /// The number of bytes to be written to the current stream. - /// Is raised when buffer is null reference. - /// Is raised when any of the arguments has invalid value. - /// Is raised when reading not supported. - public override void Write(byte[] buffer, int offset, int count) - { - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (offset < 0 || offset > buffer.Length) - { - throw new ArgumentException("Invalid argument 'offset' value."); - } - if (offset + count > buffer.Length) - { - throw new ArgumentException("Invalid argument 'count' value."); - } - if ((m_AccessMode & FileAccess.Write) == 0) - { - throw new NotSupportedException(); - } - - // Process bytes. - for (int i = 0; i < count; i++) - { - byte b = buffer[offset + i]; - - // We don't need to encode byte. - if ((b >= 33 && b <= 60) || (b >= 62 && b <= 126)) - { - // Maximum allowed quoted-printable line length reached, do soft line break. - if (m_EncodedCount >= 75) - { - m_pEncodedBuffer[m_EncodedCount++] = (byte) '='; - m_pEncodedBuffer[m_EncodedCount++] = (byte) '\r'; - m_pEncodedBuffer[m_EncodedCount++] = (byte) '\n'; - - // Write encoded data to underlying stream. - Flush(); - } - - m_pEncodedBuffer[m_EncodedCount++] = b; - } - // We need to encode byte. - else - { - // Maximum allowed quote-printable line length reached, do soft line break. - if (m_EncodedCount >= 73) - { - m_pEncodedBuffer[m_EncodedCount++] = (byte) '='; - m_pEncodedBuffer[m_EncodedCount++] = (byte) '\r'; - m_pEncodedBuffer[m_EncodedCount++] = (byte) '\n'; - - // Write encoded data to underlying stream. - Flush(); - } - - // Encode byte. - m_pEncodedBuffer[m_EncodedCount++] = (byte) '='; - m_pEncodedBuffer[m_EncodedCount++] = (byte) (b >> 4).ToString("x")[0]; - m_pEncodedBuffer[m_EncodedCount++] = (byte) (b & 0xF).ToString("x")[0]; - } - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/ReadLineEventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/ReadLineEventArgs.cs deleted file mode 100644 index 5bb7605d7..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/ReadLineEventArgs.cs +++ /dev/null @@ -1,269 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.Text; - -namespace LumiSoft.Net.IO -{ - /// - /// This class provides data to SmartStream.ReadLine method. - /// - /// This class can be reused on multiple calls of SmartStream.ReadLine method. - public class ReadLineEventArgs : EventArgs - { - private bool m_IsDisposed = false; - private bool m_IsCompleted = false; - private byte[] m_pBuffer = null; - private SizeExceededAction m_ExceededAction = SizeExceededAction.JunkAndThrowException; - private bool m_CRLFLinesOnly = false; - private int m_BytesInBuffer = 0; - private Exception m_pException = null; - - /// - /// Default constructor. - /// - /// Line buffer. - /// Specifies how line-reader behaves when maximum line size exceeded. - /// Is raised when buffer is null reference. - public ReadLineEventArgs(byte[] buffer,SizeExceededAction exceededAction) - { - if(buffer == null){ - throw new ArgumentNullException("buffer"); - } - - m_pBuffer = buffer; - m_ExceededAction = exceededAction; - } - - - #region method Start - - internal bool Start(SmartStream stream) - { - // TODO: Clear old data, if any. - m_IsCompleted = false; - m_BytesInBuffer = 0; - m_pException = null; - - return DoLineReading(); - } - - #endregion - - #region method Buffering_Completed - - /// - /// Is called when asynchronous read buffer buffering has completed. - /// - /// Exception that occured during async operation. - private void Buffering_Completed(Exception x) - { - /* - if(x != null){ - m_pException = x; - Completed(); - } - // We reached end of stream, no more data. - else if(m_pOwner.BytesInReadBuffer == 0){ - Completed(); - } - // Continue line reading. - else{ - DoLineReading(); - }*/ - } - - #endregion - - #region method DoLineReading - - /// - /// Starts/continues line reading. - /// - /// Returns true if line reading completed. - private bool DoLineReading() - { - try{ - while(true){ - /* - // Read buffer empty, buff next data block. - if(m_pOwner.BytesInReadBuffer == 0){ - // Buffering started asynchronously. - if(m_pOwner.BufferRead(true,this.Buffering_Completed)){ - return; - } - // Buffering completed synchronously, continue processing. - else{ - // We reached end of stream, no more data. - if(m_pOwner.BytesInReadBuffer == 0){ - Completed(); - return; - } - } - }*/ - - byte b = 1; //m_pOwner.m_pReadBuffer[m_pOwner.m_ReadBufferOffset++]; - //m_BytesInBuffer++; - - // TODO: Check for room, Store byte. - - // We have LF line. - if(b == '\n'){ - // TODO: - // m_CRLFLinesOnly - } - } - } - catch(Exception x){ - m_pException = x; - OnCompleted(); - } - - return true; - } - - #endregion - - - #region Properties implementation - - /// - /// Gets if this object is disposed. - /// - public bool IsDisposed - { - get{ return m_IsDisposed; } - } - - /// - /// Gets if asynchronous operation has completed. - /// - /// Is raised when this object is disposed and this property is accessed. - public bool IsCompleted - { - get{ - if(m_IsDisposed){ - throw new ObjectDisposedException(this.GetType().Name); - } - - return m_IsCompleted; - } - } - - /// - /// Gets line buffer. - /// - /// Is raised when this object is disposed and this property is accessed. - public byte[] Buffer - { - get{ - if(m_IsDisposed){ - throw new ObjectDisposedException(this.GetType().Name); - } - - return m_pBuffer; - } - } - - /// - /// Gets number of bytes stored in the buffer. Line feed characters not included. - /// - /// Is raised when this object is disposed and this property is accessed. - public int BytesInBuffer - { - get{ - if(m_IsDisposed){ - throw new ObjectDisposedException(this.GetType().Name); - } - - return m_BytesInBuffer; - } - } - - /// - /// Gets line as ASCII string. - /// - /// Is raised when this object is disposed and this property is accessed. - public string LineAscii - { - get{ - if(m_IsDisposed){ - throw new ObjectDisposedException(this.GetType().Name); - } - - return Encoding.ASCII.GetString(m_pBuffer,0,m_BytesInBuffer); - } - } - - /// - /// Gets line as UTF-8 string. - /// - /// Is raised when this object is disposed and this property is accessed. - public string LineUtf8 - { - get{ - if(m_IsDisposed){ - throw new ObjectDisposedException(this.GetType().Name); - } - - return Encoding.UTF8.GetString(m_pBuffer,0,m_BytesInBuffer); - } - } - - /// - /// Gets error occured during asynchronous operation. Value null means no error. - /// - /// Is raised when this object is disposed and this property is accessed. - public Exception Error - { - get{ - if(m_IsDisposed){ - throw new ObjectDisposedException(this.GetType().Name); - } - - return m_pException; - } - } - - #endregion - - #region Events implementation - - /// - /// Is raised when asynchronous operation has completed. - /// - public event EventHandler Completed = null; - - #region method OnCompleted - - /// - /// Raises Completed event. - /// - private void OnCompleted() - { - if(this.Completed != null){ - this.Completed(this,this); - } - } - - #endregion - - #endregion - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/ReadWriteControlledStream.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/ReadWriteControlledStream.cs deleted file mode 100644 index ba3b09d2c..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/ReadWriteControlledStream.cs +++ /dev/null @@ -1,207 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IO -{ - #region usings - - using System; - using System.IO; - - #endregion - - /// - /// This class implements read,write or read-write access stream. - /// - public class ReadWriteControlledStream : Stream - { - #region Members - - private readonly FileAccess m_AccessMode = FileAccess.ReadWrite; - private readonly Stream m_pStream; - - #endregion - - #region Properties - - /// - /// Gets a value indicating whether the current stream supports reading. - /// - /// Is raised when this object is disposed and this property is accessed. - public override bool CanRead - { - get { return (m_AccessMode & FileAccess.Read) != 0; } - } - - /// - /// Gets a value indicating whether the current stream supports seeking. - /// - /// Is raised when this object is disposed and this property is accessed. - public override bool CanSeek - { - get { return m_pStream.CanSeek; } - } - - /// - /// Gets a value indicating whether the current stream supports writing. - /// - /// Is raised when this object is disposed and this property is accessed. - public override bool CanWrite - { - get { return (m_AccessMode & FileAccess.Write) != 0; } - } - - /// - /// Gets the length in bytes of the stream. This method is not supported and always throws a NotSupportedException. - /// - /// Is raised when this object is disposed and this property is accessed. - public override long Length - { - get { return m_pStream.Length; } - } - - /// - /// Gets or sets the position within the current stream. This method is not supported and always throws a NotSupportedException. - /// - /// Is raised when this object is disposed and this property is accessed. - public override long Position - { - get { return m_pStream.Position; } - - set { m_pStream.Position = value; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Source stream. - /// This stream access mode. - /// Is raised when stream is null reference. - public ReadWriteControlledStream(Stream stream, FileAccess access) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - m_pStream = stream; - m_AccessMode = access; - } - - #endregion - - #region Methods - - /// - /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device. - /// - /// Is raised when this object is disposed and this method is accessed. - public override void Flush() - { - m_pStream.Flush(); - } - - /// - /// Sets the position within the current stream. This method is not supported and always throws a NotSupportedException. - /// - /// A byte offset relative to the origin parameter. - /// A value of type SeekOrigin indicating the reference point used to obtain the new position. - /// The new position within the current stream. - public override long Seek(long offset, SeekOrigin origin) - { - return m_pStream.Seek(offset, origin); - } - - /// - /// Sets the length of the current stream. This method is not supported and always throws a NotSupportedException. - /// - /// The desired length of the current stream in bytes. - public override void SetLength(long value) - { - m_pStream.SetLength(value); - } - - /// - /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. - /// - /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source. - /// The zero-based byte offset in buffer at which to begin storing the data read from the current stream. - /// The maximum number of bytes to be read from the current stream. - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// Is raised when buffer is null reference. - /// Is raised when any of the arguments has invalid value. - /// Is raised when reading not supported. - public override int Read(byte[] buffer, int offset, int count) - { - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (offset < 0 || offset > buffer.Length) - { - throw new ArgumentException("Invalid argument 'offset' value."); - } - if (offset + count > buffer.Length) - { - throw new ArgumentException("Invalid argument 'count' value."); - } - if ((m_AccessMode & FileAccess.Read) == 0) - { - throw new NotSupportedException(); - } - - return m_pStream.Read(buffer, offset, count); - } - - /// - /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. - /// - /// An array of bytes. This method copies count bytes from buffer to the current stream. - /// The zero-based byte offset in buffer at which to begin copying bytes to the current stream. - /// The number of bytes to be written to the current stream. - /// Is raised when buffer is null reference. - /// Is raised when any of the arguments has invalid value. - /// Is raised when reading not supported. - public override void Write(byte[] buffer, int offset, int count) - { - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (offset < 0 || offset > buffer.Length) - { - throw new ArgumentException("Invalid argument 'offset' value."); - } - if (offset + count > buffer.Length) - { - throw new ArgumentException("Invalid argument 'count' value."); - } - if ((m_AccessMode & FileAccess.Write) == 0) - { - throw new NotSupportedException(); - } - - m_pStream.Write(buffer, offset, count); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/SizeExceededAction.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/SizeExceededAction.cs deleted file mode 100644 index 5e739b3f2..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/SizeExceededAction.cs +++ /dev/null @@ -1,36 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IO -{ - /// - /// Specifies action what is done if requested action exceeds maximum allowed size. - /// - public enum SizeExceededAction - { - /// - /// Throws exception at once when maximum size exceeded. - /// - ThrowException = 1, - - /// - /// Junks all data what exceeds maximum allowed size and after requested operation completes, - /// throws exception. - /// - JunkAndThrowException = 2, - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/SmartStream.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/SmartStream.cs deleted file mode 100644 index 1eb15f53f..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IO/SmartStream.cs +++ /dev/null @@ -1,3447 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IO -{ - #region usings - - using System; - using System.Diagnostics; - using System.IO; - using System.Text; - using System.Threading; - using System.Linq; - - #endregion - - /// - /// This class is wrapper to normal stream, provides most needed stream methods which are missing from normal stream. - /// - public class SmartStream : Stream - { - #region Delegates - - private delegate void BufferCallback(Exception x); - - #endregion - - #region Members - - private readonly byte[] m_pReadBuffer; - private readonly BufferReadAsyncOP m_pReadBufferOP; - - private Stream m_pStream; - private int m_BufferSize = Workaround.Definitions.MaxStreamLineLength; - private long m_BytesReaded; - private long m_BytesWritten; - private bool m_IsDisposed; - private bool m_IsOwner; - private DateTime m_LastActivity; - private Encoding m_pEncoding = Encoding.Default; - private int m_ReadBufferCount; - private int m_ReadBufferOffset; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Stream to wrap. - /// Specifies if SmartStream is owner of stream. - /// Is raised when stream is null. - public SmartStream(Stream stream, bool owner) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - m_pStream = stream; - m_pWriteStream = stream; - m_IsOwner = owner; - m_pReadBuffer = new byte[m_BufferSize]; - m_pReadBufferOP = new BufferReadAsyncOP(this); - - m_LastActivity = DateTime.Now; - } - - #endregion - - #region Properties - - /// - /// Gets number of bytes in read buffer. - /// - /// Is raised when this object is disposed and this property is accessed. - public int BytesInReadBuffer - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - return m_ReadBufferCount - m_ReadBufferOffset; - } - } - - /// - /// Gets how many bytes are readed through this stream. - /// - /// Is raised when this object is disposed and this property is accessed. - public long BytesReaded - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - return m_BytesReaded; - } - } - - /// - /// Gets how many bytes are written through this stream. - /// - /// Is raised when this object is disposed and this property is accessed. - public long BytesWritten - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - return m_BytesWritten; - } - } - - /// - /// Gets a value indicating whether the current stream supports reading. - /// - /// Is raised when this object is disposed and this property is accessed. - public override bool CanRead - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - return m_pStream.CanRead; - } - } - - /// - /// Gets a value indicating whether the current stream supports seeking. - /// - /// Is raised when this object is disposed and this property is accessed. - public override bool CanSeek - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - return m_pStream.CanSeek; - } - } - - /// - /// Gets a value indicating whether the current stream supports writing. - /// - /// Is raised when this object is disposed and this property is accessed. - public override bool CanWrite - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - return m_pStream.CanWrite; - } - } - - /// - /// Gets or sets string related methods default encoding. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when null value is passed. - public Encoding Encoding - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - return m_pEncoding; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - if (value == null) - { - throw new ArgumentNullException(); - } - - m_pEncoding = value; - } - } - - /// - /// Gets if SmartStream is owner of source stream. This property affects like closing this stream will close SourceStream if IsOwner true. - /// - /// Is raised when this object is disposed and this property is accessed. - public bool IsOwner - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - return m_IsOwner; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - m_IsOwner = value; - } - } - - /// - /// Gets the last time when data was read or written. - /// - /// Is raised when this object is disposed and this property is accessed. - public DateTime LastActivity - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - return m_LastActivity; - } - } - - /// - /// Gets the length in bytes of the stream. - /// - /// Is raised when this object is disposed and this property is accessed. - public override long Length - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - return m_pStream.Length; - } - } - - /// - /// Gets or sets the position within the current stream. - /// - /// Is raised when this object is disposed and this property is accessed. - public override long Position - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - return m_pStream.Position; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - m_pStream.Position = value; - - // Clear read buffer. - m_ReadBufferOffset = 0; - m_ReadBufferCount = 0; - } - } - - /// - /// Gets this stream underlying stream. - /// - /// Is raised when this object is disposed and this property is accessed. - public Stream SourceStream - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - return m_pStream; - } - } - - private bool _memoryBuffer = false; - private Stream m_pWriteStream; - - public bool MemoryBuffer - { - get - { - return _memoryBuffer; - } - set - { - if (value != _memoryBuffer) - { - _memoryBuffer = value; - if (_memoryBuffer) - { - m_pWriteStream = new MemoryStream(); - } - else - { - if (m_pWriteStream is MemoryStream) - { - //Copy all - m_pWriteStream.Position = 0; - Net_Utils.StreamCopy(m_pWriteStream, m_pStream, m_BufferSize); - m_pWriteStream.Close(); - m_pWriteStream.Dispose(); - m_pStream.Flush(); - } - m_pWriteStream = m_pStream; - } - } - } - } - - #endregion - - #region Methods - - public override void Close() - { - Flush(); - m_pStream.Close(); - base.Close(); - } - - /// - /// Cleans up any resources being used. - /// - public new void Dispose() - { - if (m_IsDisposed) - { - return; - } - m_IsDisposed = true; - - if (m_IsOwner) - { - Close(); - m_pStream.Dispose(); - } - } - - // TODO: - // *) timeout support for sync versions - // *) WriteHeader SmartStream buffers !!! we may not do this if stream wont support seeking. - - /// - /// Begins an asynchronous line reading from the source stream. - /// - /// Buffer where to store readed line data. - /// The location in buffer to begin storing the data. - /// Maximum number of bytes to read. - /// Specifies how this method behaves when maximum line size exceeded. - /// The AsyncCallback delegate that is executed when asynchronous operation completes. - /// An object that contains any additional user-defined data. - /// An IAsyncResult that represents the asynchronous call. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when buffer is null reference. - /// is raised when any of the arguments has invalid value. - public IAsyncResult BeginReadLine(byte[] buffer, - int offset, - int maxCount, - SizeExceededAction exceededAction, - AsyncCallback callback, - object state) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (offset < 0) - { - throw new ArgumentOutOfRangeException("offset", "Argument 'offset' value must be >= 0."); - } - if (offset > buffer.Length) - { - throw new ArgumentOutOfRangeException("offset", - "Argument 'offset' value must be < buffer.Length."); - } - if (maxCount < 0) - { - throw new ArgumentOutOfRangeException("maxCount", "Argument 'maxCount' value must be >= 0."); - } - if (offset + maxCount > buffer.Length) - { - throw new ArgumentOutOfRangeException("maxCount", - "Argument 'maxCount' is bigger than than argument 'buffer' can store."); - } - - return new ReadLineAsyncOperation(this, buffer, offset, maxCount, exceededAction, callback, state); - } - - /// - /// Handles the end of an asynchronous line reading. - /// - /// An IAsyncResult that represents an asynchronous call. - /// Returns number of bytes stored to buffer. Returns -1 if no more data, end of stream reached. - /// Is raised when asyncResult is null reference. - /// Is raised when invalid asyncResult passed to this method. - /// Is raised when EndReadLine has already been called for specified asyncResult. - /// Is raised when maxCount value is exceeded. - public int EndReadLine(IAsyncResult asyncResult) - { - if (asyncResult == null) - { - throw new ArgumentNullException("asyncResult"); - } - if (!(asyncResult is ReadLineAsyncOperation)) - { - throw new ArgumentException( - "Argument 'asyncResult' was not returned by a call to the BeginReadLine method."); - } - - ReadLineAsyncOperation ar = (ReadLineAsyncOperation) asyncResult; - if (ar.IsEndCalled) - { - throw new InvalidOperationException( - "EndReadLine is already called for specified 'asyncResult'."); - } - ar.AsyncWaitHandle.WaitOne(); - ar.IsEndCalled = true; - - if (ar.BytesReaded == 0) - { - return -1; - } - else - { - return ar.BytesStored; - } - } - - /// - /// Begins line reading. - /// - /// Read line opeartion. - /// If true then this method can complete asynchronously. If false, this method completed always syncronously. - /// Returns true if read line completed synchronously, false if asynchronous operation pending. - /// Is raised when op is null reference. - public bool ReadLine(ReadLineAsyncOP op, bool async) - { - if (op == null) - { - throw new ArgumentNullException("op"); - } - - if (!op.Start(async, this)) - { - /* REMOVE ME: - if(!async){ - // Wait while async operation completes. - while(!op.IsCompleted){ - Thread.Sleep(1); - } - - return true; - } - else{ - return false; - }*/ - - return false; - } - // Completed synchronously. - else - { - return true; - } - } - - /// - /// Begins an asynchronous header reading from the source stream. - /// - /// Stream where to store readed header. - /// Maximum number of bytes to read. Value 0 means not limited. - /// Specifies action what is done if maxCount number of bytes has exceeded. - /// The AsyncCallback delegate that is executed when asynchronous operation completes. - /// An object that contains any additional user-defined data. - /// An IAsyncResult that represents the asynchronous call. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when storeStream is null reference. - /// Is raised when any of the arguments has invalid value. - public IAsyncResult BeginReadHeader(Stream storeStream, - int maxCount, - SizeExceededAction exceededAction, - AsyncCallback callback, - object state) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (storeStream == null) - { - throw new ArgumentNullException("storeStream"); - } - if (maxCount < 0) - { - throw new ArgumentException("Argument 'maxCount' must be >= 0."); - } - - return new ReadToTerminatorAsyncOperation(this, - "", - storeStream, - maxCount, - exceededAction, - callback, - state); - } - - /// - /// Handles the end of an asynchronous header reading. - /// - /// An IAsyncResult that represents an asynchronous call. - /// Returns number of bytes stored to storeStream. - /// Is raised when asyncResult is null reference. - /// Is raised when invalid asyncResult passed to this method. - /// Is raised when EndReadLine has already been called for specified asyncResult. - /// Is raised when source stream has too big line. - /// Is raised when reading exceeds maxCount specified value. - /// Is raised when source stream closed before header-terminator reached. - public int EndReadHeader(IAsyncResult asyncResult) - { - if (asyncResult == null) - { - throw new ArgumentNullException("asyncResult"); - } - if (!(asyncResult is ReadToTerminatorAsyncOperation)) - { - throw new ArgumentException( - "Argument 'asyncResult' was not returned by a call to the BeginReadHeader method."); - } - - ReadToTerminatorAsyncOperation ar = (ReadToTerminatorAsyncOperation) asyncResult; - if (ar.IsEndCalled) - { - throw new InvalidOperationException( - "EndReadHeader is already called for specified 'asyncResult'."); - } - ar.AsyncWaitHandle.WaitOne(); - ar.IsEndCalled = true; - if (ar.Exception != null) - { - throw ar.Exception; - } - - return (int) ar.BytesStored; - } - - /// - /// Reads header from stream and stores to the specified storeStream. - /// - /// Stream where to store readed header. - /// Maximum number of bytes to read. Value 0 means not limited. - /// Specifies action what is done if maxCount number of bytes has exceeded. - /// Returns how many bytes readed from source stream. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when storeStream is null. - /// Is raised when any of the arguments has invalid value. - /// Is raised when source stream has too big line. - /// Is raised when reading exceeds maxCount specified value. - /// Is raised when source stream closed before header-terminator reached. - public int ReadHeader(Stream storeStream, int maxCount, SizeExceededAction exceededAction) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (storeStream == null) - { - throw new ArgumentNullException("storeStream"); - } - if (maxCount < 0) - { - throw new ArgumentException("Argument 'maxCount' must be >= 0."); - } - - IAsyncResult ar = BeginReadHeader(storeStream, maxCount, exceededAction, null, null); - - return EndReadHeader(ar); - } - - /// - /// Begins period-terminated data reading. - /// - /// Read period terminated opeartion. - /// If true then this method can complete asynchronously. If false, this method completed always syncronously. - /// Returns true if read line completed synchronously, false if asynchronous operation pending. - /// Is raised when op is null reference. - public bool ReadPeriodTerminated(ReadPeriodTerminatedAsyncOP op, bool async) - { - if (op == null) - { - throw new ArgumentNullException("op"); - } - - if (!op.Start(this)) - { - if (!async) - { - // Wait while async operation completes. - while (!op.IsCompleted) - { - Thread.Sleep(1); - } - - return true; - } - else - { - return false; - } - } - // Completed synchronously. - else - { - return true; - } - } - - /// - /// Begins an asynchronous data reading from the source stream. - /// - /// Stream where to store readed header. - /// Number of bytes to read. - /// The AsyncCallback delegate that is executed when asynchronous operation completes. - /// An object that contains any additional user-defined data. - /// An IAsyncResult that represents the asynchronous call. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when storeStream is null reference. - /// Is raised when any of the arguments has invalid value. - public IAsyncResult BeginReadFixedCount(Stream storeStream, - long count, - AsyncCallback callback, - object state) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (storeStream == null) - { - throw new ArgumentNullException("storeStream"); - } - if (count < 0) - { - throw new ArgumentException("Argument 'count' value must be >= 0."); - } - - return new ReadToStreamAsyncOperation(this, storeStream, count, callback, state); - } - - /// - /// Handles the end of an asynchronous data reading. - /// - /// An IAsyncResult that represents an asynchronous call. - /// Is raised when asyncResult is null reference. - /// Is raised when invalid asyncResult passed to this method. - /// Is raised when EndReadToStream has already been called for specified asyncResult. - public void EndReadFixedCount(IAsyncResult asyncResult) - { - if (asyncResult == null) - { - throw new ArgumentNullException("asyncResult"); - } - if (!(asyncResult is ReadToStreamAsyncOperation)) - { - throw new ArgumentException( - "Argument 'asyncResult' was not returned by a call to the BeginReadFixedCount method."); - } - - ReadToStreamAsyncOperation ar = (ReadToStreamAsyncOperation) asyncResult; - if (ar.IsEndCalled) - { - throw new InvalidOperationException( - "EndReadFixedCount is already called for specified 'asyncResult'."); - } - ar.AsyncWaitHandle.WaitOne(); - ar.IsEndCalled = true; - if (ar.Exception != null) - { - throw ar.Exception; - } - } - - /// - /// Reads specified number of bytes from source stream and writes to the specified stream. - /// - /// Stream where to store readed data. - /// Number of bytes to read. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when storeStream is null reference. - /// Is raised when any of the arguments has invalid value. - public void ReadFixedCount(Stream storeStream, long count) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (storeStream == null) - { - throw new ArgumentNullException("storeStream"); - } - if (count < 0) - { - throw new ArgumentException("Argument 'count' value must be >= 0."); - } - - IAsyncResult ar = BeginReadFixedCount(storeStream, count, null, null); - - EndReadFixedCount(ar); - } - - /// - /// Reads specified number of bytes from source stream and converts it to string with current encoding. - /// - /// Number of bytes to read. - /// Returns readed data as string. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when any of the arguments has invalid value. - public string ReadFixedCountString(int count) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (count < 0) - { - throw new ArgumentException("Argument 'count' value must be >= 0."); - } - - using (MemoryStream ms = new MemoryStream()) - { - ReadFixedCount(ms, count); - - return m_pEncoding.GetString(ms.ToArray()); - } - } - - /// - /// Reads all data from source stream and stores to the specified stream. - /// - /// Stream where to store readed data. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when stream is null. - public void ReadAll(Stream stream) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - byte[] buffer = new byte[Workaround.Definitions.MaxStreamLineLength]; - while (true) - { - int readedCount = Read(buffer, 0, buffer.Length); - // End of stream reached, we readed file sucessfully. - if (readedCount == 0) - { - break; - } - else - { - stream.Write(buffer, 0, readedCount); - } - } - } - - /// - /// Returns the next available character but does not consume it. - /// - /// An integer representing the next character to be read, or -1 if no more characters are available. - /// Is raised when this object is disposed and this method is accessed. - public int Peek() - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (BytesInReadBuffer == 0) - { - BufferRead(false, null); - } - - // We are end of stream. - if (BytesInReadBuffer == 0) - { - return -1; - } - else - { - return m_pReadBuffer[m_ReadBufferOffset]; - } - } - - public int Write(byte[] data) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (data == null) - { - throw new ArgumentNullException("data"); - } - Write(data, 0, data.Length); - return data.Length; - } - - /// - /// Writes specified string data to stream. - /// - /// Data to write. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when data is null. - public int Write(string data) - { - - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (data == null) - { - throw new ArgumentNullException("data"); - } - byte[] dataBuff = Encoding.Default.GetBytes(data); - return Write(dataBuff); - } - - /// - /// Writes specified line to stream. If CRLF is missing, it will be added automatically to line data. - /// - /// Line to send. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when line is null. - /// Returns number of raw bytes written. - public int WriteLine(string line) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (line == null) - { - throw new ArgumentNullException("line"); - } - - if (!line.EndsWith("\r\n")) - { - line += "\r\n"; - } - return Write(line); - } - - /// - /// Writes all source stream data to stream. - /// - /// Stream which data to write. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when stream is null. - public void WriteStream(Stream stream) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - byte[] buffer = new byte[m_BufferSize]; - while (true) - { - int readed = stream.Read(buffer, 0, buffer.Length); - // We readed all data. - if (readed == 0) - { - break; - } - Write(buffer, 0, readed); - } - Flush(); - } - - /// - /// Writes specified number of bytes from source stream to stream. - /// - /// Stream which data to write. - /// Number of bytes to write. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when stream is null. - /// Is raised when count argument has invalid value. - public void WriteStream(Stream stream, long count) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - if (count < 0) - { - throw new ArgumentException("Argument 'count' value must be >= 0."); - } - - byte[] buffer = new byte[m_BufferSize]; - long readedCount = 0; - while (readedCount < count) - { - int readed = stream.Read(buffer, 0, (int) Math.Min(buffer.Length, count - readedCount)); - readedCount += readed; - Write(buffer, 0, readed); - } - Flush(); - } - - /// - /// Reads all data from the source stream and writes it to stream. Period handling and period terminator is added as required. - /// - /// Source stream which data to write to stream. - /// Returns number of bytes written to source stream. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when stream is null. - /// Is raised when stream has too big line. - public long WritePeriodTerminated(Stream stream) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - // We need to read lines, do period handling and write them to stream. - long totalWritten = 0; - byte[] buffer = new byte[m_BufferSize]; - ReadLineAsyncOP readLineOP = new ReadLineAsyncOP(buffer, SizeExceededAction.ThrowException); - SmartStream reader = new SmartStream(stream, false); - while (true) - { - reader.ReadLine(readLineOP, false); - if (readLineOP.Error != null) - { - throw readLineOP.Error; - } - // We reached end of stream, no more data. - if (readLineOP.BytesInBuffer == 0) - { - break; - } - - // Period handling. If line starts with period(.), additional period is added. - if (readLineOP.LineBytesInBuffer > 0 && buffer[0] == '.') - { - // Add additional period. - Write(new[] {(byte) '.'}, 0, 1); - totalWritten++; - } - - // Write line to source stream. - Write(buffer, 0, readLineOP.BytesInBuffer); - totalWritten += readLineOP.BytesInBuffer; - } - - // Write period terminator. - WriteLine("."); - - Flush(); - - return totalWritten; - } - - /// - /// Reads header from source stream and writes it to stream. - /// - /// Stream from where to read header. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when stream is null. - public void WriteHeader(Stream stream) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - SmartStream reader = new SmartStream(stream, false); - reader.ReadHeader(this, -1, SizeExceededAction.ThrowException); - } - - /// - /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device. - /// - /// Is raised when this object is disposed and this method is accessed. - public override void Flush() - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - MemoryBuffer = false;//drop mem buffer - m_pStream.Flush(); - } - - /// - /// Sets the position within the current stream. - /// - /// A byte offset relative to the origin parameter. - /// A value of type SeekOrigin indicating the reference point used to obtain the new position. - /// The new position within the current stream. - /// Is raised when this object is disposed and this method is accessed. - public override long Seek(long offset, SeekOrigin origin) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - return m_pStream.Seek(offset, origin); - } - - /// - /// Sets the length of the current stream. - /// - /// The desired length of the current stream in bytes. - /// Is raised when this object is disposed and this method is accessed. - public override void SetLength(long value) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - - m_pStream.SetLength(value); - - // Clear read buffer. - m_ReadBufferOffset = 0; - m_ReadBufferCount = 0; - } - - /// - /// Begins an asynchronous read operation. - /// - /// The buffer to read the data into. - /// The byte offset in buffer at which to begin writing data read from the stream. - /// The maximum number of bytes to read. - /// An optional asynchronous callback, to be called when the read is complete. - /// A user-provided object that distinguishes this particular asynchronous read request from other requests. - /// An IAsyncResult that represents the asynchronous read, which could still be pending. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when buffer is null reference. - /// Is raised when any of the arguments has out of valid range. - public override IAsyncResult BeginRead(byte[] buffer, - int offset, - int count, - AsyncCallback callback, - object state) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (offset < 0) - { - throw new ArgumentOutOfRangeException("offset", "Argument 'offset' value must be >= 0."); - } - if (offset > buffer.Length) - { - throw new ArgumentOutOfRangeException("offset", - "Argument 'offset' value must be < buffer.Length."); - } - if (count < 0) - { - throw new ArgumentOutOfRangeException("count", "Argument 'count' value must be >= 0."); - } - if (offset + count > buffer.Length) - { - throw new ArgumentOutOfRangeException("count", - "Argument 'count' is bigger than than argument 'buffer' can store."); - } - - return new ReadAsyncOperation(this, buffer, offset, count, callback, state); - } - - /// - /// Handles the end of an asynchronous data reading. - /// - /// The reference to the pending asynchronous request to finish. - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested - /// if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// Is raised when asyncResult is null reference. - public override int EndRead(IAsyncResult asyncResult) - { - if (asyncResult == null) - { - throw new ArgumentNullException("asyncResult"); - } - if (!(asyncResult is ReadAsyncOperation)) - { - throw new ArgumentException( - "Argument 'asyncResult' was not returned by a call to the BeginRead method."); - } - - ReadAsyncOperation ar = (ReadAsyncOperation) asyncResult; - if (ar.IsEndCalled) - { - throw new InvalidOperationException("EndRead is already called for specified 'asyncResult'."); - } - ar.AsyncWaitHandle.WaitOne(); - ar.IsEndCalled = true; - - return ar.BytesStored; - } - - /// - /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. - /// - /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source. - /// The zero-based byte offset in buffer at which to begin storing the data read from the current stream. - /// The maximum number of bytes to be read from the current stream. - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when buffer is null reference. - /// Is raised when any of the arguments has out of valid range. - public override int Read(byte[] buffer, int offset, int count) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (offset < 0) - { - throw new ArgumentOutOfRangeException("offset", "Argument 'offset' value must be >= 0."); - } - if (count < 0) - { - throw new ArgumentOutOfRangeException("count", "Argument 'count' value must be >= 0."); - } - if (offset + count > buffer.Length) - { - throw new ArgumentOutOfRangeException("count", - "Argument 'count' is bigger than than argument 'buffer' can store."); - } - - if (BytesInReadBuffer == 0) - { - BufferRead(false, null); - } - - if (BytesInReadBuffer == 0) - { - return 0; - } - else - { - int countToCopy = Math.Min(count, BytesInReadBuffer); - Array.Copy(m_pReadBuffer, m_ReadBufferOffset, buffer, offset, countToCopy); - m_ReadBufferOffset += countToCopy; - - return countToCopy; - } - } - - /// - /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. - /// - /// An array of bytes. This method copies count bytes from buffer to the current stream. - /// The zero-based byte offset in buffer at which to begin copying bytes to the current stream. - /// The number of bytes to be written to the current stream. - /// Is raised when this object is disposed and this method is accessed. - public override void Write(byte[] buffer, int offset, int count) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SmartStream"); - } - //Log dbg - //Debug.Write(Encoding.UTF8.GetString(buffer, offset, count)); - m_pWriteStream.Write(buffer, offset, count); - - m_LastActivity = DateTime.Now; - m_BytesWritten += count; - } - - #endregion - - #region Utility methods - - /// - /// Begins buffering read-buffer. - /// - /// If true then this method can complete asynchronously. If false, this method completed always syncronously. - /// The callback that is executed when asynchronous operation completes. - /// If operation completes synchronously, no callback called. - /// - /// Returns true if the I/O operation is pending. The BufferReadAsyncEventArgs.Completed event on the context parameter will be raised upon completion of the operation. - /// Returns false if the I/O operation completed synchronously. The BufferReadAsyncEventArgs.Completed event on the context parameter will not be raised and the context object passed as a parameter may be examined immediately after the method call returns to retrieve the result of the operation. - /// - /// Is raised when there is data in read buffer and this method is called. - private bool BufferRead(bool async, BufferCallback asyncCallback) - { - if (BytesInReadBuffer != 0) - { - throw new InvalidOperationException("There is already data in read buffer."); - } - - m_ReadBufferOffset = 0; - m_ReadBufferCount = 0; - - m_pReadBufferOP.ReleaseEvents(); - m_pReadBufferOP.Completed += delegate(object s, EventArgs e) - { - if (e.Value.Error != null) - { - if (asyncCallback != null) - { - asyncCallback(e.Value.Error); - } - } - else - { - m_ReadBufferOffset = 0; - m_ReadBufferCount = e.Value.BytesInBuffer; - m_BytesReaded += e.Value.BytesInBuffer; - m_LastActivity = DateTime.Now; - - if (asyncCallback != null) - { - asyncCallback(null); - } - } - }; - - if (async) - { - //Console.WriteLine(new StackTrace().ToString()); - } - - if (!m_pReadBufferOP.Start(async, m_pReadBuffer, m_pReadBuffer.Length)) - { - return true; - } - else - { - if (m_pReadBufferOP.Error != null) - { - throw m_pReadBufferOP.Error; - } - else - { - m_ReadBufferOffset = 0; - m_ReadBufferCount = m_pReadBufferOP.BytesInBuffer; - m_BytesReaded += m_pReadBufferOP.BytesInBuffer; - m_LastActivity = DateTime.Now; - } - - return false; - } - - /* REMOVE ME: - if(!m_pReadBufferOP.Start(m_pReadBuffer,m_pReadBuffer.Length)){ - if(async == false){ - // Wait while async operation completes. - while(!m_pReadBufferOP.IsCompleted){ - Thread.Sleep(1); - } - - return false; - } - else{ - return true; - } - } - else{ - if(m_pReadBufferOP.Error != null){ - throw m_pReadBufferOP.Error; - } - else{ - m_ReadBufferOffset = 0; - m_ReadBufferCount = m_pReadBufferOP.BytesInBuffer; - m_BytesReaded += m_pReadBufferOP.BytesInBuffer; - m_LastActivity = DateTime.Now; - } - - return false; - } */ - } - - #endregion - - #region Nested type: BufferReadAsyncOP - - /// - /// This class implements asynchronous read buffering. - /// - private class BufferReadAsyncOP : AsyncOP, IDisposable - { - #region Events - - /// - /// Is raised when asynchronous operation has completed. - /// - public event EventHandler> Completed = null; - - #endregion - - #region Members - - private int m_BytesInBuffer; - - private bool m_IsCompleted; - private bool m_IsCompletedSync; - private bool m_IsDisposed; - private int m_MaxCount; - private byte[] m_pBuffer; - private Exception m_pException; - private SmartStream m_pOwner; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner stream. - /// Is raised when owner is null reference. - public BufferReadAsyncOP(SmartStream owner) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - - m_pOwner = owner; - } - - #endregion - - #region Properties - - /// - /// Gets read buffer. - /// - /// Is raised when this object is disposed and this property is accessed. - public byte[] Buffer - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pBuffer; - } - } - - /// - /// Gets number of bytes stored in read buffer. - /// - /// Is raised when this object is disposed and this property is accessed. - public int BytesInBuffer - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_BytesInBuffer; - } - } - - /// - /// Gets error occured during asynchronous operation. Value null means no error. - /// - /// Is raised when this object is disposed and this property is accessed. - public Exception Error - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pException; - } - } - - /// - /// Gets if asynchronous operation has completed. - /// - /// Is raised when this object is disposed and this property is accessed. - public override bool IsCompleted - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_IsCompleted; - } - } - - /// - /// Gets if operation completed synchronously. - /// - /// Is raised when this object is disposed and this property is accessed. - public override bool IsCompletedSynchronously - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_IsCompletedSync; - } - } - - /// - /// Gets if this object is disposed. - /// - public override bool IsDisposed - { - get { return m_IsDisposed; } - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public void Dispose() - { - if (m_IsDisposed) - { - return; - } - m_IsDisposed = true; - - m_pOwner = null; - m_pBuffer = null; - Completed = null; - } - - #endregion - - #region Overrides - - /// - /// Destructor. - /// - ~BufferReadAsyncOP() - { - Dispose(); - } - - #endregion - - #region Internal methods - - /// - /// Starts asynchronous operation. - /// - /// If true then this method can complete asynchronously. If false, this method completed always syncronously. - /// Buffer where to store readed data. - /// Maximum number of bytes to read. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when buffer is null reference. - /// Is raised when any of the arguments has invalid value. - /// Returns true if operation completed synchronously, false if asynchronous operation pending. - internal bool Start(bool async, byte[] buffer, int count) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (count < 0) - { - throw new ArgumentException("Argument 'count' value must be >= 0."); - } - if (count > buffer.Length) - { - throw new ArgumentException("Argumnet 'count' value must be <= buffer.Length."); - } - - m_IsCompleted = false; - m_pBuffer = buffer; - m_MaxCount = count; - m_BytesInBuffer = 0; - m_pException = null; - - // Operation may complete asynchronously; - if (async) - { - IAsyncResult ar = m_pOwner.m_pStream.BeginRead(buffer, - 0, - count, - delegate(IAsyncResult r) - { - try - { - m_BytesInBuffer = - m_pOwner.m_pStream.EndRead( - r); - } - catch (Exception x) - { - m_pException = x; - } - - if (!r.CompletedSynchronously) - { - OnCompleted(); - } - - m_IsCompleted = true; - }, - null); - - m_IsCompletedSync = ar.CompletedSynchronously; - } - // Operation must complete synchronously. - else - { - m_BytesInBuffer = m_pOwner.m_pStream.Read(buffer, 0, count); - m_IsCompleted = true; - m_IsCompletedSync = true; - } - - return m_IsCompletedSync; - } - - /// - /// Releases all events attached to this class. - /// - internal void ReleaseEvents() - { - Completed = null; - } - - #endregion - - #region Utility methods - - /// - /// Raises Completed event. - /// - private void OnCompleted() - { - if (Completed != null) - { - Completed(this, new EventArgs(this)); - } - } - - #endregion - } - - #endregion - - #region Nested type: ReadAsyncOperation - - /// - /// This class implements asynchronous data reader. - /// - private class ReadAsyncOperation : IAsyncResult - { - #region Members - - private readonly int m_MaxSize; - private readonly int m_OffsetInBuffer; - private readonly AsyncCallback m_pAsyncCallback; - private readonly object m_pAsyncState; - private readonly AutoResetEvent m_pAsyncWaitHandle; - private readonly byte[] m_pBuffer; - private readonly SmartStream m_pOwner; - private int m_BytesStored; - private bool m_CompletedSynchronously; - private bool m_IsCompleted; - private Exception m_pException; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner stream. - /// Buffer where to store data. - /// The location in buffer to begin storing the data. - /// Maximum number of bytes to read. - /// The AsyncCallback delegate that is executed when asynchronous operation completes. - /// User-defined object that qualifies or contains information about an asynchronous operation. - public ReadAsyncOperation(SmartStream owner, - byte[] buffer, - int offset, - int maxSize, - AsyncCallback callback, - object asyncState) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (offset < 0) - { - throw new ArgumentOutOfRangeException("offset", "Argument 'offset' value must be >= 0."); - } - if (offset > buffer.Length) - { - throw new ArgumentOutOfRangeException("offset", - "Argument 'offset' value must be < buffer.Length."); - } - if (maxSize < 0) - { - throw new ArgumentOutOfRangeException("maxSize", "Argument 'maxSize' value must be >= 0."); - } - if (offset + maxSize > buffer.Length) - { - throw new ArgumentOutOfRangeException("maxSize", - "Argument 'maxSize' is bigger than than argument 'buffer' can store."); - } - - m_pOwner = owner; - m_pBuffer = buffer; - m_OffsetInBuffer = offset; - m_MaxSize = maxSize; - m_pAsyncCallback = callback; - m_pAsyncState = asyncState; - - m_pAsyncWaitHandle = new AutoResetEvent(false); - - DoRead(); - } - - #endregion - - #region Properties - - /// - /// Gets store buffer. - /// - internal byte[] Buffer - { - get { return m_pBuffer; } - } - - /// - /// Gets number of bytes stored in to Buffer. - /// - internal int BytesStored - { - get { return m_BytesStored; } - } - - /// - /// Gets or sets if EndReadLine method is called for this asynchronous operation. - /// - internal bool IsEndCalled { get; set; } - - #endregion - - #region Utility methods - - /// - /// Is called when asynchronous read buffer buffering has completed. - /// - /// Exception that occured during async operation. - private void Buffering_Completed(Exception x) - { - if (x != null) - { - m_pException = x; - Completed(); - } - // We reached end of stream, no more data. - else if (m_pOwner.BytesInReadBuffer == 0) - { - Completed(); - } - // Continue data reading. - else - { - DoRead(); - } - } - - /// - /// Does asynchronous data reading. - /// - private void DoRead() - { - try - { - // Read buffer empty, buff next data block. - if (m_pOwner.BytesInReadBuffer == 0) - { - // Buffering started asynchronously. - if (m_pOwner.BufferRead(true, Buffering_Completed)) - { - return; - } - // Buffering completed synchronously, continue processing. - else - { - // We reached end of stream, no more data. - if (m_pOwner.BytesInReadBuffer == 0) - { - Completed(); - return; - } - } - } - - int readedCount = Math.Min(m_MaxSize, m_pOwner.BytesInReadBuffer); - Array.Copy(m_pOwner.m_pReadBuffer, - m_pOwner.m_ReadBufferOffset, - m_pBuffer, - m_OffsetInBuffer, - readedCount); - m_pOwner.m_ReadBufferOffset += readedCount; - m_pOwner.m_LastActivity = DateTime.Now; - m_BytesStored += readedCount; - - Completed(); - } - catch (Exception x) - { - m_pException = x; - Completed(); - } - } - - /// - /// This method must be called when asynchronous operation has completed. - /// - private void Completed() - { - m_IsCompleted = true; - m_pAsyncWaitHandle.Set(); - if (m_pAsyncCallback != null) - { - m_pAsyncCallback(this); - } - } - - #endregion - - #region IAsyncResult Members - - /// - /// Gets a user-defined object that qualifies or contains information about an asynchronous operation. - /// - public object AsyncState - { - get { return m_pAsyncState; } - } - - /// - /// Gets a WaitHandle that is used to wait for an asynchronous operation to complete. - /// - public WaitHandle AsyncWaitHandle - { - get { return m_pAsyncWaitHandle; } - } - - /// - /// Gets an indication of whether the asynchronous operation completed synchronously. - /// - public bool CompletedSynchronously - { - get { return m_CompletedSynchronously; } - } - - /// - /// Gets an indication whether the asynchronous operation has completed. - /// - public bool IsCompleted - { - get { return m_IsCompleted; } - } - - #endregion - } - - #endregion - - #region Nested type: ReadLineAsyncOP - - /// - /// This class implements read line operation. - /// - /// This class can be reused on multiple calls of SmartStream.ReadLine method. - public class ReadLineAsyncOP : AsyncOP, IDisposable - { - #region Events - - /// - /// Is raised when asynchronous operation has completed. - /// - public event EventHandler> Completed = null; - - #endregion - - #region Members - - private readonly SizeExceededAction m_ExceededAction = SizeExceededAction.JunkAndThrowException; - private int m_BytesInBuffer; - private bool m_CRLFLinesOnly = true; - private bool m_IsCompleted; - private bool m_IsCompletedSync; - private bool m_IsDisposed; - private int m_LastByte = -1; - private byte[] m_pBuffer; - private Exception m_pException; - private SmartStream m_pOwner; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Line buffer. - /// Specifies how line-reader behaves when maximum line size exceeded. - /// Is raised when buffer is null reference. - public ReadLineAsyncOP(byte[] buffer, SizeExceededAction exceededAction) - { - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - - m_pBuffer = buffer; - m_ExceededAction = exceededAction; - } - - #endregion - - #region Properties - - /// - /// Gets line buffer. - /// - /// Is raised when this object is disposed and this property is accessed. - public byte[] Buffer - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pBuffer; - } - } - - /// - /// Gets number of bytes stored in the buffer. Ending line-feed characters included. - /// - /// Is raised when this object is disposed and this property is accessed. - public int BytesInBuffer - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_BytesInBuffer; - } - } - - /// - /// Gets error occured during asynchronous operation. Value null means no error. - /// - /// Is raised when this object is disposed and this property is accessed. - public Exception Error - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pException; - } - } - - /// - /// Gets if asynchronous operation has completed. - /// - /// Is raised when this object is disposed and this property is accessed. - public override bool IsCompleted - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_IsCompleted; - } - } - - /// - /// Gets if operation completed synchronously. - /// - /// Is raised when this object is disposed and this property is accessed. - public override bool IsCompletedSynchronously - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_IsCompletedSync; - } - } - - /// - /// Gets if this object is disposed. - /// - public override bool IsDisposed - { - get { return m_IsDisposed; } - } - - /// - /// Gets line as ASCII string. Returns null if EOS(end of stream) reached. Ending line-feed characters not included. - /// - /// Is raised when this object is disposed and this property is accessed. - public string LineAscii - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (BytesInBuffer == 0) - { - return null; - } - else - { - return Encoding.ASCII.GetString(m_pBuffer, 0, LineBytesInBuffer); - } - } - } - - /// - /// Gets number of line data bytes stored in the buffer. Ending line-feed characters not included. - /// - /// Is raised when this object is disposed and this property is accessed. - public int LineBytesInBuffer - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - int retVal = m_BytesInBuffer; - - if (m_BytesInBuffer > 1) - { - if (m_pBuffer[m_BytesInBuffer - 1] == '\n') - { - retVal--; - if (m_pBuffer[m_BytesInBuffer - 2] == '\r') - { - retVal--; - } - } - } - else if (m_BytesInBuffer > 0) - { - if (m_pBuffer[m_BytesInBuffer - 1] == '\n') - { - retVal--; - } - } - - return retVal; - } - } - - /// - /// Gets line as UTF-32 string. Returns null if EOS(end of stream) reached. Ending line-feed characters not included. - /// - /// Is raised when this object is disposed and this property is accessed. - public string LineUtf32 - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (BytesInBuffer == 0) - { - return null; - } - else - { - return Encoding.UTF32.GetString(m_pBuffer, 0, LineBytesInBuffer); - } - } - } - - /// - /// Gets line as UTF-8 string. Returns null if EOS(end of stream) reached. Ending line-feed characters not included. - /// - /// Is raised when this object is disposed and this property is accessed. - public string LineUtf8 - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (BytesInBuffer == 0) - { - return null; - } - else - { - return Encoding.UTF8.GetString(m_pBuffer, 0, LineBytesInBuffer); - } - } - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public void Dispose() - { - if (m_IsDisposed) - { - return; - } - m_IsDisposed = true; - - m_pOwner = null; - m_pBuffer = null; - m_pException = null; - Completed = null; - } - - #endregion - - #region Overrides - - /// - /// Destructor. - /// - ~ReadLineAsyncOP() - { - Dispose(); - } - - #endregion - - #region Internal methods - - /// - /// Starts reading line. - /// - /// If true then this method can complete asynchronously. If false, this method completed always syncronously. - /// Owner SmartStream. - /// Returns true if read line completed synchronously, false if asynchronous operation pending. - /// Is raised when stream is null reference. - internal bool Start(bool async, SmartStream stream) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - m_pOwner = stream; - - // Clear old data, if any. - m_IsCompleted = false; - m_BytesInBuffer = 0; - m_LastByte = -1; - m_pException = null; - - m_IsCompletedSync = DoLineReading(async); - - return m_IsCompletedSync; - } - - #endregion - - #region Utility methods - - /// - /// Is called when asynchronous read buffer buffering has completed. - /// - /// Exception that occured during async operation. - private void Buffering_Completed(Exception x) - { - if (x != null) - { - m_pException = x; - OnCompleted(); - } - // We reached end of stream, no more data. - else if (m_pOwner.BytesInReadBuffer == 0) - { - OnCompleted(); - } - // Continue line reading. - else - { - if (DoLineReading(true)) - { - OnCompleted(); - } - } - } - - /// - /// Starts/continues line reading. - /// - /// If true then this method can complete asynchronously. If false, this method completed always syncronously. - /// Returns true if line reading completed. - private bool DoLineReading(bool async) - { - try - { - while (true) - { - // Read buffer empty, buff next data block. - if (m_pOwner.BytesInReadBuffer == 0) - { - // Buffering started asynchronously. - if (m_pOwner.BufferRead(async, Buffering_Completed)) - { - return false; - } - // Buffering completed synchronously, continue processing. - else - { - // We reached end of stream, no more data. - if (m_pOwner.BytesInReadBuffer == 0) - { - return true; - } - } - } - - byte b = m_pOwner.m_pReadBuffer[m_pOwner.m_ReadBufferOffset++]; - - // Line buffer full. - if (m_BytesInBuffer >= m_pBuffer.Length) - { - m_pException = new LineSizeExceededException(); - - if (m_ExceededAction == SizeExceededAction.ThrowException) - { - return true; - } - } - // Store byte. - else - { - m_pBuffer[m_BytesInBuffer++] = b; - } - - // We have LF line. - if (b == '\n') - { - if (!m_CRLFLinesOnly || m_CRLFLinesOnly && m_LastByte == '\r') - { - return true; - } - } - - m_LastByte = b; - } - } - catch (Exception x) - { - m_pException = x; - } - - return true; - } - - /// - /// Raises Completed event. - /// - private void OnCompleted() - { - m_IsCompleted = true; - - if (Completed != null) - { - Completed(this, new EventArgs(this)); - } - } - - #endregion - } - - #endregion - - #region Nested type: ReadLineAsyncOperation - - /// - /// This class implements asynchronous line reading. - /// - private class ReadLineAsyncOperation : IAsyncResult - { - #region Members - - private readonly int m_MaxCount; - - private readonly AsyncCallback m_pAsyncCallback; - private readonly object m_pAsyncState; - private readonly AutoResetEvent m_pAsyncWaitHandle; - private readonly byte[] m_pBuffer; - private readonly SmartStream m_pOwner; - - private readonly SizeExceededAction m_SizeExceededAction = - SizeExceededAction.JunkAndThrowException; - - private int m_BytesReaded; - private int m_BytesStored; - private bool m_CompletedSynchronously; - private bool m_IsCompleted; - private int m_OffsetInBuffer; - private Exception m_pException; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner stream. - /// Buffer where to store data. - /// The location in buffer to begin storing the data. - /// Maximum number of bytes to read. - /// Specifies how this method behaves when maximum line size exceeded. - /// The AsyncCallback delegate that is executed when asynchronous operation completes. - /// User-defined object that qualifies or contains information about an asynchronous operation. - /// Is raised when owner,buffer is null reference. - /// Is raised when any of the arguments has out of valid range. - public ReadLineAsyncOperation(SmartStream owner, - byte[] buffer, - int offset, - int maxCount, - SizeExceededAction exceededAction, - AsyncCallback callback, - object asyncState) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (offset < 0) - { - throw new ArgumentOutOfRangeException("offset", "Argument 'offset' value must be >= 0."); - } - if (offset > buffer.Length) - { - throw new ArgumentOutOfRangeException("offset", - "Argument 'offset' value must be < buffer.Length."); - } - if (maxCount < 0) - { - throw new ArgumentOutOfRangeException("maxCount", - "Argument 'maxCount' value must be >= 0."); - } - if (offset + maxCount > buffer.Length) - { - throw new ArgumentOutOfRangeException("maxCount", - "Argument 'maxCount' is bigger than than argument 'buffer' can store."); - } - - m_pOwner = owner; - m_pBuffer = buffer; - m_OffsetInBuffer = offset; - m_MaxCount = maxCount; - m_SizeExceededAction = exceededAction; - m_pAsyncCallback = callback; - m_pAsyncState = asyncState; - - m_pAsyncWaitHandle = new AutoResetEvent(false); - - DoLineReading(); - } - - #endregion - - #region Properties - - /// - /// Gets store buffer. - /// - internal byte[] Buffer - { - get { return m_pBuffer; } - } - - /// - /// Gets number of bytes readed from source stream. - /// - internal int BytesReaded - { - get { return m_BytesReaded; } - } - - /// - /// Gets number of bytes stored in to Buffer. - /// - internal int BytesStored - { - get { return m_BytesStored; } - } - - /// - /// Gets or sets if EndReadLine method is called for this asynchronous operation. - /// - internal bool IsEndCalled { get; set; } - - #endregion - - #region Utility methods - - /// - /// Is called when asynchronous read buffer buffering has completed. - /// - /// Exception that occured during async operation. - private void Buffering_Completed(Exception x) - { - if (x != null) - { - m_pException = x; - Completed(); - } - // We reached end of stream, no more data. - else if (m_pOwner.BytesInReadBuffer == 0) - { - Completed(); - } - // Continue line reading. - else - { - DoLineReading(); - } - } - - /// - /// Does line reading. - /// - private void DoLineReading() - { - try - { - while (true) - { - // Read buffer empty, buff next data block. - if (m_pOwner.BytesInReadBuffer == 0) - { - // Buffering started asynchronously. - if (m_pOwner.BufferRead(true, Buffering_Completed)) - { - return; - } - // Buffering completed synchronously, continue processing. - else - { - // We reached end of stream, no more data. - if (m_pOwner.BytesInReadBuffer == 0) - { - Completed(); - return; - } - } - } - - byte b = m_pOwner.m_pReadBuffer[m_pOwner.m_ReadBufferOffset++]; - m_BytesReaded++; - - // We have LF line. - if (b == '\n') - { - break; - } - // We have CRLF line. - else if (b == '\r' && m_pOwner.Peek() == '\n') - { - // Consume LF char. - m_pOwner.ReadByte(); - m_BytesReaded++; - - break; - } - // We have CR line. - else if (b == '\r') - { - break; - } - // We have normal line data char. - else - { - // Line buffer full. - if (m_BytesStored >= m_MaxCount) - { - if (m_SizeExceededAction == SizeExceededAction.ThrowException) - { - throw new LineSizeExceededException(); - } - // Just skip storing. - else {} - } - else - { - m_pBuffer[m_OffsetInBuffer++] = b; - m_BytesStored++; - } - } - } - } - catch (Exception x) - { - m_pException = x; - } - - Completed(); - } - - /// - /// This method must be called when asynchronous operation has completed. - /// - private void Completed() - { - m_IsCompleted = true; - m_pAsyncWaitHandle.Set(); - if (m_pAsyncCallback != null) - { - m_pAsyncCallback(this); - } - } - - #endregion - - #region IAsyncResult Members - - /// - /// Gets a user-defined object that qualifies or contains information about an asynchronous operation. - /// - public object AsyncState - { - get { return m_pAsyncState; } - } - - /// - /// Gets a WaitHandle that is used to wait for an asynchronous operation to complete. - /// - public WaitHandle AsyncWaitHandle - { - get { return m_pAsyncWaitHandle; } - } - - /// - /// Gets an indication of whether the asynchronous operation completed synchronously. - /// - public bool CompletedSynchronously - { - get { return m_CompletedSynchronously; } - } - - /// - /// Gets an indication whether the asynchronous operation has completed. - /// - public bool IsCompleted - { - get { return m_IsCompleted; } - } - - #endregion - } - - #endregion - - #region Nested type: ReadPeriodTerminatedAsyncOP - - /// - /// This class implements read period-terminated operation. - /// - public class ReadPeriodTerminatedAsyncOP : AsyncOP, IDisposable - { - #region Events - - /// - /// Is raised when asynchronous operation has completed. - /// - public event EventHandler> Completed = null; - - #endregion - - #region Members - - private readonly long m_MaxCount; - private long m_BytesStored; - private SizeExceededAction m_ExceededAction = SizeExceededAction.JunkAndThrowException; - private bool m_IsCompleted; - private bool m_IsCompletedSync; - private bool m_IsDisposed; - private int m_LinesStored; - private Exception m_pException; - private SmartStream m_pOwner; - private ReadLineAsyncOP m_pReadLineOP; - private Stream m_pStream; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Stream wehre to sore readed data. - /// Maximum number of bytes to read. Value 0 means not limited. - /// Specifies how period-terminated reader behaves when maxCount exceeded. - /// Is raised when stream is null reference. - public ReadPeriodTerminatedAsyncOP(Stream stream, long maxCount, SizeExceededAction exceededAction) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - m_pStream = stream; - m_MaxCount = maxCount; - m_ExceededAction = exceededAction; - - m_pReadLineOP = new ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength], exceededAction); - m_pReadLineOP.Completed += m_pReadLineOP_Completed; - } - - #endregion - - #region Properties - - /// - /// Gets number of bytes stored to Stream stream. - /// - /// Is raised when this object is disposed and this property is accessed. - public long BytesStored - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_BytesStored; - } - } - - /// - /// Gets error occured during asynchronous operation. Value null means no error. - /// - /// Is raised when this object is disposed and this property is accessed. - public Exception Error - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pException; - } - } - - /// - /// Gets if asynchronous operation has completed. - /// - /// Is raised when this object is disposed and this property is accessed. - public override bool IsCompleted - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_IsCompleted; - } - } - - /// - /// Gets if operation completed synchronously. - /// - /// Is raised when this object is disposed and this property is accessed. - public override bool IsCompletedSynchronously - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_IsCompletedSync; - } - } - - /// - /// Gets if this object is disposed. - /// - public override bool IsDisposed - { - get { return m_IsDisposed; } - } - - /// - /// Gets number of lines stored to Stream stream. - /// - /// Is raised when this object is disposed and this property is accessed. - public int LinesStored - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_LinesStored; - } - } - - /// - /// Gets stream where period terminated data has stored. - /// - public Stream Stream - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pStream; - } - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public void Dispose() - { - if (m_IsDisposed) - { - return; - } - m_IsDisposed = true; - - m_pOwner = null; - m_pStream = null; - m_pReadLineOP.Dispose(); - m_pReadLineOP = null; - m_pException = null; - Completed = null; - } - - #endregion - - #region Overrides - - /// - /// Destructor. - /// - ~ReadPeriodTerminatedAsyncOP() - { - Dispose(); - } - - #endregion - - #region Internal methods - - /// - /// Starts period-terminated data reading. - /// - /// Owner SmartStream. - /// Returns true if read line completed synchronously, false if asynchronous operation pending. - /// Is raised when stream is null reference. - internal bool Start(SmartStream stream) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - m_pOwner = stream; - - // Clear old data, if any. - m_IsCompleted = false; - m_BytesStored = 0; - m_LinesStored = 0; - m_pException = null; - - m_IsCompletedSync = DoRead(); - - return m_IsCompletedSync; - } - - #endregion - - #region Utility methods - - /// - /// Is called when asynchronous line reading has completed. - /// - /// Sender. - /// Event data. - private void m_pReadLineOP_Completed(object sender, EventArgs e) - { - if (ProcessReadedLine()) - { - OnCompleted(); - } - else - { - if (DoRead()) - { - OnCompleted(); - } - } - } - - /// - /// Continues period-terminated reading. - /// - /// Returns true if read line completed synchronously, false if asynchronous operation pending. - private bool DoRead() - { - try - { - while (true) - { - if (m_pOwner.ReadLine(m_pReadLineOP, true)) - { - if (ProcessReadedLine()) - { - break; - } - } - // Goto next while loop. - else - { - return false; - } - } - } - catch (Exception x) - { - m_pException = x; - } - - return true; - } - - /// - /// Processes readed line. - /// - /// Returns true if read period-terminated operation has completed. - private bool ProcessReadedLine() - { - if (m_pReadLineOP.Error != null) - { - m_pException = m_pReadLineOP.Error; - - return true; - } - // We reached end of stream, no more data. - else if (m_pReadLineOP.BytesInBuffer == 0) - { - m_pException = new IncompleteDataException("Data is not period-terminated."); - - return true; - } - // We have period terminator. - else if (m_pReadLineOP.LineBytesInBuffer == 1 && m_pReadLineOP.Buffer[0] == '.') - { - return true; - } - // Normal line. - else - { - if (m_MaxCount < 1 || m_BytesStored < m_MaxCount) - { - byte[] buf = m_pReadLineOP.Buffer; - int bytesInBuffer = m_pReadLineOP.BytesInBuffer; - - if (m_pReadLineOP.LineBytesInBuffer > 2 && - m_pReadLineOP.Buffer[0] == '.' && - m_pReadLineOP.Buffer[1] == '.') - { - buf = m_pReadLineOP.Buffer.Skip(1).ToArray(); - bytesInBuffer--; - } - - m_pStream.Write(buf, 0, bytesInBuffer); - m_BytesStored += bytesInBuffer; - m_LinesStored++; - } - } - - return false; - } - - /// - /// Raises Completed event. - /// - private void OnCompleted() - { - m_IsCompleted = true; - - if (Completed != null) - { - Completed(this, new EventArgs(this)); - } - } - - #endregion - } - - #endregion - - #region Nested type: ReadToStreamAsyncOperation - - /// - /// This class implements asynchronous read to stream data reader. - /// - private class ReadToStreamAsyncOperation : IAsyncResult - { - #region Members - - private readonly long m_Count; - private readonly AsyncCallback m_pAsyncCallback; - private readonly object m_pAsyncState; - private readonly AutoResetEvent m_pAsyncWaitHandle; - private readonly SmartStream m_pOwner; - private readonly Stream m_pStoreStream; - private long m_BytesStored; - private bool m_CompletedSynchronously; - private bool m_IsCompleted; - private Exception m_pException; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner stream. - /// Stream where to store readed data. - /// Number of bytes to read from source stream. - /// The AsyncCallback delegate that is executed when asynchronous operation completes. - /// User-defined object that qualifies or contains information about an asynchronous operation. - /// Is raised when owner or storeStream is null reference. - /// Is raised when any of the arguments has invalid value. - public ReadToStreamAsyncOperation(SmartStream owner, - Stream storeStream, - long count, - AsyncCallback callback, - object asyncState) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - if (storeStream == null) - { - throw new ArgumentNullException("storeStream"); - } - if (count < 0) - { - throw new ArgumentException("Argument 'count' must be >= 0."); - } - - m_pOwner = owner; - m_pStoreStream = storeStream; - m_Count = count; - m_pAsyncCallback = callback; - m_pAsyncState = asyncState; - - m_pAsyncWaitHandle = new AutoResetEvent(false); - - if (m_Count == 0) - { - Completed(); - } - else - { - DoDataReading(); - } - } - - #endregion - - #region Properties - - /// - /// Gets number of bytes stored in to storeStream. - /// - internal long BytesStored - { - get { return m_BytesStored; } - } - - /// - /// Gets exception happened on asynchronous operation. Returns null if operation was successfull. - /// - internal Exception Exception - { - get { return m_pException; } - } - - /// - /// Gets or sets if EndReadLine method is called for this asynchronous operation. - /// - internal bool IsEndCalled { get; set; } - - #endregion - - #region Utility methods - - /// - /// Is called when asynchronous read buffer buffering has completed. - /// - /// Exception that occured during async operation. - private void Buffering_Completed(Exception x) - { - if (x != null) - { - m_pException = x; - Completed(); - } - // We reached end of stream, no more data. - else if (m_pOwner.BytesInReadBuffer == 0) - { - m_pException = new IncompleteDataException(); - Completed(); - } - // Continue line reading. - else - { - DoDataReading(); - } - } - - /// - /// Does data reading. - /// - private void DoDataReading() - { - try - { - while (true) - { - // Read buffer empty, buff next data block. - if (m_pOwner.BytesInReadBuffer == 0) - { - // Buffering started asynchronously. - if (m_pOwner.BufferRead(true, Buffering_Completed)) - { - return; - } - // Buffering completed synchronously, continue processing. - else - { - // We reached end of stream, no more data. - if (m_pOwner.BytesInReadBuffer == 0) - { - throw new IncompleteDataException(); - } - } - } - - int countToRead = (int) Math.Min(m_Count - m_BytesStored, m_pOwner.BytesInReadBuffer); - m_pStoreStream.Write(m_pOwner.m_pReadBuffer, m_pOwner.m_ReadBufferOffset, countToRead); - m_BytesStored += countToRead; - m_pOwner.m_ReadBufferOffset += countToRead; - - // We have readed all data. - if (m_Count == m_BytesStored) - { - Completed(); - return; - } - } - } - catch (Exception x) - { - m_pException = x; - Completed(); - } - } - - /// - /// This method must be called when asynchronous operation has completed. - /// - private void Completed() - { - m_IsCompleted = true; - m_pAsyncWaitHandle.Set(); - if (m_pAsyncCallback != null) - { - m_pAsyncCallback(this); - } - } - - #endregion - - #region IAsyncResult Members - - /// - /// Gets a user-defined object that qualifies or contains information about an asynchronous operation. - /// - public object AsyncState - { - get { return m_pAsyncState; } - } - - /// - /// Gets a WaitHandle that is used to wait for an asynchronous operation to complete. - /// - public WaitHandle AsyncWaitHandle - { - get { return m_pAsyncWaitHandle; } - } - - /// - /// Gets an indication of whether the asynchronous operation completed synchronously. - /// - public bool CompletedSynchronously - { - get { return m_CompletedSynchronously; } - } - - /// - /// Gets an indication whether the asynchronous operation has completed. - /// - public bool IsCompleted - { - get { return m_IsCompleted; } - } - - #endregion - } - - #endregion - - #region Nested type: ReadToTerminatorAsyncOperation - - /// - /// This class implements asynchronous line-based terminated data reader, where terminator is on line itself. - /// - private class ReadToTerminatorAsyncOperation : IAsyncResult - { - #region Members - - private readonly long m_MaxCount; - - private readonly AsyncCallback m_pAsyncCallback; - private readonly object m_pAsyncState; - private readonly AutoResetEvent m_pAsyncWaitHandle; - private readonly byte[] m_pLineBuffer; - private readonly SmartStream m_pOwner; - private readonly Stream m_pStoreStream; - private readonly byte[] m_pTerminatorBytes; - - private readonly SizeExceededAction m_SizeExceededAction = - SizeExceededAction.JunkAndThrowException; - - private readonly string m_Terminator = ""; - - private long m_BytesStored; - private bool m_CompletedSynchronously; - private bool m_IsCompleted; - private Exception m_pException; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner stream. - /// Data terminator. - /// Stream where to store readed header. - /// Maximum number of bytes to read. Value 0 means not limited. - /// Specifies how this method behaves when maximum line size exceeded. - /// The AsyncCallback delegate that is executed when asynchronous operation completes. - /// User-defined object that qualifies or contains information about an asynchronous operation. - /// Is raised when owner,terminator or storeStream is null reference. - public ReadToTerminatorAsyncOperation(SmartStream owner, - string terminator, - Stream storeStream, - long maxCount, - SizeExceededAction exceededAction, - AsyncCallback callback, - object asyncState) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - if (terminator == null) - { - throw new ArgumentNullException("terminator"); - } - if (storeStream == null) - { - throw new ArgumentNullException("storeStream"); - } - if (maxCount < 0) - { - throw new ArgumentException("Argument 'maxCount' must be >= 0."); - } - - m_pOwner = owner; - m_Terminator = terminator; - m_pTerminatorBytes = Encoding.ASCII.GetBytes(terminator); - m_pStoreStream = storeStream; - m_MaxCount = maxCount; - m_SizeExceededAction = exceededAction; - m_pAsyncCallback = callback; - m_pAsyncState = asyncState; - - m_pAsyncWaitHandle = new AutoResetEvent(false); - - m_pLineBuffer = new byte[Workaround.Definitions.MaxStreamLineLength]; - - // Start reading data. - m_pOwner.BeginReadLine(m_pLineBuffer, - 0, - m_pLineBuffer.Length - 2, - m_SizeExceededAction, - ReadLine_Completed, - null); - } - - #endregion - - #region Properties - - /// - /// Gets terminator. - /// - public string Terminator - { - get { return m_Terminator; } - } - - /// - /// Gets number of bytes stored in to storeStream. - /// - internal long BytesStored - { - get { return m_BytesStored; } - } - - /// - /// Gets exception happened on asynchronous operation. Returns null if operation was successfull. - /// - internal Exception Exception - { - get { return m_pException; } - } - - /// - /// Gets or sets if EndReadLine method is called for this asynchronous operation. - /// - internal bool IsEndCalled { get; set; } - - #endregion - - #region Utility methods - - /// - /// This method is called when asyynchronous line reading has completed. - /// - /// An IAsyncResult that represents an asynchronous call. - private void ReadLine_Completed(IAsyncResult asyncResult) - { - try - { - int storedCount = 0; - try - { - storedCount = m_pOwner.EndReadLine(asyncResult); - } - catch (LineSizeExceededException lx) - { - if (m_SizeExceededAction == SizeExceededAction.ThrowException) - { - throw lx; - } - m_pException = new LineSizeExceededException(); - storedCount = Workaround.Definitions.MaxStreamLineLength - 2; - } - - // Source stream closed berore we reached terminator. - if (storedCount == -1) - { - throw new IncompleteDataException(); - } - - // Check for terminator. - if (Net_Utils.CompareArray(m_pTerminatorBytes, m_pLineBuffer, storedCount)) - { - Completed(); - } - else - { - // We have exceeded maximum allowed data count. - if (m_MaxCount > 0 && (m_BytesStored + storedCount + 2) > m_MaxCount) - { - if (m_SizeExceededAction == SizeExceededAction.ThrowException) - { - throw new DataSizeExceededException(); - } - // Just skip storing. - else - { - m_pException = new DataSizeExceededException(); - } - } - else - { - // Store readed line. - m_pLineBuffer[storedCount++] = (byte) '\r'; - m_pLineBuffer[storedCount++] = (byte) '\n'; - m_pStoreStream.Write(m_pLineBuffer, 0, storedCount); - m_BytesStored += storedCount; - } - - // Strart reading new line. - m_pOwner.BeginReadLine(m_pLineBuffer, - 0, - m_pLineBuffer.Length - 2, - m_SizeExceededAction, - ReadLine_Completed, - null); - } - } - catch (Exception x) - { - m_pException = x; - Completed(); - } - } - - /// - /// This method must be called when asynchronous operation has completed. - /// - private void Completed() - { - m_IsCompleted = true; - m_pAsyncWaitHandle.Set(); - if (m_pAsyncCallback != null) - { - m_pAsyncCallback(this); - } - } - - #endregion - - #region IAsyncResult Members - - /// - /// Gets a user-defined object that qualifies or contains information about an asynchronous operation. - /// - public object AsyncState - { - get { return m_pAsyncState; } - } - - /// - /// Gets a WaitHandle that is used to wait for an asynchronous operation to complete. - /// - public WaitHandle AsyncWaitHandle - { - get { return m_pAsyncWaitHandle; } - } - - /// - /// Gets an indication of whether the asynchronous operation completed synchronously. - /// - public bool CompletedSynchronously - { - get { return m_CompletedSynchronously; } - } - - /// - /// Gets an indication whether the asynchronous operation has completed. - /// - public bool IsCompleted - { - get { return m_IsCompleted; } - } - - #endregion - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IPBindInfo.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IPBindInfo.cs deleted file mode 100644 index 6310bd51d..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/IPBindInfo.cs +++ /dev/null @@ -1,245 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - #region usings - - using System; - using System.Net; - using System.Security.Cryptography.X509Certificates; - - #endregion - - /// - /// Holds IP bind info. - /// - public class IPBindInfo - { - #region Members - - private readonly string m_HostName = ""; - private readonly X509Certificate2 m_pCertificate; - private readonly IPEndPoint m_pEndPoint; - private readonly BindInfoProtocol m_Protocol = BindInfoProtocol.TCP; - private readonly SslMode m_SslMode = SslMode.None; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Host name. - /// Bind protocol. - /// IP address to listen. - /// Port to listen. - /// Is raised when ip is null. - public IPBindInfo(string hostName, BindInfoProtocol protocol, IPAddress ip, int port) - { - if (ip == null) - { - throw new ArgumentNullException("ip"); - } - - m_HostName = hostName; - m_Protocol = protocol; - m_pEndPoint = new IPEndPoint(ip, port); - } - - /// - /// Default constructor. - /// - /// Host name. - /// IP address to listen. - /// Port to listen. - /// Specifies SSL mode. - /// Certificate to use for SSL connections. - /// Is raised when ip is null. - public IPBindInfo(string hostName, - IPAddress ip, - int port, - SslMode sslMode, - X509Certificate2 sslCertificate) - : this(hostName, BindInfoProtocol.TCP, ip, port, sslMode, sslCertificate) {} - - /// - /// Default constructor. - /// - /// Host name. - /// Bind protocol. - /// IP address to listen. - /// Port to listen. - /// Specifies SSL mode. - /// Certificate to use for SSL connections. - /// Is raised when ip is null. - /// Is raised when any of the arguments has invalid value. - public IPBindInfo(string hostName, - BindInfoProtocol protocol, - IPAddress ip, - int port, - SslMode sslMode, - X509Certificate2 sslCertificate) - { - if (ip == null) - { - throw new ArgumentNullException("ip"); - } - - m_HostName = hostName; - m_Protocol = protocol; - m_pEndPoint = new IPEndPoint(ip, port); - m_SslMode = sslMode; - m_pCertificate = sslCertificate; - if ((sslMode == SslMode.SSL || sslMode == SslMode.TLS) && sslCertificate == null) - { - throw new ArgumentException("SSL requested, but argument 'sslCertificate' is not provided."); - } - } - - #endregion - - #region Properties - - /// - /// Gets SSL certificate. - /// - public X509Certificate2 Certificate - { - get { return m_pCertificate; } - } - - /// - /// Gets IP end point. - /// - public IPEndPoint EndPoint - { - get { return m_pEndPoint; } - } - - /// - /// Gets host name. - /// - public string HostName - { - get { return m_HostName; } - } - - /// - /// Gets IP address. - /// - public IPAddress IP - { - get { return m_pEndPoint.Address; } - } - - /// - /// Gets port. - /// - public int Port - { - get { return m_pEndPoint.Port; } - } - - /// - /// Gets protocol. - /// - public BindInfoProtocol Protocol - { - get { return m_Protocol; } - } - - /// - /// Gets SSL certificate. - /// - [Obsolete("Use property Certificate instead.")] - public X509Certificate2 SSL_Certificate - { - get { return m_pCertificate; } - } - - /// - /// Gets SSL mode. - /// - public SslMode SslMode - { - get { return m_SslMode; } - } - - /// - /// Gets or sets user data. This is used internally don't use it !!!. - /// - public object Tag { get; set; } - - #endregion - - #region Methods - - /// - /// Compares the current instance with another object of the same type. - /// - /// An object to compare with this instance. - /// Returns true if two objects are equal. - public override bool Equals(object obj) - { - if (obj == null) - { - return false; - } - if (!(obj is IPBindInfo)) - { - return false; - } - - IPBindInfo bInfo = (IPBindInfo) obj; - if (bInfo.HostName != m_HostName) - { - return false; - } - if (bInfo.Protocol != m_Protocol) - { - return false; - } - if (!bInfo.EndPoint.Equals(m_pEndPoint)) - { - return false; - } - if (bInfo.SslMode != m_SslMode) - { - return false; - } - if (!Equals(bInfo.Certificate, m_pCertificate)) - { - return false; - } - - return true; - } - - /// - /// Returns the hash code. - /// - /// Returns the hash code. - public override int GetHashCode() - { - return base.GetHashCode(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/JunkingStream.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/JunkingStream.cs deleted file mode 100644 index ed916f453..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/JunkingStream.cs +++ /dev/null @@ -1,128 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - #region usings - - using System; - using System.IO; - using System.Runtime.InteropServices; - - #endregion - - /// - /// This stream just junks all written data. - /// - public class JunkingStream : Stream - { - #region Properties - - /// - /// Gets a value indicating whether the stream supports reading. This property always returns false. - /// - public override bool CanRead - { - get { return false; } - } - - /// - /// Gets a value indicating whether the stream supports seeking. This property always returns false. - /// - public override bool CanSeek - { - get { return false; } - } - - /// - /// Gets a value that indicates whether the stream supports writing. - /// - public override bool CanWrite - { - get { return true; } - } - - /// - /// Gets the length of the data available on the stream. This property always throws a NotSupportedException. - /// - public override long Length - { - get { throw new NotSupportedException(); } - } - - /// - /// Gets or sets the current position in the stream. This property always throws a NotSupportedException. - /// - public override long Position - { - get { throw new NotSupportedException(); } - - set { throw new NotSupportedException(); } - } - - #endregion - - #region Methods - - /// - /// Not used. - /// - public override void Flush() {} - - /// - /// Sets the current position of the stream to the given value. This method always throws a NotSupportedException. - /// - /// This parameter is not used. - /// This parameter is not used. - /// - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - /// - /// Sets the length of the stream. This method always throws a NotSupportedException. - /// - /// This parameter is not used. - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - /// - /// Reads data from the stream. This method always throws a NotSupportedException. - /// - /// This parameter is not used. - /// This parameter is not used. - /// This parameter is not used. - /// - public override int Read([In, Out] byte[] buffer, int offset, int size) - { - throw new NotSupportedException(); - } - - /// - /// Writes data to the stream. - /// - /// An array of type Byte that contains the data to write to the stream. - /// The location in buffer from which to start writing data. - /// The number of bytes to write to the stream. - public override void Write(byte[] buffer, int offset, int size) {} - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Log/LogEntry.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Log/LogEntry.cs deleted file mode 100644 index a3c3a202b..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Log/LogEntry.cs +++ /dev/null @@ -1,217 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Log -{ - #region usings - - using System; - using System.Net; - using System.Security.Principal; - - #endregion - - /// - /// Implements log entry. - /// - public class LogEntry - { - #region Members - - private readonly string m_ID = ""; - private readonly byte[] m_pData; - private readonly Exception m_pException; - private readonly IPEndPoint m_pLocalEP; - private readonly IPEndPoint m_pRemoteEP; - private readonly GenericIdentity m_pUserIdentity; - private readonly long m_Size; - private readonly string m_Text = ""; - private readonly DateTime m_Time; - private readonly LogEntryType m_Type = LogEntryType.Text; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Log entry type. - /// Log entry ID. - /// Specified how much data was readed or written. - /// Description text. - public LogEntry(LogEntryType type, string id, long size, string text) - { - m_Type = type; - m_ID = id; - m_Size = size; - m_Text = text; - - m_Time = DateTime.Now; - } - - /// - /// Default constructor. - /// - /// Log entry type. - /// Log entry ID. - /// Log entry owner user or null if none. - /// Log entry read/write size in bytes. - /// Log text. - /// Local IP end point. - /// Remote IP end point. - /// Log data. - public LogEntry(LogEntryType type, - string id, - GenericIdentity userIdentity, - long size, - string text, - IPEndPoint localEP, - IPEndPoint remoteEP, - byte[] data) - { - m_Type = type; - m_ID = id; - m_pUserIdentity = userIdentity; - m_Size = size; - m_Text = text; - m_pLocalEP = localEP; - m_pRemoteEP = remoteEP; - m_pData = data; - - m_Time = DateTime.Now; - } - - /// - /// Default constructor. - /// - /// Log entry type. - /// Log entry ID. - /// Log entry owner user or null if none. - /// Log entry read/write size in bytes. - /// Log text. - /// Local IP end point. - /// Remote IP end point. - /// Exception happened. Can be null. - public LogEntry(LogEntryType type, - string id, - GenericIdentity userIdentity, - long size, - string text, - IPEndPoint localEP, - IPEndPoint remoteEP, - Exception exception) - { - m_Type = type; - m_ID = id; - m_pUserIdentity = userIdentity; - m_Size = size; - m_Text = text; - m_pLocalEP = localEP; - m_pRemoteEP = remoteEP; - m_pException = exception; - - m_Time = DateTime.Now; - } - - #endregion - - #region Properties - - /// - /// Gest log data. Value null means no log data. - /// - public byte[] Data - { - get { return m_pData; } - } - - /// - /// Gets log entry type. - /// - public LogEntryType EntryType - { - get { return m_Type; } - } - - /// - /// Gets exception happened. This property is available only if LogEntryType.Exception. - /// - public Exception Exception - { - get { return m_pException; } - } - - /// - /// Gets log entry ID. - /// - public string ID - { - get { return m_ID; } - } - - /// - /// Gets local IP end point. Value null means no local end point. - /// - public IPEndPoint LocalEndPoint - { - get { return m_pLocalEP; } - } - - /// - /// Gets remote IP end point. Value null means no remote end point. - /// - public IPEndPoint RemoteEndPoint - { - get { return m_pRemoteEP; } - } - - /// - /// Gets how much data was readed or written, depends on LogEntryType. - /// - public long Size - { - get { return m_Size; } - } - - /// - /// Gets describing text. - /// - public string Text - { - get { return m_Text; } - } - - /// - /// Gets time when log entry was created. - /// - public DateTime Time - { - get { return m_Time; } - } - - /// - /// Gets log entry related user identity. - /// - public GenericIdentity UserIdentity - { - get { return m_pUserIdentity; } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Log/LogEntryType.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Log/LogEntryType.cs deleted file mode 100644 index 135da1b06..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Log/LogEntryType.cs +++ /dev/null @@ -1,45 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Log -{ - /// - /// Specifies log entry type. - /// - public enum LogEntryType - { - /// - /// Read entry. - /// - Read, - - /// - /// Write entry. - /// - Write, - - /// - /// Text entry. - /// - Text, - - /// - /// Exception entry. - /// - Exception, - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Log/Logger.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Log/Logger.cs deleted file mode 100644 index 1f9276cd8..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Log/Logger.cs +++ /dev/null @@ -1,250 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Log -{ - #region usings - - using System; - using System.Net; - using System.Security.Principal; - - #endregion - - /// - /// General logging module. - /// - public class Logger : IDisposable - { - #region Events - - /// - /// Is raised when new log entry is available. - /// - public event EventHandler WriteLog = null; - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public void Dispose() {} - - /// - /// Adds read log entry. - /// - /// Readed data size in bytes. - /// Log text. - public void AddRead(long size, string text) - { - OnWriteLog(new LogEntry(LogEntryType.Read, "", size, text)); - } - - /// - /// Adds read log entry. - /// - /// Log entry ID. - /// Readed data size in bytes. - /// Log text. - /// Authenticated user identity. - /// Local IP endpoint. - /// Remote IP endpoint. - public void AddRead(string id, - GenericIdentity userIdentity, - long size, - string text, - IPEndPoint localEP, - IPEndPoint remoteEP) - { - OnWriteLog(new LogEntry(LogEntryType.Read, - id, - userIdentity, - size, - text, - localEP, - remoteEP, - (byte[]) null)); - } - - /// - /// Adds read log entry. - /// - /// Log entry ID. - /// Readed data size in bytes. - /// Log text. - /// Authenticated user identity. - /// Local IP endpoint. - /// Remote IP endpoint. - /// Log data. - public void AddRead(string id, - GenericIdentity userIdentity, - long size, - string text, - IPEndPoint localEP, - IPEndPoint remoteEP, - byte[] data) - { - OnWriteLog(new LogEntry(LogEntryType.Read, id, userIdentity, size, text, localEP, remoteEP, data)); - } - - /// - /// Add write log entry. - /// - /// Written data size in bytes. - /// Log text. - public void AddWrite(long size, string text) - { - OnWriteLog(new LogEntry(LogEntryType.Write, "", size, text)); - } - - /// - /// Add write log entry. - /// - /// Log entry ID. - /// Written data size in bytes. - /// Log text. - /// Authenticated user identity. - /// Local IP endpoint. - /// Remote IP endpoint. - public void AddWrite(string id, - GenericIdentity userIdentity, - long size, - string text, - IPEndPoint localEP, - IPEndPoint remoteEP) - { - OnWriteLog(new LogEntry(LogEntryType.Write, - id, - userIdentity, - size, - text, - localEP, - remoteEP, - (byte[]) null)); - } - - /// - /// Add write log entry. - /// - /// Log entry ID. - /// Written data size in bytes. - /// Log text. - /// Authenticated user identity. - /// Local IP endpoint. - /// Remote IP endpoint. - /// Log data. - public void AddWrite(string id, - GenericIdentity userIdentity, - long size, - string text, - IPEndPoint localEP, - IPEndPoint remoteEP, - byte[] data) - { - OnWriteLog(new LogEntry(LogEntryType.Write, id, userIdentity, size, text, localEP, remoteEP, data)); - } - - /// - /// Adds text entry. - /// - /// Log text. - public void AddText(string text) - { - OnWriteLog(new LogEntry(LogEntryType.Text, "", 0, text)); - } - - /// - /// Adds text entry. - /// - /// Log entry ID. - /// Log text. - public void AddText(string id, string text) - { - OnWriteLog(new LogEntry(LogEntryType.Text, id, 0, text)); - } - - /// - /// Adds text entry. - /// - /// Log entry ID. - /// Log text. - /// Authenticated user identity. - /// Local IP endpoint. - /// Remote IP endpoint. - public void AddText(string id, - GenericIdentity userIdentity, - string text, - IPEndPoint localEP, - IPEndPoint remoteEP) - { - OnWriteLog(new LogEntry(LogEntryType.Read, - id, - userIdentity, - 0, - text, - localEP, - remoteEP, - (byte[]) null)); - } - - /// - /// Adds exception entry. - /// - /// Log entry ID. - /// Log text. - /// Authenticated user identity. - /// Local IP endpoint. - /// Remote IP endpoint. - /// Exception happened. - public void AddException(string id, - GenericIdentity userIdentity, - string text, - IPEndPoint localEP, - IPEndPoint remoteEP, - Exception exception) - { - OnWriteLog(new LogEntry(LogEntryType.Exception, - id, - userIdentity, - 0, - text, - localEP, - remoteEP, - exception)); - } - - #endregion - - #region Utility methods - - /// - /// Raises WriteLog event. - /// - /// Log entry. - private void OnWriteLog(LogEntry entry) - { - if (WriteLog != null) - { - WriteLog(this, new WriteLogEventArgs(entry)); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Log/WriteLogEventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Log/WriteLogEventArgs.cs deleted file mode 100644 index b5f6bc74e..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Log/WriteLogEventArgs.cs +++ /dev/null @@ -1,68 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Log -{ - #region usings - - using System; - - #endregion - - /// - /// This class provides data for Logger.WriteLog event. - /// - public class WriteLogEventArgs : EventArgs - { - #region Members - - private readonly LogEntry m_pLogEntry; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// New log entry. - /// Is raised when logEntry is null. - public WriteLogEventArgs(LogEntry logEntry) - { - if (logEntry == null) - { - throw new ArgumentNullException("logEntry"); - } - - m_pLogEntry = logEntry; - } - - #endregion - - #region Properties - - /// - /// Gets new log entry. - /// - public LogEntry LogEntry - { - get { return m_pLogEntry; } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/LumiSoft.Net b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/LumiSoft.Net deleted file mode 100644 index f226014a6..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/LumiSoft.Net +++ /dev/null @@ -1,5910 +0,0 @@ - - - - LumiSoft.Net - - - - - Provides helper methods for authentications(APOP,CRAM-MD5,DIGEST-MD5). - - - - - Calculates APOP authentication compare value. - - Password. - Password tag. - Returns value what must be used for comparing passwords. - - - - Calculates CRAM-MD5 authentication compare value. - - Password. - Hash calculation key - Returns value what must be used for comparing passwords. - - - - Calculates DIGEST-MD5 authentication compare value. - - Specifies if client or server value calculated. - Client and server has diffrent calculation method. - Use domain or machine name for this. - User name. - Password. - Server password tag. - Client password tag. - - Returns value what must be used for comparing passwords. - - - - Creates AUTH Digest-md5 server response what server must send to client. - - Use domain or machine name for this. - Server password tag.Random hex string is suggested. - - - - - Generates random nonce value. - - - - - - Calculates keyed md5 hash from specifieed text and with specified hash key. - - - - - - - - Calculates md5 hash from specified string. - - - - - - - Converts specified string to hexa string. - - - - - - - Encodes specified string to base64 string. - - Text to encode. - Returns encoded string. - - - - Decodes specified base64 string. - - Base64 string to decode. - Returns decoded string. - - - - - - - - - Rfc 2104. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SASL authentications - - - - - Non authentication - - - - - LOGIN. - - - - - CRAM-MD5 - - - - - DIGEST-MD5. - - - - - All authentications. - - - - - This class implement dns query cache. - - - - - Tries to get dns records from cache, if any. - - - - Returns null if not in cache. - - - - Adds dns records to cache. - - - - - - - - A record class. - - - - - Default constructor. - - IP address. - - - - Gets mail host dns name. - - - - - NS record class. - - - - - Default constructor. - - Name server name. - - - - Gets name server name. - - - - - CNAME record class. - - - - - Default constructor. - - Alias. - - - - Gets alias. - - - - - SOA record class. - - - - - Default constructor. - - Name server. - - - - Gets name server. - - - - - PTR record class. - - - - - Default constructor. - - DomainName. - - - - Gets domain name. - - - - - MX record class. - - - - - Default constructor. - - MX record preference. - Mail host dns name. - - - - Gets MX record preference. - - - - - Gets mail host dns name. - - - - - TXT record class. - - - - - Default constructor. - - Text. - - - - Gets text. - - - - - Dns reply codes. - - - - - Requested records retrieved sucessfully. - - - - - No requested records found. - - - - - There was error retrieving records. - - - - - Dns resolver. - - - - // Set dns servers - DnsEx.DnsServers = new string[]{"194.126.115.18"}; - - DnsEx dns = DnsEx(); - - // Get MX records - MX_Record[] mxRecords = dns.GetMXRecords("lumisoft.ee") - - // Do your stuff - - - - - - Default constructor. - - - - - Queries server with specified query. - - Query text. It depends on queryType. - Query type. - - - - - Gets IPv4 host addess records. - - Domain name which A records to get. - - - - - Gets name server records. - - Domain name which A records to get. - - - - - Gets CNAME records. - - Domain name which CNAME records to get. - - - - - Gets PTR records. - - IP address which domain names to get. - - - - - Gets MX records.(MX records are sorted by preference, lower array element is prefered) - - - - - - - Gets MX records.(MX records are sorted by preference, lower array element is prefered) - - - - - - - - Gets text records. - - Domain name which A records to get. - - - - - Gets IPv6 host addess records. - - Domain name which AAAA records to get. - - - - - Filters out specified type of records from answer. - - - - - - - - Parses MX record. - - - - Returns null, if failed. - - - - Sends query to server. - - Query text. - Query type. - Query class. - - - - - Creates new query. - - - - - - - - - - Parses answer. - - - - Returns Dns_Answer[] collection if answer parsed successfully or throws exception if failed. - - - - Gets or sets dns servers. - - - - - Gets or sets if to use dns caching. - - - - - Get next query ID. - - - - - This class holds dns server response. - - - - - Gets IPv4 host addess records. - - - - - - Gets name server records. - - - - - - Gets CNAME records. - - - - - - Gets SOA records. - - - - - - Gets PTR records. - - - - - - Gets MX records.(MX records are sorted by preference, lower array element is prefered) - - - - - - Gets text records. - - - - - - Gets IPv6 host addess records. - - - - - - Filters out specified type of records from answer. - - - - - - - - Gets if connection dns server was successfull. - - - - - Gets dns server response code. - - - - - Gets dns server returned answers. NOTE: Before using this property ensure that ConnectionOk=true and ResponseCode=RCODE.NO_ERROR. - - - - - - - - - - a standard query. - - - - - an inverse query. - - - - - a server status request. - - - - - ´Query type. - - - - - IPv4 host address - - - - - an authoritative name server - - - - - the canonical name for an alias - - - - - marks the start of a zone of authority - - - - - a domain name pointer - - - - - mail exchange - - - - - text strings - - - - - IPv6 host address - - - - - Dns server reply codes. - - - - - No error condition. - - - - - Format error - The name server was unable to interpret the query. - - - - - Server failure - The name server was unable to process this query due to a problem with the name server. - - - - - Name Error - Meaningful only for responses from an authoritative name server, this code signifies that the - domain name referenced in the query does not exist. - - - - - Not Implemented - The name server does not support the requested kind of query. - - - - - Refused - The name server refuses to perform the specified operation for policy reasons. - - - - - Transfer mode. - - - - - ASCII transfer mode. - - - - - Binary transfer mode. - - - - - Ftp client. - - - - - Default connection. - - - - - Clears resources and closes connection if open. - - - - - Connects to specified host. - - Host name. - Port. - - - - Disconnects from active host. - - - - - Authenticates user. - - User name. - Password. - - - - Sets current directory. - - Directory. - - - - Gets directory listing. - - Returns DataSet(DirInfo DataTable) with directory listing info. - - - - Creates directory. - - Directory name. - - - - Renames directory. - - Name of directory which to rename. - New directory name. - - - - Deletes directory. - - Name of directory which to delete. - - - - Recieves specified file from server. - - File name of file which to receive. - File path+name which to store. - - - - Recieves specified file from server. - - File name of file which to receive. - Stream where to store file. - - - - Stores specified file to server. - - File path+name which to store in server. - - - - Stores specified file to server. - - Stream from where to gets file. - File name to store in server. - - - - Deletes specified file or directory. - - File name. - - - - Renames specified file or directory. - - File name of file what to rename. - New file name. - - - - Sets transfer mode. - - Transfer mode. - - - - Parses server returned directory listing. - - - - - - - Gets data connection mode. - Passive - client connects to ftp server. - Active - ftp server connects to client. - - - - - Provides data for the AuthUser event for FTP_Server. - - - - - Default constructor. - - Reference to pop3 session. - Username. - Password data. - Authentication specific data(as tag). - Authentication type. - - - - Gets reference to pop3 session. - - - - - User name. - - - - - Password data. eg. for AUTH=PLAIN it's password and for AUTH=APOP it's md5HexHash. - - - - - Authentication specific data(as tag). - - - - - Authentication type. - - - - - Gets or sets if user is valid. - - - - - Provides data for the filesytem related events for FTP_Server. - - - - - Default constructor. - - - - - - - - Gets reference to FTP session. - - - - - Gets directory or file name with path. - - - - - Gets new directory or new file name with path. This filled for Rename event only. - - - - - Gets or sets file stream. - - - - - Gets or sets if operation was successful. NOTE: default value is true. - - - - - Gets reference to dir listing info. Please Fill .Tables["DirInfo"] table with required fields. - - - - - Represents the method that will handle the AuthUser event for FTP_Server. - - The source of the event. - A AuthUser_EventArgs that contains the event data. - - - - Represents the method that will handle the filsystem rerlated events for FTP_Server. - - - - - FTP Server component. - - - - - This is base class for Socket and Session based servers. - - - - - Default constructor. - - - - - Clean up any resources being used and stops server. - - - - - Starts server. - - - - - Stops server. NOTE: Active sessions aren't cancled. - - - - - Starts proccessiong incoming connections (Accepts and queues connections). - - - - - Starts queueed connections proccessing (Creates and starts session foreach connection). - - - - - - - - - - - - - - - - - - - - - - - - This method must get timedout sessions and end them. - - - - - Initialize and start new session here. Session isn't added to session list automatically, - session must add itself to server session list by calling AddSession(). - - Connected client socket. - - - - Occurs when server or session has system error(unhandled error). - - - - - Gets or sets which IP address to listen. - - - - - Gets or sets which port to listen. - - - - - Gets or sets maximum session threads. - - - - - Command idle timeout in milliseconds. - - - - - Gets or sets IPEndPoint server to listen. NOTE: If server running and changeing IPEndPoint, server will be restarted automatically. - - - - - Gets or sets maximum allowed connections. - - - - - Runs and stops server. - - - - - Gets or sets if to log commands. - - - - - Session idle timeout in milliseconds. - - - - - Gets or sets maximum bad commands allowed to session. - - - - - Gets or set host name that is reported to clients. - - - - - Gets active sessions. - - - - - Defalut constructor. - - - - - - - - - - - Raises event ValidateIP event. - - Server IP. - Connected client IP. - Returns true if connection allowed. - - - - Authenticates user. - - Reference to current pop3 session. - User name. - - - - - - - - Occurs when new computer connected to FTP server. - - - - - Occurs when connected user tryes to authenticate. - - - - - Occurs when server needs directory info (directories,files in deirectory). - - - - - Occurs when server needs to validatee directory. - - - - - Occurs when server needs needs to create directory. - - - - - Occurs when server needs needs to delete directory. - - - - - Occurs when server needs needs validate file. - - - - - Occurs when server needs needs to store file. - - - - - Occurs when server needs needs to get file. - - - - - Occurs when server needs needs to delete file. - - - - - Occurs when server needs needs to rname directory or file. - - - - - Occurs when POP3 session has finished and session log is available. - - - - - FTP Session. - - - - - Summary description for ISocketServerSession. - - - - - - - - - - - - - - - Default constructor. - - Referance to socket. - Referance to FTP server. - Session ID which is assigned to this session. - Log writer. - - - - Starts session. - - - - - Ends session, closes socket. - - - - - Is called by server when session has timed out. - - - - - Is called if there was some activity on socket, some data sended or received. - - - - - - - Is called when error occures. - - - - - - Starts recieveing command. - - - - - Is called if command is recieved. - - - - - - - - - Parses and executes POP3 commmand. - - FTP command text. - Returns true,if session must be terminated. - - - - Gets session ID. - - - - - Gets session start time. - - - - - Gets last data activity time. - - - - - Gets loggded in user name (session owner). - - - - - Gets EndPoint which accepted conection. - - - - - Gets connected Host(client) EndPoint. - - - - - Gets or sets custom user data. - - - - - ICMP type. - - - - - Echo rely. - - - - - Time to live exceeded reply. - - - - - Echo. - - - - - Echo reply message. - - - - - - - - - - - - - - - - - - - - - - - - - - Icmp utils. - - - - - Traces specified ip. - - - - - - - IMAP client. - - - - using(IMAP_Client c = new IMAP_Client()){ - c.Connect("ivx",143); - c.Authenticate("test","test"); - - c.SelectFolder("Inbox"); - - // Get messages header here - IMAP_FetchItem msgsInfo = c.FetchMessages(1,-1,false,true,true); - - // Do your suff - } - - - - - - Default constructor. - - - - - Clean up any resources being used. - - - - - Connects to IMAP server. - - Host name. - Port number. - - - - Disconnects from IMAP server. - - - - - Authenticates user. - - User name. - Password. - - - - Creates specified folder. - - Folder name. Eg. test, Inbox/SomeSubFolder. NOTE: use GetFolderSeparator() to get right folder separator. - - - - Deletes specified folder. - - Folder name. - - - - Renames specified folder. - - Source folder name. - Destination folder name. - - - - Gets all available folders. - - - - - - Gets all subscribed folders. - - - - - - Subscribes specified folder. - - Folder name. - - - - UnSubscribes specified folder. - - Folder name, - - - - Selects specified folder. - - Folder name. - - - - Sets specified user ACL permissions for specified folder. - - Folder name which ACL to set. - User name who's ACL to set. - ACL permissions to set. - - - - Deletes specifieed user access to specified folder. - - Folder which ACL to remove. - User name who's ACL to remove. - - - - Gets myrights to specified folder. - - - - - - - Makes copy of messages to specified folder. - - Start message number. - End message number. -1 = last. - Folder where to cpoy messages. - Specifies if startMsgNo and endMsgNo is message UIDs. - - - - Moves messages to specified folder. - - Start message number. - End message number. -1 = last. - Folder where to cpoy messages. - Specifies if startMsgNo and endMsgNo is message UIDs. - - - - Deletes specified messages. - - Start message number. - End message number. -1 = last. - Specifies if startMsgNo and endMsgNo is message UIDs. - - - - Stores message to specified folder. - - Folder where to store message. - Message data which to store. - - - - Fetches messages headers or full messages data. - - Start message number. - End message number. -1 = last. - Specifies if startMsgNo and endMsgNo is message UIDs. - If true message headers are retrieved, otherwise full message retrieved. - If true message seen flag is setted. - - - - - Stores message folgs to sepcified messages range. - - Start message number. - End message number. - Sepcifies if message numbers are message UID numbers. - Message flags to store. - - - - Gets messages total size in selected folder. - - - - - - Gets unseen messages count in selected folder. - - - - - - Gets IMAP server folder separator char. - - - - - - Gets selected folder. - - - - - Gets numbers of recent(not accessed messages) in selected folder. - - - - - Gets numbers of messages in selected folder. - - - - - IMAP fetch item. - - - - - Default constructor. - - Message UID. - Message size. - Message data. - Specifies if message data contains headers only or full message. - Specifies if unseen message. - Specifies if message is answered. - - - - Gets message UID. - - - - - Gets message size. - - - - - Gets message data(headers or full message), it depends on HeadersOnly property. - - - - - Gets if headers or full message requested in fetch. - - - - - Gets if message is unseen. - - - - - Gets if message is answered. - - - - - FETCH command helper methods. - - - - - Returns requested header fields lines. - - Header fields to get. - Message data. - - - - - Returns header fields lines except requested. - - Header fields to skip. - Message data. - - - - - Returns requested mime entry data. - - - - Returns requested mime entry data or NULL if requested entri doesn't exist. - - - - Construct FETCH ENVELOPE response. - - - - - - - Constructs FETCH BODY and BODYSTRUCTURE response. - - - - - - - - Summary description for _SearchHelper. - - - - - Checks if message matches for specified search key. - - - - - - - - - - Parses SEARCH command date. - - Date string. - Returns date. - - - - Provides data for the AuthUser event for POP3_Server and SMTP_Server. - - - - - Default constructor. - - Reference to IMAP session. - Username. - Password data. - Authentication specific data(as tag). - Authentication type. - - - - Gets reference to smtp session. - - - - - User name. - - - - - Password data. eg. for AUTH=PLAIN it's password and for AUTH=APOP it's md5HexHash. - - - - - Authentication specific data(as tag). - - - - - Authentication type. - - - - - Gets or sets if user is valid. - - - - - Gets or sets authentication data what must be returned for connected client. - - - - - Provides data for IMAP events. - - - - - Default constructor. - - - - - - Folder rename constructor. - - - - - - - Gets folder. - - - - - Gets new folder name, this is available for rename only. - - - - - Gets or sets custom error text, which is returned to client. - - - - - Provides data for DeleteFolderACL event. - - - - - Default constructor. - - Owner IMAP session. - Folder name which ACL to delete. - User name which ACL to delete. - - - - Gets current IMAP session. - - - - - Gets folder name which ACL to delete. - - - - - Gets user name which ACL to delete. - - - - - Gets or sets error text returned to connected client. - - - - - Provides data for GetFolderACL event. - - - - - Default constructor. - - Owner IMAP session. - Folder name which ACL to get. - - - - Gets current IMAP session. - - - - - Gets folder name which ACL to get. - - - - - Gets ACL collection. Key = userName, Value = IMAP_ACL_Flags. - - - - - Gets or sets error text returned to connected client. - - - - - Provides data for GetUserACL event. - - - - - Default constructor. - - Owner IMAP session. - Folder name which ACL to get. - User name which ACL to get. - - - - Gets current IMAP session. - - - - - Gets folder name which ACL to get. - - - - - Gets user name which ACL to get. - - - - - Gets or sets user permissions(ACL) for specified folder. - - - - - Gets or sets error text returned to connected client. - - - - - Provides data for SetFolderACL event. - - - - - Default constructor. - - Owner IMAP session. - Folder name which ACL to set. - User name which ACL to set. - Specifies how flags must be stored. - Flags which to store. - - - - Gets current IMAP session. - - - - - Gets folder name which ACL to set. - - - - - Gets user name which ACL to set. - - - - - Gets how ACL flags must be stored. - - - - - Gets ACL flags. NOTE: See this.FlagsSetType how to store flags. - - - - - Gets or sets error text returned to connected client. - - - - - Summary description for IMAP_Flags_SetType. - - - - - Flags are added to existing ones. - - - - - Flags are removed from existing ones. - - - - - Flags are replaced. - - - - - IMAP mailbox - - - - - Default cnstructor. - - Full path to folder, path separator = '/'. Eg. Inbox/myFolder . - Gets or sets if folder is selectable(SELECT command can select this folder). - - - - IMAP folder name. Eg. Inbox, Inbox/myFolder, ... . - - - - - Gets or sets if folder is selectable(SELECT command can select this folder). - - - - - IMAP folders collection. - - - - - Default constructor. - - Owner IMAP session. - Folder Path. Eg. Inbox\. - Folder name. - - - - Adds folder to folders list. - - Full path to folder, path separator = '/'. Eg. Inbox/myFolder . - Gets or sets if folder is selectable(SELECT command can select this folder). - - - - Gets current IMAP session. - - - - - Gest list of IMAP folders. - - - - - IMAP message info. - - - - - Default constructor. - - - Internal messageID. - Message UID. NOTE: message uid must increase all the time, for new messages. - Message flags. - Message size. - Message receive date. - - - - Converts message flags to string. Eg. \SEEN \DELETED . - - - - - - Gets message number. - - - - - Gets internal messageID. - - - - - Gets message UID. - - - - - Gets message flags. - - - - - Gets message size. - - - - - Gets message size. - - - - - IMAP messages info collection. - - - - - Default constructor. - - - - - - Adds new message to list. - - Internal messageID. - Message UID. NOTE: message uid must increase all the time, for new messages. - Message flags. - Message size. - Message receive date. - - - - Removes message from list. - - Message which to remove. - - - - Gets message 1-based index. - - - - - - - Gets message 1-based message index from message UID. - - - - - - - Gets messages marked for delete. - - - - - - Gets specified message. - - - - - Gets first unseen message number. - - - - - - Gets unseen messages count. - - - - - - Gets new messages count. - - - - - - Gets messages marked for delete count. - - - - - - Gets messages total count. - - - - - Gets mailbox which messages contains. - - - - - Gets or sets mailbox UID value. - - - - - Gets predictable next UID value.(Max(messageUID) + 1). - - - - - Gets or sets if messages folder is read only. - - - - - Gets or sets custom error text, which is returned to client. - - - - - Represents the method that will handle the AuthUser event for SMTP_Server. - - The source of the event. - A AuthUser_EventArgs that contains the event data. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IMAP server componet. - - - - - Defalut constructor. - - - - - - - - - - - Raises event ValidateIP event. - - Server IP. - Connected client IP. - Returns true if connection allowed. - - - - Raises event AuthUser. - - Reference to current IMAP session. - User name. - Password compare data,it depends of authentication type. - For md5 eg. md5 calculation hash.It depends of authentication type. - Authentication type. - Returns true if user is authenticated ok. - - - - Raises event 'SubscribeMailbox'. - - Reference to IMAP session. - Mailbox which to subscribe. - - - - - Raises event 'UnSubscribeMailbox'. - - Reference to IMAP session. - Mailbox which to unsubscribe. - - - - - Raises event 'GetSubscribedMailboxes'. - - Reference to IMAP session. - Mailbox reference. - Mailbox search pattern or mailbox. - - - - - Raises event 'GetMailboxes'. - - Reference to IMAP session. - Mailbox reference. - Mailbox search pattern or mailbox. - - - - - Raises event 'CreateMailbox'. - - Reference to IMAP session. - Mailbox to create. - - - - - Raises event 'DeleteMailbox'. - - Reference to IMAP session. - Mailbox which to delete. - - - - - Raises event 'RenameMailbox'. - - Reference to IMAP session. - Mailbox which to rename. - New mailbox name. - - - - - Raises event 'GetMessagesInfo'. - - Reference to IMAP session. - Mailbox which messages info to get. - - - - - Raises event 'GetMessage'. - - Reference to IMAP session. - Message which to get. - Specifies if message header or full message is wanted. - - - - - Raises event 'DeleteMessage'. - - Reference to IMAP session. - Message which to delete. - - - - - Raises event 'CopyMessage'. - - Reference to IMAP session. - Message which to copy. - New message location. - - - - - Raises event 'StoreMessage'. - - Reference to IMAP session. - Folder where to store. - Message which to store. - Message data which to store. - - - - - Raises event 'StoreMessageFlags'. - - Reference to IMAP session. - Message which flags to store. - - - - - Occurs when new computer connected to IMAP server. - - - - - Occurs when connected user tryes to authenticate. - - - - - Occurs when server requests to subscribe folder. - - - - - Occurs when server requests to unsubscribe folder. - - - - - Occurs when server requests all available folders. - - - - - Occurs when server requests subscribed folders. - - - - - Occurs when server requests to create folder. - - - - - Occurs when server requests to delete folder. - - - - - Occurs when server requests to rename folder. - - - - - Occurs when server requests to folder messages info. - - - - - Occurs when server requests to delete message. - - - - - Occurs when server requests to store message. - - - - - Occurs when server requests to store message flags. - - - - - Occurs when server requests to copy message to new location. - - - - - Occurs when server requests to get message. - - - - - Occurs when IMAP session has finished and session log is available. - - - - - Occurs when IMAP server requests shared root folders info. - - - - - Occurs when IMAP server requests folder ACL. - - - - - Occurs when IMAP server requests to delete folder ACL. - - - - - Occurs when IMAP server requests to set folder ACL. - - - - - Occurs when IMAP server requests to get user ACL for specified folder. - - - - - Maximum message size. - - - - - IMAP session. - - - - - Default constructor. - - Referance to socket. - Referance to IMAP server. - Log writer. - - - - Starts session. - - - - - Ends session, closes socket. - - - - - Is called by server when session has timed out. - - - - - Is called if there was some activity on socket, some data sended or received. - - - - - - - Is called when error occures. - - - - - - Starts recieveing command. - - - - - Is called if command is recieved. - - - - - - - - - Executes IMAP command. - - Original command text. - Returns true if must end session(command loop). - - - - Returns true if command ended syncronously. - - - - - Is called when DATA command is finnished. - - - - - - - - - Parses message flags from string. - - - - - - - Gets session ID. - - - - - Gets if session authenticated. - - - - - Gets loggded in user name (session owner). - - - - - Gets selected mailbox. - - - - - Gets connected Host(client) EndPoint. - - - - - Gets local EndPoint which accepted client(connected host). - - - - - Gets session start time. - - - - - Gets last data activity time. - - - - - Gets or sets custom user data. - - - - - Provides utility methods for IMAP. - - - - - Parses message flags from string. - - - - - - - Converts message flags to string. Eg. \SEEN \DELETED . - - - - - - Converts IMAP_ACL_Flags to string. - - Flags to convert. - - - - - Parses IMAP_ACL_Flags from string. - - String from where to convert - - - - - Normalizes folder path. Example: /Inbox/SubFolder/ will be Inbox/SubFolder. - - Folder path to normalize. - Returns normalized folder path. - - - - Parses [quoted] parameter from args text. Parameter may be not quoted, then parameter is - terminated by SP. Example: argsText="string gdkga agkgs";argsText=stringValue 10. - - This method also removes parsed parameter from argsText. - - Arguments line from where to parse param. - - - - - Parses bracket parameter from args text. Parameter may be not between (), then - then args text is considered as value. Example: (test test);test test. - - This method also removes parsed parameter from argsText. - - - - - - - Provides data for message related events. - - - - - Default constructor. - - IMAP folder which message is. - - - - - Copy constructor. - - IMAP folder which message is. - - - - - - GetMessage constructor. - - IMAP folder which message is. - - Specifies if messages headers or full message is needed. - - - - Gets IMAP folder. - - - - - Gets IMAP message info. - - - - - Gets message new location. NOTE: this is available for copy command only. - - - - - Gets or sets message data. NOTE: this is available for GetMessage and StoreMessage event only. - - - - - Gets if message headers or full message wanted. NOTE: this is available for GetMessage event only. - - - - - Gets or sets custom error text, which is returned to client. - - - - - Summary description for SharedRootFolders_EventArgs. - - - - - Default constructor. - - - - - Gets reference to smtp session. - - - - - Gets or sets users shared root folders. Ususaly there is only one root folder 'Shared Folders'. - - - - - Gets or sets public root folders. Ususaly there is only one root folder 'Public Folders'. - - - - - IMAP ACL(access control list) rights. - - - - - No permissions at all. - - - - - Lookup (mailbox is visible to LIST/LSUB commands). - - - - - Read (SELECT the mailbox, perform CHECK, FETCH, PARTIAL,SEARCH, COPY from mailbox). - - - - - Keep seen/unseen information across sessions (STORE SEEN flag). - - - - - Write (STORE flags other than SEEN and DELETED). - - - - - Insert (perform APPEND, COPY into mailbox). - - - - - Post (send mail to submission address for mailbox,not enforced by IMAP4 itself). - - - - - Create (CREATE new sub-mailboxes in any implementation-defined hierarchy). - - - - - Delete (STORE DELETED flag, perform EXPUNGE). - - - - - Administer (perform SETACL). - - - - - All permissions - - - - - IMAP message flags. - - - - - Message has been read. - - - - - Message has been answered. - - - - - Message is "flagged" for urgent/special attention. - - - - - Message is "deleted" for removal by later EXPUNGE. - - - - - Message has not completed composition. - - - - - Message is "recently" arrived in this mailbox. - - - - - Electronic address. - - - - - Default constructor. - - Electronic address. Eg. "Ivar Lumi" ivar@lumisoft.ee. - - - - Gets email address. Eg. ivar@lumisoft.ee . - - - - - Gets mailbox. Eg. mailbox=ivar from ivar@lumisoft.ee . - - - - - Gets domain. Eg. domain=lumisoft.ee from ivar@lumisoft.ee . - - - - - Gets name. Eg. name='Ivar Lumi' from "Ivar Lumi" ivar@lumisoft.ee . - - - - - Gets full electronic address. Eg. "Ivar Lumi" ivar@lumisoft.ee . - - - - - Mime entry. - - - - - Default constructor. - - - - - - - Parse charset. - - - - - - - Parse encoding. quoted-printable,7bit,8bit,base64 is supported. - - - - - - - Decode entry data. - - - - - - - Gets content encoding. - - - - - Gets content type. - - - - - Gets content disposition type. - - - - - Gets headers. - - - - - Gets file name. NOTE: available only if ContentDisposition.Attachment. - - - - - Gets mime entry decoded data. - - - - - Gets mime entry non decoded data. - - - - - Gets string data. NOTE: available only if content-type=text/xxx. - - - - - Gets nested mime entries. - - - - - Content disposition. - - - - - Content is attachment. - - - - - Content is embbed resource. - - - - - Content is unknown. - - - - - Mime parser. - - - - // NOTE: load you message yo byte[] here (from file,pop3 or imap server, ...). - byte[] data = null; - - MimeParser p = new MimeParser(data); - - // Do your stuff here - string from = p.Form; - - - - - - - Default constructor. - - Mime message which to parse. - - - - Parse content type. - - - - - - - Parses mime entries. - - - - - - - - Gets mime entries, including nested entries. - - - - - - - Parses rfc2822 datetime. - - Date string - - - - - Parses headers from message or mime entry. - - Stream from where to read headers. - Returns header lines. - - - - Parse header specified header field value. - - Use this method only if you need to get only one header field, otherwise use - MimeParser.ParseHeaderField(string fieldName,string headers). - This avoid parsing headers multiple times. - - Header field which to parse. Eg. Subject: . - Stream from where to read headers. - - - - - Parse header specified header field value. - - Header field which to parse. Eg. Subject: . - Full headers string. Use MimeParser.ParseHeaders() to get this value. - - - - Parses header field sub field value. - For example: CONTENT-TYPE: application\octet-stream; name="yourFileName.xxx", - fieldName="CONTENT-TYPE:" and subFieldName="name". - - Main header field name. - Header field's sub filed name. - Full headrs string. - - - - - Gets message headers. - - - - - Gets sender. - - - - - Gets recipients. - - - - - Gets cc. - - - - - Gets bcc. - - - - - Gets subject. - - - - - Gets message body text. - - - - - Gets message body HTML. - - - - - Gets messageID. - - - - - Gets message content type. - - - - - Gets message date. - - - - - Gets message mime entries. - - - - - Gets mime entries which content-disposition = attachment. - - - - - Attachment. - - - - - Creates attechment from file. - - File name which to attach. Eg. c:\aaa.eml . - - - - Creates attachment from stream with specified file name - - File name. Eg. aaa.eml . - Stream which data to attach. NOTE: Data will be taken from stream current position. - - - - Creates attachment from data with specified file name. - - File name. Eg. aaa.eml . - Data which to attach. - - - - Gets file name. - - - - - Gets file data. - - - - - - - - - - Attachments collection. - - - - - - - - - - - - - - - - - - - - - - Mime constructor. - - - - - Default constructor. - - - - - Constructs mime message. - - - - - - Constructs mime message. - - - - - - Gets or sets mesaage ID. - - - - - Gets or sets receptients. - - - - - Gets or sets . - - - - - Gets or sets . - - - - - Gets or sets sender. - - - - - Gets or sets subject. - - - - - Gets or sets message date. - - - - - Gets or sets body text. - - - - - Gets or sets html body. - - - - - Gets or sets message charset. Default is 'utf-8'. - - - - - Gets referance to attachments collection. - - - - - POP3 Client. - - - - using(POP3_Client c = new POP3_Client()){ - c.Connect("ivx",110); - c.Authenticate("test","test",true); - - POP3_MessagesInfo mInf = c.GetMessagesInfo(); - - // Do your suff - } - - - - - - Default constructor. - - - - - Clean up any resources being used. - - - - - Connects to specified host. - - Host name. - Port number. - - - - Closes connection to POP3 server. - - - - - Authenticates user. - - User login name. - Password. - If true and POP3 server supports APOP, then APOP is used, otherwise normal login used. - - - - Gets messages info. - - - - - Gets uid listing. - - Returns Hashtable containing uidl listing. Key column contains message NR and value contains message UID. - - - - Gets specified message. - - Message number. - - - - Deletes specified message - - Message number. - - - - Gets top lines of message. - - Message number which top lines to get. - Number of lines to get. - - - - Resets session. - - - - - Occurs when POP3 session has finished and session log is available. - - - - - Gets or sets if to log commands. - - - - - Holds POP3 message info. - - - - - Default constructor. - - - - - - - - Gets message unique ID returned by pop3 server. - - - - - Gets message number in POP3 server. - - - - - Gets message size. - - - - - Holds POP3 messages info. - - - - - Default constructor. - - - - - Gets specified message info. - - - - - - - Gets total size of messages. - - - - - Gets messages count. - - - - - Gets list of messages. - - - - - Provides data for the AuthUser event for POP3_Server and SMTP_Server. - - - - - Default constructor. - - Reference to pop3 session. - Username. - Password data. - Authentication specific data(as tag). - Authentication type. - - - - Gets reference to pop3 session. - - - - - User name. - - - - - Password data. eg. for AUTH=PLAIN it's password and for AUTH=APOP it's md5HexHash. - - - - - Authentication specific data(as tag). - - - - - Authentication type. - - - - - Gets or sets if user is valid. - - - - - Gets or sets authentication data what must be returned for connected client. - - - - - Provides data for the GetMessgesList event. - - - - - Default constructor. - - Reference to pop3 session. - - Mailbox name. - - - - Gets reference to pop3 session. - - - - - Gets referance to POP3 messages info. - - - - - User Name. - - - - - Mailbox name. - - - - - Holds POP3_Message info (ID,Size,...). - - - - - Default constructor. - - - - - - Gets or sets message ID. - - - - - Gets or sets message UID. This UID is reported in UIDL command. - - - - - Gets or sets message size. - - - - - Gets or sets message state flag. - - - - - Gets message number. NOTE message number is 1 based (not zero based). - - - - - Gets or sets user data for message. - - - - - Provides data for the GetMailEvent,DeleteMessage,GetTopLines event. - - - - - Default constructor. - - Reference to pop3 session. - Message which to get. - Connected socket. - - - - TopLines constructor. - - Reference to pop3 session. - Message which to get. - Connected socket. - Number of lines to get. - - - - Gets reference to pop3 session. - - - - - Gets reference to message, which to get. - - - - - ID of message which to retrieve. - - - - - UID of message which to retrieve. - - - - - Mail message which is delivered to user. NOTE: may be full message or top lines of message. - - - - - Number of lines to get. - - - - - User Name. - - - - - POP3 messages collection. - - - - - Default constructor. - - - - - Adds new message to message list. - - Message ID. - Message UID. This UID is reported in UIDL command. - Message size in bytes. - - - - Adds new message to message list. - - Message ID. - Message UID. This UID is reported in UIDL command. - Message size in bytes. - User data for message. - - - - Gets specified message from message list. - - Number of message which to get. - - - - - Checks if message exists. NOTE marked for delete messages returns false. - - Number of message which to check. - - - - - Gets messages total sizes. NOTE messages marked for deletion is excluded. - - - - - - Unmarks all messages, which are marked for deletion. - - - - - Gets count of messages. NOTE messages marked for deletion are excluded. - - - - - Gets messages, which aren't marked for deletion. - - - - - Referance to Messages ArrayList. - - - - - Gets specified message. - - - - - Represents the method that will handle the AuthUser event for POP3_Server. - - The source of the event. - A AuthUser_EventArgs that contains the event data. - - - - Represents the method that will handle the GetMessgesList event for POP3_Server. - - The source of the event. - A GetMessagesInfo_EventArgs that contains the event data. - - - - Represents the method that will handle the GetMessage,DeleteMessage,GetTopLines event for POP3_Server. - - The source of the event. - A GetMessage_EventArgs that contains the event data. - - - - POP3 server component. - - - - - Defalut constructor. - - - - - - - - - - - Checks if user is logged in. - - User name. - - - - - Raises event ValidateIP event. - - Server IP. - Connected client IP. - Returns true if connection allowed. - - - - Authenticates user. - - Reference to current pop3 session. - User name. - - - - - - - - Gest pop3 messages info. - - - - - - - Raises event get message. - - - Message which to get. - Message which to get. - - - - - Raises delete message event. - - - Message which to delete. - - - - - Raises event GetTopLines. - - - Message wich top lines to get. - Header + number of body lines to get. - - - - - Raises SessionEnd event. - - Session which is ended. - - - - Raises SessionResetted event. - - Session which is resetted. - - - - Occurs when new computer connected to POP3 server. - - - - - Occurs when connected user tryes to authenticate. - - - - - Occurs user session ends. This is place for clean up. - - - - - Occurs user session resetted. Messages marked for deletion are unmarked. - - - - - Occurs when server needs to know logged in user's maibox messages. - - - - - Occurs when user requests specified message. - - - - - Occurs when user requests delete message. - - - - - Occurs when user requests specified message TOP lines. - - - - - Occurs when POP3 session has finished and session log is available. - - - - - POP3 Session. - - - - - Default constructor. - - Referance to socket. - Referance to POP3 server. - Log writer. - - - - Starts session. - - - - - Ends session, closes socket. - - - - - Is called by server when session has timed out. - - - - - Is called if there was some activity on socket, some data sended or received. - - - - - - - Is called when error occures. - - - - - - Starts recieveing command. - - - - - Is called if command is recieved. - - - - - - - - - Parses and executes POP3 commmand. - - POP3 command text. - Returns true,if session must be terminated. - - - - Is called when asynchronous send completes. - - If true, then send was successfull. - Count sended. - Exception happend on send. NOTE: available only is result=false. - User data. - - - - Gets session ID. - - - - - Gets session start time. - - - - - Gets last data activity time. - - - - - Gets loggded in user name (session owner). - - - - - Gets EndPoint which accepted conection. - - - - - Gets connected Host(client) EndPoint. - - - - - Gets or sets custom user data. - - - - - Fixed Stack, last-in-first-out. Fix me: this isn't Stack, this is Queue. - - - - - Terminator holder and checker stack. - - - - - - Pushes new bytes to stack.(Last in, first out). - - - Count to push from bytes parameter - Returns number of bytes may be pushed next push. - NOTE: returns 0 if stack contains terminator. - - - - - Check if stack contains terminator. - - - - - - Default constructor. - - - - - - - - - - - - - - - - - Summary description for _ParamParser. - - - - - Parses name-value params. - - Parse source. - Expressions importance order. NOTE: must contain param and value groups. - - - - To be supplied. - - - - - To be supplied. - - - - - Represents the method that will handle the SMTP_Server.ValidateIPAddress and POP3_Server.ValidateIPAddressevent. - - The source of the event. - A ValidateIP_EventArgs that contains the event data. - - - - Provides data for the SysError event for servers. - - - - - Default constructor. - - - - - - - Occured error's exception. - - - - - Occured error's stacktrace. - - - - - Gets comment text. - - - - - Provides data for the SessionLog event for POP3_Server and SMTP_Server. - - - - - Default constructor. - - - - - - - Gets log text. - - - - - Gets logger. - - - - - Socket log entry. - - - - - Default constructor. - - Log text. - Data size. - Log entry type - - - - Gets log text. - - - - - Gets size of data readed or sent. - - - - - Gets log entry type. - - - - - Log entry type. - - - - - Summary description for SocketLogger. - - - - - Default constructor. - - - - - - - Adds data read(from remoteEndpoint) entry. - - - - - - - Adds data send(to remoteEndpoint) entry. - - - - - - - Adds free text entry. - - - - - - - - - - - Gets or sets session ID. - - - - - Gets log entries. - - - - - Gets local endpoint. - - - - - Gets remote endpoint. - - - - - Provides data for the ValidateIPAddress event for servers. - - - - - Default constructor. - - Server IP. - Connected client IP. - - - - IP address of computer, which is sending mail to here. - - - - - Gets local endpoint. - - - - - Gets remote endpoint. - - - - - Gets or sets if IP is allowed access. - - - - - Gets or sets user data what is stored to session.Tag property. - - - - - Gets sor sets error text what is sent to connected socket. NOTE: This is only used if Validated = false. - - - - - Summary description for PartOfMessage_EventArgs. - - - - - Default constructor. - - - Size of sent block. - - - - - - Gets job ID which these properties are. - - - - - Gets bytes what has sent on this sendjob. - - - - - Gets total bytes what has been sent on this sendjob. - - - - - Gets message size. - - - - - Summary description for SendJob_Eventargs. - - - - - - - - - - - - - - - - - - - - Gets send job ID. - - - - - Gets this send job's email addresses. - - - - - Gets email addresses to which message couldn't be sent. - - - - - SMTP error types. - - - - - Connection related error. - - - - - Email address doesn't exist. - - - - - Some feature isn't supported. - - - - - Authentication failed - - - - - Unknown error. - - - - - - - - - - - - - - - - - - - - SMTP Client. - - - - SMTP_Client c = new SMTP_Client(); - c.UseSmartHost = false; - c.DnsServers = new string[]{"194.126.115.18"}; - - // Construct,load message here. - // You can use here MimeConstructor to create new message. - MemoryStream msgStrm = new MemoryStream(); - - c.Send(new string[]{"recipeint@xx.ww"},"sender@dd.rr",msgStrm); - - - - - - - Default constructor. - - - - - Use this constructor if you use this component on UI component. - NOTE: Events are invoked on UI Thread. - - - - - - Starts asynchronous sending. - - Recipients, may be from different e-domain when using dns or relay is allowed in smart host. - Sendres email address. - Stream which contains message. NOTE: reading from stream is started from stream current position. - - - - This function just controls sending. - - - - - Sends message. - - Recipients. NOTE: recipients must be desination server recipients or when using SmartHost, then smart host must allow relay. - Sendrer's email address. - tream which contains message. NOTE: reading from stream is started from stream current position. - Returns true if send comeleted successfully. - - - - Removes sender Thread - Thread has finnished sending. - - - - - - Checks if reply code. - - Replay code to check. - Full repaly. - Retruns true if reply is as specified. - - - - Raises PartOfMessageIsSent event. - - - - - - - - Raises NewSendJob event. - - - - - - - Raises SendJobCompleted event. - - - - - - - - Raises CompletedAll event. - - - - - Raises Error event. - - Error type. - Affected email addresses. - Error text. - - - - Is raised when some send jobs message part is sent. - - - - - Is raised when new send job starts. - - - - - Is raised when send job completes. - - - - - Is raised when error occurs. - - - - - Is raised when all sedjobs are completed. - - - - - Occurs when SMTP session has finished and session log is available. - - - - Stores the username used to authenticate on the SMTP server. - If no authentication is needed leave this value blank. - - - Stores the password used to authenticate on the SMTP server. - If no authentication is needed leave this value blank. - - - - Gets or sets host name reported to SMTP server. Eg. 'mail.yourserver.net'. - - - - - Gets or sets smart host. Eg. 'mail.yourserver.net'. - - - - - Gets or sets dns servers(IP addresses). - - - - - Gets or sets if mail is sent through smart host or using dns. - - - - - Gets or sets SMTP port. - - - - - Gets or sets maximum sender Threads. - - - - - Gets last send attempt errors. - - - - - Gets or sets if to log commands. - - - - - Gets if some send job is active. - - - - - This class holds smtp error info. - - - - - Default constructor. - - - - - - - - Gets SMTP error type. - - - - - Gets list of email addresses which are affected by this error. - - - - - Gets additional error text. - - - - - Is called when asynchronous command had completed. - - - - - SMTP client. - - - - - Default constructor. - - - - - Cleasns up resources and disconnect smtp client if open. - - - - - Is called when socket has sent or recieved data. - - - - - - - Connects to sepcified host. - - Host name or IP address. - Port where to connect. - - - - Connects to sepcified host. - - Sets local endpoint. Pass null, to use default. - Host name or IP address. - Port where to connect. - - - - Starts connection to specified host. - - Host name or IP address. - Port where to connect. - Callback to be called if connect ends. - - - - Starts connection to specified host. - - Sets local endpoint. Pass null, to use default. - Host name or IP address. - Port where to connect. - Callback to be called if connect ends. - - - - Is called from ThreadPool Thread. This method just call synchrounous Connect. - - - - - - Disconnects smtp client from server. - - - - - Begins EHLO command. - - Host name which is reported to SMTP server. - Callback to be called if command ends. - - - - Is called when smtp client has finished EHLO command sending. - - - - - - - - - Is called when smtp client has finished reading EHLO command server response line. - - - - - - - - - Is called when smtp client has finished HELO command sending. - - - - - - - - - Is called when smtp client has finished reading EHLO command server response line. - - - - - - - - - Begins authenticate. - - Uesr name. - Password. - Callback to be called if command ends. - - - - Is called when smtp client has finished AUTH CRAM-MD5 command sending. - - - - - - - - - Is called when smtp client has finished reading AUTH CRAM-MD% server response line. - - - - - - - - - Is called when smtp client has finished sending username and password to smtp server. - - - - - - - - - Is called when smtp client has finished reading user name and password send server response line. - - - - - - - - - Is called when smtp client has finished AUTH LOGIN command sending. - - - - - - - - - Is called when smtp client has finished reading MAIL FROM: command server response line. - - - - - - - - - Is called when smtp client has finished sending user name to SMTP server. - - - - - - - - - Is called when smtp client has finished reading AUTH LOGIN user send server response line. - - - - - - - - - Is called when smtp client has finished sending password to SMTP server. - - - - - - - - - Is called when smtp client has finished reading AUTH LOGIN password send server response line. - - - - - - - - - Begin setting sender. - - Sender email address what is reported to smtp server. - Message size in bytes or -1 if message size isn't known. - Callback to be called if command ends. - - - - Is called when smtp client has finished MAIL FROM: command sending. - - - - - - - - - Is called when smtp client has finished reading MAIL FROM: command server response line. - - - - - - - - - Begin adding recipient. - - - Callback to be called if command ends. - - - - Is called when smtp client has finished RCPT TO: command sending. - - - - - - - - - Is called when smtp client has finished reading RCPT TO: command server response line. - - - - - - - - - Starts sending message. - - - Callback to be called if command ends. - - - - Is called when smtp client has finished BDAT command sending. - - - - - - - - - Is called when smtp client has finished sending BDAT message data to smtp server. - - - - - - - - - Is called when smtp client has finished reading BDAT: command server response line. - - - - - - - - - Is called when smtp client has finished DATA command sending. - - - - - - - - - Is called when smtp client has finished reading DATA command server response line. - - - - - - - - - Is called when smtp client has sending MESSAGE to smtp server. - - - - - - - - - Is called when smtp client has finished reading MESSAGE send smtp server response line. - - - - - - - - - Handles socket errors. - - - - - - - Occurs when SMTP session has finished and session log is available. - - - - - Gets local endpoint. Returns null if smtp client isn't connected. - - - - - Gets remote endpoint. Returns null if smtp client isn't connected. - - - - - Gets or sets dns servers. - - - - - Gets if smtp client is connected. - - - - - Gets when was last activity. - - - - - SMTP command order validator. - - - - - Default constructor. - - - - - Resets state. - - - - - Gets if may handle MAIL command. - - - - - Gets if may handle RCPT command. - - - - - Gets if may handle DATA command. - - - - - Gets if may handle BDAT command. - - - - - Gets if may handle AUTH command. - - - - - Gest or sets if HELO command handled. - - - - - Gest or sets if AUTH command handled. - - - - - Gest or sets if MAIL command handled. - - - - - Gest or sets if RCPT command handled. - - - - - Gest or sets if BinaryMime. - - - - - Provides data for the AuthUser event for POP3_Server and SMTP_Server. - - - - - Default constructor. - - Reference to pop3 session. - Username. - Password data. - Authentication specific data(as tag). - Authentication type. - - - - Gets reference to smtp session. - - - - - User name. - - - - - Password data. eg. for AUTH=PLAIN it's password and for AUTH=APOP it's md5HexHash. - - - - - Authentication specific data(as tag). - - - - - Authentication type. - - - - - Gets or sets if user is valid. - - - - - Gets or sets authentication data what must be returned for connected client. - - - - - Represents the method that will handle the AuthUser event for SMTP_Server. - - The source of the event. - A AuthUser_EventArgs that contains the event data. - - - - Represents the method that will handle the ValidateMailFrom event for POP3_Server. - - The source of the event. - A ValidateSender_EventArgs that contains the event data. - - - - Represents the method that will handle the ValidateMailTo event for POP3_Server. - - The source of the event. - A ValidateRecipient_EventArgs that contains the event data. - - - - Represents the method that will handle the ValidateMailboxSize event for POP3_Server. - - The source of the event. - A ValidateMailboxSize_EventArgs that contains the event data. - - - - Represents the method that will handle the StoreMessage event for POP3_Server. - - The source of the event. - A NewMail_EventArgs that contains the event data. - - - - SMTP server component. - - - - - Defalut constructor. - - - - - - - - - - - Raises event ValidateIP event. - - Reference to current smtp session. - - - - Raises event AuthUser. - - Reference to current smtp session. - User name. - Password compare data,it depends of authentication type. - For md5 eg. md5 calculation hash.It depends of authentication type. - Authentication type. - - - - - Raises event ValidateMailFrom. - - - - - - - - - Raises event ValidateMailTo. - - - - - - - - - - Raises event ValidateMailboxSize. - - - - - - - - - Raises event StoreMessage. - - - - - - - Occurs when new computer connected to POP3 server. - - - - - Occurs when connected user tryes to authenticate. - - - - - Occurs when server needs to validate sender. - - - - - Occurs when server needs to validate recipient. - - - - - Occurs when server needs to validate recipient mailbox size. - - - - - Occurs when server has accepted message to store. - - - - - Occurs when SMTP session has finished and session log is available. - - - - - Gets or sets server greeting text. - - - - - Maximum message size. - - - - - Maximum recipients per message. - - - - - Gets or sets server supported authentication types. - - - - - SMTP Session. - - - - - Default constructor. - - Referance to socket. - Referance to SMTP server. - Log writer. - - - - Starts session. - - - - - Ends session, closes socket. - - - - - Is called by server when session has timed out. - - - - - Is called if there was some activity on socket, some data sended or received. - - - - - - - Is called when error occures. - - - - - - Starts recieveing command. - - - - - Is called if command is recieved. - - - - - - - - - Executes SMTP command. - - Original command text. - Returns true if must end session(command loop). - - - - Is called when DATA command is finnished. - - - - - - - - - Is called when asynchronous send completes. - - If true, then send was successfull. - Count sended. - Exception happend on send. NOTE: available only is result=false. - User data. - - - - Gets session ID. - - - - - Gets client reported EHLO/HELO name. - - - - - Gets if session authenticated. - - - - - Gets loggded in user name (session owner). - - - - - Gets body type. - - - - - Gets local EndPoint which accepted client(connected host). - - - - - Gets connected Host(client) EndPoint. - - - - - Gets sender. - - - - - Gets recipients. - - - - - Gets session start time. - - - - - Gets last data activity time. - - - - - Gets or sets custom user data. - - - - - Provides data for the NewMailEvent event. - - - - - Default constructor. - - Reference to smtp session. - Message stream. - - - - Gets reference to smtp session. - - - - - Message stream - stream where message has stored. - - - - - Message size. - - - - - Sender's email address. - - - - - Receptient's email address. - - - - - Provides data for the ValidateMailboxSize event. - - - - - Default constructor. - - Reference to smtp session. - Email address of recipient. - Message size. - - - - Gets reference to smtp session. - - - - - Email address which mailbox size to check. - - - - - Message size.NOTE: value 0 means that size is unknown. - - - - - Gets or sets if mailbox size is valid. - - - - - Provides data for the ValidateMailTo event. - - - - - Default constructor. - - Reference to smtp session. - Recipient email address. - Specifies if connected user is authenticated. - - - - Gets reference to smtp session. - - - - - Recipient's email address. - - - - - Gets if connected user is authenticated. - - - - - IP address of computer, which is sending mail to here. - - - - - Gets or sets if reciptient is allowed to send mail here. - - - - - Gets or sets if recipient is local or needs relay. - - - - - Provides data for the ValidateMailFrom event. - - - - - Default constructor. - - Reference to smtp session. - Sender email address. - - - - Gets reference to smtp session. - - - - - Sender's email address. - - - - - Gets or sets error text reported to connected client. - - - - - Gets or sets if sender is ok. - - - - - Holds body(mime) type. - - - - - ASCII body. - - - - - ANSI body. - - - - - Binary body. - - - - - Summary description for _SocketState. - - - - - Gets - - - - - Gets - - - - - Gets - - - - - Gets - - - - - Gets - - - - - - - - - - Asynchronous command execute result. - - - - - Operation was successfull. - - - - - Exceeded maximum allowed size. - - - - - Connected client closed connection. - - - - - Exception happened. - - - - - Sokcet + buffer. Socket data reads are buffered. At first Recieve returns data from - internal buffer and if no data available, gets more from socket. Socket buffer is also - user settable, you can add data to socket buffer directly with AppendBuffer(). - - - - - Default constructor. - - Source socket which to buffer. - - - - - - - - - - - - - - - - - - - - - - - - - - Receives data from buffer. If there isn't data in buffer, then receives more data from socket. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Binds socket to local endpoint. - - - - - - - - - - - - - - - - - Receives data from buffer. - - - - - - - Reads line from socket. - - - - - - Reads line from socket. - - Maximum length to read. - - - - - Starts reading line from socket asynchronously. - - Stream where to store line. - Maximum line length. - User data. - Method to call, if receive completes. - - - - Sends line to socket. - - - - - - Starts sending line to socket asynchronously. - - Data line. - Callback to be called if sending ends. - - - - Starts sending line to socket asynchronously. - - Data line. - User data. - Callback to be called if sending ends. - - - - Reads data from socket while specified terminator is reached. - If maximum length is exceeded, reading continues but data won't be stored to stream. - - Stream where to store readed data. - Maximum length to read. - Terminator which trminates reading. - Part of trminator what to remove from end. Can be empty or max part is terminator. - - - - - Reads specified amount of data from socket. - - Stream where to store readed data. - Amount of data to read. - Specifes if to store readed data to stream or junk it. - - - - - Reads data while socket is closed with shutdown. - If maximum length is exceeded, reading continues but data won't be stored to stream. - - Stream where to store readed data. - Maximum length to read. - - - - Sends data to socket. - - - - - - Sends data to socket. - - - - - - Sends data to socket. - - - - - - Begins asynchronous data reading. - - Stream where to store data. - Maximum length of data which may read. - Terminator string which terminates reading. eg '\r\n'. - Removes following string at end of data. - User data. - Method to call, if receive completes. - - - - Begins asynchronous data reading. - - Stream where to store data. - Length of data to read. - Maximum length of data which may read. - User data. - Method to call, if receive completes. - - - - Is called from asynchronous socket if data is recieved. - - - - - - Begins asynchronous sending. - - Data to send. - User data. - Method to call, if send completes. - - - - Begins asynchronous sending. - - Data to send. - User data. - Method to call, if send completes. - - - - Is called from asynchronous socket if data is sended. - - - - - - Starts sending block of data. - - Data to send. - User data. - Method to call, if send completes. - - - - Is called when there is some activity on socket (Read or Send). - - - - - Gets or sets socket encoding for string(ReadLine,SendLine,....) operations. - - - - - Gets or sets logging source. If this is setted, reads/writes are logged to it. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Gets the amount of data in buffer. - - - - - Authentication type. - - - - - Plain username/password authentication. - - - - - APOP - - - - - Not implemented. - - - - - Cram-md5 authentication. - - - - - DIGEST-md5 authentication. - - - - - Provides net core utility methods. - - - - - Does period handling. - - - If true add periods, else removes periods. - - - - - Does period handling. - - Input stream. - If true add periods, else removes periods. - - - - - Does period handling. - - Input stream. - If true add periods, else removes periods. - If true sets stream position to 0. - - - - - Scans invalid CR or LF combination in stream. Returns true if contains invalid CR or LF combination. - - Stream which to check. - Returns true if contains invalid CR or LF combination. - - - - Gets host name. If fails returns 'UnkownHost'. - - - - - - - Gets argument part of command text. - - Input srting from where to remove value. - Command text which to remove. - - - - - Checks if specified string is number(long). - - - - - - - quoted-printable decoder. - - Input string encoding. - Data which to encode. - Specified if line breaks are included or skipped. - Returns decoded data with specified encoding. - - - - quoted-printable decoder. - - Data which to encode. - Specified if line breaks are included or skipped. - Returns decoded data. - - - - "Q" decoder. This is same as quoted-printable, except '_' is converted to ' '. - - Input string encoding. - String which to encode. - Returns decoded string. - - - - Canonical decoding. Decodes all canonical encoding occurences in specified text. - Usually mime message header unicode/8bit values are encoded as Canonical. - Format: =?charSet?type[Q or B]?encoded string?= . - - Text to decode. - Returns decoded text. - - - - Canonical encoding. - - String to encode. - To which charset to encode string. - Returns encoded text. - - - - Checks if specified string data is acii data. - - - - - - - Reply reading return codes. - - - - - Read completed successfully. - - - - - Read timed out. - - - - - Maximum allowed Length exceeded. - - - - - Connected client closed connection. - - - - - UnKnown error, eception raised. - - - - - Summary description for ReadException. - - - - - - - - - - - - Gets read error. - - - - - Byte[] line parser. - - - - - Default constructor. - - - - - - Reads byte[] line from stream. - - Return null if end of stream reached. - - - diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_DispositionTypes .cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_DispositionTypes .cs deleted file mode 100644 index 119ba2380..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_DispositionTypes .cs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - /// - /// This class holds MIME content disposition types. Defined in RFC 2183. - /// - public class MIME_DispositionTypes - { - #region Members - - /// - /// Bodyparts can be designated `attachment' to indicate that they are separate from the main body of the mail message, - /// and that their display should not be automatic, but contingent upon some further action of the user. - /// - public static readonly string Attachment = "attachment"; - - /// - /// A bodypart should be marked `inline' if it is intended to be displayed automatically upon display of the message. - /// Inline bodyparts should be presented in the order in which they occur, subject to the normal semantics of multipart messages. - /// - public static readonly string Inline = "inline"; - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_EncodedWordEncoding.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_EncodedWordEncoding.cs deleted file mode 100644 index f46cf67df..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_EncodedWordEncoding.cs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - /// - /// This enum specifies MIME RFC 2047 'encoded-word' encoding method. - /// - public enum MIME_EncodedWordEncoding - { - /// - /// The "B" encoding. Defined in RFC 2047 (section 4.1). - /// - Q, - - /// - /// The "Q" encoding. Defined in RFC 2047 (section 4.2). - /// - B - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_Encoding_EncodedWord.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_Encoding_EncodedWord.cs deleted file mode 100644 index 18e6347c7..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_Encoding_EncodedWord.cs +++ /dev/null @@ -1,434 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System.Text.RegularExpressions; - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using System.IO; - using System.Text; - - #endregion - - /// - /// Implements 'encoded-word' encoding. Defined in RFC 2047. - /// - public class MIME_Encoding_EncodedWord - { - #region Members - - private readonly MIME_EncodedWordEncoding m_Encoding; - private readonly Encoding m_pCharset; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Encoding to use to encode text. - /// Charset to use for encoding. If not sure UTF-8 is strongly recommended. - /// Is raised when charset is null reference. - public MIME_Encoding_EncodedWord(MIME_EncodedWordEncoding encoding, Encoding charset) - { - if (charset == null) - { - throw new ArgumentNullException("charset"); - } - - m_Encoding = encoding; - m_pCharset = charset; - } - - #endregion - - #region Methods - - /// - /// Checks if specified text must be encoded. - /// - /// Text to encode. - /// Returns true if specified text must be encoded, otherwise false. - /// Is raised when text is null reference. - public static bool MustEncode(string text) - { - if (text == null) - { - throw new ArgumentNullException("text"); - } - - // Encoding is needed only for non-ASCII chars. - - foreach (char c in text) - { - if (c > 127) - { - return true; - } - } - - return false; - } - - /// - /// Encodes specified text if it contains 8-bit chars, otherwise text won't be encoded. - /// - /// Encoding to use to encode text. - /// Charset to use for encoding. If not sure UTF-8 is strongly recommended. - /// Text to encode. - /// Returns encoded text. - /// Is raised when charset or text is null reference. - public static string EncodeS(MIME_EncodedWordEncoding encoding, Encoding charset, string text) - { - if (charset == null) - { - throw new ArgumentNullException("charset"); - } - if (text == null) - { - throw new ArgumentNullException("text"); - } - - /* RFC 2047 2. - encoded-word = "=?" charset "?" encoding "?" encoded-text "?=" - - An 'encoded-word' may not be more than 75 characters long, including - 'charset', 'encoding', 'encoded-text', and delimiters. If it is - desirable to encode more text than will fit in an 'encoded-word' of - 75 characters, multiple 'encoded-word's (separated by CRLF SPACE) may - be used. - - RFC 2231 (updates syntax) - encoded-word := "=?" charset ["*" language] "?" encoded-text "?=" - */ - - if (MustEncode(text)) - { - StringBuilder retVal = new StringBuilder(); - byte[] data = charset.GetBytes(text); - int maxEncodedTextSize = 75 - (("=?" + charset.WebName + "?" + encoding + "?" + "?=")).Length; - - #region B encode - - if (encoding == MIME_EncodedWordEncoding.B) - { - retVal.Append("=?" + charset.WebName + "?B?"); - int stored = 0; - string base64 = Convert.ToBase64String(data); - for (int i = 0; i < base64.Length; i += 4) - { - // Encoding buffer full, create new encoded-word. - if (stored + 4 > maxEncodedTextSize) - { - retVal.Append("?=\r\n =?" + charset.WebName + "?B?"); - stored = 0; - } - - retVal.Append(base64, i, 4); - stored += 4; - } - retVal.Append("?="); - } - - #endregion - - #region Q encode - - else - { - retVal.Append("=?" + charset.WebName + "?Q?"); - int stored = 0; - foreach (byte b in data) - { - string val = null; - // We need to encode byte. Defined in RFC 2047 4.2. - if (b > 127 || b == '=' || b == '?' || b == '_' || b == ' ') - { - val = "=" + b.ToString("X2"); - } - else - { - val = ((char)b).ToString(); - } - - // Encoding buffer full, create new encoded-word. - if (stored + val.Length > maxEncodedTextSize) - { - retVal.Append("?=\r\n =?" + charset.WebName + "?Q?"); - stored = 0; - } - - retVal.Append(val); - stored += val.Length; - } - retVal.Append("?="); - } - - #endregion - - return retVal.ToString(); - } - else - { - return text; - } - } - - public static string DecodeSubject(string text) - { - if (string.IsNullOrEmpty(text)) - { - return text; - } - if (ParseRegex.IsMatch(text)) - { - //Kill spaces - text = Regex.Replace(text,@"(\s+|\t+)",""); - - string[] separator = { @"?==?" }; - string[] splitStr = text.Split(separator, StringSplitOptions.None); - for (int i = 1; i < splitStr.Length; i++) - { - if (splitStr[i - 1][splitStr[i - 1].Length - 1] != '=') - splitStr[i] = Regex.Replace(splitStr[i], @"(?.*?)\?(?[qQbB])\?", ""); - else splitStr[i] = @"?==?" + splitStr[i]; - - } - if(splitStr.Length >1) text = string.Join("", splitStr); - - } - return ParseRegex.Replace(text, new MatchEvaluator(Evalute)); - } - - - private static readonly Regex ParseRegex = new Regex(@"=\?(?.*?)\?(?[qQbB])\?(?.*?)\?=", RegexOptions.Multiline|RegexOptions.Compiled); - - public static string DecodeAll(string text) - { - if (string.IsNullOrEmpty(text)) - { - return text; - } - - return ParseRegex.Replace(text, new MatchEvaluator(Evalute)); - - - //StringBuilder builder = new StringBuilder(); - //StringBuilder notDecoded = new StringBuilder(); - //builder.Append(text); - ////Try decode as single - //var fullWordParts = text.Split('?'); - - - //try - //{ - // if (fullWordParts.Length == 5) - // { - // if (fullWordParts[2].Equals("B", StringComparison.OrdinalIgnoreCase)) - // { - // //do processing - // using (var ms = new MemoryStream()) - // { - // byte[] bytes = Convert.FromBase64String(fullWordParts[3]); - // ms.Write(bytes, 0, bytes.Length); - // notDecoded.Append( - // Encoding.GetEncoding(fullWordParts[1].Split('*')[0]).GetString(ms.ToArray())); - // builder = notDecoded; - // } - // } - // else - // { - // notDecoded.Append(DecodeS(builder.ToString())); - // builder = notDecoded; - // } - - // } - // else - // { - - - // var words = text.Split(' ', '\t'); - // //Get encoded word start - // int decodedWordIndex = 0; - // while (decodedWordIndex < words.Length) - // { - // string[] wordparts = words[decodedWordIndex].Split('?'); - - // if (wordparts.Length == 5) - // { - // if (wordparts[2].Equals("B", StringComparison.OrdinalIgnoreCase)) - // { - // //do processing - // using (MemoryStream ms = new MemoryStream()) - // { - // for (int i = decodedWordIndex; i < words.Length; i++) - // { - // string[] parts = words[i].Split('?'); - // // Not encoded-word. - // if (parts.Length == 5) - // { - // byte[] bytes = Convert.FromBase64String(parts[3]); - // ms.Write(bytes, 0, bytes.Length); - // } - // } - // notDecoded.Append( - // Encoding.GetEncoding(wordparts[1].Split('*')[0]).GetString(ms.ToArray())); - // builder = notDecoded; - // break; - - // } - // } - // else - // { - - - // //Found decoded - // //encoded - // builder = new StringBuilder(); - // //Get the first part - // builder.Append(words[decodedWordIndex].Substring(0, - // words[decodedWordIndex]. - // LastIndexOf('?'))); - // //extract data from others - // for (int i = decodedWordIndex + 1; i < words.Length; i++) - // { - // string[] parts = words[i].Split('?'); - // // Not encoded-word. - // if (parts.Length == 5) - // { - // builder.Append(parts[3]); - // } - // } - // builder.Append("?="); - // notDecoded.Append(DecodeS(builder.ToString())); - // builder = notDecoded; - // break; - // } - // } - // else - // { - // notDecoded.Append(words[decodedWordIndex] + " "); - // decodedWordIndex++; - // } - // } - - // } - //} - //catch - //{ - // return text; - //} - //return builder.ToString(); - } - - private static string Evalute(Match match) - { - try - { - if (match.Success) - { - string charSet = match.Groups["charset"].Value; - string encoding = match.Groups["encoding"].Value.ToUpper(); - string value = match.Groups["value"].Value; - - Encoding enc = EncodingTools.GetEncodingByCodepageName(charSet) ?? - ("utf-8".Equals(encoding,StringComparison.OrdinalIgnoreCase) ? Encoding.UTF8 : Encoding.Default); - - if (encoding.ToLower().Equals("b")) - return enc.GetString(Convert.FromBase64String(value)); - return Core.QDecode(enc, value); - } - } - catch (Exception) - { - - } - return match.Value; - } - - /// - /// Decodes non-ascii word with MIME encoded-word method. Defined in RFC 2047 2. - /// - /// MIME encoded-word value. - /// Returns decoded word. - /// If word is not encoded-word or has invalid syntax, word is leaved as is. - /// Is raised when word is null reference. - public static string DecodeS(string word) - { - if (word == null) - { - throw new ArgumentNullException("word"); - } - - /* RFC 2047 2. - encoded-word = "=?" charset "?" encoding "?" encoded-text "?=" - - RFC 2231. - encoded-word := "=?" charset ["*" language] "?" encoded-text "?=" - */ - - try - { - return ParseRegex.Replace(word, new MatchEvaluator(Evalute)); - } - catch - { - // Failed to parse encoded-word, leave it as is. RFC 2047 6.3. - return word; - } - } - - /// - /// Encodes specified text if it contains 8-bit chars, otherwise text won't be encoded. - /// - /// Text to encode. - /// Returns encoded text. - public string Encode(string text) - { - if (MustEncode(text)) - { - return EncodeS(m_Encoding, m_pCharset, text); - } - else - { - return text; - } - } - - /// - /// Decodes specified encoded-word. - /// - /// Encoded-word value. - /// Returns decoded text. - /// Is raised when text is null reference. - public string Decode(string text) - { - if (text == null) - { - throw new ArgumentNullException("text"); - } - - return DecodeS(text); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_Entity.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_Entity.cs deleted file mode 100644 index bd081f874..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_Entity.cs +++ /dev/null @@ -1,972 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using System.IO; - using System.Text; - using IO; - - #endregion - - /// - /// Represents a MIME entity. Defined in RFC 2045 2.4. - /// - public class MIME_Entity : IDisposable - { - #region Members - - private readonly MIME_b_Provider m_pBodyProvider; - private bool m_IsDisposed; - private MIME_b m_pBody; - private MIME_h_Collection m_pHeader; - private MIME_Entity m_pParent; - - #endregion - - // Permanent headerds list: http://www.rfc-editor.org/rfc/rfc4021.txt - - #region Properties - - /// - /// Gets if this object is disposed. - /// - public bool IsDisposed - { - get { return m_IsDisposed; } - } - - /// - /// Gets if this entity is modified since it has loaded. - /// - /// Is riased when this class is disposed and this property is accessed. - public bool IsModified - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pHeader.IsModified || m_pBody.IsModified; - } - } - - /// - /// Gets the parent entity of this entity, returns null if this is the root entity. - /// - /// Is raised when this object is disposed and this property is accessed. - public MIME_Entity Parent - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pParent; - } - } - - /// - /// Gets MIME entity header field collection. - /// - /// Is raised when this object is disposed and this property is accessed. - public MIME_h_Collection Header - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pHeader; - } - } - - /// - /// Gets or sets MIME version number. Value null means that header field does not exist. Normally this value is 1.0. Defined in RFC 2045 section 4. - /// - /// Is raised when this object is disposed and this property is accessed. - /// An indicator that this message is formatted according to the MIME - /// standard, and an indication of which version of MIME is used. - public string MimeVersion - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = m_pHeader.GetFirst("MIME-Version"); - if (h != null) - { - return h.ToString(); - } - else - { - return null; - } - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - m_pHeader.RemoveAll("MIME-Version"); - } - else - { - MIME_h h = m_pHeader.GetFirst("MIME-Version"); - if (h == null) - { - h = new MIME_h_Unstructured("MIME-Version", value); - m_pHeader.Add(h); - } - else - { - ((MIME_h_Unstructured) h).Value = value; - } - } - } - } - - /// - /// Gets or sets content body part ID. Value null means that header field does not exist. Defined in RFC 2045 7. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Specifies a Unique ID for one MIME body part of the content of a message. - public string ContentID - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = m_pHeader.GetFirst("Content-ID"); - if (h != null) - { - if (h is MIME_h_Unstructured) - { - return (h as MIME_h_Unstructured).Value; - } - return h.ToString(); - } - else - { - return null; - } - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - m_pHeader.RemoveAll("Content-ID"); - } - else - { - MIME_h h = m_pHeader.GetFirst("Content-ID"); - if (h == null) - { - h = new MIME_h_Unstructured("Content-ID", value); - m_pHeader.Add(h); - } - else - { - ((MIME_h_Unstructured) h).Value = value; - } - } - } - } - - /// - /// Gets or sets description of message body part. Value null means that header field does not exist. Defined in RFC 2045 8. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Description of a particular body part of a message; for example, a caption for an image body part. - public string ContentDescription - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = m_pHeader.GetFirst("Content-Description"); - if (h != null) - { - return h.ToString(); - } - else - { - return null; - } - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - m_pHeader.RemoveAll("Content-Description"); - } - else - { - MIME_h h = m_pHeader.GetFirst("Content-Description"); - if (h == null) - { - h = new MIME_h_Unstructured("Content-Description", value); - m_pHeader.Add(h); - } - else - { - ((MIME_h_Unstructured) h).Value = value; - } - } - } - } - - /// - /// Gets or sets content transfer encoding. Value null means that header field does not exist. - /// RFC defined values are in MIME_TransferEncodings. Defined in RFC 2045 6. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Coding method used in a MIME message body part. - public string ContentTransferEncoding - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h_Unstructured h = (MIME_h_Unstructured) m_pHeader.GetFirst("Content-Transfer-Encoding"); - if (h != null) - { - return h.Value; - } - else - { - return null; - } - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - m_pHeader.RemoveAll("Content-Transfer-Encoding"); - } - else - { - MIME_h h = m_pHeader.GetFirst("Content-Transfer-Encoding"); - if (h == null) - { - h = new MIME_h_Unstructured("Content-Transfer-Encoding", value); - m_pHeader.Add(h); - } - else - { - ((MIME_h_Unstructured) h).Value = value; - } - } - } - } - - /// - /// Gets or sets content length. - /// - public long ContentLength { get; set; } - - - /// - /// Gets or sets MIME content type. Value null means that header field does not exist. Defined in RFC 2045 5. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when header field parsing errors. - public MIME_h_ContentType ContentType - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = m_pHeader.GetFirst("Content-Type"); - if (h != null) - { - if (!(h is MIME_h_ContentType)) - { - throw new ParseException("Header field 'ContentType' parsing failed."); - } - - return (MIME_h_ContentType) h; - } - else - { - return null; - } - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - m_pHeader.RemoveAll("Content-Type"); - } - else - { - MIME_h h = m_pHeader.GetFirst("Content-Type"); - if (h == null) - { - m_pHeader.Add(value); - } - else - { - m_pHeader.ReplaceFirst(value); - } - } - } - } - - /// - /// Gets or sets base to be used for resolving relative URIs within this content part. Value null means that header field does not exist. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Base to be used for resolving relative URIs within this content part. See also Content-Location. - public string ContentBase - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = m_pHeader.GetFirst("Content-Base"); - if (h != null) - { - return h.ToString(); - } - else - { - return null; - } - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - m_pHeader.RemoveAll("Content-Base"); - } - else - { - MIME_h h = m_pHeader.GetFirst("Content-Base"); - if (h == null) - { - h = new MIME_h_Unstructured("Content-Base", value); - m_pHeader.Add(h); - } - else - { - ((MIME_h_Unstructured) h).Value = value; - } - } - } - } - - /// - /// Gets or sets URI for retrieving a body part. Value null means that header field does not exist. - /// - /// Is raised when this object is disposed and this property is accessed. - /// URI using which the content of this body-part part was retrieved, - /// might be retrievable, or which otherwise gives a globally unique identification of the content. - public string ContentLocation - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = m_pHeader.GetFirst("Content-Location"); - if (h != null) - { - return h.ToString(); - } - else - { - return null; - } - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - m_pHeader.RemoveAll("Content-Location"); - } - else - { - MIME_h h = m_pHeader.GetFirst("Content-Location"); - if (h == null) - { - h = new MIME_h_Unstructured("Content-Location", value); - m_pHeader.Add(h); - } - else - { - ((MIME_h_Unstructured) h).Value = value; - } - } - } - } - - /// - /// Gets or sets content features of a MIME body part. Value null means that header field does not exist. - /// - /// Is raised when this object is disposed and this property is accessed. - /// The 'Content-features:' header can be used to annotate a MIME body part with a media feature expression, - /// to indicate features of the body part content. See also RFC 2533, RFC 2506, and RFC 2045. - public string Contentfeatures - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = m_pHeader.GetFirst("Content-features"); - if (h != null) - { - return h.ToString(); - } - else - { - return null; - } - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - m_pHeader.RemoveAll("Content-features"); - } - else - { - MIME_h h = m_pHeader.GetFirst("Content-features"); - if (h == null) - { - h = new MIME_h_Unstructured("Content-features", value); - m_pHeader.Add(h); - } - else - { - ((MIME_h_Unstructured) h).Value = value; - } - } - } - } - - /// - /// Gets or sets content disposition. Value null means that header field does not exist. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Indicates whether a MIME body part is to be shown inline or is an attachment; can also indicate a - /// suggested filename for use when saving an attachment to a file. - /// Is raised when header field parsing errors. - public MIME_h_ContentDisposition ContentDisposition - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = m_pHeader.GetFirst("Content-Disposition"); - if (h != null) - { - if (!(h is MIME_h_ContentDisposition)) - { - throw new ParseException("Header field 'ContentDisposition' parsing failed."); - } - - return (MIME_h_ContentDisposition) h; - } - else - { - return null; - } - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - m_pHeader.RemoveAll("Content-Dispostition"); - } - else - { - MIME_h h = m_pHeader.GetFirst("Content-Dispostition"); - if (h == null) - { - m_pHeader.Add(value); - } - else - { - m_pHeader.ReplaceFirst(value); - } - } - } - } - - /// - /// Gets or sets language of message content. Value null means that header field does not exist. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Can include a code for the natural language used in a message; e.g., 'en' for English. - /// Can also contain a list of languages for a message containing more than one language. - public string ContentLanguage - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = m_pHeader.GetFirst("Content-Language"); - if (h != null) - { - return h.ToString(); - } - else - { - return null; - } - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - m_pHeader.RemoveAll("Content-Language"); - } - else - { - MIME_h h = m_pHeader.GetFirst("Content-Language"); - if (h == null) - { - h = new MIME_h_Unstructured("Content-Language", value); - m_pHeader.Add(h); - } - else - { - ((MIME_h_Unstructured) h).Value = value; - } - } - } - } - - /// - /// Gets or sets message alternative content. Value null means that header field does not exist. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Information about the media features of alternative content formats available for the current message. - public string ContentAlternative - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = m_pHeader.GetFirst("Content-Alternative"); - if (h != null) - { - return h.ToString(); - } - else - { - return null; - } - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - m_pHeader.RemoveAll("Content-Alternative"); - } - else - { - MIME_h h = m_pHeader.GetFirst("Content-Alternative"); - if (h == null) - { - h = new MIME_h_Unstructured("Content-Alternative", value); - m_pHeader.Add(h); - } - else - { - ((MIME_h_Unstructured) h).Value = value; - } - } - } - } - - /// - /// Gets or sets content MD5 checksum. Value null means that header field does not exist. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Checksum of content to ensure that it has not been modified. - public string ContentMD5 - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = m_pHeader.GetFirst("Content-MD5"); - if (h != null) - { - return h.ToString(); - } - else - { - return null; - } - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - m_pHeader.RemoveAll("Content-MD5"); - } - else - { - MIME_h h = m_pHeader.GetFirst("Content-MD5"); - if (h == null) - { - h = new MIME_h_Unstructured("Content-MD5", value); - m_pHeader.Add(h); - } - else - { - ((MIME_h_Unstructured) h).Value = value; - } - } - } - } - - /// - /// Gets or sets time duration of content. Value null means that header field does not exist. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Time duration of body part content, in seconds (e.g., for audio message). - public string ContentDuration - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = m_pHeader.GetFirst("Content-Duration"); - if (h != null) - { - return h.ToString(); - } - else - { - return null; - } - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - m_pHeader.RemoveAll("Content-Duration"); - } - else - { - MIME_h h = m_pHeader.GetFirst("Content-Duration"); - if (h == null) - { - h = new MIME_h_Unstructured("Content-Duration", value); - m_pHeader.Add(h); - } - else - { - ((MIME_h_Unstructured) h).Value = value; - } - } - } - } - - /// - /// Gets or sets MIME entity body. - /// - /// Is raised when null reference passed. - public MIME_b Body - { - get { return m_pBody; } - - set - { - if (value == null) - { - throw new ArgumentNullException("Body"); - } - - m_pBody = value; - m_pBody.SetParent(this); - ContentType = m_pBody.ContentType; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public MIME_Entity() - { - m_pHeader = new MIME_h_Collection(new MIME_h_Provider()); - m_pBodyProvider = new MIME_b_Provider(); - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. This method is thread-safe. - /// - public void Dispose() - { - lock (this) - { - if (m_IsDisposed) - { - return; - } - m_IsDisposed = true; - - m_pHeader = null; - m_pParent = null; - } - } - - /// - /// Stores MIME entity to the specified file. - /// - /// File name with path where to store MIME entity. - /// Header 8-bit words ecnoder. Value null means that words are not encoded. - /// Charset to use to encode 8-bit header parameters. Value null means parameters not encoded. - /// Is raised when file is null. - /// Is raised when any of the arguments has invalid value. - public void ToFile(string file, - MIME_Encoding_EncodedWord headerWordEncoder, - Encoding headerParmetersCharset) - { - if (file == null) - { - throw new ArgumentNullException("file"); - } - if (file == "") - { - throw new ArgumentException("Argument 'file' value must be specified."); - } - - using (FileStream fs = File.Create(file)) - { - ToStream(fs, headerWordEncoder, headerParmetersCharset); - } - } - - /// - /// Store MIME enity to the specified stream. - /// - /// Stream where to store MIME entity. Storing starts form stream current position. - /// Header 8-bit words ecnoder. Value null means that words are not encoded. - /// Charset to use to encode 8-bit header parameters. Value null means parameters not encoded. - /// Is raised when stream is null. - public void ToStream(Stream stream, - MIME_Encoding_EncodedWord headerWordEncoder, - Encoding headerParmetersCharset) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - m_pHeader.ToStream(stream, headerWordEncoder, headerParmetersCharset); - stream.Write(new byte[] {(int) '\r', (int) '\n'}, 0, 2); - m_pBody.ToStream(stream, headerWordEncoder, headerParmetersCharset); - } - - public byte[] ToByteArray(MIME_Encoding_EncodedWord headerWordEncoder, - Encoding headerParmetersCharset) - { - using (MemoryStream ms = new MemoryStream()) - { - ToStream(ms, headerWordEncoder, headerParmetersCharset); - return ms.GetBuffer(); - } - } - - /// - /// Returns MIME entity as string. - /// - /// Returns MIME entity as string. - public override string ToString() - { - using (MemoryStream ms = new MemoryStream()) - { - ToStream(ms, null, null); - - return Encoding.UTF8.GetString(ms.ToArray()); - } - } - - #endregion - - #region Internal methods - - /// - /// Parses MIME entiry from the specified stream. - /// - /// Source stream. - /// Default content type. - /// Is raised when stream or defaultContentType is null reference. - internal void Parse(SmartStream stream, MIME_h_ContentType defaultContentType) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - if (defaultContentType == null) - { - throw new ArgumentNullException("defaultContentType"); - } - - m_pHeader.Parse(stream); - Body = m_pBodyProvider.Parse(this, stream, ContentType??defaultContentType); - } - - /// - /// Sets MIME entity parent entity. - /// - /// Parent entity. - internal void SetParent(MIME_Entity parent) - { - m_pParent = parent; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_EntityCollection.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_EntityCollection.cs deleted file mode 100644 index bad6c0ecf..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_EntityCollection.cs +++ /dev/null @@ -1,197 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using System.Collections; - using System.Collections.Generic; - - #endregion - - /// - /// Represents MIME child entity collection in multipart/xxx entity. - /// - public class MIME_EntityCollection : IEnumerable - { - #region Members - - private readonly List m_pCollection; - private bool m_IsModified; - - #endregion - - #region Properties - - /// - /// Gets if enity collection has modified. - /// - public bool IsModified - { - get - { - if (m_IsModified) - { - return true; - } - - foreach (MIME_Entity entity in m_pCollection) - { - if (entity.IsModified) - { - return true; - } - } - - return false; - } - } - - /// - /// Gets number of items in the collection. - /// - public int Count - { - get { return m_pCollection.Count; } - } - - /// - /// Gets MIME entity at the specified index. - /// - /// MIME entity zero-based index. - /// Returns MIME entity. - public MIME_Entity this[int index] - { - get { return m_pCollection[index]; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - internal MIME_EntityCollection() - { - m_pCollection = new List(); - } - - #endregion - - #region Methods - - /// - /// Adds specified MIME enity to the collection. - /// - /// MIME entity. - /// Is raised when entity is null reference. - public void Add(MIME_Entity entity) - { - if (entity == null) - { - throw new ArgumentNullException("entity"); - } - - m_pCollection.Add(entity); - m_IsModified = true; - } - - /// - /// Inserts a new MIME entity into the collection at the specified location. - /// - /// The location in the collection where you want to add the MIME entity. - /// MIME entity. - /// Is raised when index is out of range. - /// Is raised when entity is null reference. - public void Insert(int index, MIME_Entity entity) - { - if (entity == null) - { - throw new ArgumentNullException("entity"); - } - - m_pCollection.Insert(index, entity); - m_IsModified = true; - } - - /// - /// Removes specified MIME entity from the collection. - /// - /// MIME entity. - /// Is raised when field is null reference. - public void Remove(MIME_Entity entity) - { - if (entity == null) - { - throw new ArgumentNullException("field"); - } - - m_pCollection.Remove(entity); - m_IsModified = true; - } - - /// - /// Removes MIME entity at the specified index from the collection. - /// - /// The index of the MIME entity to remove. - /// Is raised when index is out of range. - public void Remove(int index) - { - m_pCollection.RemoveAt(index); - m_IsModified = true; - } - - /// - /// Removes all items from the collection. - /// - public void Clear() - { - m_pCollection.Clear(); - m_IsModified = true; - } - - /// - /// Gets if the collection contains specified MIME entity. - /// - /// MIME entity. - /// Returns true if the specified MIME entity exists in the collection, otherwise false. - /// Is raised when entity is null. - public bool Contains(MIME_Entity entity) - { - if (entity == null) - { - throw new ArgumentNullException("entity"); - } - - return m_pCollection.Contains(entity); - } - - /// - /// Gets enumerator. - /// - /// Returns IEnumerator interface. - public IEnumerator GetEnumerator() - { - return m_pCollection.GetEnumerator(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_MediaTypes.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_MediaTypes.cs deleted file mode 100644 index eec1c53c4..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_MediaTypes.cs +++ /dev/null @@ -1,231 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - /// - /// This class holds well known Content-Type header field media types. For example: text/plain, application/octet-stream. - /// Full IANA registered list can be found from: http://www.iana.org/assignments/media-types. - /// - public class MIME_MediaTypes - { - #region Nested type: Application - - /// - /// This class holds well-known application/xxx media types. - /// - public class Application - { - #region Members - - /// - /// "application/octet-stream". Defined in RFC 2045,2046. - /// - public static readonly string octet_stream = "application/octet-stream"; - - /// - /// "application/pdf". Defined in RFC 3778. - /// - public static readonly string pdf = "application/pdf"; - - /// - /// "application/sdp". Defined in RFC 4566. - /// - public static readonly string sdp = "application/sdp"; - - /// - /// "application/xml". Defined RFC 3023. - /// - public static readonly string xml = "application/xml"; - - /// - /// "application/zip". Defined in RFC 4566. - /// - public static readonly string zip = "application/zip"; - - #endregion - } - - #endregion - - #region Nested type: Image - - /// - /// This class holds well-known image/xxx media types. - /// - public class Image - { - #region Members - - /// - /// "image/gif". - /// - public static readonly string gif = "image/gif"; - - /// - /// "image/jpeg". - /// - public static readonly string jpeg = "image/jpeg"; - - /// - /// "image/tiff". - /// - public static readonly string tiff = "image/tiff"; - - #endregion - } - - #endregion - - #region Nested type: Message - - /// - /// This class holds well-known message/xxx media types. - /// - public class Message - { - #region Members - - /// - /// "message/disposition-notification". - /// - public static readonly string disposition_notification = "message/disposition-notification"; - - /// - /// "message/rfc822". - /// - public static readonly string rfc822 = "message/rfc822"; - - #endregion - } - - #endregion - - #region Nested type: Multipart - - /// - /// This class holds well-known multipart/xxx media types. - /// - public class Multipart - { - #region Members - - /// - /// "multipart/alternative". Defined in RFC 2045,2046. - /// - public static readonly string alternative = "multipart/alternative"; - - /// - /// "multipart/digest". Defined in RFC 2045,2046. - /// - public static readonly string digest = "multipart/digest"; - - /// - /// "multipart/digest". Defined in RFC 1847. - /// - public static readonly string encrypted = "multipart/digest"; - - /// - /// "multipart/form-data". Defined in RFC 2388. - /// - public static readonly string form_data = "multipart/form-data"; - - /// - /// "multipart/mixed". Defined in RFC 2045,2046. - /// - public static readonly string mixed = "multipart/mixed"; - - /// - /// "multipart/parallel". Defined in RFC 2045,2046. - /// - public static readonly string parallel = "multipart/parallel"; - - /// - /// "multipart/related". Defined in RFC 2387. - /// - public static readonly string related = "multipart/related"; - - /// - /// "multipart/report". Defined in RFC 1892. - /// - public static readonly string report = "multipart/report"; - - /// - /// "multipart/signed". Defined in RFC 1847. - /// - public static readonly string signed = "multipart/signed"; - - /// - /// "multipart/voice-message". Defined in RFC 2421,2423. - /// - public static readonly string voice_message = "multipart/voice-message"; - - #endregion - } - - #endregion - - #region Nested type: Text - - /// - /// This class holds well-known text/xxx media types. - /// - public class Text - { - #region Members - - /// - /// "text/calendar". Defined in RFC 2445. - /// - public static readonly string calendar = "text/calendar"; - - /// - /// "text/css". Defined in RFC 2854 - /// - public static readonly string css = "text/css"; - - /// - /// "text/html". Defined in RFC 2854. - /// - public static readonly string html = "text/html"; - - /// - /// "text/plain". Defined in RFC 2646,2046. - /// - public static readonly string plain = "text/plain"; - - /// - /// "text/rfc822-headers". Defined in RFC 1892. - /// - public static readonly string rfc822_headers = "text/rfc822-headers"; - - /// - /// "text/richtext". Defined in RFC 2045,2046. - /// - public static readonly string richtext = "text/richtext"; - - /// - /// "text/xml". Defined in RFC 3023. - /// - public static readonly string xml = "text/xml"; - - #endregion - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_Message.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_Message.cs deleted file mode 100644 index c3ffbaeeb..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_Message.cs +++ /dev/null @@ -1,194 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using System.Collections.Generic; - using System.IO; - using IO; - - #endregion - - /// - /// Represents a MIME message. Defined in RFC 2045 2.3. - /// - public class MIME_Message : MIME_Entity - { - #region Members - - private bool m_IsDisposed = false; - - #endregion - - #region Properties - - /// - /// Gets all MIME entities as list. - /// - /// Is raised when this class is disposed and this property is accessed. - public MIME_Entity[] AllEntities - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - List retVal = new List(); - Queue entitiesQueue = new Queue(); - entitiesQueue.Enqueue(this); - - while (entitiesQueue.Count > 0) - { - MIME_Entity currentEntity = entitiesQueue.Dequeue(); - - // Current entity is multipart entity, add it's body-parts for processing. - if (Body != null && currentEntity.Body.GetType().IsSubclassOf(typeof (MIME_b_Multipart))) - { - foreach (MIME_Entity bodypart in ((MIME_b_Multipart) currentEntity.Body).BodyParts) - { - entitiesQueue.Enqueue(bodypart); - } - } - - retVal.Add(currentEntity); - } - - return retVal.ToArray(); - } - } - - #endregion - - #region Methods - - /// - /// Parses MIME message from the specified file. - /// - /// File name with path from where to parse MIME message. - /// Returns parsed MIME message. - /// Is raised when file is null. - /// Is raised when any of the arguments has invalid value. - public static MIME_Message ParseFromFile(string file) - { - if (file == null) - { - throw new ArgumentNullException("file"); - } - if (file == "") - { - throw new ArgumentException("Argument 'file' value must be specified."); - } - - return ParseFromStream(File.OpenRead(file)); - } - - /// - /// Parses MIME message from the specified stream. - /// - /// Stream from where to parse MIME message. Parsing starts from current stream position. - /// Returns parsed MIME message. - /// Is raised when stream is null. - public static MIME_Message ParseFromStream(Stream stream) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - MIME_Message retVal = new MIME_Message(); - retVal.Parse(new SmartStream(stream, false), new MIME_h_ContentType("text/plain")); - - return retVal; - } - - /// - /// Creates attachment entity. - /// - /// File name with optional path. - /// Returns created attachment entity. - /// Is raised when file is null reference. - public static MIME_Entity CreateAttachment(string file) - { - if (file == null) - { - throw new ArgumentNullException("file"); - } - - MIME_Entity retVal = new MIME_Entity(); - MIME_b_Application body = new MIME_b_Application(MIME_MediaTypes.Application.octet_stream); - retVal.Body = body; - body.SetBodyDataFromFile(file, MIME_TransferEncodings.Base64); - retVal.ContentType.Param_Name = Path.GetFileName(file); - - FileInfo fileInfo = new FileInfo(file); - MIME_h_ContentDisposition disposition = - new MIME_h_ContentDisposition(MIME_DispositionTypes.Attachment); - disposition.Param_FileName = Path.GetFileName(file); - disposition.Param_Size = fileInfo.Length; - disposition.Param_CreationDate = fileInfo.CreationTime; - disposition.Param_ModificationDate = fileInfo.LastWriteTime; - disposition.Param_ReadDate = fileInfo.LastAccessTime; - retVal.ContentDisposition = disposition; - - return retVal; - } - - /// - /// Gets MIME entity with the specified Content-ID. Returns null if no such entity. - /// - /// Content ID. - /// Returns MIME entity with the specified Content-ID or null if no such entity. - /// Is raised when this class is disposed and this method is accessed. - /// Is raised when cid is null. - /// Is raised when any of the arguments has invalid value. - public MIME_Entity GetEntityByCID(string cid) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (cid == null) - { - throw new ArgumentNullException("cid"); - } - if (cid == "") - { - throw new ArgumentException("Argument 'cid' value must be specified.", "cid"); - } - - foreach (MIME_Entity entity in AllEntities) - { - if (entity.ContentID == cid) - { - return entity; - } - } - - return null; - } - - #endregion - - // TODO: - //public MIME_Entity GetEntityByPartsSpecifier(string partsSpecifier) - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_Reader.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_Reader.cs deleted file mode 100644 index 8db9338aa..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_Reader.cs +++ /dev/null @@ -1,943 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// MIME lexical tokens parser. - /// - public class MIME_Reader - { - #region Members - - private static readonly char[] atextChars = new[] - { - '!', '#', '$', '%', '&', '\'', '*', '+', '-', '/', - '=', '?', '^', '_', '`', '{', '|', '}', '~' - }; - - private static readonly char[] specials = new[] - { - '(', ')', '<', '>', '[', ']', ':', ';', '@', '\\', ',', - '.', '"' - }; - - private static readonly char[] tspecials = new[] - { - '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/' - , '[', ']', '?', '=' - }; - - private readonly string m_Source = ""; - private int m_Offset; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Value to read. - /// Is raised when value is null. - public MIME_Reader(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - m_Source = value; - } - - #endregion - - #region Properties - - /// - /// Gets number of chars has left for processing. - /// - public int Available - { - get { return m_Source.Length - m_Offset; } - } - - #endregion - - #region Methods - - /// - /// Gets if the specified char is RFC 822 'ALPHA'. - /// - /// Char to check. - /// Returns true if specified char is RFC 822 'ALPHA'. - public static bool IsAlpha(char c) - { - /* RFC 822 3.3. - ALPHA = ; (65.- 90.); (97.-122.) - */ - - if ((c >= 65 && c <= 90) || (c >= 97 && c <= 122)) - { - return true; - } - else - { - return false; - } - } - - /// - /// Gets if the specified char is RFC 2822 'atext'. - /// - /// Char to check. - /// Returns true if specified char is RFC 2822 'atext'. - public static bool IsAText(char c) - { - /* RFC 2822 3.2.4. - * atext = ALPHA / DIGIT / - * "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / - * "-" / "/" / "=" / "?" / "^" / "_" / "`" / "{" / - * "|" / "}" / "~" - */ - - if (IsAlpha(c) || char.IsDigit(c)) - { - return true; - } - else - { - if (c == '.') - { - return true; - } - foreach (char aC in atextChars) - { - if (c == aC) - { - return true; - } - } - } - - return false; - } - - /// - /// Gets if the specified value can be represented as "dot-atom". - /// - /// Value to check. - /// Returns true if the specified value can be represented as "dot-atom". - public static bool IsDotAtom(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - /* RFC 2822 3.2.4. - * dot-atom = [CFWS] dot-atom-text [CFWS] - * dot-atom-text = 1*atext *("." 1*atext) - */ - - foreach (char c in value) - { - if (c != '.' && !IsAText(c)) - { - return false; - } - } - - return true; - } - - /// - /// Gets if specified valu is RFC 2045 (section 5) 'token'. - /// - /// Text to check. - /// Returns true if specified char is RFC 2045 (section 5) 'token'. - /// Is raised when text is null. - public static bool IsToken(string text) - { - if (text == null) - { - throw new ArgumentNullException("text"); - } - - if (text == "") - { - return false; - } - - foreach (char c in text) - { - if (!IsToken(c)) - { - return false; - } - } - - return true; - } - - /// - /// Gets if the specified char is RFC 2045 (section 5) 'token'. - /// - /// Char to check. - /// Returns true if specified char is RFC 2045 (section 5) 'token'. - public static bool IsToken(char c) - { - /* RFC 2045 5. - * token := 1* - * - * RFC 822 3.3. - * CTL = - /// Gets if the specified char is RFC 2231 (section 7) 'attribute-char'. - /// - /// Char to check. - /// Returns true if specified char is RFC 2231 (section 7) 'attribute-char'. - public static bool IsAttributeChar(char c) - { - /* RFC 2231 7. - * attribute-char := - * - * RFC 822 3.3. - * CTL = 127) - { - return false; - } - else if (c == ' ' || c == '*' || c == '\'' || c == '%') - { - return false; - } - else - { - foreach (char cS in tspecials) - { - if (c == cS) - { - return false; - } - } - } - - return true; - } - - /// - /// Reads RFC 2822 'atom' from source stream. - /// - /// Returns RFC 2822 'atom' or null if end of stream reached. - public string Atom() - { - /* RFC 2822 3.2.4. - * atom = [CFWS] 1*atext [CFWS] - */ - - ToFirstChar(); - - string retVal = ""; - while (true) - { - int peekChar = Peek(false); - // We reached end of string. - if (peekChar == -1) - { - break; - } - else - { - char c = (char) peekChar; - if (IsAText(c)) - { - retVal += (char) Char(false); - } - // Char is not part of 'atom', break. - else - { - break; - } - } - } - - if (retVal.Length > 0) - { - return retVal; - } - else - { - return null; - } - } - - /// - /// Reads RFC 2822 'dot-atom' from source stream. - /// - /// Returns RFC 2822 'dot-atom' or null if end of stream reached. - public string DotAtom() - { - /* RFC 2822 3.2.4. - * dot-atom = [CFWS] dot-atom-text [CFWS] - * dot-atom-text = 1*atext *("." 1*atext) - */ - - ToFirstChar(); - - string retVal = ""; - while (true) - { - string atom = Atom(); - // We reached end of string. - if (atom == null) - { - break; - } - else - { - retVal += atom; - - // dot-atom-text continues. - if (Peek(false) == '.') - { - retVal += (char) Char(false); - } - else - { - break; - } - } - } - - if (retVal.Length > 0) - { - return retVal; - } - else - { - return null; - } - } - - /// - /// Reads RFC 2045 (section 5) 'token' from source stream. - /// - /// Returns RFC 2045 (section 5) 'token' or null if end of stream reached. - public string Token() - { - /* RFC 2045 5. - * token := 1* - */ - - ToFirstChar(); - - string retVal = ""; - while (true) - { - int peekChar = Peek(false); - // We reached end of string. - if (peekChar == -1) - { - break; - } - else - { - char c = (char) peekChar; - if (IsToken(c)) - { - retVal += (char) Char(false); - } - // Char is not part of 'token', break. - else - { - break; - } - } - } - - if (retVal.Length > 0) - { - return retVal; - } - else - { - return null; - } - } - - /// - /// Reads RFC 822 'comment' from source stream. - /// - /// Returns RFC 822 'comment' or null if end of stream reached. - public string Comment() - { - /* RFC 822 3.3. - * comment = "(" *(ctext / quoted-pair / comment) ")" - * ctext = - * quoted-pair = "\" CHAR - */ - - ToFirstChar(); - - if (Peek(false) != '(') - { - throw new InvalidOperationException("No 'comment' value available."); - } - - string retVal = ""; - - // Remove '('. - Char(false); - - int nestedParenthesis = 0; - while (true) - { - int intC = Char(false); - // End of stream reached, invalid 'comment' value. - if (intC == -1) - { - throw new ArgumentException("Invalid 'comment' value, no closing ')'."); - } - else if (intC == '(') - { - nestedParenthesis++; - } - else if (intC == ')') - { - // We readed whole 'comment' ok. - if (nestedParenthesis == 0) - { - break; - } - else - { - nestedParenthesis--; - } - } - else - { - retVal += (char) intC; - } - } - - return retVal; - } - - /// - /// Reads RFC 2822 (section 3.2.6) 'word' from source stream. - /// - /// Returns RFC 2822 (section 3.2.6) 'word' or null if end of stream reached. - public string Word() - { - /* RFC 2822 3.2.6. - * word = atom / quoted-string - */ - - if (Peek(true) == '"') - { - return QuotedString(); - } - else - { - return Atom(); - } - } - - /// - /// Reads RFC 2047 'encoded-word' from source stream. - /// - /// Returns RFC 2047 'encoded-word' or null if end of stream reached. - /// Is raised when source stream has no encoded-word at current position. - public string EncodedWord() - { - /* RFC 2047 2. - * encoded-word = "=?" charset "?" encoding "?" encoded-text "?=" - * - * An 'encoded-word' may not be more than 75 characters long, including - * 'charset', 'encoding', 'encoded-text', and delimiters. If it is - * desirable to encode more text than will fit in an 'encoded-word' of - * 75 characters, multiple 'encoded-word's (separated by CRLF SPACE) may - * be used. - */ - - ToFirstChar(); - - if (Peek(false) != '=') - { - throw new InvalidOperationException("No encoded-word available."); - } - - string retVal = ""; - while (true) - { - string encodedWord = Atom(); - try - { - string[] parts = encodedWord.Split('?'); - if (parts[2].ToUpper() == "Q") - { - retVal += Core.QDecode(EncodingTools.GetEncodingByCodepageName_Throws(parts[1]), parts[3]); - } - else if (parts[2].ToUpper() == "B") - { - retVal += - EncodingTools.GetEncodingByCodepageName_Throws(parts[1]).GetString( - Core.Base64Decode(Encoding.Default.GetBytes(parts[3]))); - } - else - { - throw new Exception(""); - } - } - catch - { - // Failed to parse encoded-word, leave it as is. RFC 2047 6.3. - retVal += encodedWord; - } - - ToFirstChar(); - // encoded-word does not continue. - if (Peek(false) != '=') - { - break; - } - } - - return retVal; - } - - /// - /// Reads RFC 822 'quoted-string' from source stream. - /// - /// Returns RFC 822 'quoted-string' or null if end of stream reached. - /// Is raised when source stream has no quoted-string at current position. - /// Is raised when not valid 'quoted-string'. - public string QuotedString() - { - /* RFC 2822 3.2.5. - * qtext = NO-WS-CTL / ; Non white space controls - %d33 / ; The rest of the US-ASCII - %d35-91 / ; characters not including "\" - %d93-126 ; or the quote character - qcontent = qtext / quoted-pair - quoted-string = [CFWS] DQUOTE *([FWS] qcontent) [FWS] DQUOTE [CFWS] - */ - - ToFirstChar(); - - if (Peek(false) != '"') - { - throw new InvalidOperationException("No quoted-string available."); - } - - // Read start DQUOTE. - Char(false); - - string retVal = ""; - bool escape = false; - while (true) - { - int intC = Char(false); - // We reached end of stream, invalid quoted string, end quote is missing. - if (intC == -1) - { - throw new ArgumentException("Invalid quoted-string, end quote is missing."); - } - // This char is escaped. - else if (escape) - { - escape = false; - - retVal += (char) intC; - } - // Closing DQUOTE. - else if (intC == '"') - { - break; - } - // Next char is escaped. - else if (intC == '\\') - { - escape = true; - } - // Skip folding chars. - else if (intC == '\r' || intC == '\n') {} - // Normal char in quoted-string. - else - { - retVal += (char) intC; - } - } - - return MIME_Encoding_EncodedWord.DecodeAll(retVal); - } - - /// - /// Reads RFC 2045 (section 5) 'token' from source stream. - /// - /// Returns 2045 (section 5) 'token' or null if end of stream reached. - public string Value() - { - // value := token / quoted-string - - if (Peek(true) == '"') - { - return QuotedString(); - } - else - { - return Token(); - } - } - - /// - /// Reads RFC 2047 (section 5) 'phrase' from source stream. - /// - /// Returns RFC 2047 (section 5) 'phrase' or null if end of stream reached. - public string Phrase() - { - /* RFC 2047 5. - * phrase = 1*( encoded-word / word ) - * word = atom / quoted-string - */ - - throw new NotImplementedException(); - - /* - int peek = m_pStringReader.Peek(); - if(peek == '"'){ - return QuotedString(); - } - else if(peek == '='){ - return EncodedWord(); - } - else{ - return Atom(); - }*/ - - //return ""; - } - - /// - /// Reads RFC 822 '*text' from source stream. - /// - /// Returns RFC 822 '*text' or null if end of stream reached. - public string Text() - { - throw new NotImplementedException(); - } - - /// - /// Reads all white-space chars + CR and LF. - /// - /// Returns readed chars. - public string ToFirstChar() - { - // NOTE: Never call Peek or Char method here or stack overflow ! - - string retVal = ""; - while (true) - { - int peekChar = -1; - if (m_Offset > m_Source.Length - 1) - { - peekChar = -1; - } - else - { - peekChar = m_Source[m_Offset]; - } - // We reached end of string. - if (peekChar == -1) - { - break; - } - else if (peekChar == ' ' || peekChar == '\t' || peekChar == '\r' || peekChar == '\n') - { - retVal += m_Source[m_Offset++]; - } - else - { - break; - } - } - - return retVal; - } - - /// - /// Reads 1 char from source stream. - /// - /// Specifies if postion is moved to char(skips white spaces). - /// Returns readed char or -1 if end of stream reached. - public int Char(bool readToFirstChar) - { - if (readToFirstChar) - { - ToFirstChar(); - } - - if (m_Offset > m_Source.Length - 1) - { - return -1; - } - else - { - return m_Source[m_Offset++]; - } - } - - /// - /// Shows next char in source stream, this method won't consume that char. - /// - /// Specifies if postion is moved to char(skips white spaces). - /// Returns next char in source stream, returns -1 if end of stream. - public int Peek(bool readToFirstChar) - { - if (readToFirstChar) - { - ToFirstChar(); - } - - if (m_Offset > m_Source.Length - 1) - { - return -1; - } - else - { - return m_Source[m_Offset]; - } - } - - /// - /// Gets if source stream valu starts with the specified value. Compare is case-insensitive. - /// - /// Value to check. - /// Returns true if source steam satrs with specified string. - /// Is raised when value is null. - public bool StartsWith(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - return m_Source.Substring(m_Offset).StartsWith(value, StringComparison.InvariantCultureIgnoreCase); - } - - /// - /// Reads all data from current postion to the end. - /// - /// Retruns readed data. Returns null if end of string is reached. - public string ToEnd() - { - if (m_Offset >= m_Source.Length) - { - return null; - } - - string retVal = m_Source.Substring(m_Offset); - m_Offset = m_Source.Length; - - return retVal; - } - - /// - /// Reads parenthesized value. Supports {},(),[],<> parenthesis. - /// Throws exception if there isn't parenthesized value or closing parenthesize is missing. - /// - /// Returns value between parenthesized. - public string ReadParenthesized() - { - ToFirstChar(); - - char startingChar = ' '; - char closingChar = ' '; - - if (m_Source[m_Offset] == '{') - { - startingChar = '{'; - closingChar = '}'; - } - else if (m_Source[m_Offset] == '(') - { - startingChar = '('; - closingChar = ')'; - } - else if (m_Source[m_Offset] == '[') - { - startingChar = '['; - closingChar = ']'; - } - else if (m_Source[m_Offset] == '<') - { - startingChar = '<'; - closingChar = '>'; - } - else - { - throw new Exception("No parenthesized value '" + m_Source.Substring(m_Offset) + "' !"); - } - m_Offset++; - - bool inQuotedString = false; // Holds flag if position is quoted string or not - char lastChar = (char) 0; - int nestedStartingCharCounter = 0; - for (int i = m_Offset; i < m_Source.Length; i++) - { - // Skip escaped(\) " - if (lastChar != '\\' && m_Source[i] == '\"') - { - // Start/end quoted string area - inQuotedString = !inQuotedString; - } - // We need to skip parenthesis in quoted string - else if (!inQuotedString) - { - // There is nested parenthesis - if (m_Source[i] == startingChar) - { - nestedStartingCharCounter++; - } - // Closing char - else if (m_Source[i] == closingChar) - { - // There isn't nested parenthesis closing chars left, this is closing char what we want. - if (nestedStartingCharCounter == 0) - { - string retVal = m_Source.Substring(m_Offset, i - m_Offset); - m_Offset = i + 1; - - return retVal; - } - // This is nested parenthesis closing char - else - { - nestedStartingCharCounter--; - } - } - } - - lastChar = m_Source[i]; - } - - throw new ArgumentException("There is no closing parenthesize for '" + - m_Source.Substring(m_Offset) + "' !"); - } - - /// - /// Reads string to specified delimiter or to end of underlying string. Notes: Delimiters in quoted string is skipped. - /// For example: delimiter = ',', text = '"aaaa,eee",qqqq' - then result is '"aaaa,eee"'. - /// - /// Data delimiters. - /// Returns readed string or null if end of string reached. - /// Is raised when delimiters is null reference. - public string QuotedReadToDelimiter(char[] delimiters) - { - if (delimiters == null) - { - throw new ArgumentNullException("delimiters"); - } - - if (Available == 0) - { - return null; - } - - ToFirstChar(); - - string currentSplitBuffer = ""; // Holds active - bool inQuotedString = false; // Holds flag if position is quoted string or not - char lastChar = (char) 0; - - for (int i = m_Offset; i < m_Source.Length; i++) - { - char c = (char) Peek(false); - - // Skip escaped(\) " - if (lastChar != '\\' && c == '\"') - { - // Start/end quoted string area - inQuotedString = !inQuotedString; - } - - // See if char is delimiter - bool isDelimiter = false; - foreach (char delimiter in delimiters) - { - if (c == delimiter) - { - isDelimiter = true; - break; - } - } - - // Current char is split char and it isn't in quoted string, do split - if (!inQuotedString && isDelimiter) - { - return currentSplitBuffer; - } - else - { - currentSplitBuffer += c; - m_Offset++; - } - - lastChar = c; - } - - // If we reached so far then we are end of string, return it. - return currentSplitBuffer; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_TransferEncodings.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_TransferEncodings.cs deleted file mode 100644 index 4598a00e7..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_TransferEncodings.cs +++ /dev/null @@ -1,62 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - /// - /// This class holds MIME content transfer encodings. Defined in RFC 2045 6. - /// - public class MIME_TransferEncodings - { - #region Members - - /// - /// Used to encode arbitrary octet sequences into a form that satisfies the rules of 7bit. Has a fixed overhead and is - /// intended for non text data and text that is not ASCII heavy. - /// Defined in RFC 2045 6.8. - /// - public static readonly string Base64 = "base64"; - - /// - /// Any sequence of octets. This type is not widely used. Defined in RFC 3030. - /// - public static readonly string Binary = "binary"; - - /// - /// Up to 998 octets per line with CR and LF (codes 13 and 10 respectively) only allowed to appear as part of a CRLF line ending. - /// Defined in RFC 2045 6.2. - /// - public static readonly string EightBit = "8bit"; - - /// - /// Used to encode arbitrary octet sequences into a form that satisfies the rules of 7bit. - /// Designed to be efficient and mostly human readable when used for text data consisting primarily of US-ASCII characters - /// but also containing a small proportion of bytes with values outside that range. - /// Defined in RFC 2045 6.7. - /// - public static readonly string QuotedPrintable = "quoted-printable"; - - /// - /// Up to 998 octets per line of the code range 1..127 with CR and LF (codes 13 and 10 respectively) only allowed to - /// appear as part of a CRLF line ending. This is the default value. - /// Defined in RFC 2045 6.2. - /// - public static readonly string SevenBit = "7bit"; - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_Utils.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_Utils.cs deleted file mode 100644 index f53b559c2..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_Utils.cs +++ /dev/null @@ -1,546 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using System.Globalization; - - #endregion - - /// - /// Provides MIME related utility methods. - /// - public class MIME_Utils - { - #region Methods - - /// - /// Converts date to RFC 2822 date time string. - /// - /// Date time value to convert.. - /// Returns RFC 2822 date time string. - public static string DateTimeToRfc2822(DateTime dateTime) - { - return dateTime.ToString("ddd, dd MMM yyyy HH':'mm':'ss ", DateTimeFormatInfo.InvariantInfo) + - dateTime.ToString("zzz").Replace(":", ""); - } - - /// - /// Parses RFC 2822 date-time from the specified value. - /// - /// RFC 2822 date-time string value. - /// Returns parsed datetime value. - /// Is raised when value is null. - /// Is raised when any of the arguments has invalid value. - public static DateTime ParseRfc2822DateTime(string value) - { - if (value == null) - { - throw new ArgumentNullException(value); - } - //Try parse dt - DateTime parsedTime; - if (DateTime.TryParse(value, out parsedTime)) - { - return parsedTime; - } - /* RFC 2822 3. - * date-time = [ day-of-week "," ] date FWS time [CFWS] - * day-of-week = ([FWS] day-name) / obs-day-of-week - * day-name = "Mon" / "Tue" / "Wed" / "Thu" / "Fri" / "Sat" / "Sun" - * date = day month year - * year = 4*DIGIT / obs-year - * month = (FWS month-name FWS) / obs-month - * month-name = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" / "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec" - * day = ([FWS] 1*2DIGIT) / obs-day - * time = time-of-day FWS zone - * time-of-day = hour ":" minute [ ":" second ] - * hour = 2DIGIT / obs-hour - * minute = 2DIGIT / obs-minute - * second = 2DIGIT / obs-second - * zone = (( "+" / "-" ) 4DIGIT) / obs-zone - * - * The date and time-of-day SHOULD express local time. - */ - - try - { - MIME_Reader r = new MIME_Reader(value); - string v = r.Atom(); - // Skip optional [ day-of-week "," ] and read "day". - if (v.Length == 3) - { - r.Char(true); - v = r.Atom(); - } - int day = Convert.ToInt32(v); - v = r.Atom().ToLower(); - int month = 1; - if (v == "jan") - { - month = 1; - } - else if (v == "feb") - { - month = 2; - } - else if (v == "mar") - { - month = 3; - } - else if (v == "apr") - { - month = 4; - } - else if (v == "may") - { - month = 5; - } - else if (v == "jun") - { - month = 6; - } - else if (v == "jul") - { - month = 7; - } - else if (v == "aug") - { - month = 8; - } - else if (v == "sep") - { - month = 9; - } - else if (v == "oct") - { - month = 10; - } - else if (v == "nov") - { - month = 11; - } - else if (v == "dec") - { - month = 12; - } - else - { - throw new ArgumentException("Invalid month-name value '" + value + "'."); - } - int year = Convert.ToInt32(r.Atom()); - int hour = Convert.ToInt32(r.Atom()); - r.Char(true); - int minute = Convert.ToInt32(r.Atom()); - int second = 0; - // We have optional "second". - if (r.Peek(true) == ':') - { - r.Char(true); - second = Convert.ToInt32(r.Atom()); - } - int timeZoneMinutes = 0; - v = r.Atom(); - // We have RFC 2822 date. For example: +2000. - if (v[0] == '+' || v[0] == '-') - { - if (v[0] == '+') - { - timeZoneMinutes = (Convert.ToInt32(v.Substring(1, 2))*60 + - Convert.ToInt32(v.Substring(3, 2))); - } - else - { - timeZoneMinutes = - -(Convert.ToInt32(v.Substring(1, 2))*60 + Convert.ToInt32(v.Substring(3, 2))); - } - } - // We have RFC 822 date with abbrevated time zone name. For example: GMT. - else - { - v = v.ToUpper(); - - #region time zones - - // Alpha Time Zone (military). - if (v == "A") - { - timeZoneMinutes = ((01*60) + 00); - } - // Australian Central Daylight Time. - else if (v == "ACDT") - { - timeZoneMinutes = ((10*60) + 30); - } - // Australian Central Standard Time. - else if (v == "ACST") - { - timeZoneMinutes = ((09*60) + 30); - } - // Atlantic Daylight Time. - else if (v == "ADT") - { - timeZoneMinutes = -((03*60) + 00); - } - // Australian Eastern Daylight Time. - else if (v == "AEDT") - { - timeZoneMinutes = ((11*60) + 00); - } - // Australian Eastern Standard Time. - else if (v == "AEST") - { - timeZoneMinutes = ((10*60) + 00); - } - // Alaska Daylight Time. - else if (v == "AKDT") - { - timeZoneMinutes = -((08*60) + 00); - } - // Alaska Standard Time. - else if (v == "AKST") - { - timeZoneMinutes = -((09*60) + 00); - } - // Atlantic Standard Time. - else if (v == "AST") - { - timeZoneMinutes = -((04*60) + 00); - } - // Australian Western Daylight Time. - else if (v == "AWDT") - { - timeZoneMinutes = ((09*60) + 00); - } - // Australian Western Standard Time. - else if (v == "AWST") - { - timeZoneMinutes = ((08*60) + 00); - } - // Bravo Time Zone (millitary). - else if (v == "B") - { - timeZoneMinutes = ((02*60) + 00); - } - // British Summer Time. - else if (v == "BST") - { - timeZoneMinutes = ((01*60) + 00); - } - // Charlie Time Zone (millitary). - else if (v == "C") - { - timeZoneMinutes = ((03*60) + 00); - } - // Central Daylight Time. - else if (v == "CDT") - { - timeZoneMinutes = -((05*60) + 00); - } - // Central European Daylight Time. - else if (v == "CEDT") - { - timeZoneMinutes = ((02*60) + 00); - } - // Central European Summer Time. - else if (v == "CEST") - { - timeZoneMinutes = ((02*60) + 00); - } - // Central European Time. - else if (v == "CET") - { - timeZoneMinutes = ((01*60) + 00); - } - // Central Standard Time. - else if (v == "CST") - { - timeZoneMinutes = -((06*60) + 00); - } - // Christmas Island Time. - else if (v == "CXT") - { - timeZoneMinutes = ((01*60) + 00); - } - // Delta Time Zone (military). - else if (v == "D") - { - timeZoneMinutes = ((04*60) + 00); - } - // Echo Time Zone (military). - else if (v == "E") - { - timeZoneMinutes = ((05*60) + 00); - } - // Eastern Daylight Time. - else if (v == "EDT") - { - timeZoneMinutes = -((04*60) + 00); - } - // Eastern European Daylight Time. - else if (v == "EEDT") - { - timeZoneMinutes = ((03*60) + 00); - } - // Eastern European Summer Time. - else if (v == "EEST") - { - timeZoneMinutes = ((03*60) + 00); - } - // Eastern European Time. - else if (v == "EET") - { - timeZoneMinutes = ((02*60) + 00); - } - // Eastern Standard Time. - else if (v == "EST") - { - timeZoneMinutes = -((05*60) + 00); - } - // Foxtrot Time Zone (military). - else if (v == "F") - { - timeZoneMinutes = (06*60 + 00); - } - // Golf Time Zone (military). - else if (v == "G") - { - timeZoneMinutes = ((07*60) + 00); - } - // Greenwich Mean Time. - else if (v == "GMT") - { - timeZoneMinutes = 0000; - } - // Hotel Time Zone (military). - else if (v == "H") - { - timeZoneMinutes = ((08*60) + 00); - } - // India Time Zone (military). - else if (v == "I") - { - timeZoneMinutes = ((09*60) + 00); - } - // Irish Summer Time. - else if (v == "IST") - { - timeZoneMinutes = ((01*60) + 00); - } - // Kilo Time Zone (millitary). - else if (v == "K") - { - timeZoneMinutes = ((10*60) + 00); - } - // Lima Time Zone (millitary). - else if (v == "L") - { - timeZoneMinutes = ((11*60) + 00); - } - // Mike Time Zone (millitary). - else if (v == "M") - { - timeZoneMinutes = ((12*60) + 00); - } - // Mountain Daylight Time. - else if (v == "MDT") - { - timeZoneMinutes = -((06*60) + 00); - } - // Mountain Standard Time. - else if (v == "MST") - { - timeZoneMinutes = -((07*60) + 00); - } - // November Time Zone (military). - else if (v == "N") - { - timeZoneMinutes = -((01*60) + 00); - } - // Newfoundland Daylight Time. - else if (v == "NDT") - { - timeZoneMinutes = -((02*60) + 30); - } - // Norfolk (Island) Time. - else if (v == "NFT") - { - timeZoneMinutes = ((11*60) + 30); - } - // Newfoundland Standard Time. - else if (v == "NST") - { - timeZoneMinutes = -((03*60) + 30); - } - // Oscar Time Zone (military). - else if (v == "O") - { - timeZoneMinutes = -((02*60) + 00); - } - // Papa Time Zone (military). - else if (v == "P") - { - timeZoneMinutes = -((03*60) + 00); - } - // Pacific Daylight Time. - else if (v == "PDT") - { - timeZoneMinutes = -((07*60) + 00); - } - // Pacific Standard Time. - else if (v == "PST") - { - timeZoneMinutes = -((08*60) + 00); - } - // Quebec Time Zone (military). - else if (v == "Q") - { - timeZoneMinutes = -((04*60) + 00); - } - // Romeo Time Zone (military). - else if (v == "R") - { - timeZoneMinutes = -((05*60) + 00); - } - // Sierra Time Zone (military). - else if (v == "S") - { - timeZoneMinutes = -((06*60) + 00); - } - // Tango Time Zone (military). - else if (v == "T") - { - timeZoneMinutes = -((07*60) + 00); - } - // Uniform Time Zone (military). - else if (v == "") - { - timeZoneMinutes = -((08*60) + 00); - } - // Coordinated Universal Time. - else if (v == "UTC") - { - timeZoneMinutes = 0000; - } - // Victor Time Zone (militray). - else if (v == "V") - { - timeZoneMinutes = -((09*60) + 00); - } - // Whiskey Time Zone (military). - else if (v == "W") - { - timeZoneMinutes = -((10*60) + 00); - } - // Western European Daylight Time. - else if (v == "WEDT") - { - timeZoneMinutes = ((01*60) + 00); - } - // Western European Summer Time. - else if (v == "WEST") - { - timeZoneMinutes = ((01*60) + 00); - } - // Western European Time. - else if (v == "WET") - { - timeZoneMinutes = 0000; - } - // Western Standard Time. - else if (v == "WST") - { - timeZoneMinutes = ((08*60) + 00); - } - // X-ray Time Zone (military). - else if (v == "X") - { - timeZoneMinutes = -((11*60) + 00); - } - // Yankee Time Zone (military). - else if (v == "Y") - { - timeZoneMinutes = -((12*60) + 00); - } - // Zulu Time Zone (military). - else if (v == "Z") - { - timeZoneMinutes = 0000; - } - - #endregion - } - - // Convert time to UTC and then back to local. - DateTime timeUTC = - new DateTime(year, month, day, hour, minute, second).AddMinutes(-(timeZoneMinutes)); - return - new DateTime(timeUTC.Year, - timeUTC.Month, - timeUTC.Day, - timeUTC.Hour, - timeUTC.Minute, - timeUTC.Second, - DateTimeKind.Utc).ToLocalTime(); - } - catch (Exception x) - { - string dymmy = x.Message; - throw new ArgumentException("Argumnet 'value' value '" + value + - "' is not valid RFC 822/2822 date-time string."); - } - } - - /// - /// Unfolds folded header field. - /// - /// Header field. - /// Returns unfolded header field. - /// Is raised when value is null reference. - public static string UnfoldHeader(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - /* RFC 2822 2.2.3 Long Header Fields. - The process of moving from this folded multiple-line representation - of a header field to its single line representation is called - "unfolding". Unfolding is accomplished by simply removing any CRLF - that is immediately followed by WSP. - */ - - return value.Replace("\r\n", ""); - } - - /// - /// Creates Rfc 2822 3.6.4 message-id. Syntax: '<' id-left '@' id-right '>'. - /// - /// - public static string CreateMessageID() - { - return "<" + Guid.NewGuid().ToString().Replace("-", "").Substring(16) + "@" + - Guid.NewGuid().ToString().Replace("-", "").Substring(16) + ">"; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b.cs deleted file mode 100644 index 77152879e..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b.cs +++ /dev/null @@ -1,141 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using System.IO; - using System.Text; - using IO; - - #endregion - - /// - /// This class is base class for MIME entity bodies. - /// - public abstract class MIME_b - { - #region Members - - private readonly MIME_h_ContentType m_pContentType; - private MIME_Entity m_pEntity; - - #endregion - - #region Properties - - /// - /// Gets if body has modified. - /// - public abstract bool IsModified { get; } - - /// - /// Gets body owner entity. Returns null if body not bounded to any entity yet. - /// - public MIME_Entity Entity - { - get { return m_pEntity; } - } - - /// - /// Gets MIME entity body content type. - /// - public MIME_h_ContentType ContentType - { - get { return m_pContentType; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Content type. - /// Is raised when contentType is null reference. - /// Is raised when any of the arguments has invalid value. - public MIME_b(MIME_h_ContentType contentType) - { - if (contentType == null) - { - throw new ArgumentNullException("contentType"); - } - - m_pContentType = contentType; - } - - #endregion - - #region Abstract methods - - /// - /// Stores MIME entity body to the specified stream. - /// - /// Stream where to store body data. - /// Header 8-bit words ecnoder. Value null means that words are not encoded. - /// Charset to use to encode 8-bit header parameters. Value null means parameters not encoded. - /// Is raised when stream is null reference. - protected internal abstract void ToStream(Stream stream, - MIME_Encoding_EncodedWord headerWordEncoder, - Encoding headerParmetersCharset); - - #endregion - - #region Internal methods - - /// - /// Sets body parent. - /// - /// Owner entity. - internal void SetParent(MIME_Entity entity) - { - m_pEntity = entity; - } - - #endregion - - /// - /// Parses body from the specified stream - /// - /// Owner MIME entity. - /// MIME media type. For example: text/plain. - /// Stream from where to read body. - /// Returns parsed body. - /// Is raised when stream, mediaType or stream is null reference. - /// Is raised when any parsing errors. - protected static MIME_b Parse(MIME_Entity owner, string mediaType, SmartStream stream) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - if (mediaType == null) - { - throw new ArgumentNullException("mediaType"); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - throw new NotImplementedException("Body provider class does not implement required Parse method."); - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Application.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Application.cs deleted file mode 100644 index eae42ddf3..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Application.cs +++ /dev/null @@ -1,79 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using IO; - - #endregion - - /// - /// This class represents MIME application/xxx bodies. Defined in RFC 2046 4.2. - /// - /// - /// The "application" media type is to be used for discrete data which do - /// not fit in any of the other categories, and particularly for data to - /// be processed by some type of application program. - /// - public class MIME_b_Application : MIME_b_SinglepartBase - { - #region Constructor - - /// - /// Default constructor. - /// - /// MIME media type. - /// Is raised when mediaType is null reference. - public MIME_b_Application(string mediaType) : base(mediaType) {} - - #endregion - - /// - /// Parses body from the specified stream - /// - /// Owner MIME entity. - /// MIME media type. For example: text/plain. - /// Stream from where to read body. - /// Returns parsed body. - /// Is raised when stream, mediaType or strean is null reference. - /// Is raised when any parsing errors. - protected new static MIME_b Parse(MIME_Entity owner, string mediaType, SmartStream stream) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - if (mediaType == null) - { - throw new ArgumentNullException("mediaType"); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - MIME_b_Application retVal = new MIME_b_Application(mediaType); - - Net_Utils.StreamCopy(stream, retVal.EncodedStream, Workaround.Definitions.MaxStreamLineLength); - - return retVal; - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Audio.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Audio.cs deleted file mode 100644 index ed11f4518..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Audio.cs +++ /dev/null @@ -1,75 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using IO; - - #endregion - - /// - /// This class represents MIME audio/xxx bodies. Defined in RFC 2046 4.3. - /// - /// A media type of "audio" indicates that the body contains audio data. - public class MIME_b_Audio : MIME_b_SinglepartBase - { - #region Constructor - - /// - /// Default constructor. - /// - /// MIME media type. - /// Is raised when mediaType is null reference. - public MIME_b_Audio(string mediaType) : base(mediaType) {} - - #endregion - - /// - /// Parses body from the specified stream - /// - /// Owner MIME entity. - /// MIME media type. For example: text/plain. - /// Stream from where to read body. - /// Returns parsed body. - /// Is raised when stream, mediaType or strean is null reference. - /// Is raised when any parsing errors. - protected new static MIME_b Parse(MIME_Entity owner, string mediaType, SmartStream stream) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - if (mediaType == null) - { - throw new ArgumentNullException("mediaType"); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - MIME_b_Audio retVal = new MIME_b_Audio(mediaType); - - Net_Utils.StreamCopy(stream, retVal.EncodedStream, Workaround.Definitions.MaxStreamLineLength); - - return retVal; - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Image.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Image.cs deleted file mode 100644 index 52f949661..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Image.cs +++ /dev/null @@ -1,78 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using IO; - - #endregion - - /// - /// This class represents MIME image/xxx bodies. Defined in RFC 2046 4.2. - /// - /// - /// A media type of "image" indicates that the body contains an image. - /// The subtype names the specific image format. - /// - public class MIME_b_Image : MIME_b_SinglepartBase - { - #region Constructor - - /// - /// Default constructor. - /// - /// MIME media type. - /// Is raised when mediaType is null reference. - public MIME_b_Image(string mediaType) : base(mediaType) {} - - #endregion - - /// - /// Parses body from the specified stream - /// - /// Owner MIME entity. - /// MIME media type. For example: text/plain. - /// Stream from where to read body. - /// Returns parsed body. - /// Is raised when stream, mediaType or strean is null reference. - /// Is raised when any parsing errors. - protected new static MIME_b Parse(MIME_Entity owner, string mediaType, SmartStream stream) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - if (mediaType == null) - { - throw new ArgumentNullException("mediaType"); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - MIME_b_Image retVal = new MIME_b_Image(mediaType); - - Net_Utils.StreamCopy(stream, retVal.EncodedStream, Workaround.Definitions.MaxStreamLineLength); - - return retVal; - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Message.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Message.cs deleted file mode 100644 index 72689486a..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Message.cs +++ /dev/null @@ -1,74 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using IO; - - #endregion - - /// - /// This class represents MIME message/xxx bodies. Defined in RFC 2046 5.2. - /// - public class MIME_b_Message : MIME_b_SinglepartBase - { - #region Constructor - - /// - /// Default constructor. - /// - /// MIME media type. - /// Is raised when mediaType is null reference. - public MIME_b_Message(string mediaType) : base(mediaType) {} - - #endregion - - /// - /// Parses body from the specified stream - /// - /// Owner MIME entity. - /// MIME media type. For example: text/plain. - /// Stream from where to read body. - /// Returns parsed body. - /// Is raised when stream, mediaType or strean is null reference. - /// Is raised when any parsing errors. - protected new static MIME_b Parse(MIME_Entity owner, string mediaType, SmartStream stream) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - if (mediaType == null) - { - throw new ArgumentNullException("mediaType"); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - MIME_b_Message retVal = new MIME_b_Message(mediaType); - - Net_Utils.StreamCopy(stream, retVal.EncodedStream, Workaround.Definitions.MaxStreamLineLength); - - return retVal; - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MessageRfc822.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MessageRfc822.cs deleted file mode 100644 index fd57edc7d..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MessageRfc822.cs +++ /dev/null @@ -1,140 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using System.IO; - using System.Text; - using IO; - using Mail; - - #endregion - - /// - /// This class represents MIME message/rfc822 body. Defined in RFC 2046 5.2.1. - /// - public class MIME_b_MessageRfc822 : MIME_b - { - #region Members - - private Mail_Message m_pMessage; - - #endregion - - #region Properties - - /// - /// Gets if body has modified. - /// - public override bool IsModified - { - get { return m_pMessage.IsModified; } - } - - /// - /// Gets embbed mail message. - /// - public Mail_Message Message - { - get { return m_pMessage; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public MIME_b_MessageRfc822() : base(new MIME_h_ContentType("message/rfc822")) - { - m_pMessage = new Mail_Message(); - } - - #endregion - - #region Overrides - - /// - /// Stores MIME entity body to the specified stream. - /// - /// Stream where to store body data. - /// Header 8-bit words ecnoder. Value null means that words are not encoded. - /// Charset to use to encode 8-bit header parameters. Value null means parameters not encoded. - /// Is raised when stream is null reference. - protected internal override void ToStream(Stream stream, - MIME_Encoding_EncodedWord headerWordEncoder, - Encoding headerParmetersCharset) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - m_pMessage.ToStream(stream, headerWordEncoder, headerParmetersCharset); - } - - #endregion - - /// - /// Parses body from the specified stream - /// - /// Owner MIME entity. - /// MIME media type. For example: text/plain. - /// Stream from where to read body. - /// Returns parsed body. - /// Is raised when stream, mediaType or stream is null reference. - /// Is raised when any parsing errors. - protected new static MIME_b Parse(MIME_Entity owner, string mediaType, SmartStream stream) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - if (mediaType == null) - { - throw new ArgumentNullException("mediaType"); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - MIME_b_MessageRfc822 retVal = new MIME_b_MessageRfc822(); - if (owner.ContentTransferEncoding != null && owner.ContentTransferEncoding.Equals("base64", StringComparison.OrdinalIgnoreCase)) - { - Stream decodedDataStream = new MemoryStream(); - using (StreamReader reader = new StreamReader(stream)) - { - byte[] decoded = Convert.FromBase64String(reader.ReadToEnd()); - decodedDataStream.Write(decoded, 0, decoded.Length); - decodedDataStream.Seek(0, SeekOrigin.Begin); - } - - //Create base64 decoder - stream = new SmartStream(decodedDataStream,true); - - } - retVal.m_pMessage = Mail_Message.ParseFromStream(stream); - - return retVal; - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Multipart.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Multipart.cs deleted file mode 100644 index b1114a93b..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Multipart.cs +++ /dev/null @@ -1,616 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using System.IO; - using System.Text; - using IO; - - #endregion - - /// - /// This class represents MIME application/xxx bodies. Defined in RFC 2046 5.1. - /// - /// - /// The "multipart" represents single MIME body containing multiple child MIME entities. - /// The "multipart" body must contain at least 1 MIME entity. - /// - public class MIME_b_Multipart : MIME_b - { - #region Nested type: _MultipartReader - - /// - /// Implements multipart "body parts" reader. - /// - public class _MultipartReader : Stream - { - #region Nested type: State - - /// - /// This enum specified multipart reader sate. - /// - private enum State - { - /// - /// First boundary must be seeked. - /// - SeekFirst = 0, - - /// - /// Read next boundary. - /// - ReadNext = 1, - - /// - /// All boundraies readed. - /// - Done = 2, - } - - #endregion - - #region Members - - private readonly string m_Boundary = ""; - private readonly SmartStream.ReadLineAsyncOP m_pReadLineOP; - private readonly SmartStream m_pStream; - private readonly StringBuilder m_pTextEpilogue; - private readonly StringBuilder m_pTextPreamble; - private State m_State = State.SeekFirst; - - #endregion - - #region Properties - - /// - /// Gets a value indicating whether the current stream supports reading. - /// - public override bool CanRead - { - get { return true; } - } - - /// - /// Gets a value indicating whether the current stream supports seeking. - /// - public override bool CanSeek - { - get { return false; } - } - - /// - /// Gets a value indicating whether the current stream supports writing. - /// - public override bool CanWrite - { - get { return false; } - } - - /// - /// Gets the length in bytes of the stream. This method is not supported and always throws a NotSupportedException. - /// - /// Is raised when this property is accessed. - public override long Length - { - get { throw new NotSupportedException(); } - } - - /// - /// Gets or sets the position within the current stream. This method is not supported and always throws a NotSupportedException. - /// - /// Is raised when this property is accessed. - public override long Position - { - get { throw new NotSupportedException(); } - - set { throw new NotSupportedException(); } - } - - /// - /// Gets "preamble" text. Defined in RFC 2046 5.1.1. - /// - /// Preamble text is text between MIME entiy headers and first boundary. - public string TextPreamble - { - get { return m_pTextPreamble.ToString(); } - } - - /// - /// Gets "epilogue" text. Defined in RFC 2046 5.1.1. - /// - /// Epilogue text is text after last boundary end. - public string TextEpilogue - { - get { return m_pTextEpilogue.ToString(); } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Stream from where to read body part. - /// Boundry ID what separates body parts. - /// Is raised when stream or boundary is null reference. - public _MultipartReader(SmartStream stream, string boundary) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - if (boundary == null) - { - throw new ArgumentNullException("boundary"); - } - - m_pStream = stream; - m_Boundary = boundary; - - m_pReadLineOP = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength], - SizeExceededAction.ThrowException); - m_pTextPreamble = new StringBuilder(); - m_pTextEpilogue = new StringBuilder(); - } - - #endregion - - #region Methods - - /// - /// Moves to next "body part". Returns true if moved to next "body part" or false if there are no more parts. - /// - /// Returns true if moved to next "body part" or false if there are no more body parts. - public bool Next() - { - if (m_State == State.Done) - { - return false; - } - else if (m_State == State.SeekFirst) - { - while (true) - { - m_pStream.ReadLine(m_pReadLineOP, false); - if (m_pReadLineOP.Error != null) - { - throw m_pReadLineOP.Error; - } - // We reached end of stream. Bad boundary: boundary end tag missing. - else if (m_pReadLineOP.BytesInBuffer == 0) - { - m_State = State.Done; - - return false; - } - else - { - // Check if we have boundary start/end. - if (m_pReadLineOP.Buffer[0] == '-') - { - string boundary = m_pReadLineOP.LineUtf8; - // We have readed all MIME entity body parts. - if ("--" + m_Boundary + "--" == boundary) - { - m_State = State.Done; - - return false; - } - // We have next boundary. - else if ("--" + m_Boundary == boundary) - { - m_State = State.ReadNext; - - return true; - } - // Not boundary or not boundary we want. - //else{ - } - - m_pTextPreamble.Append(m_pReadLineOP.LineUtf8 + "\r\n"); - } - } - } - else if (m_State == State.ReadNext) - { - return true; - } - - return false; - } - - /// - /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device. - /// - public override void Flush() {} - - /// - /// Sets the position within the current stream. This method is not supported and always throws a NotSupportedException. - /// - /// A byte offset relative to the origin parameter. - /// A value of type SeekOrigin indicating the reference point used to obtain the new position. - /// The new position within the current stream. - /// Is raised when this method is accessed. - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - /// - /// Sets the length of the current stream. This method is not supported and always throws a NotSupportedException. - /// - /// The desired length of the current stream in bytes. - /// Is raised when this method is accessed. - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - /// - /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. - /// - /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source. - /// The zero-based byte offset in buffer at which to begin storing the data read from the current stream. - /// The maximum number of bytes to be read from the current stream. - /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. - /// Is raised when buffer is null reference. - public override int Read(byte[] buffer, int offset, int count) - { - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - - if (m_State == State.Done) - { - return 0; - } - - m_pStream.ReadLine(m_pReadLineOP, false); - if (m_pReadLineOP.Error != null) - { - throw m_pReadLineOP.Error; - } - // We reached end of stream. Bad boundary: boundary end tag missing. - else if (m_pReadLineOP.BytesInBuffer == 0) - { - m_State = State.Done; - - return 0; - } - else - { - // Check if we have boundary start/end. - if (m_pReadLineOP.Buffer[0] == '-') - { - string boundary = m_pReadLineOP.LineUtf8; - // We have readed all MIME entity body parts. - if ("--" + m_Boundary + "--" == boundary) - { - m_State = State.Done; - - // Read "epilogoue" if any. - while (true) - { - m_pStream.ReadLine(m_pReadLineOP, false); - - if (m_pReadLineOP.Error != null) - { - throw m_pReadLineOP.Error; - } - // We reached end of stream. Epilogue reading completed. - else if (m_pReadLineOP.BytesInBuffer == 0) - { - break; - } - else - { - m_pTextEpilogue.Append(m_pReadLineOP.LineUtf8 + "\r\n"); - } - } - - return 0; - } - // We have next boundary. - else if ("--" + m_Boundary == boundary) - { - return 0; - } - // Not boundary or not boundary we want. - //else{ - } - // We have body part data line - //else{ - - if (count < m_pReadLineOP.BytesInBuffer) - { - //throw new ArgumentException("Argument 'buffer' is to small. This should never happen."); - } - Array.Copy(m_pReadLineOP.Buffer, 0, buffer, offset, m_pReadLineOP.BytesInBuffer); - - return m_pReadLineOP.BytesInBuffer; - } - } - - /// - /// Writes sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written. - /// - /// An array of bytes. This method copies count bytes from buffer to the current stream. - /// The zero-based byte offset in buffer at which to begin copying bytes to the current stream. - /// The number of bytes to be written to the current stream. - /// Is raised when this method is accessed. - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); - } - - #endregion - } - - #endregion - - #region Members - - private readonly MIME_EntityCollection m_pBodyParts; - private string m_TextEpilogue = ""; - private string m_TextPreamble = ""; - - #endregion - - #region Properties - - /// - /// Gets if body has modified. - /// - public override bool IsModified - { - get { return m_pBodyParts.IsModified; } - } - - /// - /// Gets default body part Content-Type. For more info see RFC 2046 5.1. - /// - public virtual MIME_h_ContentType DefaultBodyPartContentType - { - /* RFC 2026 5.1. - The absence of a Content-Type header usually indicates that the corresponding body has - a content-type of "text/plain; charset=US-ASCII". - */ - - get - { - MIME_h_ContentType retVal = new MIME_h_ContentType("text/plain"); - retVal.Param_Charset = "US-ASCII"; - - return retVal; - } - } - - /// - /// Gets multipart body body-parts collection. - /// - /// Multipart entity child entities are called "body parts" in RFC 2045. - public MIME_EntityCollection BodyParts - { - get { return m_pBodyParts; } - } - - /// - /// Gets or sets "preamble" text. Defined in RFC 2046 5.1.1. - /// - /// Preamble text is text between MIME entiy headers and first boundary. - public string TextPreamble - { - get { return m_TextPreamble; } - - set { m_TextPreamble = value; } - } - - /// - /// Gets or sets "epilogue" text. Defined in RFC 2046 5.1.1. - /// - /// Epilogue text is text after last boundary end. - public string TextEpilogue - { - get { return m_TextEpilogue; } - - set { m_TextEpilogue = value; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Content type. - /// Is raised when contentType is null reference. - /// Is raised when any of the arguments has invalid value. - public MIME_b_Multipart(MIME_h_ContentType contentType) : base(contentType) - { - if (contentType == null) - { - throw new ArgumentNullException("contentType"); - } - if (string.IsNullOrEmpty(contentType.Param_Boundary)) - { - throw new ArgumentException( - "Argument 'contentType' doesn't contain required boundary parameter."); - } - - m_pBodyParts = new MIME_EntityCollection(); - } - - #endregion - - #region Overrides - - /// - /// Stores MIME entity body to the specified stream. - /// - /// Stream where to store body data. - /// Header 8-bit words ecnoder. Value null means that words are not encoded. - /// Charset to use to encode 8-bit header parameters. Value null means parameters not encoded. - /// Is raised when stream is null reference. - protected internal override void ToStream(Stream stream, - MIME_Encoding_EncodedWord headerWordEncoder, - Encoding headerParmetersCharset) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - // Set "preamble" text if any. - if (!string.IsNullOrEmpty(m_TextPreamble)) - { - byte[] preableBytes = null; - if (m_TextPreamble.EndsWith("\r\n")) - { - preableBytes = Encoding.UTF8.GetBytes(m_TextPreamble); - } - else - { - preableBytes = Encoding.UTF8.GetBytes(m_TextPreamble + "\r\n"); - } - stream.Write(preableBytes, 0, preableBytes.Length); - } - - for (int i = 0; i < m_pBodyParts.Count; i++) - { - MIME_Entity bodyPart = m_pBodyParts[i]; - // Start new body part. - byte[] bStart = Encoding.UTF8.GetBytes("--" + ContentType.Param_Boundary + "\r\n"); - stream.Write(bStart, 0, bStart.Length); - - bodyPart.ToStream(stream, headerWordEncoder, headerParmetersCharset); - - // Last body part, close boundary. - if (i == (m_pBodyParts.Count - 1)) - { - byte[] bEnd = Encoding.UTF8.GetBytes("--" + ContentType.Param_Boundary + "--\r\n"); - stream.Write(bEnd, 0, bEnd.Length); - } - } - - // Set "epilogoue" text if any. - if (!string.IsNullOrEmpty(m_TextEpilogue)) - { - byte[] epilogoueBytes = null; - if (m_TextEpilogue.EndsWith("\r\n")) - { - epilogoueBytes = Encoding.UTF8.GetBytes(m_TextEpilogue); - } - else - { - epilogoueBytes = Encoding.UTF8.GetBytes(m_TextEpilogue + "\r\n"); - } - stream.Write(epilogoueBytes, 0, epilogoueBytes.Length); - } - } - - #endregion - - /// - /// Parses body from the specified stream - /// - /// Owner MIME entity. - /// MIME media type. For example: text/plain. - /// Stream from where to read body. - /// Returns parsed body. - /// Is raised when stream, mediaType or stream is null reference. - /// Is raised when any parsing errors. - protected new static MIME_b Parse(MIME_Entity owner, string mediaType, SmartStream stream) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - if (mediaType == null) - { - throw new ArgumentNullException("mediaType"); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - if (owner.ContentType == null || owner.ContentType.Param_Boundary == null) - { - throw new ParseException("Multipart entity has not required 'boundary' paramter."); - } - - MIME_b_Multipart retVal = new MIME_b_Multipart(owner.ContentType); - ParseInternal(owner, mediaType, stream, retVal); - - return retVal; - } - - /// - /// Internal body parsing. - /// - /// Owner MIME entity. - /// MIME media type. For example: text/plain. - /// Stream from where to read body. - /// Multipart body instance. - /// Is raised when stream, mediaType, stream or body is null reference. - /// Is raised when any parsing errors. - protected static void ParseInternal(MIME_Entity owner, - string mediaType, - SmartStream stream, - MIME_b_Multipart body) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - if (mediaType == null) - { - throw new ArgumentNullException("mediaType"); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - if (owner.ContentType == null || owner.ContentType.Param_Boundary == null) - { - throw new ParseException("Multipart entity has not required 'boundary' parameter."); - } - if (body == null) - { - throw new ArgumentNullException("body"); - } - - _MultipartReader multipartReader = new _MultipartReader(stream, owner.ContentType.Param_Boundary); - while (multipartReader.Next()) - { - MIME_Entity entity = new MIME_Entity(); - entity.Parse(new SmartStream(multipartReader, false), body.DefaultBodyPartContentType); - body.m_pBodyParts.Add(entity); - entity.SetParent(owner); - } - - body.m_TextPreamble = multipartReader.TextPreamble; - body.m_TextEpilogue = multipartReader.TextEpilogue; - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartAlternative.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartAlternative.cs deleted file mode 100644 index 913786c1f..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartAlternative.cs +++ /dev/null @@ -1,94 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using IO; - - #endregion - - /// - /// This class represents MIME multipart/alternative body. Defined in RFC 2046 5.1.4. - /// - /// - /// The "multipart/alternative" is intended for use when each of the body parts is an "alternative" version of the same information. - /// In general, user agents that compose "multipart/alternative" entities - /// must place the body parts in increasing order of preference, that is, - /// with the preferred format last. - /// - public class MIME_b_MultipartAlternative : MIME_b_Multipart - { - #region Constructor - - /// - /// Default constructor. - /// - /// Content type. - /// Is raised when contentType is null reference. - /// Is raised when any of the arguments has invalid value. - public MIME_b_MultipartAlternative(MIME_h_ContentType contentType) : base(contentType) - { - if ( - !string.Equals(contentType.TypeWithSubype, - "multipart/alternative", - StringComparison.CurrentCultureIgnoreCase)) - { - throw new ArgumentException( - "Argument 'contentType.TypeWithSubype' value must be 'multipart/alternative'."); - } - } - - #endregion - - /// - /// Parses body from the specified stream - /// - /// Owner MIME entity. - /// MIME media type. For example: text/plain. - /// Stream from where to read body. - /// Returns parsed body. - /// Is raised when stream, mediaType or stream is null reference. - /// Is raised when any parsing errors. - protected new static MIME_b Parse(MIME_Entity owner, string mediaType, SmartStream stream) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - if (mediaType == null) - { - throw new ArgumentNullException("mediaType"); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - if (owner.ContentType == null || owner.ContentType.Param_Boundary == null) - { - throw new ParseException("Multipart entity has not required 'boundary' paramter."); - } - - MIME_b_MultipartAlternative retVal = new MIME_b_MultipartAlternative(owner.ContentType); - ParseInternal(owner, mediaType, stream, retVal); - - return retVal; - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartDigest.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartDigest.cs deleted file mode 100644 index 9b769a409..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartDigest.cs +++ /dev/null @@ -1,113 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using IO; - - #endregion - - /// - /// This class represents MIME multipart/digest body. Defined in RFC 2046 5.1.5. - /// - /// - /// The "multipart/digest" Content-Type is intended to be used to send collections of messages. - /// - public class MIME_b_MultipartDigest : MIME_b_Multipart - { - #region Properties - - /// - /// Gets default body part Content-Type. For more info see RFC 2046 5.1. - /// - public override MIME_h_ContentType DefaultBodyPartContentType - { - /* RFC 2046 5.1.6. - The absence of a Content-Type header usually indicates that the corresponding body has - a content-type of "message/rfc822". - */ - - get - { - MIME_h_ContentType retVal = new MIME_h_ContentType("message/rfc822"); - - return retVal; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Content type. - /// Is raised when contentType is null reference. - /// Is raised when any of the arguments has invalid value. - public MIME_b_MultipartDigest(MIME_h_ContentType contentType) : base(contentType) - { - if ( - !string.Equals(contentType.TypeWithSubype, - "multipart/digest", - StringComparison.CurrentCultureIgnoreCase)) - { - throw new ArgumentException( - "Argument 'contentType.TypeWithSubype' value must be 'multipart/digest'."); - } - } - - #endregion - - /// - /// Parses body from the specified stream - /// - /// Owner MIME entity. - /// MIME media type. For example: text/plain. - /// Stream from where to read body. - /// Returns parsed body. - /// Is raised when stream, mediaType or stream is null reference. - /// Is raised when any parsing errors. - protected new static MIME_b Parse(MIME_Entity owner, string mediaType, SmartStream stream) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - if (mediaType == null) - { - throw new ArgumentNullException("mediaType"); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - if (owner.ContentType == null || owner.ContentType.Param_Boundary == null) - { - throw new ParseException("Multipart entity has not required 'boundary' paramter."); - } - - MIME_b_MultipartDigest retVal = new MIME_b_MultipartDigest(owner.ContentType); - ParseInternal(owner, mediaType, stream, retVal); - - return retVal; - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartEncrypted.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartEncrypted.cs deleted file mode 100644 index 6bd0cac13..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartEncrypted.cs +++ /dev/null @@ -1,88 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using IO; - - #endregion - - /// - /// This class represents MIME multipart/encrypted body. Defined in rfc 1847. - /// - public class MIME_b_MultipartEncrypted : MIME_b_Multipart - { - #region Constructor - - /// - /// Default constructor. - /// - /// Content type. - /// Is raised when contentType is null reference. - /// Is raised when any of the arguments has invalid value. - public MIME_b_MultipartEncrypted(MIME_h_ContentType contentType) : base(contentType) - { - if ( - !string.Equals(contentType.TypeWithSubype, - "multipart/encrypted", - StringComparison.CurrentCultureIgnoreCase)) - { - throw new ArgumentException( - "Argument 'contentType.TypeWithSubype' value must be 'multipart/encrypted'."); - } - } - - #endregion - - /// - /// Parses body from the specified stream - /// - /// Owner MIME entity. - /// MIME media type. For example: text/plain. - /// Stream from where to read body. - /// Returns parsed body. - /// Is raised when stream, mediaType or stream is null reference. - /// Is raised when any parsing errors. - protected new static MIME_b Parse(MIME_Entity owner, string mediaType, SmartStream stream) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - if (mediaType == null) - { - throw new ArgumentNullException("mediaType"); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - if (owner.ContentType == null || owner.ContentType.Param_Boundary == null) - { - throw new ParseException("Multipart entity has not required 'boundary' paramter."); - } - - MIME_b_MultipartEncrypted retVal = new MIME_b_MultipartEncrypted(owner.ContentType); - ParseInternal(owner, mediaType, stream, retVal); - - return retVal; - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartFormData.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartFormData.cs deleted file mode 100644 index ae0dc4817..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartFormData.cs +++ /dev/null @@ -1,88 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using IO; - - #endregion - - /// - /// This class represents MIME multipart/from-data body. Defined in RFC 2046. - /// - public class MIME_b_MultipartFormData : MIME_b_Multipart - { - #region Constructor - - /// - /// Default constructor. - /// - /// Content type. - /// Is raised when contentType is null reference. - /// Is raised when any of the arguments has invalid value. - public MIME_b_MultipartFormData(MIME_h_ContentType contentType) : base(contentType) - { - if ( - !string.Equals(contentType.TypeWithSubype, - "multipart/from-data", - StringComparison.CurrentCultureIgnoreCase)) - { - throw new ArgumentException( - "Argument 'contentType.TypeWithSubype' value must be 'multipart/from-data'."); - } - } - - #endregion - - /// - /// Parses body from the specified stream - /// - /// Owner MIME entity. - /// MIME media type. For example: text/plain. - /// Stream from where to read body. - /// Returns parsed body. - /// Is raised when stream, mediaType or stream is null reference. - /// Is raised when any parsing errors. - protected new static MIME_b Parse(MIME_Entity owner, string mediaType, SmartStream stream) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - if (mediaType == null) - { - throw new ArgumentNullException("mediaType"); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - if (owner.ContentType == null || owner.ContentType.Param_Boundary == null) - { - throw new ParseException("Multipart entity has not required 'boundary' paramter."); - } - - MIME_b_MultipartFormData retVal = new MIME_b_MultipartFormData(owner.ContentType); - ParseInternal(owner, mediaType, stream, retVal); - - return retVal; - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartMixed.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartMixed.cs deleted file mode 100644 index 76d7c2148..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartMixed.cs +++ /dev/null @@ -1,92 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using IO; - - #endregion - - /// - /// This class represents MIME multipart/mixed body. Defined in RFC 2046 5.1.3. - /// - /// - /// The "mixed" subtype of "multipart" is intended for use when the body - /// parts are independent and need to be bundled in a particular order. - /// - public class MIME_b_MultipartMixed : MIME_b_Multipart - { - #region Constructor - - /// - /// Default constructor. - /// - /// Content type. - /// Is raised when contentType is null reference. - /// Is raised when any of the arguments has invalid value. - public MIME_b_MultipartMixed(MIME_h_ContentType contentType) : base(contentType) - { - if ( - !string.Equals(contentType.TypeWithSubype, - "multipart/mixed", - StringComparison.CurrentCultureIgnoreCase)) - { - throw new ArgumentException( - "Argument 'contentType.TypeWithSubype' value must be 'multipart/mixed'."); - } - } - - #endregion - - /// - /// Parses body from the specified stream - /// - /// Owner MIME entity. - /// MIME media type. For example: text/plain. - /// Stream from where to read body. - /// Returns parsed body. - /// Is raised when stream, mediaType or stream is null reference. - /// Is raised when any parsing errors. - protected new static MIME_b Parse(MIME_Entity owner, string mediaType, SmartStream stream) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - if (mediaType == null) - { - throw new ArgumentNullException("mediaType"); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - if (owner.ContentType == null || owner.ContentType.Param_Boundary == null) - { - throw new ParseException("Multipart entity has not required 'boundary' paramter."); - } - - MIME_b_MultipartMixed retVal = new MIME_b_MultipartMixed(owner.ContentType); - ParseInternal(owner, mediaType, stream, retVal); - - return retVal; - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartParallel.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartParallel.cs deleted file mode 100644 index 8ef720a5e..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartParallel.cs +++ /dev/null @@ -1,92 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using IO; - - #endregion - - /// - /// This class represents MIME message/parallel bodies. Defined in RFC 2046 5.1.6. - /// - /// - /// The "parallel" subtype of "multipart" is intended for use when the body - /// parts are independent and their order is not important. Parts can be processed parallel. - /// - public class MIME_b_MultipartParallel : MIME_b_Multipart - { - #region Constructor - - /// - /// Default constructor. - /// - /// Content type. - /// Is raised when contentType is null reference. - /// Is raised when any of the arguments has invalid value. - public MIME_b_MultipartParallel(MIME_h_ContentType contentType) : base(contentType) - { - if ( - !string.Equals(contentType.TypeWithSubype, - "multipart/parallel", - StringComparison.CurrentCultureIgnoreCase)) - { - throw new ArgumentException( - "Argument 'contentType.TypeWithSubype' value must be 'multipart/parallel'."); - } - } - - #endregion - - /// - /// Parses body from the specified stream - /// - /// Owner MIME entity. - /// MIME media type. For example: text/plain. - /// Stream from where to read body. - /// Returns parsed body. - /// Is raised when stream, mediaType or stream is null reference. - /// Is raised when any parsing errors. - protected new static MIME_b Parse(MIME_Entity owner, string mediaType, SmartStream stream) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - if (mediaType == null) - { - throw new ArgumentNullException("mediaType"); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - if (owner.ContentType == null || owner.ContentType.Param_Boundary == null) - { - throw new ParseException("Multipart entity has not required 'boundary' paramter."); - } - - MIME_b_MultipartParallel retVal = new MIME_b_MultipartParallel(owner.ContentType); - ParseInternal(owner, mediaType, stream, retVal); - - return retVal; - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartRelated.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartRelated.cs deleted file mode 100644 index e8ab2a472..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartRelated.cs +++ /dev/null @@ -1,92 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using IO; - - #endregion - - /// - /// This class represents MIME multipart/related body. Defined in RFC 2387. - /// - /// - /// The Multipart/Related content-type provides a common mechanism for - /// representing objects that are aggregates of related MIME body parts. - /// - public class MIME_b_MultipartRelated : MIME_b_Multipart - { - #region Constructor - - /// - /// Default constructor. - /// - /// Content type. - /// Is raised when contentType is null reference. - /// Is raised when any of the arguments has invalid value. - public MIME_b_MultipartRelated(MIME_h_ContentType contentType) : base(contentType) - { - if ( - !string.Equals(contentType.TypeWithSubype, - "multipart/related", - StringComparison.CurrentCultureIgnoreCase)) - { - throw new ArgumentException( - "Argument 'contentType.TypeWithSubype' value must be 'multipart/related'."); - } - } - - #endregion - - /// - /// Parses body from the specified stream - /// - /// Owner MIME entity. - /// MIME media type. For example: text/plain. - /// Stream from where to read body. - /// Returns parsed body. - /// Is raised when stream, mediaType or stream is null reference. - /// Is raised when any parsing errors. - protected new static MIME_b Parse(MIME_Entity owner, string mediaType, SmartStream stream) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - if (mediaType == null) - { - throw new ArgumentNullException("mediaType"); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - if (owner.ContentType == null || owner.ContentType.Param_Boundary == null) - { - throw new ParseException("Multipart entity has not required 'boundary' paramter."); - } - - MIME_b_MultipartRelated retVal = new MIME_b_MultipartRelated(owner.ContentType); - ParseInternal(owner, mediaType, stream, retVal); - - return retVal; - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartReport.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartReport.cs deleted file mode 100644 index 045e7d2e5..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartReport.cs +++ /dev/null @@ -1,93 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using IO; - - #endregion - - /// - /// This class represents MIME multipart/report body. Defined in RFC 3462. - /// - /// - /// The Multipart/Report Multipurpose Internet Mail Extensions (MIME) - /// content-type is a general "family" or "container" type for electronic - /// mail reports of any kind. - /// - public class MIME_b_MultipartReport : MIME_b_Multipart - { - #region Constructor - - /// - /// Default constructor. - /// - /// Content type. - /// Is raised when contentType is null reference. - /// Is raised when any of the arguments has invalid value. - public MIME_b_MultipartReport(MIME_h_ContentType contentType) : base(contentType) - { - if ( - !string.Equals(contentType.TypeWithSubype, - "multipart/report", - StringComparison.CurrentCultureIgnoreCase)) - { - throw new ArgumentException( - "Argument 'contentType.TypeWithSubype' value must be 'multipart/report'."); - } - } - - #endregion - - /// - /// Parses body from the specified stream - /// - /// Owner MIME entity. - /// MIME media type. For example: text/plain. - /// Stream from where to read body. - /// Returns parsed body. - /// Is raised when stream, mediaType or stream is null reference. - /// Is raised when any parsing errors. - protected new static MIME_b Parse(MIME_Entity owner, string mediaType, SmartStream stream) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - if (mediaType == null) - { - throw new ArgumentNullException("mediaType"); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - if (owner.ContentType == null || owner.ContentType.Param_Boundary == null) - { - throw new ParseException("Multipart entity has not required 'boundary' paramter."); - } - - MIME_b_MultipartReport retVal = new MIME_b_MultipartReport(owner.ContentType); - ParseInternal(owner, mediaType, stream, retVal); - - return retVal; - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartSigned.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartSigned.cs deleted file mode 100644 index fadf6430f..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_MultipartSigned.cs +++ /dev/null @@ -1,104 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using IO; - - #endregion - - /// - /// This class represents MIME multipart/signed body. Defined in rfc 1847. - /// - public class MIME_b_MultipartSigned : MIME_b_Multipart - { - #region Constructor - - /// - /// Default constructor. - /// - /// Content type. - /// Is raised when contentType is null reference. - /// Is raised when any of the arguments has invalid value. - public MIME_b_MultipartSigned(MIME_h_ContentType contentType) : base(contentType) - { - if ( - !string.Equals(contentType.TypeWithSubype, - "multipart/signed", - StringComparison.CurrentCultureIgnoreCase)) - { - throw new ArgumentException( - "Argument 'contentType.TypeWithSubype' value must be 'multipart/signed'."); - } - } - - #endregion - - /// - /// Parses body from the specified stream - /// - /// Owner MIME entity. - /// MIME media type. For example: text/plain. - /// Stream from where to read body. - /// Returns parsed body. - /// Is raised when stream, mediaType or stream is null reference. - /// Is raised when any parsing errors. - protected new static MIME_b Parse(MIME_Entity owner, string mediaType, SmartStream stream) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - if (mediaType == null) - { - throw new ArgumentNullException("mediaType"); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - if (owner.ContentType == null || owner.ContentType.Param_Boundary == null) - { - throw new ParseException("Multipart entity has not required 'boundary' paramter."); - } - - MIME_b_MultipartSigned retVal = new MIME_b_MultipartSigned(owner.ContentType); - ParseInternal(owner, mediaType, stream, retVal); - - return retVal; - } - - /* - /// - /// Signs entiy data. - /// - /// - public void Sign(X509Certificate2 cert) - { - } - - /// - /// Verifies that body content has not changed after it was siiged. - /// - public void Verify() - { - }*/ - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Provider.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Provider.cs deleted file mode 100644 index fd4eb01b2..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Provider.cs +++ /dev/null @@ -1,153 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using System.Collections.Generic; - using System.Reflection; - using IO; - - #endregion - - /// - /// This class represent MIME entity body provider. - /// - public class MIME_b_Provider - { - #region Members - - private readonly Dictionary m_pBodyTypes; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public MIME_b_Provider() - { - m_pBodyTypes = new Dictionary(StringComparer.CurrentCultureIgnoreCase); - m_pBodyTypes.Add("message/rfc822", typeof (MIME_b_MessageRfc822)); - m_pBodyTypes.Add("multipart/alternative", typeof (MIME_b_MultipartAlternative)); - m_pBodyTypes.Add("multipart/digest", typeof (MIME_b_MultipartDigest)); - m_pBodyTypes.Add("multipart/encrypted", typeof (MIME_b_MultipartEncrypted)); - m_pBodyTypes.Add("multipart/form-data", typeof (MIME_b_MultipartFormData)); - m_pBodyTypes.Add("multipart/mixed", typeof (MIME_b_MultipartMixed)); - m_pBodyTypes.Add("multipart/parallel", typeof (MIME_b_MultipartParallel)); - m_pBodyTypes.Add("multipart/related", typeof (MIME_b_MultipartRelated)); - m_pBodyTypes.Add("multipart/report", typeof (MIME_b_MultipartReport)); - m_pBodyTypes.Add("multipart/signed", typeof (MIME_b_MultipartSigned)); - } - - #endregion - - #region Methods - - /// - /// Parses MIME entity body from specified stream. - /// - /// Owner MIME entity. - /// Stream from where to parse entity body. - /// Default content type. - /// Returns parsed body. - /// Is raised when owner, strean or defaultContentType is null reference. - /// Is raised when header field parsing errors. - public MIME_b Parse(MIME_Entity owner, SmartStream stream, MIME_h_ContentType defaultContentType) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - if (defaultContentType == null) - { - throw new ArgumentNullException("defaultContentType"); - } - - string mediaType = defaultContentType.TypeWithSubype; - string mediaTypeWithParams = defaultContentType.ToString().Split(':')[1].TrimStart(' '); - if (owner.ContentType != null) - { - mediaType = owner.ContentType.TypeWithSubype; - mediaTypeWithParams = owner.ContentType.ToString().Split(':')[1].TrimStart(' '); - } - - Type bodyType = null; - - // We have exact body provider for specified mediaType. - if (m_pBodyTypes.ContainsKey(mediaType)) - { - bodyType = m_pBodyTypes[mediaType]; - } - // Use default mediaType. - else - { - // Registered list of mediaTypes are available: http://www.iana.org/assignments/media-types/. - - string mediaRootType = mediaType.Split('/')[0].ToLowerInvariant(); - if (mediaRootType == "application") - { - bodyType = typeof (MIME_b_Application); - } - else if (mediaRootType == "audio") - { - bodyType = typeof (MIME_b_Audio); - } - else if (mediaRootType == "image") - { - bodyType = typeof (MIME_b_Image); - } - else if (mediaRootType == "message") - { - bodyType = typeof (MIME_b_Message); - } - else if (mediaRootType == "multipart") - { - bodyType = typeof (MIME_b_Multipart); - } - else if (mediaRootType == "text") - { - bodyType = typeof (MIME_b_Text); - } - else if (mediaRootType == "video") - { - bodyType = typeof (MIME_b_Video); - } - else - { - throw new ParseException("Invalid media-type '" + mediaType + "'."); - } - } - - return - (MIME_b) - bodyType.GetMethod("Parse", - BindingFlags.Static | BindingFlags.NonPublic | - BindingFlags.FlattenHierarchy).Invoke(null, - new object[] { owner, mediaTypeWithParams, stream }); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_SinglepartBase.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_SinglepartBase.cs deleted file mode 100644 index 16254b3c9..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_SinglepartBase.cs +++ /dev/null @@ -1,348 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using System.IO; - using System.Text; - using IO; - - #endregion - - /// - /// This class is base class for singlepart media bodies like: text,video,audio,image. - /// - public abstract class MIME_b_SinglepartBase : MIME_b, IDisposable - { - #region Members - - private readonly Stream m_pEncodedDataStream; - private bool m_IsModified; - private string m_MediaType = ""; - - #endregion - - #region Properties - - /// - /// Gets if body has modified. - /// - public override bool IsModified - { - get { return m_IsModified; } - } - - /// - /// Gets encoded body data size in bytes. - /// - public int EncodedDataSize - { - get { return (int) m_pEncodedDataStream.Length; } - } - - /// - /// Gets body encoded data. - /// - /// NOTE: Use this property with care, because body data may be very big and you may run out of memory. - /// For bigger data use method instead. - public byte[] EncodedData - { - get - { - MemoryStream ms = new MemoryStream(); - Net_Utils.StreamCopy(GetEncodedDataStream(), ms, Workaround.Definitions.MaxStreamLineLength); - - return ms.ToArray(); - } - } - - /// - /// Gets body decoded data. - /// - /// NOTE: Use this property with care, because body data may be very big and you may run out of memory. - /// For bigger data use method instead. - /// Is raised when body contains not supported Content-Transfer-Encoding. - /// fucking idiot! - /// - private byte[] _dataCached = null; - - public byte[] Data - { - get - { - if (_dataCached == null) - { - using (var ms = new MemoryStream()) - { - Net_Utils.StreamCopy(GetDataStream(), ms, Workaround.Definitions.MaxStreamLineLength); - _dataCached = ms.ToArray(); - } - } - return _dataCached; - } - } - - /// - /// Gets encoded data stream. - /// - protected Stream EncodedStream - { - get { return m_pEncodedDataStream; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Is raised when mediaType is null reference. - public MIME_b_SinglepartBase(string mediaType) : base(new MIME_h_ContentType(mediaType)) - { - if (mediaType == null) - { - throw new ArgumentNullException("mediaType"); - } - - m_MediaType = mediaType; - - /*m_pEncodedDataStream = new FileStream(Path.GetTempFileName(), - FileMode.Create, - FileAccess.ReadWrite, - FileShare.None, - Workaround.Definitions.MaxStreamLineLength, - FileOptions.DeleteOnClose);*/ - m_pEncodedDataStream = new MemoryStream(); - } - - #endregion - - #region Methods - - /// - /// Gets body encoded data stream. - /// - /// Returns body encoded data stream. - public Stream GetEncodedDataStream() - { - m_pEncodedDataStream.Position = 0; - - return m_pEncodedDataStream; - } - - /// - /// Sets body encoded data from specified stream. - /// - /// Content-Transfer-Encoding in what encoding stream data is. - /// Stream data to add. - /// Is raised when contentTransferEncoding or stream is null reference. - /// Is raised when any of the argumennts has invalid value. - public void SetEncodedData(string contentTransferEncoding, Stream stream) - { - if (contentTransferEncoding == null) - { - throw new ArgumentNullException("contentTransferEncoding"); - } - if (contentTransferEncoding == string.Empty) - { - throw new ArgumentException("Argument 'contentTransferEncoding' value must be specified."); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - m_pEncodedDataStream.SetLength(0); - Net_Utils.StreamCopy(stream, m_pEncodedDataStream, Workaround.Definitions.MaxStreamLineLength); - - // If body won't end with CRLF, add CRLF. - if (m_pEncodedDataStream.Length >= 2) - { - m_pEncodedDataStream.Position = m_pEncodedDataStream.Length - 2; - } - if (m_pEncodedDataStream.ReadByte() != '\r' && m_pEncodedDataStream.ReadByte() != '\n') - { - m_pEncodedDataStream.Write(new[] {(byte) '\r', (byte) '\n'}, 0, 2); - } - Entity.ContentTransferEncoding = contentTransferEncoding; - - m_IsModified = true; - } - - /// - /// Gets body decoded data stream. - /// - /// Returns body decoded data stream. - /// Is raised when body contains not supported Content-Transfer-Encoding. - /// The returned stream should be clossed/disposed as soon as it's not needed any more. - public Stream GetDataStream() - { - /* RFC 2045 6.1. - This is the default value -- that is, "Content-Transfer-Encoding: 7BIT" is assumed if the - Content-Transfer-Encoding header field is not present. - */ - string transferEncoding = MIME_TransferEncodings.SevenBit; - if (Entity.ContentTransferEncoding != null) - { - transferEncoding = Entity.ContentTransferEncoding.ToLowerInvariant(); - } - - m_pEncodedDataStream.Position = 0; - m_pEncodedDataStream.Seek(0, SeekOrigin.Begin); - if (transferEncoding == MIME_TransferEncodings.QuotedPrintable) - { - return new QuotedPrintableStream(new SmartStream(m_pEncodedDataStream, false), FileAccess.Read); - } - else if (transferEncoding == MIME_TransferEncodings.Base64) - { - return new Base64Stream(m_pEncodedDataStream, false, true, FileAccess.Read); - } - else if (transferEncoding == MIME_TransferEncodings.Binary) - { - return new ReadWriteControlledStream(m_pEncodedDataStream, FileAccess.Read); - } - else if (transferEncoding == MIME_TransferEncodings.EightBit) - { - return new ReadWriteControlledStream(m_pEncodedDataStream, FileAccess.Read); - } - else if (transferEncoding == MIME_TransferEncodings.SevenBit) - { - return new ReadWriteControlledStream(m_pEncodedDataStream, FileAccess.Read); - } - else - { - throw new NotSupportedException("Not supported Content-Transfer-Encoding '" + - Entity.ContentTransferEncoding + "'."); - } - } - - /// - /// Sets body data from the specified stream. - /// - /// Source stream. - /// Specifies content-transfer-encoding to use to encode data. - /// Is raised when stream or transferEncoding is null reference. - public void SetData(Stream stream, string transferEncoding) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - if (transferEncoding == null) - { - throw new ArgumentNullException("transferEncoding"); - } - - if (transferEncoding == MIME_TransferEncodings.QuotedPrintable) - { - using (MemoryStream mem_stream = new MemoryStream()) - { - QuotedPrintableStream encoder = new QuotedPrintableStream(new SmartStream(mem_stream, false), - FileAccess.ReadWrite); - Net_Utils.StreamCopy(stream, encoder, Workaround.Definitions.MaxStreamLineLength); - encoder.Flush(); - mem_stream.Position = 0; - SetEncodedData(transferEncoding, mem_stream); - } - } - else if (transferEncoding == MIME_TransferEncodings.Base64) - { - using (MemoryStream mem_stream = new MemoryStream()) - { - Base64Stream encoder = new Base64Stream(mem_stream, false, true, FileAccess.ReadWrite); - Net_Utils.StreamCopy(stream, encoder, Workaround.Definitions.MaxStreamLineLength); - encoder.Finish(); - mem_stream.Position = 0; - SetEncodedData(transferEncoding, mem_stream); - } - } - else if (transferEncoding == MIME_TransferEncodings.Binary) - { - SetEncodedData(transferEncoding, stream); - } - else if (transferEncoding == MIME_TransferEncodings.EightBit) - { - SetEncodedData(transferEncoding, stream); - } - else if (transferEncoding == MIME_TransferEncodings.SevenBit) - { - SetEncodedData(transferEncoding, stream); - } - else - { - throw new NotSupportedException("Not supported Content-Transfer-Encoding '" + transferEncoding + - "'."); - } - } - - /// - /// Sets body data from the specified file. - /// - /// File name with optional path. - /// Specifies content-transfer-encoding to use to encode data. - /// Is raised when file is null reference. - public void SetBodyDataFromFile(string file, string transferEncoding) - { - if (file == null) - { - throw new ArgumentNullException("file"); - } - - using (FileStream fs = File.OpenRead(file)) - { - SetData(fs, transferEncoding); - } - } - - #endregion - - #region Overrides - - /// - /// Stores MIME entity body to the specified stream. - /// - /// Stream where to store body data. - /// Header 8-bit words ecnoder. Value null means that words are not encoded. - /// Charset to use to encode 8-bit header parameters. Value null means parameters not encoded. - /// Is raised when stream is null reference. - protected internal override void ToStream(Stream stream, - MIME_Encoding_EncodedWord headerWordEncoder, - Encoding headerParmetersCharset) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - Net_Utils.StreamCopy(GetEncodedDataStream(), stream, Workaround.Definitions.MaxStreamLineLength); - } - - #endregion - - public void Dispose() - { - if (m_pEncodedDataStream != null) - { - m_pEncodedDataStream.Dispose(); - } - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Text.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Text.cs deleted file mode 100644 index e68ac989d..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Text.cs +++ /dev/null @@ -1,148 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using System.IO; - using System.Text; - using IO; - - #endregion - - /// - /// This class represents MIME text/xxx bodies. Defined in RFC 2045. - /// - /// - /// The "text" media type is intended for sending material which is principally textual in form. - /// - public class MIME_b_Text : MIME_b_SinglepartBase - { - #region Properties - - /// - /// Gets body decoded text. - /// - /// Is raised when not supported content-type charset or not supported content-transfer-encoding value. - /// Is raised when body contains not supported Content-Transfer-Encoding. - public string Text - { - get { return GetCharset().GetString(Data); } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// MIME media type. - /// Is raised when mediaSubType is null reference. - /// Is raised when any of the arguments has invalid value. - public MIME_b_Text(string mediaType) : base(mediaType) {} - - #endregion - - #region Methods - - /// - /// Sets text. - /// - /// Content transfer encoding. - /// Charset to use to encode text. If not sure, utf-8 is recommended. - /// Text. - /// Is raised when transferEncoding, charset or text is null reference. - /// Is raised when body contains not supported Content-Transfer-Encoding. - public void SetText(string transferEncoding, Encoding charset, string text) - { - if (transferEncoding == null) - { - throw new ArgumentNullException("transferEncoding"); - } - if (charset == null) - { - throw new ArgumentNullException("charset"); - } - if (text == null) - { - throw new ArgumentNullException("text"); - } - - SetEncodedData(transferEncoding, new MemoryStream(charset.GetBytes(text))); - ContentType.Param_Charset = charset.WebName; - } - - #endregion - - #region Utility methods - - /// - /// Gets charset from Content-Type. If char set isn't specified, "ascii" is defined as default and it will be returned. - /// - /// Returns content charset. - /// Is raised when Content-Type has not supported charset parameter value. - private Encoding GetCharset() - { - // RFC 2046 4.1.2. The default character set, US-ASCII. - - if (string.IsNullOrEmpty(ContentType.Param_Charset)) - { - return Encoding.ASCII; - } - else - { - return EncodingTools.GetEncodingByCodepageName(ContentType.Param_Charset) ?? Encoding.ASCII; - } - } - - #endregion - - /// - /// Parses body from the specified stream - /// - /// Owner MIME entity. - /// MIME media type. For example: text/plain. - /// Stream from where to read body. - /// Returns parsed body. - /// Is raised when stream, mediaType or stream is null reference. - /// Is raised when any parsing errors. - protected new static MIME_b Parse(MIME_Entity owner, string mediaType, SmartStream stream) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - if (mediaType == null) - { - throw new ArgumentNullException("mediaType"); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - MIME_b_Text retVal = new MIME_b_Text(mediaType); - - Net_Utils.StreamCopy(stream, retVal.EncodedStream, Workaround.Definitions.MaxStreamLineLength); - - return retVal; - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Video.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Video.cs deleted file mode 100644 index 327f8e614..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_b_Video.cs +++ /dev/null @@ -1,78 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using IO; - - #endregion - - /// - /// This class represents MIME video/xxx bodies. Defined in RFC 2046 4.4. - /// - /// - /// A media type of "video" indicates that the body contains a time- - /// varying-picture image, possibly with color and coordinated sound. - /// - public class MIME_b_Video : MIME_b_SinglepartBase - { - #region Constructor - - /// - /// Default constructor. - /// - /// MIME media type. - /// Is raised when mediaType is null reference. - public MIME_b_Video(string mediaType) : base(mediaType) {} - - #endregion - - /// - /// Parses body from the specified stream - /// - /// Owner MIME entity. - /// MIME media type. For example: text/plain. - /// Stream from where to read body. - /// Returns parsed body. - /// Is raised when stream, mediaType or strean is null reference. - /// Is raised when any parsing errors. - protected new static MIME_b Parse(MIME_Entity owner, string mediaType, SmartStream stream) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - if (mediaType == null) - { - throw new ArgumentNullException("mediaType"); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - MIME_b_Video retVal = new MIME_b_Video(mediaType); - - Net_Utils.StreamCopy(stream, retVal.EncodedStream, Workaround.Definitions.MaxStreamLineLength); - - return retVal; - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h.cs deleted file mode 100644 index 8318e850d..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h.cs +++ /dev/null @@ -1,70 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// This is base class for MIME header fields. Defined in RFC 2045 3. - /// - public abstract class MIME_h - { - #region Properties - - /// - /// Gets if this header field is modified since it has loaded. - /// - /// All new added header fields has IsModified = true. - /// Is riased when this class is disposed and this property is accessed. - public abstract bool IsModified { get; } - - /// - /// Gets header field name. For example "Content-Type". - /// - public abstract string Name { get; } - - #endregion - - #region Methods - - /// - /// Returns header field as string. - /// - /// Returns header field as string. - public override string ToString() - { - return ToString(null, null); - } - - /// - /// Returns header field as string. - /// - /// 8-bit words ecnoder. Value null means that words are not encoded. - /// Charset to use to encode 8-bit characters. Value null means parameters not encoded. - /// If encoding needed, UTF-8 is strongly reccomended if not sure. - /// Returns header field as string. - public abstract string ToString(MIME_Encoding_EncodedWord wordEncoder, Encoding parmetersCharset); - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_Collection.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_Collection.cs deleted file mode 100644 index 4f8eecb99..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_Collection.cs +++ /dev/null @@ -1,614 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System.Linq; -using System.Net.Mail; - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using System.Collections; - using System.Collections.Generic; - using System.IO; - using System.Text; - using IO; - - #endregion - - /// - /// This class represents MIME header fields collection. Defined in RFC 2045. - /// - public class MIME_h_Collection : IEnumerable - { - #region Members - - private readonly List m_pFields; - private readonly MIME_h_Provider m_pProvider; - private bool m_IsModified; - - #endregion - - #region Properties - - /// - /// Gets if header has modified since it was loaded. - /// - public bool IsModified - { - get - { - if (m_IsModified) - { - return true; - } - - foreach (MIME_h field in m_pFields) - { - if (field.IsModified) - { - return true; - } - } - - return false; - } - } - - /// - /// Gets number of items in the collection. - /// - public int Count - { - get { return m_pFields.Count; } - } - - /// - /// Gets the element at the specified index. - /// - /// The zero-based index of the element to get. - /// Returns the element at the specified index. - /// Is raised when index is out of range. - public MIME_h this[int index] - { - get - { - if (index < 0 || index >= m_pFields.Count) - { - throw new ArgumentOutOfRangeException("index"); - } - - return m_pFields[index]; - } - } - - /// - /// Gets header fields with the specified name. - /// - /// Header field name. - /// Returns header fields with the specified name. - /// Is raised when name is null reference. - public MIME_h[] this[string name] - { - get - { - if (name == null) - { - throw new ArgumentNullException("name"); - } - - List retVal = new List(); - foreach (MIME_h field in m_pFields.ToArray()) - { - if (string.Compare(name, field.Name, true) == 0) - { - retVal.Add(field); - } - } - - return retVal.ToArray(); - } - } - - /// - /// Gets header fields provider. - /// - public MIME_h_Provider FieldsProvider - { - get { return m_pProvider; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Header fields provider. - /// Is raised when provider is null reference. - public MIME_h_Collection(MIME_h_Provider provider) - { - if (provider == null) - { - throw new ArgumentNullException("provider"); - } - - m_pProvider = provider; - - m_pFields = new List(); - } - - #endregion - - #region Methods - - /// - /// Inserts a new header field into the collection at the specified location. - /// - /// The location in the collection where you want to add the item. - /// Header field to insert. - /// Is raised when index is out of range. - /// Is raised when field is null reference. - public void Insert(int index, MIME_h field) - { - if (index < 0 || index > m_pFields.Count) - { - throw new ArgumentOutOfRangeException("index"); - } - if (field == null) - { - throw new ArgumentNullException("field"); - } - - m_pFields.Insert(index, field); - m_IsModified = true; - } - - /// - /// Parses and adds specified header field to the end of the collection. - /// - /// Header field string (Name: value). - /// Retunrs added header field. - /// Is raised when field is null reference. - public MIME_h Add(string field) - { - if (field == null) - { - throw new ArgumentNullException("field"); - } - - MIME_h h = m_pProvider.Parse(field); - m_pFields.Add(h); - m_IsModified = true; - - return h; - } - - /// - /// Adds specified header field to the end of the collection. - /// - /// Header field to add. - /// Is raised when field is null reference value. - public void Add(MIME_h field) - { - if (field == null) - { - throw new ArgumentNullException("field"); - } - - m_pFields.Add(field); - m_IsModified = true; - } - - /// - /// Removes specified header field from the collection. - /// - /// Header field to remove. - /// Is raised when field is null reference value. - public void Remove(MIME_h field) - { - if (field == null) - { - throw new ArgumentNullException("field"); - } - - m_pFields.Remove(field); - m_IsModified = true; - } - - /// - /// Removes all header fields with the specified name. - /// - /// Header field name. - /// Is raised when name is null reference. - /// Is raised when any of the arguments has invalid value. - public void RemoveAll(string name) - { - if (name == null) - { - throw new ArgumentNullException("name"); - } - if (name == string.Empty) - { - throw new ArgumentException("Argument 'name' value must be specified.", "name"); - } - - foreach (MIME_h field in m_pFields.ToArray()) - { - if (string.Compare(name, field.Name, true) == 0) - { - m_pFields.Remove(field); - } - } - m_IsModified = true; - } - - /// - /// Removes all items from the collection. - /// - public void Clear() - { - m_pFields.Clear(); - m_IsModified = true; - } - - /// - /// Gets if collection has item with the specified name. - /// - /// Header field name. - /// Returns true if specified item exists in the collection, otherwise false. - /// Is raised when name is null reference. - /// Is raised when any of the arguments has invalid value. - public bool Contains(string name) - { - if (name == null) - { - throw new ArgumentNullException("name"); - } - if (name == string.Empty) - { - throw new ArgumentException("Argument 'name' value must be specified.", "name"); - } - - foreach (MIME_h field in m_pFields.ToArray()) - { - if (string.Compare(name, field.Name, true) == 0) - { - return true; - } - } - - return false; - } - - /// - /// Gets if collection contains the specified item. - /// - /// Header field. - /// Returns true if specified item exists in the collection, otherwise false. - /// Is raised when field is null reference. - public bool Contains(MIME_h field) - { - if (field == null) - { - throw new ArgumentNullException("field"); - } - - return m_pFields.Contains(field); - } - - /// - /// Gets first header field with the specified name. returns null if specified header field doesn't exist. - /// - /// Header field name. - /// Returns first header field with the specified name. returns null if specified header field doesn't exist. - /// Is raised when name is null reference. - public MIME_h GetFirst(string name) - { - if (name == null) - { - throw new ArgumentNullException("name"); - } - - foreach (MIME_h field in m_pFields.ToArray()) - { - if (string.Compare(name, field.Name, true) == 0) - { - return field; - } - } - - return null; - } - - /// - /// Replaces first header field with specified name with specified value. - /// - /// Hedaer field. - /// Is raised when field is null reference. - public void ReplaceFirst(MIME_h field) - { - if (field == null) - { - throw new ArgumentNullException("field"); - } - - for (int i = 0; i < m_pFields.Count; i++) - { - if (string.Equals(field.Name, m_pFields[i].Name, StringComparison.CurrentCultureIgnoreCase)) - { - m_pFields.RemoveAt(i); - m_pFields.Insert(i, field); - - return; - } - } - } - - /// - /// Copies header fields to new array. - /// - /// Returns header fields array. - public MIME_h[] ToArray() - { - return m_pFields.ToArray(); - } - - /// - /// Stores header to the specified file. - /// - /// File name with optional path. - /// 8-bit words ecnoder. Value null means that words are not encoded. - /// Charset to use to encode 8-bit header parameters. Value null means parameters not encoded. - /// Is raised when fileName is null reference. - public void ToFile(string fileName, MIME_Encoding_EncodedWord wordEncoder, Encoding parmetersCharset) - { - if (fileName == null) - { - throw new ArgumentNullException("fileName"); - } - - using (FileStream fs = File.Create(fileName)) - { - ToStream(fs, wordEncoder, parmetersCharset); - } - } - - /// - /// Stores header to the specified stream. - /// - /// Stream where to store header. - /// 8-bit words ecnoder. Value null means that words are not encoded. - /// Charset to use to encode 8-bit header parameters. Value null means parameters not encoded. - /// Is raised when stream is null reference. - public void ToStream(Stream stream, MIME_Encoding_EncodedWord wordEncoder, Encoding parmetersCharset) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - byte[] header = Encoding.UTF8.GetBytes(ToString(wordEncoder, parmetersCharset)); - stream.Write(header, 0, header.Length); - } - - /// - /// Returns MIME header as string. - /// - /// Returns MIME header as string. - public override string ToString() - { - return ToString(null, null); - } - - /// - /// Returns MIME header as string. - /// - /// 8-bit words ecnoder. Value null means that words are not encoded. - /// Charset to use to encode 8-bit header parameters. Value null means parameters not encoded. - /// Returns MIME header as string. - public string ToString(MIME_Encoding_EncodedWord wordEncoder, Encoding parmetersCharset) - { - StringBuilder retVal = new StringBuilder(); - foreach (MIME_h field in m_pFields) - { - retVal.Append(field.ToString(wordEncoder, parmetersCharset)); - } - - return retVal.ToString(); - } - - /// - /// Parses MIME header from the specified value. - /// - /// MIME header string. - /// Is raised when value is null reference. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new SmartStream(new MemoryStream(Encoding.UTF8.GetBytes(value)), true)); - } - - /// - /// Parses MIME header from the specified stream. - /// - /// MIME header stream. - /// Is raised when stream is null. - public void Parse(SmartStream stream) - { - //TODO: ���� ��������� �������! �������� ����! �� ��� ���� �������� � utf8 ����� - - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - var headers = new List>(); - var currentMemStream = new MemoryStream(); - SmartStream.ReadLineAsyncOP readLineOP = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength], - SizeExceededAction. - ThrowException); - while (true) - { - stream.ReadLine(readLineOP, false); - if (readLineOP.Error != null) - { - throw readLineOP.Error; - } - // We reached end of stream. - if (readLineOP.BytesInBuffer == 0) - { - if (currentMemStream.Length > 0) - { - AddToBinaryDict(headers, currentMemStream); - } - m_IsModified = false; - - break; - } - // We got blank header terminator line. - if (readLineOP.LineBytesInBuffer == 0) - { - if (currentMemStream.Length > 0) - { - AddToBinaryDict(headers, currentMemStream); - } - m_IsModified = false; - - break; - } - - string line = Encoding.UTF8.GetString(readLineOP.Buffer, 0, readLineOP.BytesInBuffer); - var realBuffer = new List(); - - if ((line.StartsWith("From: \"") || line.StartsWith("To: \"")) && !line.EndsWith(">\r\n")) - { - var tmpArr = new byte[readLineOP.BytesInBuffer]; - Array.Copy(readLineOP.Buffer, 0, tmpArr, 0, readLineOP.BytesInBuffer); - realBuffer.AddRange(tmpArr); - do - { - stream.ReadLine(readLineOP, false); - - if (readLineOP.LineBytesInBuffer == 0) - break; - - line = Encoding.UTF8.GetString(readLineOP.Buffer, 0, readLineOP.BytesInBuffer); - - tmpArr = new byte[readLineOP.BytesInBuffer]; - Array.Copy(readLineOP.Buffer, 0, tmpArr, 0, readLineOP.BytesInBuffer); - realBuffer.AddRange(tmpArr); - - } while (!line.EndsWith(">\r\n")); - - if (realBuffer.Count > 0) - { - line = Encoding.UTF8.GetString(realBuffer.ToArray()); - } - } - - - // New header field starts. - if (currentMemStream.Length == 0) - { - currentMemStream.Write(readLineOP.Buffer, 0, readLineOP.BytesInBuffer); - } - // Header field continues. - else if (char.IsWhiteSpace(line[0])) - { - currentMemStream.Write(readLineOP.Buffer, 0, readLineOP.BytesInBuffer); - } - // Current header field closed, new starts. - else - { - AddToBinaryDict(headers, currentMemStream); - - currentMemStream = new MemoryStream(); - if (realBuffer.Count > 0) - currentMemStream.Write(realBuffer.ToArray(), 0, realBuffer.Count); - else - currentMemStream.Write(readLineOP.Buffer, 0, readLineOP.BytesInBuffer); - } - } - //Process dictionary - //Find content type - var contentTypeHeader = - headers - .Where(x => x.Value != null) - .Where(x => "content-type".Equals(x.Key, StringComparison.OrdinalIgnoreCase)) - .Select(x => Encoding.UTF8.GetString(x.Value)) - .SingleOrDefault(); - var encoding = Encoding.UTF8; - if (contentTypeHeader != null) - { - var mime = MIME_h_ContentType.Parse(contentTypeHeader); - if (!string.IsNullOrEmpty(mime.Param_Charset)) - { - encoding = EncodingTools.GetEncodingByCodepageName(mime.Param_Charset) ?? Encoding.UTF8; - } - else - { - //Join headers - var subjectRaw = - headers - .Where(x => x.Value != null) - .Where(x => "subject".Equals(x.Key, StringComparison.OrdinalIgnoreCase)) - .Select(x => x.Value) - .SingleOrDefault(); - //Try to detect hueristic - encoding = subjectRaw != null ? EncodingTools.DetectInputCodepage(subjectRaw) : Encoding.UTF8; - } - } - - foreach (var keyValuePair in headers) - { - Add(encoding.GetString(keyValuePair.Value)); - } - } - - private void AddToBinaryDict(List> dictionary, MemoryStream ms) - { - var buffer = new byte[ms.Length]; - ms.Position = 0; - ms.Read(buffer, 0, buffer.Length); - var headerString = Encoding.UTF8.GetString(buffer, 0, buffer.Length); - //Parse - var headerNames = headerString.Split(':'); - if (headerNames.Length > 0) - { - dictionary.Add(new KeyValuePair(headerNames[0].Trim().ToLowerInvariant(), buffer)); - } - } - - /// - /// Gets enumerator. - /// - /// - public IEnumerator GetEnumerator() - { - return m_pFields.GetEnumerator(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_ContentDisposition.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_ContentDisposition.cs deleted file mode 100644 index 4c27cdbf3..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_ContentDisposition.cs +++ /dev/null @@ -1,341 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Represents "Content-Disposition:" header. Defined in RFC 2183. - /// - /// - /// - /// RFC 2183. - /// In the extended BNF notation of [RFC 822], the Content-Disposition - /// header field is defined as follows: - /// - /// disposition := "Content-Disposition" ":" disposition-type *(";" disposition-parm) - /// - /// disposition-type := "inline" / "attachment" / extension-token - /// ; values are not case-sensitive - /// - /// disposition-parm := filename-parm - /// / creation-date-parm - /// / modification-date-parm - /// / read-date-parm - /// / size-parm - /// / parameter - /// - /// filename-parm := "filename" "=" value - /// - /// creation-date-parm := "creation-date" "=" quoted-date-time - /// - /// modification-date-parm := "modification-date" "=" quoted-date-time - /// - /// read-date-parm := "read-date" "=" quoted-date-time - /// - /// size-parm := "size" "=" 1*DIGIT - /// - /// quoted-date-time := quoted-string - /// ; contents MUST be an RFC 822 `date-time' - /// ; numeric timezones (+HHMM or -HHMM) MUST be used - /// - /// - public class MIME_h_ContentDisposition : MIME_h - { - #region Members - - private readonly bool m_IsModified; - private readonly MIME_h_ParameterCollection m_pParameters; - private string m_DispositionType = ""; - private string m_ParseValue; - - #endregion - - #region Properties - - /// - /// Gets if this header field is modified since it has loaded. - /// - /// All new added header fields has IsModified = true. - /// Is riased when this class is disposed and this property is accessed. - public override bool IsModified - { - get { return m_IsModified || m_pParameters.IsModified; } - } - - /// - /// Returns always "Content-Disposition". - /// - public override string Name - { - get { return "Content-Disposition"; } - } - - /// - /// Gets the disposition-type. Known values are in MIME_DispositionTypes. - /// - public string DispositionType - { - get { return m_DispositionType; } - } - - /// - /// Gets Content-Type parameters collection. - /// - public MIME_h_ParameterCollection Parameters - { - get { return m_pParameters; } - } - - /// - /// Gets or sets the suggested file name. Value DateTime.MinValue means not specified. Defined in RFC 2183 2.3. - /// - public string Param_FileName - { - get { return Parameters["filename"]; } - - set { m_pParameters["filename"] = value; } - } - - /// - /// Gets or sets the creation date for a file. Value DateTime.MinValue means not specified. Defined in RFC 2183 2.4. - /// - public DateTime Param_CreationDate - { - get - { - string value = Parameters["creation-date"]; - if (value == null) - { - return DateTime.MinValue; - } - else - { - return MIME_Utils.ParseRfc2822DateTime(value); - } - } - - set - { - if (value == DateTime.MinValue) - { - Parameters.Remove("creation-date"); - } - else - { - Parameters["creation-date"] = MIME_Utils.DateTimeToRfc2822(value); - } - } - } - - /// - /// Gets or sets the modification date of a file. Value DateTime.MinValue means not specified. Defined in RFC 2183 2.5. - /// - public DateTime Param_ModificationDate - { - get - { - string value = Parameters["modification-date"]; - if (value == null) - { - return DateTime.MinValue; - } - else - { - return MIME_Utils.ParseRfc2822DateTime(value); - } - } - - set - { - if (value == DateTime.MinValue) - { - Parameters.Remove("modification-date"); - } - else - { - Parameters["modification-date"] = MIME_Utils.DateTimeToRfc2822(value); - } - } - } - - /// - /// Gets or sets the last read date of a file. Value DateTime.MinValue means not specified. Defined in RFC 2183 2.6. - /// - public DateTime Param_ReadDate - { - get - { - string value = Parameters["read-date"]; - if (value == null) - { - return DateTime.MinValue; - } - else - { - return MIME_Utils.ParseRfc2822DateTime(value); - } - } - - set - { - if (value == DateTime.MinValue) - { - Parameters.Remove("read-date"); - } - else - { - Parameters["read-date"] = MIME_Utils.DateTimeToRfc2822(value); - } - } - } - - /// - /// Gets or sets the size of a file. Value -1 means not specified. Defined in RFC 2183 2.7. - /// - public long Param_Size - { - get - { - string value = Parameters["size"]; - if (value == null) - { - return -1; - } - else - { - return Convert.ToInt64(value); - } - } - - set - { - if (value < 0) - { - Parameters.Remove("size"); - } - else - { - Parameters["size"] = value.ToString(); - } - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// The disposition-type. Known values are in MIME_DispositionTypes. - /// Is raised when dispositionType is null reference. - /// Is raised when any of the arguments has invalid value. - public MIME_h_ContentDisposition(string dispositionType) - { - if (dispositionType == null) - { - throw new ArgumentNullException("dispositionType"); - } - if (dispositionType == string.Empty) - { - throw new ArgumentException("Argument 'dispositionType' value must be specified."); - } - - m_DispositionType = dispositionType; - - m_pParameters = new MIME_h_ParameterCollection(this); - m_IsModified = true; - } - - /// - /// Internal parser constructor. - /// - private MIME_h_ContentDisposition() - { - m_pParameters = new MIME_h_ParameterCollection(this); - } - - #endregion - - #region Methods - - /// - /// Parses header field from the specified value. - /// - /// Header field value. Header field name must be included. For example: 'Content-Type: text/plain'. - /// Returns parsed header field. - /// Is raised when value is null reference. - /// Is raised when header field parsing errors. - public static MIME_h_ContentDisposition Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - MIME_h_ContentDisposition retVal = new MIME_h_ContentDisposition(); - - string[] name_value = value.Split(new[] {':'}, 2); - if (name_value.Length != 2) - { - throw new ParseException("Invalid Content-Type: header field value '" + value + "'."); - } - - MIME_Reader r = new MIME_Reader(name_value[1]); - string type = r.Token(); - if (type == null) - { - throw new ParseException("Invalid Content-Disposition: header field value '" + value + "'."); - } - retVal.m_DispositionType = type; - - retVal.m_pParameters.Parse(r); - - retVal.m_ParseValue = value; - - return retVal; - } - - /// - /// Returns header field as string. - /// - /// 8-bit words ecnoder. Value null means that words are not encoded. - /// Charset to use to encode 8-bit characters. Value null means parameters not encoded. - /// Returns header field as string. - public override string ToString(MIME_Encoding_EncodedWord wordEncoder, Encoding parmetersCharset) - { - if (IsModified) - { - return "Content-Disposition: " + m_DispositionType + m_pParameters.ToString(parmetersCharset) + - "\r\n"; - } - else - { - return m_ParseValue; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_ContentType.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_ContentType.cs deleted file mode 100644 index 476d826f2..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_ContentType.cs +++ /dev/null @@ -1,327 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Represents "Content-Type:" header. Defined in RFC 2045 5.1. - /// - /// - /// - /// RFC 2045 5.1. - /// In the Augmented BNF notation of RFC 822, a Content-Type header field - /// value is defined as follows: - /// - /// content := "Content-Type" ":" type "/" subtype - /// *(";" parameter) - /// ; Matching of media type and subtype - /// ; is ALWAYS case-insensitive. - /// - /// type := discrete-type / composite-type - /// - /// discrete-type := "text" / "image" / "audio" / "video" / "application" / extension-token - /// - /// composite-type := "message" / "multipart" / extension-token - /// - /// extension-token := ietf-token / x-token - /// - /// ietf-token := (An extension token defined by a standards-track RFC and registered with IANA.) - /// - /// x-token := (The two characters "X-" or "x-" followed, with no intervening white space, by any token) - /// - /// subtype := extension-token / iana-token - /// - /// iana-token := (A publicly-defined extension token. Tokens of this form must be registered with IANA as specified in RFC 2048.) - /// - /// parameter := attribute "=" value - /// - /// attribute := token - /// ; Matching of attributes - /// ; is ALWAYS case-insensitive. - /// - /// value := token / quoted-string - /// - /// token := 1*(any (US-ASCII) CHAR except SPACE, CTLs,or tspecials) - /// - /// tspecials := "(" / ")" / "<" / ">" / "@" / - /// "," / ";" / ":" / "\" / " - /// "/" / "[" / "]" / "?" / "=" - /// ; Must be in quoted-string, - /// ; to use within parameter values - /// - /// - public class MIME_h_ContentType : MIME_h - { - #region Members - - private readonly MIME_h_ParameterCollection m_pParameters; - private bool m_IsModified; - private string m_ParseValue; - private string m_SubType = ""; - private string m_Type = ""; - - #endregion - - #region Properties - - /// - /// Gets if this header field is modified since it has loaded. - /// - /// All new added header fields has IsModified = true. - /// Is riased when this class is disposed and this property is accessed. - public override bool IsModified - { - get { return m_IsModified || m_pParameters.IsModified; } - } - - /// - /// Returns always "Content-Type". - /// - public override string Name - { - get { return "Content-Type"; } - } - - /// - /// Gets media type. For example: application,image,text, ... . - /// - /// The official list of reggistered types are http://www.iana.org/assignments/media-types . - public string Type - { - get { return m_Type; } - } - - /// - /// Gets media sub-type. For example for text/plain, sub-type is 'plain'. - /// - /// The official list of reggistered types are http://www.iana.org/assignments/media-types . - public string SubType - { - get { return m_SubType; } - } - - /// - /// Gets media type with subtype as Type/SubType. Well known value are in MIME_MediaTypes. For example: text/plain. - /// - public string TypeWithSubype - { - get { return m_Type + "/" + m_SubType; } - } - - /// - /// Gets Content-Type parameters collection. - /// - public MIME_h_ParameterCollection Parameters - { - get { return m_pParameters; } - } - - /// - /// Gets or sets Content-Type name parameter value. Value null means not specified. - /// - public string Param_Name - { - get { return m_pParameters["name"]; } - - set { m_pParameters["name"] = value; } - } - - /// - /// Gets or sets Content-Type charset parameter value. Value null means not specified. - /// - public string Param_Charset - { - get { return m_pParameters["charset"]; } - - set { m_pParameters["charset"] = value; } - } - - /// - /// Gets or sets Content-Type boundary parameter value. Value null means not specified. - /// - public string Param_Boundary - { - get { return m_pParameters["boundary"]; } - - set { m_pParameters["boundary"] = value; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Media type with subtype. For example text/plain. - /// Is raised when mediaType is null reference. - public MIME_h_ContentType(string mediaType) - { - if (mediaType == null) - { - throw new ArgumentNullException(mediaType); - } - - /*string[] type_subtype = mediaType.Split(new[] {'/',}, 2); - if (type_subtype.Length == 2) - { - if (type_subtype[0] == "" || !MIME_Reader.IsToken(type_subtype[0])) - { - throw new ArgumentException("Invalid argument 'mediaType' value '" + mediaType + - "', value must be token."); - } - if (type_subtype[1] == "" || !MIME_Reader.IsToken(type_subtype[1])) - { - throw new ArgumentException("Invalid argument 'mediaType' value '" + mediaType + - "', value must be token."); - } - - m_Type = type_subtype[0]; - m_SubType = type_subtype[1]; - } - else - { - throw new ArgumentException("Invalid argument 'mediaType' value '" + mediaType + "'."); - } - - m_pParameters = new MIME_h_ParameterCollection(this); - m_IsModified = true;*/ - - MIME_Reader r = new MIME_Reader(mediaType); - string type = r.Token(); - if (type == null) - { - throw new ParseException("Invalid Content-Type: header field value '" + mediaType + "'."); - } - m_Type = type; - - if (r.Char(false) != '/') - { - throw new ParseException("Invalid Content-Type: header field value '" + mediaType + "'."); - } - - string subtype = r.Token(); - if (subtype == null) - { - //throw new ParseException("Invalid Content-Type: header field value '" + value + "'."); - subtype = ""; - } - m_SubType = subtype; - m_pParameters = new MIME_h_ParameterCollection(this); - m_pParameters.Parse(r); - - m_ParseValue = mediaType; - m_IsModified = true; - - } - - /// - /// Internal parser constructor. - /// - private MIME_h_ContentType() - { - m_pParameters = new MIME_h_ParameterCollection(this); - } - - #endregion - - #region Methods - - /// - /// Parses header field from the specified value. - /// - /// Header field value. Header field name must be included. For example: 'Content-Type: text/plain'. - /// Returns parsed header field. - /// Is raised when value is null reference. - /// Is raised when header field parsing errors. - public static MIME_h_ContentType Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - MIME_h_ContentType retVal = new MIME_h_ContentType(); - - string[] name_value = value.Split(new[] {':'}, 2); - if (name_value.Length != 2) - { - throw new ParseException("Invalid Content-Type: header field value '" + value + "'."); - } - - MIME_Reader r = new MIME_Reader(name_value[1]); - string type = r.Token(); - if (type == null) - { - throw new ParseException("Invalid Content-Type: header field value '" + value + "'."); - } - retVal.m_Type = type; - - if (r.Char(false) != '/') - { - throw new ParseException("Invalid Content-Type: header field value '" + value + "'."); - } - - string subtype = r.Token(); - if (subtype == null) - { - //throw new ParseException("Invalid Content-Type: header field value '" + value + "'."); - subtype = ""; - } - retVal.m_SubType = subtype; - - retVal.m_pParameters.Parse(r); - - retVal.m_ParseValue = value; - retVal.m_IsModified = false; - - return retVal; - } - - /// - /// Returns header field as string. - /// - /// 8-bit words ecnoder. Value null means that words are not encoded. - /// Charset to use to encode 8-bit characters. Value null means parameters not encoded. - /// Returns header field as string. - public override string ToString(MIME_Encoding_EncodedWord wordEncoder, Encoding parmetersCharset) - { - if (!IsModified) - { - return m_ParseValue; - } - else - { - StringBuilder retVal = new StringBuilder(); - retVal.Append("Content-Type: " + m_Type + "/" + m_SubType); - retVal.Append(m_pParameters.ToString(parmetersCharset)); - retVal.Append("\r\n"); - - return retVal.ToString(); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_Parameter.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_Parameter.cs deleted file mode 100644 index 852e1d75e..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_Parameter.cs +++ /dev/null @@ -1,95 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - - #endregion - - /// - /// Represents MIME header field parameter. - /// - public class MIME_h_Parameter - { - #region Members - - private readonly string m_Name = ""; - private bool m_IsModified; - private string m_Value = ""; - - #endregion - - #region Properties - - /// - /// Gets if this header field parameter is modified since it has loaded. - /// - /// All new added header fields parameters has IsModified = true. - /// Is riased when this class is disposed and this property is accessed. - public bool IsModified - { - get { return m_IsModified; } - } - - /// - /// Gets parameter name. - /// - public string Name - { - get { return m_Name; } - } - - /// - /// Gets or sets parameter value. Value null means not specified. - /// - public string Value - { - get { return m_Value; } - - set - { - m_Value = value; - m_IsModified = true; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Parameter name. - /// Parameter value. Value null means not specified. - public MIME_h_Parameter(string name, string value) - { - if (name == null) - { - throw new ArgumentNullException("name"); - } - - m_Name = name; - m_Value = value; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_ParameterCollection.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_ParameterCollection.cs deleted file mode 100644 index 6ff6babb2..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_ParameterCollection.cs +++ /dev/null @@ -1,487 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using System.Collections; - using System.Collections.Generic; - using System.Globalization; - using System.Text; - using System.Web; - - #endregion - - /// - /// Represents MIME header field parameters collection. - /// - public class MIME_h_ParameterCollection : IEnumerable - { - #region Members - - private readonly MIME_h m_pOwner; - private readonly Dictionary m_pParameters; - private bool m_IsModified; - - #endregion - - #region Properties - - /// - /// Gets if this header field parameters are modified since it has loaded. - /// - /// All new added header fields has IsModified = true. - /// Is riased when this class is disposed and this property is accessed. - public bool IsModified - { - get - { - if (m_IsModified) - { - return true; - } - else - { - foreach (MIME_h_Parameter parameter in ToArray()) - { - if (parameter.IsModified) - { - return true; - } - } - - return false; - } - } - } - - /// - /// Gets owner MIME header field. - /// - public MIME_h Owner - { - get { return m_pOwner; } - } - - /// - /// Gets number of items in the collection. - /// - public int Count - { - get { return m_pParameters.Count; } - } - - /// - /// Gets or sets specified header field parameter value. Value null means not specified. - /// - /// Header field name. - /// Returns specified header field value or null if specified parameter doesn't exist. - /// Is raised when name is null reference. - public string this[string name] - { - get - { - if (name == null) - { - throw new ArgumentNullException("name"); - } - - MIME_h_Parameter retVal = null; - if (m_pParameters.TryGetValue(name, out retVal)) - { - return retVal.Value; - } - else - { - return null; - } - } - - set - { - if (name == null) - { - throw new ArgumentNullException("name"); - } - - MIME_h_Parameter retVal = null; - if (m_pParameters.TryGetValue(name, out retVal)) - { - retVal.Value = value; - } - else - { - m_pParameters.Add(name, new MIME_h_Parameter(name, value)); - } - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner MIME header field. - /// Is raised when owner is null reference. - public MIME_h_ParameterCollection(MIME_h owner) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - - m_pOwner = owner; - - m_pParameters = new Dictionary(StringComparer.CurrentCultureIgnoreCase); - } - - #endregion - - #region Methods - - /// - /// Removes specified parametr from the collection. - /// - /// Parameter name. - /// Is raised when name is null reference. - public void Remove(string name) - { - if (name == null) - { - throw new ArgumentNullException("name"); - } - - if (m_pParameters.Remove(name)) - { - m_IsModified = true; - } - } - - /// - /// Removes all items from the collection. - /// - public void Clear() - { - m_pParameters.Clear(); - m_IsModified = true; - } - - /// - /// Copies header fields parameters to new array. - /// - /// Returns header fields parameters array. - public MIME_h_Parameter[] ToArray() - { - MIME_h_Parameter[] retVal = new MIME_h_Parameter[m_pParameters.Count]; - m_pParameters.Values.CopyTo(retVal, 0); - - return retVal; - } - - /// - /// Returns header field parameters as string. - /// - /// Returns header field parameters as string. - public override string ToString() - { - return ToString(null); - } - - /// - /// Returns header field parameters as string. - /// - /// Charset to use to encode 8-bit characters. Value null means parameters not encoded. - /// Returns header field parameters as string. - public string ToString(Encoding charset) - { - /* RFC 2231. - * If parameter conatins 8-bit byte, we need to encode parameter value - * If parameter value length bigger than MIME maximum allowed line length, - * we need split value. - */ - - if (charset == null) - { - charset = Encoding.Default; - } - - StringBuilder retVal = new StringBuilder(); - foreach (MIME_h_Parameter parameter in ToArray()) - { - if (string.IsNullOrEmpty(parameter.Value)) - { - retVal.Append(";\r\n\t" + parameter.Name); - } - // We don't need to encode or split value. - else if ((charset == null || Core.IsAscii(parameter.Value)) && parameter.Value.Length < 76) - { - retVal.Append(";\r\n\t" + parameter.Name + "=" + TextUtils.QuoteString(parameter.Value)); - } - // We need to encode/split value. - else - { - byte[] byteValue = charset.GetBytes(parameter.Value); - - List values = new List(); - // Do encoding/splitting. - int offset = 0; - char[] valueBuff = new char[50]; - foreach (byte b in byteValue) - { - // We need split value as RFC 2231 says. - if (offset >= (50 - 3)) - { - values.Add(new string(valueBuff, 0, offset)); - offset = 0; - } - - // Normal char, we don't need to encode. - if (MIME_Reader.IsAttributeChar((char) b)) - { - valueBuff[offset++] = (char) b; - } - // We need to encode byte as %X2. - else - { - valueBuff[offset++] = '%'; - valueBuff[offset++] = (b >> 4).ToString("X")[0]; - valueBuff[offset++] = (b & 0xF).ToString("X")[0]; - } - } - // Add pending buffer value. - if (offset > 0) - { - values.Add(new string(valueBuff, 0, offset)); - } - - for (int i = 0; i < values.Count; i++) - { - // Only fist value entry has charset and language info. - if (charset != null && i == 0) - { - retVal.Append(";\r\n\t" + parameter.Name + "*" + i + "*=" + charset.WebName + "''" + - values[i]); - } - else - { - retVal.Append(";\r\n\t" + parameter.Name + "*" + i + "*=" + values[i]); - } - } - } - } - - return retVal.ToString(); - } - - /// - /// Parses parameters from the specified value. - /// - /// Header field parameters string. - /// Is raised when value is null reference. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new MIME_Reader(value)); - } - - /// - /// Parses parameters from the specified reader. - /// - /// MIME reader. - /// Is raised when reader is null reference. - public void Parse(MIME_Reader reader) - { - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - /* RFC 2231. - */ - - while (true) - { - // End os stream reached. - if (reader.Peek(true) == -1) - { - break; - } - // Next parameter start, just eat that char. - else if (reader.Peek(true) == ';') - { - reader.Char(false); - } - else - { - string name = reader.Token(); - - if (name == null) - break; - - string value = ""; - // Parameter value specified. - if (reader.Peek(true) == '=') - { - reader.Char(false); - - string v = reader.Word(); - // Normally value may not be null, but following case: paramName=EOS. - if (v != null) - { - value = v; - } - } - - // RFC 2231 encoded/splitted parameter. - if (name.IndexOf('*') > -1) - { - string[] name_x_no_x = name.Split('*'); - name = name_x_no_x[0]; - - Encoding charset = Encoding.ASCII; - StringBuilder valueBuffer = new StringBuilder(); - // We must have charset'language'value. - // Examples: - // URL*=utf-8''test; - // URL*0*=utf-8''"test"; - if ((name_x_no_x.Length == 2 && name_x_no_x[1] == "") || name_x_no_x.Length == 3) - { - string[] charset_language_value = value.Split('\''); - charset = EncodingTools.GetEncodingByCodepageName(charset_language_value[0]) ?? Encoding.ASCII; - valueBuffer.Append(charset_language_value[2]); - } - // No encoding, probably just splitted ASCII value. - // Example: - // URL*0="value1"; - // URL*1="value2"; - else - { - valueBuffer.Append(value); - } - - // Read while value continues. - while (true) - { - // End os stream reached. - if (reader.Peek(true) == -1) - { - break; - } - // Next parameter start, just eat that char. - else if (reader.Peek(true) == ';') - { - reader.Char(false); - } - else - { - if (!reader.StartsWith(name + "*")) - { - break; - } - reader.Token(); - - // Parameter value specified. - if (reader.Peek(true) == '=') - { - reader.Char(false); - - string v = reader.Word(); - // Normally value may not be null, but following case: paramName=EOS. - if (v != null) - { - valueBuffer.Append(v); - } - } - } - } - - this[name] = DecodeExtOctet(valueBuffer.ToString(),charset); - } - // Regular parameter. - else - { - this[name] = value; - } - } - } - - m_IsModified = false; - } - - /// - /// Gets enumerator. - /// - /// - public IEnumerator GetEnumerator() - { - return m_pParameters.GetEnumerator(); - } - - #endregion - - #region Utility methods - - /// - /// Decodes non-ascii text with MIME ext-octet method. Defined in RFC 2231 7. - /// - /// Text to decode, - /// Charset to use. - /// Returns decoded text. - /// Is raised when text or charset is null. - private static string DecodeExtOctet(string text, Encoding charset) - { - if (text == null) - { - throw new ArgumentNullException("text"); - } - if (charset == null) - { - throw new ArgumentNullException("charset"); - } - - return HttpUtility.UrlDecode(text, charset); - /* - int offset = 0; - byte[] decodedBuffer = new byte[text.Length]; - for (int i = 0; i < text.Length; i++) - { - if (text[i] == '%') - { - decodedBuffer[offset++] = byte.Parse(text[i + 1] + text[i + 2].ToString(), - NumberStyles.HexNumber); - i += 2; - } - else - { - decodedBuffer[offset++] = (byte) text[i]; - } - } - - return charset.GetString(decodedBuffer, 0, offset);*/ - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_Provider.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_Provider.cs deleted file mode 100644 index b03dd6433..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_Provider.cs +++ /dev/null @@ -1,144 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using System.Collections.Generic; - - #endregion - - /// - /// This class represents MIME headers provider. - /// - public class MIME_h_Provider - { - #region Members - - private readonly Dictionary m_pHeadrFields; - private Type m_pDefaultHeaderField; - - #endregion - - #region Properties - - /// - /// Gets or sets default header field what is used to reperesent unknown header fields. - /// - /// This property value value must be based on class. - /// Is raised when null reference passed. - /// Is raised when invalid value is passed. - public Type DefaultHeaderField - { - get { return m_pDefaultHeaderField; } - - set - { - if (value == null) - { - throw new ArgumentNullException("DefaultHeaderField"); - } - if (!value.GetType().IsSubclassOf(typeof (MIME_h))) - { - throw new ArgumentException( - "Property 'DefaultHeaderField' value must be based on MIME_h class."); - } - - m_pDefaultHeaderField = value; - } - } - - /// - /// Gets header fields parsers collection. - /// - public Dictionary HeaderFields - { - get { return m_pHeadrFields; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public MIME_h_Provider() - { - m_pDefaultHeaderField = typeof (MIME_h_Unstructured); - - m_pHeadrFields = new Dictionary(StringComparer.CurrentCultureIgnoreCase); - m_pHeadrFields.Add("content-type", typeof (MIME_h_ContentType)); - m_pHeadrFields.Add("content-disposition", typeof (MIME_h_ContentDisposition)); //BUG: was there - } - - #endregion - - #region Methods - - /// - /// Parses specified header field. - /// - /// Header field string (Name: value). - /// Returns parsed header field. - /// Is raised when field is null reference. - /// Is raised when header field parsing errors. - public MIME_h Parse(string field) - { - if (field == null) - { - throw new ArgumentNullException("field"); - } - - MIME_h headerField = null; - string[] name_value = field.Split(new[] {':'}, 2); - string name = name_value[0].Trim().ToLowerInvariant(); - if (name == string.Empty) - { - throw new ParseException("Invalid header field value '" + field + "'."); - } - else - { - try - { - if (m_pHeadrFields.ContainsKey(name)) - { - headerField = - (MIME_h) - m_pHeadrFields[name].GetMethod("Parse").Invoke(null, new object[] {field}); - } - else - { - headerField = - (MIME_h) - m_pDefaultHeaderField.GetMethod("Parse").Invoke(null, new object[] {field}); - } - } - catch (Exception x) - { - headerField = new MIME_h_Unparsed(field, x.InnerException); - } - } - - return headerField; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_Unparsed.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_Unparsed.cs deleted file mode 100644 index 4707da61c..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_Unparsed.cs +++ /dev/null @@ -1,135 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// This class represent header field what parsing has failed. - /// - public class MIME_h_Unparsed : MIME_h - { - #region Members - - private readonly string m_Name; - private readonly string m_ParseValue; - private readonly Exception m_pException; - private readonly string m_Value; - - #endregion - - #region Properties - - /// - /// Gets if this header field is modified since it has loaded. - /// - /// All new added header fields has IsModified = true. - /// Is riased when this class is disposed and this property is accessed. - public override bool IsModified - { - get { return false; } - } - - /// - /// Gets header field name. - /// - public override string Name - { - get { return m_Name; } - } - - /// - /// Gets header field value. - /// - public string Value - { - get { return m_Value; } - } - - /// - /// Gets error happened during parse. - /// - public Exception Exception - { - get { return m_pException; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Header field value. Header field name must be included. For example: 'Content-Type: text/plain'. - /// Parsing error. - /// Is raised when value is null reference. - /// Is raised when header field parsing errors. - internal MIME_h_Unparsed(string value, Exception exception) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - string[] name_value = value.Split(new[] {':'}, 2); - if (name_value.Length != 2) - { - throw new ParseException("Invalid Content-Type: header field value '" + value + "'."); - } - - m_Name = name_value[0]; - m_Value = name_value[1].Trim(); - m_ParseValue = value; - m_pException = exception; - } - - #endregion - - #region Methods - - /// - /// Parses header field from the specified value. - /// - /// Header field value. Header field name must be included. For example: 'Content-Type: text/plain'. - /// Returns parsed header field. - /// Is alwyas raised when this mewthod is accsessed. - public static MIME_h_Unparsed Parse(string value) - { - throw new InvalidOperationException(); - } - - /// - /// Returns header field as string. - /// - /// 8-bit words ecnoder. Value null means that words are not encoded. - /// Charset to use to encode 8-bit characters. Value null means parameters not encoded. - /// Returns header field as string. - public override string ToString(MIME_Encoding_EncodedWord wordEncoder, Encoding parmetersCharset) - { - return m_ParseValue; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_Unstructured.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_Unstructured.cs deleted file mode 100644 index ffca47280..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/MIME_h_Unstructured.cs +++ /dev/null @@ -1,177 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.MIME -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// This class represents normal unstructured text header field. - /// - public class MIME_h_Unstructured : MIME_h - { - #region Members - - private string m_Name = ""; - private string m_ParseValue; - private string m_Value = ""; - - #endregion - - #region Properties - - /// - /// Gets if this header field is modified since it has loaded. - /// - /// All new added header fields has IsModified = true. - /// Is riased when this class is disposed and this property is accessed. - public override bool IsModified - { - get { return m_ParseValue == null; } - } - - /// - /// Gets header field name. - /// - public override string Name - { - get { return m_Name; } - } - - /// - /// Gets or sets header field value. - /// - /// Is raised when when null reference is passed. - public string Value - { - get { return m_Value; } - - set - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - m_Value = value; - // Reset parse value. - m_ParseValue = null; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Header field name. - /// Header field value. - /// Is raised when name or value is null reference. - public MIME_h_Unstructured(string name, string value) - { - if (name == null) - { - throw new ArgumentNullException("name"); - } - if (name == string.Empty) - { - throw new ArgumentException("Argument 'name' value must be specified.", "name"); - } - if (value == null) - { - throw new ArgumentNullException("value"); - } - - m_Name = name; - m_Value = value; - } - - /// - /// Internal parser constructor. - /// - private MIME_h_Unstructured() {} - - #endregion - - #region Methods - - /// - /// Parses header field from the specified value. - /// - /// Header field value. Header field name must be included. For example: 'Content-Type: text/plain'. - /// Returns parsed header field. - /// Is raised when value is null reference. - /// Is raised when header field parsing errors. - public static MIME_h_Unstructured Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - MIME_h_Unstructured retVal = new MIME_h_Unstructured(); - - string[] name_value = value.Split(new[] {':'}, 2); - if (name_value[0].Trim() == string.Empty) - { - throw new ParseException("Invalid header field '" + value + "' syntax."); - } - - retVal.m_Name = name_value[0]; - retVal.m_Value = MIME_Encoding_EncodedWord.DecodeAll( - MIME_Utils.UnfoldHeader(name_value.Length == 2 ? name_value[1].TrimStart() : "")); - - retVal.m_ParseValue = value; - - return retVal; - } - - /// - /// Returns header field as string. - /// - /// 8-bit words ecnoder. Value null means that words are not encoded. - /// Charset to use to encode 8-bit characters. Value null means parameters not encoded. - /// Returns header field as string. - public override string ToString(MIME_Encoding_EncodedWord wordEncoder, Encoding parmetersCharset) - { - if (m_ParseValue != null) - { - return m_ParseValue; - } - else - { - if (wordEncoder != null) - { - return m_Name + ": " + wordEncoder.Encode(m_Value) + "\r\n"; - } - else - { - return m_Name + ": " + m_Value + "\r\n"; - } - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/O/MIME_MultipartBody.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/O/MIME_MultipartBody.cs deleted file mode 100644 index 86a44e02f..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/MIME/O/MIME_MultipartBody.cs +++ /dev/null @@ -1,281 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.IO; -using System.Collections.Generic; -using System.Text; - -using LumiSoft.Net.IO; - -namespace LumiSoft.Net.MIME -{ - /// - /// Represents MIME entity multipart/... body. Defined in RFC 2045. - /// - public class MIME_MultipartBody : MIME_Body - { - #region class _MIME_MultipartReader - - /// - /// This class implements mulitpart/xxx body parts reader. - /// - internal class _MIME_MultipartReader : LineReader - { - #region enum State - - /// - /// Specifies reader state. - /// - private enum State - { - /// - /// Body reading pending, the whole body isn't readed yet. - /// - InBody, - - /// - /// First "body part" start must be searched. - /// - SeekFirst, - - /// - /// Multipart "body part" reading has completed, next "body part" reading is pending. - /// - NextWaited, - - /// - /// All "body parts" readed. - /// - Finished, - } - - #endregion - - private LineReader m_pReader = null; - private string m_Boundary = ""; - private State m_State = State.SeekFirst; - - /// - /// Default constructor. - /// - /// Line reader. - /// Boundary ID. - public _MIME_MultipartReader(LineReader reader,string boundary) : base(reader.Stream,false,32000) - { - m_pReader = reader; - m_Boundary = boundary; - - if(reader.CanSyncStream){ - reader.SyncStream(); - } - } - - - #region method Next - - /// - /// Moves to next "body part". Returns true if moved to next "body part" or false if there are no more parts. - /// - /// Returns true if moved to next "body part" or false if there are no more parts. - public bool Next() - { - // Seek first. - if(m_State == State.SeekFirst){ - while(true){ - string line = m_pReader.ReadLine(); - // We reached end of stream, no more data. - if(line == null){ - m_State = State.Finished; - - return false; - } - else if(line == "--" + m_Boundary){ - m_State = State.InBody; - - return true; - } - } - } - else if(m_State != State.NextWaited){ - return false; - } - else{ - m_State = State.InBody; - - return true; - } - } - - #endregion - - - #region method override ReadLine - - /// - /// Reads binary line and stores it to the specified buffer. - /// - /// Buffer where to store line data. - /// Start offset in the buffer. - /// Maximum number of bytes store to the buffer. - /// Specifies how reader acts when line buffer too small. - /// Gets raw number of bytes readed from source. - /// Returns number of bytes stored to buffer or -1 if end of stream reached. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when buffer is null. - /// Is raised when any of the arguments has invalid value. - /// Is raised when line is bigger than buffer can store. - public override int ReadLine(byte[] buffer,int offset,int count,SizeExceededAction exceededAction,out int rawBytesReaded) - { - rawBytesReaded = 0; - - // We are at the end of body or "body part". - if(m_State == State.Finished || m_State == State.NextWaited){ - return -1; - } - // Read next line. - else{ - int readedCount = m_pReader.ReadLine(buffer,offset,count,exceededAction,out rawBytesReaded); - // End of stream reached, no more data. - if(readedCount == -1){ - m_State = State.Finished; - - return -1; - } - // For multipart we must check boundary tags. - else if(!string.IsNullOrEmpty(m_Boundary) && readedCount > 2 && buffer[0] == '-'){ - string line = Encoding.Default.GetString(buffer,0,readedCount); - - // Boundray end-tag reached, no more "body parts". - if(line == "--" + m_Boundary + "--"){ - m_State = State.Finished; - - return -1; - } - // Boundary start-tag reached, wait for this.Next() call. - else if(line == "--" + m_Boundary){ - m_State = State.NextWaited; - - return -1; - } - } - - return readedCount; - } - } - - #endregion - - #region method SyncStream - - /// - /// Sets stream position to the place we have consumed from stream and clears buffer data. - /// For example if we have 10 byets in buffer, stream position is actually +10 bigger than - /// we readed, the result is that stream.Position -= 10 and buffer is cleared. - /// - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when source stream won't support seeking. - public override void SyncStream() - { - m_pReader.SyncStream(); - } - - #endregion - - - #region Properties Implementation - - #endregion - } - - #endregion - - private string m_Boundary = ""; - private MIME_EntityCollection m_pParts = null; - - /// - /// Default constructor. - /// - /// Owner MIME entity. - /// Is raised when owner is null. - internal MIME_MultipartBody(MIME_Entity owner) : base(owner) - { - m_Boundary = owner.ContentType.Param_Boundary; - - m_pParts = new MIME_EntityCollection(); - } - - - #region override method ToStream - - /// - /// Stores MIME entity body data to the specified stream - /// - /// Stream where to store body. Storing starts from stream current position. - /// If true, encoded data is stored, if false, decoded data will be stored. - /// Is raised when stream is null. - public override void ToStream(Stream stream,bool encoded) - { - } - - #endregion - - - #region override method ParseFromReader - - /// - /// Parses MIME entity body from the specified reader. - /// - /// Body reader from where to parse body. - /// Specifies if body will be stream owner. - /// Returns true if this is last boundary in the message or in multipart "body parts". - internal override void ParseFromReader(LineReader reader,bool owner) - { - // For multipart we need todo new limiting(limits to specified boundary) reader. - _MIME_MultipartReader r = new _MIME_MultipartReader(reader,m_Boundary); - while(r.Next()){ - MIME_Entity bodyPart = new MIME_Entity(); - bodyPart.Parse(r,owner); - m_pParts.Add(bodyPart); - } - } - - #endregion - - - #region Properties Implementation - - /// - /// Gets boundary ID. - /// - public string Boundary - { - get{ return m_Boundary; } - } - - /// - /// Gets body parts collection. - /// - public MIME_EntityCollection BodyParts - { - get{ return m_pParts; } - } - - #endregion - - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_Message.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_Message.cs deleted file mode 100644 index 316b3322b..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_Message.cs +++ /dev/null @@ -1,2293 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mail -{ - #region usings - - using System; - using System.Collections.Generic; - using System.IO; - using IO; - using MIME; - - #endregion - - /// - /// This class represent electronic mail message. Defined in RFC 5322. - /// - public class Mail_Message : MIME_Message - { - // Permanent headerds list: http://www.rfc-editor.org/rfc/rfc4021.txt - - #region Properties - - /// - /// Gets or sets message date and time. Value DateTime.MinValue means not specified. - /// - /// Specifies the date and time at which the creator of the message indicated that the - /// message was complete and ready to enter the mail delivery system. - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when header field parsing errors. - public DateTime Date - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("Date"); - if (h != null) - { - try - { - return MIME_Utils.ParseRfc2822DateTime(((MIME_h_Unstructured) h).Value); - } - catch - { - //throw new ParseException("Header field 'Date' parsing failed."); - return DateTime.MinValue; - } - } - else - { - return DateTime.MinValue; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == DateTime.MinValue) - { - Header.RemoveAll("Date"); - } - else - { - MIME_h h = Header.GetFirst("Date"); - if (h == null) - { - Header.Add(new MIME_h_Unstructured("Date", MIME_Utils.DateTimeToRfc2822(value))); - } - else - { - Header.ReplaceFirst(new MIME_h_Unstructured("Date", - MIME_Utils.DateTimeToRfc2822(value))); - } - } - } - } - - /// - /// Gets or sets message author(s). Value null means not specified. - /// - /// Specifies the author(s) of the message; that is, the mailbox(es) of the person(s) or - /// system(s) responsible for the writing of the message. - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when header field parsing errors. - public Mail_t_MailboxList From - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("From"); - if (h != null) - { - if (!(h is Mail_h_MailboxList)) - { - throw new ParseException("Header field 'From' parsing failed."); - } - - return ((Mail_h_MailboxList) h).Addresses; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("From"); - } - else - { - MIME_h h = Header.GetFirst("From"); - if (h == null) - { - Header.Add(new Mail_h_MailboxList("From", value)); - } - else - { - Header.ReplaceFirst(new Mail_h_MailboxList("From", value)); - } - } - } - } - - /// - /// Gets or sets message sender. Value null means not specified. - /// - /// Specifies the mailbox of the agent responsible for the actual transmission of the message. - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when header field parsing errors. - public Mail_t_Mailbox Sender - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("Sender"); - if (h != null) - { - if (!(h is Mail_h_Mailbox)) - { - throw new ParseException("Header field 'Sender' parsing failed."); - } - - return ((Mail_h_Mailbox) h).Address; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("Sender"); - } - else - { - MIME_h h = Header.GetFirst("Sender"); - if (h == null) - { - Header.Add(new Mail_h_Mailbox("Sender", value)); - } - else - { - Header.ReplaceFirst(new Mail_h_Mailbox("Sender", value)); - } - } - } - } - - /// - /// Gets or sets mailbox for replies to message. Value null means not specified. - /// - /// When the "Reply-To:" field is present, it indicates the mailbox(es) to which the author of - /// the message suggests that replies be sent. - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when header field parsing errors. - public Mail_t_AddressList ReplyTo - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("Reply-To"); - if (h != null) - { - if (!(h is Mail_h_AddressList)) - { - throw new ParseException("Header field 'Reply-To' parsing failed."); - } - - return ((Mail_h_AddressList) h).Addresses; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("Reply-To"); - } - else - { - MIME_h h = Header.GetFirst("Reply-To"); - if (h == null) - { - Header.Add(new Mail_h_AddressList("Reply-To", value)); - } - else - { - Header.ReplaceFirst(new Mail_h_AddressList("Reply-To", value)); - } - } - } - } - - /// - /// Gets or sets message primary recipient(s). Value null means not specified. - /// - /// Contains the address(es) of the primary recipient(s) of the message. - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when header field parsing errors. - public Mail_t_AddressList To - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("To"); - if (h != null) - { - if (!(h is Mail_h_AddressList)) - { - throw new ParseException("Header field 'To' parsing failed."); - } - - return ((Mail_h_AddressList) h).Addresses; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("To"); - } - else - { - MIME_h h = Header.GetFirst("To"); - if (h == null) - { - Header.Add(new Mail_h_AddressList("To", value)); - } - else - { - Header.ReplaceFirst(new Mail_h_AddressList("To", value)); - } - } - } - } - - /// - /// Gets or sets carbon-copy recipient mailbox. Value null means not specified. - /// - /// Contains the addresses of others who are to receive the message, though the content of the message may not be directed at them. - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when header field parsing errors. - public Mail_t_AddressList Cc - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("Cc"); - if (h != null) - { - if (!(h is Mail_h_AddressList)) - { - throw new ParseException("Header field 'Cc' parsing failed."); - } - - return ((Mail_h_AddressList) h).Addresses; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("Cc"); - } - else - { - MIME_h h = Header.GetFirst("Cc"); - if (h == null) - { - Header.Add(new Mail_h_AddressList("Cc", value)); - } - else - { - Header.ReplaceFirst(new Mail_h_AddressList("Cc", value)); - } - } - } - } - - /// - /// Gets or sets blind-carbon-copy recipient mailbox. Value null means not specified. - /// - /// Contains addresses of recipients of the message whose addresses are not to be revealed to other recipients of the message. - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when header field parsing errors. - public Mail_t_AddressList Bcc - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("Bcc"); - if (h != null) - { - if (!(h is Mail_h_AddressList)) - { - throw new ParseException("Header field 'Bcc' parsing failed."); - } - - return ((Mail_h_AddressList) h).Addresses; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("Bcc"); - } - else - { - MIME_h h = Header.GetFirst("Bcc"); - if (h == null) - { - Header.Add(new Mail_h_AddressList("Bcc", value)); - } - else - { - Header.ReplaceFirst(new Mail_h_AddressList("Bcc", value)); - } - } - } - } - - /// - /// Gets or sets message identifier. Value null means not specified. - /// - /// Contains a single unique message identifier that refers to a particular version of a particular message. - /// If the message is resent without changes, the original Message-ID is retained. - /// Is raised when this object is disposed and this property is accessed. - public string MessageID - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("Message-ID"); - if (h != null) - { - return ((MIME_h_Unstructured) h).Value; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("Message-ID"); - } - else - { - MIME_h h = Header.GetFirst("Message-ID"); - if (h == null) - { - Header.Add(new MIME_h_Unstructured("Message-ID", value)); - } - else - { - Header.ReplaceFirst(new MIME_h_Unstructured("Message-ID", value)); - } - } - } - } - - /// - /// Gets or sets identify replied-to message(s). Value null means not specified. - /// - /// The message identifier(s) of the original message(s) to which the current message is a reply. - /// Is raised when this object is disposed and this property is accessed. - public string InReplyTo - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("In-Reply-To"); - if (h != null) - { - return ((MIME_h_Unstructured) h).Value; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("In-Reply-To"); - } - else - { - MIME_h h = Header.GetFirst("In-Reply-To"); - if (h == null) - { - Header.Add(new MIME_h_Unstructured("In-Reply-To", value)); - } - else - { - Header.ReplaceFirst(new MIME_h_Unstructured("In-Reply-To", value)); - } - } - } - } - - /// - /// Gets or sets related message identifier(s). Value null means not specified. - /// - /// The message identifier(s) of other message(s) to which the current message may be related. - /// In RFC 2822, the definition was changed to say that this header field contains a list of all Message-IDs - /// of messages in the preceding reply chain. - /// Is raised when this object is disposed and this property is accessed. - public string References - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("References"); - if (h != null) - { - return ((MIME_h_Unstructured) h).Value; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("References"); - } - else - { - MIME_h h = Header.GetFirst("References"); - if (h == null) - { - Header.Add(new MIME_h_Unstructured("References", value)); - } - else - { - Header.ReplaceFirst(new MIME_h_Unstructured("References", value)); - } - } - } - } - - /// - /// Gets or sets topic of message. Value null means not specified. - /// - /// Contains a short string identifying the topic of the message. - /// Is raised when this object is disposed and this property is accessed. - public string Subject - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("Subject"); - if (h != null) - { - return ((MIME_h_Unstructured) h).Value; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("Subject"); - } - else - { - MIME_h h = Header.GetFirst("Subject"); - if (h == null) - { - Header.Add(new MIME_h_Unstructured("Subject", value)); - } - else - { - Header.ReplaceFirst(new MIME_h_Unstructured("Subject", value)); - } - } - } - } - - /// - /// Gets or sets additional comments about the message. Value null means not specified. - /// - /// Contains any additional comments on the text of the body of the message. - /// Warning: Some mailers will not show this field to recipients. - /// Is raised when this object is disposed and this property is accessed. - public string Comments - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("Comments"); - if (h != null) - { - return ((MIME_h_Unstructured) h).Value; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("Comments"); - } - else - { - MIME_h h = Header.GetFirst("Comments"); - if (h == null) - { - Header.Add(new MIME_h_Unstructured("Comments", value)); - } - else - { - Header.ReplaceFirst(new MIME_h_Unstructured("Comments", value)); - } - } - } - } - - /// - /// Gets or sets message key words and/or phrases. Value null means not specified. - /// - /// Contains a comma-separated list of important words and phrases that might be useful for the recipient. - /// Is raised when this object is disposed and this property is accessed. - public string Keywords - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("Keywords"); - if (h != null) - { - return ((MIME_h_Unstructured) h).Value; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("Keywords"); - } - else - { - MIME_h h = Header.GetFirst("Keywords"); - if (h == null) - { - Header.Add(new MIME_h_Unstructured("Keywords", value)); - } - else - { - Header.ReplaceFirst(new MIME_h_Unstructured("Keywords", value)); - } - } - } - } - - /// - /// Gets or sets date and time message is resent. Value DateTime.MinValue means not specified. - /// - /// Contains the date and time that a message is reintroduced into the message transfer system. - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when header field parsing errors. - public DateTime ResentDate - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("Resent-Date"); - if (h != null) - { - try - { - return MIME_Utils.ParseRfc2822DateTime(((MIME_h_Unstructured) h).Value); - } - catch - { - throw new ParseException("Header field 'Resent-Date' parsing failed."); - } - } - else - { - return DateTime.MinValue; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == DateTime.MinValue) - { - Header.RemoveAll("Resent-Date"); - } - else - { - MIME_h h = Header.GetFirst("Resent-Date"); - if (h == null) - { - Header.Add(new MIME_h_Unstructured("Resent-Date", MIME_Utils.DateTimeToRfc2822(value))); - } - else - { - Header.ReplaceFirst(new MIME_h_Unstructured("Resent-Date", - MIME_Utils.DateTimeToRfc2822(value))); - } - } - } - } - - /// - /// Gets or sets mailbox of person for whom message is resent. Value null means not specified. - /// - /// Contains the mailbox of the agent who has reintroduced the message into - /// the message transfer system, or on whose behalf the message has been resent. - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when header field parsing errors. - public Mail_t_MailboxList ResentFrom - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("Resent-From"); - if (h != null) - { - if (!(h is Mail_h_MailboxList)) - { - throw new ParseException("Header field 'Resent-From' parsing failed."); - } - - return ((Mail_h_MailboxList) h).Addresses; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("Resent-From"); - } - else - { - MIME_h h = Header.GetFirst("Resent-From"); - if (h == null) - { - Header.Add(new Mail_h_MailboxList("Resent-From", value)); - } - else - { - Header.ReplaceFirst(new Mail_h_MailboxList("Resent-From", value)); - } - } - } - } - - /// - /// Gets or sets mailbox of person who actually resends the message. Value null means not specified. - /// - /// Contains the mailbox of the agent who has reintroduced the message into - /// the message transfer system, if this is different from the Resent-From value. - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when header field parsing errors. - public Mail_t_Mailbox ResentSender - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("Resent-Sender"); - if (h != null) - { - if (!(h is Mail_h_Mailbox)) - { - throw new ParseException("Header field 'Resent-Sender' parsing failed."); - } - - return ((Mail_h_Mailbox) h).Address; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("Resent-Sender"); - } - else - { - MIME_h h = Header.GetFirst("Resent-Sender"); - if (h == null) - { - Header.Add(new Mail_h_Mailbox("Resent-Sender", value)); - } - else - { - Header.ReplaceFirst(new Mail_h_Mailbox("Resent-Sender", value)); - } - } - } - } - - /// - /// Gets or sets mailbox to which message is resent. Value null means not specified. - /// - /// Contains the mailbox(es) to which the message has been resent. - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when header field parsing errors. - public Mail_t_AddressList ResentTo - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("Resent-To"); - if (h != null) - { - if (!(h is Mail_h_AddressList)) - { - throw new ParseException("Header field 'Resent-To' parsing failed."); - } - - return ((Mail_h_AddressList) h).Addresses; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("Resent-To"); - } - else - { - MIME_h h = Header.GetFirst("Resent-To"); - if (h == null) - { - Header.Add(new Mail_h_AddressList("Resent-To", value)); - } - else - { - Header.ReplaceFirst(new Mail_h_AddressList("Resent-To", value)); - } - } - } - } - - /// - /// Gets or sets mailbox(es) to which message is cc'ed on resend. Value null means not specified. - /// - /// Contains the mailbox(es) to which message is cc'ed on resend. - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when header field parsing errors. - public Mail_t_AddressList ResentCc - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("Resent-Cc"); - if (h != null) - { - if (!(h is Mail_h_AddressList)) - { - throw new ParseException("Header field 'Resent-Cc' parsing failed."); - } - - return ((Mail_h_AddressList) h).Addresses; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("Resent-Cc"); - } - else - { - MIME_h h = Header.GetFirst("Resent-Cc"); - if (h == null) - { - Header.Add(new Mail_h_AddressList("Resent-Cc", value)); - } - else - { - Header.ReplaceFirst(new Mail_h_AddressList("Resent-Cc", value)); - } - } - } - } - - /// - /// Gets or sets mailbox(es) to which message is bcc'ed on resend. Value null means not specified. - /// - /// Contains the mailbox(es) to which message is bcc'ed on resend. - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when header field parsing errors. - public Mail_t_AddressList ResentBcc - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("Resent-Bcc"); - if (h != null) - { - if (!(h is Mail_h_AddressList)) - { - throw new ParseException("Header field 'Resent-Bcc' parsing failed."); - } - - return ((Mail_h_AddressList) h).Addresses; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("Resent-Bcc"); - } - else - { - MIME_h h = Header.GetFirst("Resent-Bcc"); - if (h == null) - { - Header.Add(new Mail_h_AddressList("Resent-Bcc", value)); - } - else - { - Header.ReplaceFirst(new Mail_h_AddressList("Resent-Bcc", value)); - } - } - } - } - - /// - /// Gets or sets resent reply-to. Value null means not specified. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when header field parsing errors. - public Mail_t_AddressList ResentReplyTo - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("Resent-Reply-To"); - if (h != null) - { - if (!(h is Mail_h_AddressList)) - { - throw new ParseException("Header field 'Resent-Reply-To' parsing failed."); - } - - return ((Mail_h_AddressList) h).Addresses; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("Resent-Reply-To"); - } - else - { - MIME_h h = Header.GetFirst("Resent-Reply-To"); - if (h == null) - { - Header.Add(new Mail_h_AddressList("Resent-Reply-To", value)); - } - else - { - Header.ReplaceFirst(new Mail_h_AddressList("Resent-Reply-To", value)); - } - } - } - } - - /// - /// Gets or sets message identifier for resent message. Value null means not specified. - /// - /// Is raised when this object is disposed and this property is accessed. - public string ResentMessageID - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("Resent-Message-ID"); - if (h != null) - { - return ((MIME_h_Unstructured) h).Value; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("Resent-Message-ID"); - } - else - { - MIME_h h = Header.GetFirst("Resent-Message-ID"); - if (h == null) - { - Header.Add(new MIME_h_Unstructured("Resent-Message-ID", value)); - } - else - { - Header.ReplaceFirst(new MIME_h_Unstructured("Resent-Message-ID", value)); - } - } - } - } - - /// - /// Gets or sets message return path. Value null means not specified. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when header field parsing errors. - public Mail_h_ReturnPath ReturnPath - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("Return-Path"); - if (h != null) - { - if (!(h is Mail_h_ReturnPath)) - { - throw new ParseException("Header field 'Return-Path' parsing failed."); - } - - return (Mail_h_ReturnPath) h; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("Return-Path"); - } - else - { - MIME_h h = Header.GetFirst("Return-Path"); - if (h == null) - { - Header.Add(value); - } - else - { - Header.ReplaceFirst(value); - } - } - } - } - - /// - /// Gets mail transfer trace information. Value null means not specified. - /// - /// Contains information about receipt of the current message by a mail transfer agent on the transfer path. - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when header field parsing errors. - public Mail_h_Received[] Received - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h[] h = Header["Received"]; - if (h != null) - { - List retVal = new List(); - for (int i = 0; i < h.Length; i++) - { - if (!(h[i] is Mail_h_Received)) - { - throw new ParseException("Header field 'Received' parsing failed."); - } - - retVal.Add((Mail_h_Received) h[i]); - } - - return retVal.ToArray(); - } - else - { - return null; - } - } - } - - // Obsoleted by RFC 2822. - // public string Encypted - - /// - /// Gets or sets mailbox for sending disposition notification. Value null means not specified. - /// - /// Indicates that the sender wants a disposition notification when this message - /// is received (read, processed, etc.) by its recipients. - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when header field parsing errors. - public Mail_t_Mailbox DispositionNotificationTo - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("Disposition-Notification-To"); - if (h != null) - { - if (!(h is Mail_h_Mailbox)) - { - throw new ParseException("Header field 'Disposition-Notification-To' parsing failed."); - } - - return ((Mail_h_Mailbox) h).Address; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("Disposition-Notification-To"); - } - else - { - MIME_h h = Header.GetFirst("Disposition-Notification-To"); - if (h == null) - { - Header.Add(new Mail_h_Mailbox("Disposition-Notification-To", value)); - } - else - { - Header.ReplaceFirst(new Mail_h_Mailbox("Disposition-Notification-To", value)); - } - } - } - } - - /// - /// Gets or sets disposition notification options. Value null means not specified. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when header field parsing errors. - public Mail_h_DispositionNotificationOptions DispositionNotificationOptions - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("Disposition-Notification-Options"); - if (h != null) - { - if (!(h is Mail_h_DispositionNotificationOptions)) - { - throw new ParseException( - "Header field 'Disposition-Notification-Options' parsing failed."); - } - - return (Mail_h_DispositionNotificationOptions) h; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("Disposition-Notification-Options"); - } - else - { - MIME_h h = Header.GetFirst("Disposition-Notification-Options"); - if (h == null) - { - Header.Add(value); - } - else - { - Header.ReplaceFirst(value); - } - } - } - } - - /// - /// Gets or sets language that the message sender requests to be used for responses. Value null means not specified. - /// - /// - /// Indicates a language that the message sender requests to be used for responses. - /// - /// Is raised when this object is disposed and this property is accessed. - public string AcceptLanguage - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("Accept-Language"); - if (h != null) - { - return ((MIME_h_Unstructured) h).Value; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("Accept-Language"); - } - else - { - MIME_h h = Header.GetFirst("Accept-Language"); - if (h == null) - { - Header.Add(new MIME_h_Unstructured("Accept-Language", value)); - } - else - { - Header.ReplaceFirst(new MIME_h_Unstructured("Accept-Language", value)); - } - } - } - } - - /// - /// Gets or sets original message identifier. Value null means not specified. - /// - /// Original message identifier used with resend of message with alternative content format; - /// identifies the original message data to which it corresponds. - /// Is raised when this object is disposed and this property is accessed. - public string OriginalMessageID - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("Original-Message-ID"); - if (h != null) - { - return ((MIME_h_Unstructured) h).Value; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("Original-Message-ID"); - } - else - { - MIME_h h = Header.GetFirst("Original-Message-ID"); - if (h == null) - { - Header.Add(new MIME_h_Unstructured("Original-Message-ID", value)); - } - else - { - Header.ReplaceFirst(new MIME_h_Unstructured("Original-Message-ID", value)); - } - } - } - } - - /// - /// Gets or sets PICS rating label. Value null means not specified. - /// - /// Ratings label to control selection (filtering) of messages according to the PICS protocol. - /// Is raised when this object is disposed and this property is accessed. - public string PICSLabel - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("PICS-Label"); - if (h != null) - { - return ((MIME_h_Unstructured) h).Value; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("PICS-LabelD"); - } - else - { - MIME_h h = Header.GetFirst("PICS-Label"); - if (h == null) - { - Header.Add(new MIME_h_Unstructured("PICS-Label", value)); - } - else - { - Header.ReplaceFirst(new MIME_h_Unstructured("PICS-Label", value)); - } - } - } - } - - // Not widely used - // public string Encoding - - /// - /// Gets or sets URL of mailing list archive. Value null means not specified. - /// - /// Contains the URL to use to browse the archives of the mailing list from which this message was relayed. - /// Is raised when this object is disposed and this property is accessed. - public string ListArchive - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("List-Archive"); - if (h != null) - { - return ((MIME_h_Unstructured) h).Value; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("List-Archive"); - } - else - { - MIME_h h = Header.GetFirst("List-Archive"); - if (h == null) - { - Header.Add(new MIME_h_Unstructured("List-Archive", value)); - } - else - { - Header.ReplaceFirst(new MIME_h_Unstructured("List-Archive", value)); - } - } - } - } - - /// - /// Gets or sets URL for mailing list information. Value null means not specified. - /// - /// Contains the URL to use to get information about the mailing list from which this message was relayed. - /// Is raised when this object is disposed and this property is accessed. - public string ListHelp - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("List-Help"); - if (h != null) - { - return ((MIME_h_Unstructured) h).Value; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("List-Help"); - } - else - { - MIME_h h = Header.GetFirst("List-Help"); - if (h == null) - { - Header.Add(new MIME_h_Unstructured("List-Help", value)); - } - else - { - Header.ReplaceFirst(new MIME_h_Unstructured("List-Help", value)); - } - } - } - } - - /// - /// Gets or sets mailing list identifier. Value null means not specified. - /// - /// Stores an identification of the mailing list through which this message was distributed. - /// Is raised when this object is disposed and this property is accessed. - public string ListID - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("List-ID"); - if (h != null) - { - return ((MIME_h_Unstructured) h).Value; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("List-ID"); - } - else - { - MIME_h h = Header.GetFirst("List-ID"); - if (h == null) - { - Header.Add(new MIME_h_Unstructured("List-ID", value)); - } - else - { - Header.ReplaceFirst(new MIME_h_Unstructured("List-ID", value)); - } - } - } - } - - /// - /// Gets or sets URL for mailing list owner's mailbox. Value null means not specified. - /// - /// Contains the URL to send e-mail to the owner of the mailing list from which this message was relayed. - /// Is raised when this object is disposed and this property is accessed. - public string ListOwner - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("List-Owner"); - if (h != null) - { - return ((MIME_h_Unstructured) h).Value; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("List-Owner"); - } - else - { - MIME_h h = Header.GetFirst("List-Owner"); - if (h == null) - { - Header.Add(new MIME_h_Unstructured("List-Owner", value)); - } - else - { - Header.ReplaceFirst(new MIME_h_Unstructured("List-Owner", value)); - } - } - } - } - - /// - /// Gets or sets URL for mailing list posting. Value null means not specified. - /// - /// Contains the URL to use to send contributions to the mailing list from which this message was relayed. - /// Is raised when this object is disposed and this property is accessed. - public string ListPost - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("List-Post"); - if (h != null) - { - return ((MIME_h_Unstructured) h).Value; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("List-Post"); - } - else - { - MIME_h h = Header.GetFirst("List-Post"); - if (h == null) - { - Header.Add(new MIME_h_Unstructured("List-Post", value)); - } - else - { - Header.ReplaceFirst(new MIME_h_Unstructured("List-Post", value)); - } - } - } - } - - /// - /// Gets or sets URL for mailing list subscription. Value null means not specified. - /// - /// Contains the URL to use to get a subscription to the mailing list from which this message was relayed. - /// Is raised when this object is disposed and this property is accessed. - public string ListSubscribe - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("List-Subscribe"); - if (h != null) - { - return ((MIME_h_Unstructured) h).Value; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("List-Subscribe"); - } - else - { - MIME_h h = Header.GetFirst("List-Subscribe"); - if (h == null) - { - Header.Add(new MIME_h_Unstructured("List-Subscribe", value)); - } - else - { - Header.ReplaceFirst(new MIME_h_Unstructured("List-Subscribe", value)); - } - } - } - } - - /// - /// Gets or sets URL for mailing list unsubscription. Value null means not specified. - /// - /// Contains the URL to use to unsubscribe the mailing list from which this message was relayed. - /// Is raised when this object is disposed and this property is accessed. - public string ListUnsubscribe - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("List-Unsubscribe"); - if (h != null) - { - return ((MIME_h_Unstructured) h).Value; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("List-Unsubscribe"); - } - else - { - MIME_h h = Header.GetFirst("List-Unsubscribe"); - if (h == null) - { - Header.Add(new MIME_h_Unstructured("List-Unsubscribe", value)); - } - else - { - Header.ReplaceFirst(new MIME_h_Unstructured("List-Unsubscribe", value)); - } - } - } - } - - /// - /// Gets or sets type or context of message. Value null means not specified. - /// - /// Provides information about the context and presentation characteristics of a message. - /// Can have the values 'voice-message', 'fax-message', 'pager-message', 'multimedia-message', 'text-message', or 'none'. - /// Is raised when this object is disposed and this property is accessed. - public string MessageContext - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("Message-Context"); - if (h != null) - { - return ((MIME_h_Unstructured) h).Value; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("Message-Context"); - } - else - { - MIME_h h = Header.GetFirst("Message-Context"); - if (h == null) - { - Header.Add(new MIME_h_Unstructured("Message-Context", value)); - } - else - { - Header.ReplaceFirst(new MIME_h_Unstructured("Message-Context", value)); - } - } - } - } - - // Not for general use - //public string AlternateRecipient - - // Obsolete - //public string ContentReturn - - // Not for general use - // public string GenerateDeliveryReport - - // Not for general use - // public string PreventNonDeliveryReport - - // Obsolete - // public string ContentIdentifier - - // Not for general use - // public string DeliveryDate - - // Obsolete - // public string ExpiryDate - - // Not for general use - // public string Expires - - // Not for general use - // public string ReplyBy - - /// - /// Gets or sets message importance. Value null means not specified. - /// - /// A hint from the originator to the recipients about how important a message is. - /// Values: High, normal, or low. Not used to control transmission speed. - /// Is raised when this object is disposed and this property is accessed. - public string Importance - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("Importance"); - - if (h != null) - { - return ((MIME_h_Unstructured) h).Value; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("Importance"); - } - else - { - MIME_h h = Header.GetFirst("Importance"); - if (h == null) - { - Header.Add(new MIME_h_Unstructured("Importance", value)); - } - else - { - Header.ReplaceFirst(new MIME_h_Unstructured("Importance", value)); - } - } - } - } - - /// - /// Gets or sets message priority. Value null means not specified. - /// - /// Can be 'normal', 'urgent', or 'non-urgent' and can influence transmission speed and delivery. - /// Is raised when this object is disposed and this property is accessed. - public string Priority - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - MIME_h h = Header.GetFirst("X-Priority"); - - if (h == null) - h = Header.GetFirst("Priority"); - - if (h != null) - { - return ((MIME_h_Unstructured) h).Value; - } - else - { - return null; - } - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value == null) - { - Header.RemoveAll("Priority"); - } - else - { - MIME_h h = Header.GetFirst("Priority"); - if (h == null) - { - Header.Add(new MIME_h_Unstructured("Priority", value)); - } - else - { - Header.ReplaceFirst(new MIME_h_Unstructured("Priority", value)); - } - } - } - } - - // Not for general use - // public string Sensitivity - - // Not for general use - // public string Language - - // Not for general use - // public string MessageType - - // Not for general use - // public string Autosubmitted - - // Not for general use - // public string Autoforwarded - - // Not for general use - // public string DiscloseRecipients - - // Not for general use - // public string DeferredDelivery - - // Not for general use - // public string LatestDeliveryTime - - // Not for general use - // public string OriginatorReturnAddress - - /// - /// Gets message body text. Returns null if no body text available. - /// - public string BodyText - { - get - { - foreach (MIME_Entity e in AllEntities) - { - if (e.Body.ContentType.TypeWithSubype.ToLower() == MIME_MediaTypes.Text.plain) - { - if (e.ContentDisposition == null) - { - return ((MIME_b_Text) e.Body).Text; - } - if (!(MIME_DispositionTypes.Attachment.Equals(e.ContentDisposition.DispositionType))) - { - return ((MIME_b_Text)e.Body).Text; - } - } - } - - return null; - } - } - - /// - /// Gets message body html text. Returns null if no body html text available. - /// - public string BodyHtmlText - { - get - { - foreach (MIME_Entity e in AllEntities) - { - if (e.Body.ContentType.TypeWithSubype.ToLower() == MIME_MediaTypes.Text.html && (e.ContentDisposition == null || - !(MIME_DispositionTypes.Attachment.Equals(e.ContentDisposition.DispositionType)))) - { - if (e.ContentDisposition == null) - { - return ((MIME_b_Text)e.Body).Text; - } - if (!(MIME_DispositionTypes.Attachment.Equals(e.ContentDisposition.DispositionType))) - { - return ((MIME_b_Text)e.Body).Text; - } - } - } - - return null; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public Mail_Message() - { - Header.FieldsProvider.HeaderFields.Add("From", typeof (Mail_h_MailboxList)); - Header.FieldsProvider.HeaderFields.Add("Sender", typeof (Mail_h_Mailbox)); - Header.FieldsProvider.HeaderFields.Add("Reply-To", typeof (Mail_h_AddressList)); - Header.FieldsProvider.HeaderFields.Add("To", typeof (Mail_h_AddressList)); - Header.FieldsProvider.HeaderFields.Add("Cc", typeof (Mail_h_AddressList)); - Header.FieldsProvider.HeaderFields.Add("Bcc", typeof (Mail_h_AddressList)); - Header.FieldsProvider.HeaderFields.Add("Resent-From", typeof (Mail_h_MailboxList)); - Header.FieldsProvider.HeaderFields.Add("Resent-Sender", typeof (Mail_h_Mailbox)); - Header.FieldsProvider.HeaderFields.Add("Resent-To", typeof (Mail_h_AddressList)); - Header.FieldsProvider.HeaderFields.Add("Resent-Cc", typeof (Mail_h_AddressList)); - Header.FieldsProvider.HeaderFields.Add("Resent-Bcc", typeof (Mail_h_AddressList)); - Header.FieldsProvider.HeaderFields.Add("Resent-Reply-To", typeof (Mail_h_AddressList)); - Header.FieldsProvider.HeaderFields.Add("Return-Path", typeof (Mail_h_ReturnPath)); - Header.FieldsProvider.HeaderFields.Add("Received", typeof (Mail_h_Received)); - Header.FieldsProvider.HeaderFields.Add("Disposition-Notification-To", typeof (Mail_h_Mailbox)); - Header.FieldsProvider.HeaderFields.Add("Disposition-Notification-Options", - typeof (Mail_h_DispositionNotificationOptions)); - } - - #endregion - - #region Methods - - /// - /// Parses mail message from the specified byte array. - /// - /// Mail message data. - /// Returns parsed mail message. - /// Is raised when data is null reference. - public static Mail_Message ParseFromByte(byte[] data) - { - if (data == null) - { - throw new ArgumentNullException("data"); - } - - return ParseFromStream(new MemoryStream(data)); - } - - /// - /// Parses mail message from the specified file. - /// - /// File name with path from where to parse mail message. - /// Returns parsed mail message. - /// Is raised when file is null. - /// Is raised when any of the arguments has invalid value. - public new static Mail_Message ParseFromFile(string file) - { - if (file == null) - { - throw new ArgumentNullException("file"); - } - if (file == "") - { - throw new ArgumentException("Argument 'file' value must be specified."); - } - - return ParseFromStream(File.OpenRead(file)); - } - - /// - /// Parses mail message from the specified stream. - /// - /// Stream from where to parse mail message. Parsing starts from current stream position. - /// Returns parsed mail message. - /// Is raised when stream is null. - public new static Mail_Message ParseFromStream(Stream stream) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - Mail_Message retVal = new Mail_Message(); - retVal.Parse(new SmartStream(stream, false), new MIME_h_ContentType("text/plain")); - - return retVal; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_Utils.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_Utils.cs deleted file mode 100644 index 9eecabc65..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_Utils.cs +++ /dev/null @@ -1,84 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mail -{ - #region usings - - using System; - using System.Text; - using MIME; - - #endregion - - /// - /// This class provides mail message related utility methods. - /// - public class Mail_Utils - { - #region Internal methods - - /// - /// Reads SMTP "Mailbox" from the specified MIME reader. - /// - /// MIME reader. - /// Returns SMTP "Mailbox" or null if no SMTP mailbox available. - /// Is raised when reader is null reference. - internal static string SMTP_Mailbox(MIME_Reader reader) - { - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // TODO: - - /* RFC 5321. - Mailbox = Local-part "@" ( Domain / address-literal ) - Local-part = Dot-string / Quoted-string ; MAY be case-sensitive - Dot-string = Atom *("." Atom) - */ - - StringBuilder retVal = new StringBuilder(); - if (reader.Peek(true) == '\"') - { - retVal.Append("\"" + reader.QuotedString() + "\""); - } - else - { - retVal.Append(reader.DotAtom()); - } - - if (reader.Peek(true) != '@') - { - return null; ; - } - else - { - // Eat "@". - reader.Char(true); - - retVal.Append('@'); - retVal.Append(reader.DotAtom()); - } - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_h_AddressList.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_h_AddressList.cs deleted file mode 100644 index 3f84d135a..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_h_AddressList.cs +++ /dev/null @@ -1,215 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mail -{ - #region usings - - using System; - using System.Text; - using MIME; - - #endregion - - /// - /// This class represent generic address-list header fields. For example: To header. - /// - /// - /// - /// RFC 5322. - /// header = "FiledName:" address-list CRLF - /// address-list = (address *("," address)) - /// address = mailbox / group - /// - /// - public class Mail_h_AddressList : MIME_h - { - #region Members - - private readonly string m_Name; - private readonly Mail_t_AddressList m_pAddresses; - private string m_ParseValue; - - #endregion - - #region Properties - - /// - /// Gets if this header field is modified since it has loaded. - /// - /// All new added header fields has IsModified = true. - /// Is riased when this class is disposed and this property is accessed. - public override bool IsModified - { - get { return m_pAddresses.IsModified; } - } - - /// - /// Gets header field name. For example "To". - /// - public override string Name - { - get { return m_Name; } - } - - /// - /// Gets addresses collection. - /// - public Mail_t_AddressList Addresses - { - get { return m_pAddresses; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Header field name. For example: "To". - /// Addresses collection. - /// Is raised when filedName or values is null reference. - /// Is raised when any of the arguments has invalid value. - public Mail_h_AddressList(string fieldName, Mail_t_AddressList values) - { - if (fieldName == null) - { - throw new ArgumentNullException("fieldName"); - } - if (fieldName == string.Empty) - { - throw new ArgumentException("Argument 'fieldName' value must be specified."); - } - if (values == null) - { - throw new ArgumentNullException("values"); - } - - m_Name = fieldName; - m_pAddresses = values; - } - - #endregion - - #region Methods - - /// - /// Parses header field from the specified value. - /// - /// Header field value. Header field name must be included. For example: 'Content-Type: text/plain'. - /// Returns parsed header field. - /// Is raised when value is null reference. - /// Is raised when header field parsing errors. - public static Mail_h_AddressList Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - string[] name_value = value.Split(new[] {':'}, 2); - if (name_value.Length != 2) - { - throw new ParseException("Invalid header field value '" + value + "'."); - } - - MIME_Reader r = new MIME_Reader(MIME_Utils.UnfoldHeader(name_value.Length == 2 ? name_value[1].TrimStart() : "")); - - Mail_h_AddressList retVal = new Mail_h_AddressList(name_value[0], new Mail_t_AddressList()); - - - while (true) - { - string word = r.QuotedReadToDelimiter(new[] { ',', '<', ':' }); - // We processed all data. - if (word == null && r.Available == 0) - { - break; - } - // name-addr - else if (r.Peek(true) == '<') - { - retVal.m_pAddresses.Add( - new Mail_t_Mailbox( - word != null - ? MIME_Encoding_EncodedWord.DecodeAll(TextUtils.UnQuoteString(word)) - : null, - r.ReadParenthesized())); - } - // addr-spec - else - { - retVal.m_pAddresses.Add(new Mail_t_Mailbox(null, word)); - } - - // We have more addresses. - if (r.Peek(true) == ',') - { - r.Char(false); - } - } - - - retVal.m_ParseValue = value; - retVal.m_pAddresses.AcceptChanges(); - return retVal; - } - - - - /// - /// Returns header field as string. - /// - /// 8-bit words ecnoder. Value null means that words are not encoded. - /// Charset to use to encode 8-bit characters. Value null means parameters not encoded. - /// Returns header field as string. - public override string ToString(MIME_Encoding_EncodedWord wordEncoder, Encoding parmetersCharset) - { - if (IsModified) - { - StringBuilder retVal = new StringBuilder(); - retVal.Append(Name + ": "); - for (int i = 0; i < m_pAddresses.Count; i++) - { - if (i > 0) - { - retVal.Append("\t"); - } - - // Don't add ',' for last item. - if (i == (m_pAddresses.Count - 1)) - { - retVal.Append(m_pAddresses[i].ToString(wordEncoder) + "\r\n"); - } - else - { - retVal.Append(m_pAddresses[i].ToString(wordEncoder) + ",\r\n"); - } - } - - return retVal.ToString(); - } - else - { - return m_ParseValue; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_h_DispositionNotificationOptions.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_h_DispositionNotificationOptions.cs deleted file mode 100644 index ce355d260..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_h_DispositionNotificationOptions.cs +++ /dev/null @@ -1,86 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mail -{ - #region usings - - using System; - using System.Text; - using MIME; - - #endregion - - /// - /// Represents "Disposition-Notification-Options:" header. Defined in RFC 2298 2.2. - /// - public class Mail_h_DispositionNotificationOptions : MIME_h - { - /* - Disposition-Notification-Options = "Disposition-Notification-Options" ":" disposition-notification-parameters - disposition-notification-parameters = parameter *(";" parameter) - - parameter = attribute "=" importance "," 1#value - importance = "required" / "optional" - */ - - #region Properties - - /// - /// Gets if this header field is modified since it has loaded. - /// - /// All new added header fields has IsModified = true. - /// Is riased when this class is disposed and this property is accessed. - public override bool IsModified - { - get { return true; } //m_pAddresses.IsModified; } - } - - /// - /// Gets header field name. For example "Sender". - /// - public override string Name - { - get { return "Disposition-Notification-Options"; } - } - - /// - /// Gets or sets mailbox address. - /// - public string Address - { - get { return "TODO:"; } - } - - #endregion - - #region Methods - - /// - /// Returns header field as string. - /// - /// 8-bit words ecnoder. Value null means that words are not encoded. - /// Charset to use to encode 8-bit characters. Value null means parameters not encoded. - /// Returns header field as string. - public override string ToString(MIME_Encoding_EncodedWord wordEncoder, Encoding parmetersCharset) - { - return "TODO:"; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_h_Mailbox.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_h_Mailbox.cs deleted file mode 100644 index e4be3c89e..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_h_Mailbox.cs +++ /dev/null @@ -1,181 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mail -{ - #region usings - - using System; - using System.Text; - using MIME; - - #endregion - - /// - /// This class represent generic mailbox header fields. For example: Sender: header. - /// - /// - /// - /// RFC 5322. - /// header = "FiledName:" mailbox CRLF - /// - /// - public class Mail_h_Mailbox : MIME_h - { - #region Members - - private readonly string m_Name; - private readonly Mail_t_Mailbox m_pAddress; - private string m_ParseValue; - - #endregion - - #region Properties - - /// - /// Gets if this header field is modified since it has loaded. - /// - /// All new added header fields has IsModified = true. - /// Is riased when this class is disposed and this property is accessed. - public override bool IsModified - { - get { return false; } - } - - /// - /// Gets header field name. For example "Sender". - /// - public override string Name - { - get { return m_Name; } - } - - /// - /// Gets mailbox address. - /// - public Mail_t_Mailbox Address - { - get { return m_pAddress; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Header field name. For example: "Sender". - /// Mailbox value. - /// Is raised when filedName or mailbox is null reference. - /// Is raised when any of the arguments has invalid value. - public Mail_h_Mailbox(string fieldName, Mail_t_Mailbox mailbox) - { - if (fieldName == null) - { - throw new ArgumentNullException("fieldName"); - } - if (fieldName == string.Empty) - { - throw new ArgumentException("Argument 'fieldName' value must be specified."); - } - if (mailbox == null) - { - throw new ArgumentNullException("mailbox"); - } - - m_Name = fieldName; - m_pAddress = mailbox; - } - - #endregion - - #region Methods - - /// - /// Parses header field from the specified value. - /// - /// Header field value. Header field name must be included. For example: 'Sender: john.doe@domain.com'. - /// Returns parsed header field. - /// Is raised when value is null reference. - /// Is raised when header field parsing errors. - public static Mail_h_Mailbox Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - string[] name_value = value.Split(new[] {':'}, 2); - if (name_value.Length != 2) - { - throw new ParseException("Invalid header field value '" + value + "'."); - } - - MIME_Reader r = new MIME_Reader(name_value[1]); - - string word = r.QuotedReadToDelimiter(new[] {',', '<', ':'}); - // Invalid value. - if (word == null) - { - throw new ParseException("Invalid header field value '" + value + "'."); - } - // name-addr - else if (r.Peek(true) == '<') - { - Mail_h_Mailbox h = new Mail_h_Mailbox(name_value[0], - new Mail_t_Mailbox( - word != null - ? MIME_Encoding_EncodedWord.DecodeS( - TextUtils.UnQuoteString(word)) - : null, - r.ReadParenthesized())); - h.m_ParseValue = value; - - return h; - } - // addr-spec - else - { - Mail_h_Mailbox h = new Mail_h_Mailbox(name_value[0], new Mail_t_Mailbox(null, word)); - h.m_ParseValue = value; - - return h; - } - } - - /// - /// Returns header field as string. - /// - /// 8-bit words ecnoder. Value null means that words are not encoded. - /// Charset to use to encode 8-bit characters. Value null means parameters not encoded. - /// Returns header field as string. - public override string ToString(MIME_Encoding_EncodedWord wordEncoder, Encoding parmetersCharset) - { - if (m_ParseValue != null) - { - return m_ParseValue; - } - else - { - return m_Name + ": " + m_pAddress.ToString(wordEncoder) + "\r\n"; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_h_MailboxList.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_h_MailboxList.cs deleted file mode 100644 index 5c39ec979..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_h_MailboxList.cs +++ /dev/null @@ -1,223 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mail -{ - #region usings - - using System; - using System.Text; - using MIME; - - #endregion - - /// - /// This class represent generic mailbox-list header fields. For example: From header. - /// - /// - /// - /// RFC 5322. - /// header = "FiledName:" mailbox-list CRLF - /// mailbox-list = (mailbox *("," mailbox)) / obs-mbox-list - /// - /// - public class Mail_h_MailboxList : MIME_h - { - #region Members - - private readonly string m_Name; - private readonly Mail_t_MailboxList m_pAddresses; - private string m_ParseValue; - - #endregion - - #region Properties - - /// - /// Gets if this header field is modified since it has loaded. - /// - /// All new added header fields has IsModified = true. - /// Is riased when this class is disposed and this property is accessed. - public override bool IsModified - { - get { return m_pAddresses.IsModified; } - } - - /// - /// Gets header field name. For example "From". - /// - public override string Name - { - get { return m_Name; } - } - - /// - /// Gets addresses collection. - /// - public Mail_t_MailboxList Addresses - { - get { return m_pAddresses; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Header field name. For example: "To". - /// Addresses collection. - /// Is raised when filedName or values is null reference. - /// Is raised when any of the arguments has invalid value. - public Mail_h_MailboxList(string filedName, Mail_t_MailboxList values) - { - if (filedName == null) - { - throw new ArgumentNullException("filedName"); - } - if (filedName == string.Empty) - { - throw new ArgumentException("Argument 'filedName' value must be specified."); - } - if (values == null) - { - throw new ArgumentNullException("values"); - } - - m_Name = filedName; - m_pAddresses = values; - } - - #endregion - - #region Methods - - /// - /// Parses header field from the specified value. - /// - /// Header field value. Header field name must be included. For example: 'Content-Type: text/plain'. - /// Returns parsed header field. - /// Is raised when value is null reference. - /// Is raised when header field parsing errors. - public static Mail_h_MailboxList Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - string[] name_value = value.Split(new[] {':'}, 2); - if (name_value.Length != 2) - { - throw new ParseException("Invalid header field value '" + value + "'."); - } - - /* RFC 5322 3.4. - mailbox = name-addr / addr-spec - - name-addr = [display-name] angle-addr - - angle-addr = [CFWS] "<" addr-spec ">" [CFWS] / obs-angle-addr - - display-name = phrase - - mailbox-list = (mailbox *("," mailbox)) / obs-mbox-list - */ - - MIME_Reader r = new MIME_Reader(MIME_Utils.UnfoldHeader(name_value.Length == 2 ? name_value[1].TrimStart() : "")); - - Mail_h_MailboxList retVal = new Mail_h_MailboxList(name_value[0], new Mail_t_MailboxList()); - - while (true) - { - string word = r.QuotedReadToDelimiter(new[] {',', '<', ':'}); - // We processed all data. - if (word == null && r.Available == 0) - { - break; - } - // name-addr - else if (r.Peek(true) == '<') - { - retVal.m_pAddresses.Add( - new Mail_t_Mailbox( - word != null - ? MIME_Encoding_EncodedWord.DecodeAll(word) - : null, - r.ReadParenthesized())); - } - // addr-spec - else - { - retVal.m_pAddresses.Add(new Mail_t_Mailbox(null, word)); - } - - // We have more addresses. - if (r.Peek(true) == ',') - { - r.Char(false); - } - } - - retVal.m_ParseValue = value; - retVal.m_pAddresses.AcceptChanges(); - - return retVal; - } - - /// - /// Returns header field as string. - /// - /// 8-bit words ecnoder. Value null means that words are not encoded. - /// Charset to use to encode 8-bit characters. Value null means parameters not encoded. - /// Returns header field as string. - public override string ToString(MIME_Encoding_EncodedWord wordEncoder, Encoding parmetersCharset) - { - if (IsModified) - { - StringBuilder retVal = new StringBuilder(); - retVal.Append(Name + ": "); - for (int i = 0; i < m_pAddresses.Count; i++) - { - if (i > 0) - { - retVal.Append("\t"); - } - - // Don't add ',' for last item. - if (i == (m_pAddresses.Count - 1)) - { - retVal.Append(m_pAddresses[i].ToString(wordEncoder) + "\r\n"); - } - else - { - retVal.Append(m_pAddresses[i].ToString(wordEncoder) + ",\r\n"); - } - } - - return retVal.ToString(); - } - else - { - return m_ParseValue; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_h_Received.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_h_Received.cs deleted file mode 100644 index 2cb236f05..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_h_Received.cs +++ /dev/null @@ -1,517 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mail -{ - #region usings - - using System; - using System.Net; - using System.Text; - using MIME; - - #endregion - - /// - /// Represents "Received:" header. Defined in RFC 5321 4.4. - /// - /// - /// - /// RFC 5321 4.4. - /// Time-stamp-line = "Received:" FWS Stamp CRLF - /// - /// Stamp = From-domain By-domain Opt-info [CFWS] ";" FWS date-time - /// ; where "date-time" is as defined in RFC 5322 [4] - /// ; but the "obs-" forms, especially two-digit - /// ; years, are prohibited in SMTP and MUST NOT be used. - /// - /// From-domain = "FROM" FWS Extended-Domain - /// - /// By-domain = CFWS "BY" FWS Extended-Domain - /// - /// Extended-Domain = Domain / ( Domain FWS "(" TCP-info ")" ) / ( address-literal FWS "(" TCP-info ")" ) - /// - /// TCP-info = address-literal / ( Domain FWS address-literal ) - /// ; Information derived by server from TCP connection not client EHLO. - /// - /// Opt-info = [Via] [With] [ID] [For] [Additional-Registered-Clauses] - /// - /// Via = CFWS "VIA" FWS Link - /// - /// With = CFWS "WITH" FWS Protocol - /// - /// ID = CFWS "ID" FWS ( Atom / msg-id ) - /// ; msg-id is defined in RFC 5322 [4] - /// - /// For = CFWS "FOR" FWS ( Path / Mailbox ) - /// - /// Additional-Registered-Clauses = CFWS Atom FWS String - /// - /// Link = "TCP" / Addtl-Link - /// - /// Addtl-Link = Atom - /// - /// Protocol = "ESMTP" / "SMTP" / Attdl-Protocol - /// - /// Mailbox = Local-part "@" ( Domain / address-literal ) - /// - /// - public class Mail_h_Received : MIME_h - { - #region Members - - private string m_By = ""; - private string m_For; - private string m_From = ""; - private string m_ID; - private bool m_IsModified; - private string m_ParseValue; - private Mail_t_TcpInfo m_pBy_TcpInfo; - private Mail_t_TcpInfo m_pFrom_TcpInfo; - private DateTime m_Time; - private string m_Via; - private string m_With; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Host from where message was received. - /// Host name what received message. - /// Date time when message was received. - /// Is raised when from or by is null reference. - /// Is raised when any of the arguments has invalid value. - public Mail_h_Received(string from, string by, DateTime time) - { - if (from == null) - { - throw new ArgumentNullException("from"); - } - if (from == string.Empty) - { - throw new ArgumentException("Argument 'from' value must be specified.", "from"); - } - if (by == null) - { - throw new ArgumentNullException("by"); - } - if (by == string.Empty) - { - throw new ArgumentException("Argument 'by' value must be specified.", "by"); - } - - m_From = from; - m_By = by; - m_Time = time; - } - - #endregion - - #region Properties - - /// - /// Gets or sets host name what received message. - /// - /// Is raised when null reference passed. - /// Is raised when invalid value passed. - public string By - { - get { return m_By; } - - set - { - if (value == null) - { - throw new ArgumentNullException("By"); - } - if (value == string.Empty) - { - throw new ArgumentException("Property 'By' value must be specified", "By"); - } - - m_By = value; - m_IsModified = true; - } - } - - /// - /// Gets or sets By TCP-Info value. Value null means not specified. - /// - /// RFC defines it, but i don't see any point about that value. - public Mail_t_TcpInfo By_TcpInfo - { - get { return m_pBy_TcpInfo; } - - set - { - m_pBy_TcpInfo = value; - m_IsModified = true; - } - } - - /// - /// Gets or sets mailbox for who message was received. Value null means not specified. - /// - public string For - { - get { return m_For; } - - set - { - m_For = value; - m_IsModified = true; - } - } - - /// - /// Gets or sets host from where message was received. - /// - /// Normally this is just EHLO/HELO host name what client reported to SMTP server. - /// Is raised when null reference passed. - /// Is raised when invalid value passed. - public string From - { - get { return m_From; } - - set - { - if (value == null) - { - throw new ArgumentNullException("From"); - } - if (value == string.Empty) - { - throw new ArgumentException("Property 'From' value must be specified", "From"); - } - - m_From = value; - m_IsModified = true; - } - } - - /// - /// Gets or sets From TCP-Info value. Value null means not specified. - /// - /// This value is message sender host IP and optional dns host name. - /// This value is based on server connection info, not client reported info(EHLO/HELO). - /// - public Mail_t_TcpInfo From_TcpInfo - { - get { return m_pFrom_TcpInfo; } - - set - { - m_pFrom_TcpInfo = value; - m_IsModified = true; - } - } - - /// - /// Gets or sets ID value. Value null means not specified. - /// - public string ID - { - get { return m_ID; } - - set - { - m_ID = value; - m_IsModified = true; - } - } - - /// - /// Gets if this header field is modified since it has loaded. - /// - /// All new added header fields has IsModified = true. - /// Is riased when this class is disposed and this property is accessed. - public override bool IsModified - { - get { return m_IsModified; } - } - - /// - /// Returns always "Received". - /// - public override string Name - { - get { return "Received"; } - } - - /// - /// Gets or sets time when message was received. - /// - public DateTime Time - { - get { return m_Time; } - - set - { - m_Time = value; - m_IsModified = true; - } - } - - /// - /// Gets or sets non-internet transport. Value null means not specified. - /// - public string Via - { - get { return m_Via; } - - set - { - m_Via = value; - m_IsModified = true; - } - } - - /// - /// Gets or sets receiving protocol. Value null means not specified. - /// - public string With - { - get { return m_With; } - - set - { - m_With = value; - m_IsModified = true; - } - } - - #endregion - - #region Methods - - /// - /// Parses header field from the specified value. - /// - /// Header field value. Header field name must be included. For example: 'Sender: john.doe@domain.com'. - /// Returns parsed header field. - /// Is raised when value is null reference. - /// Is raised when header field parsing errors. - public static Mail_h_Received Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - string[] name_value = value.Split(new[] {':'}, 2); - if (name_value.Length != 2) - { - throw new ParseException("Invalid header field value '" + value + "'."); - } - - Mail_h_Received retVal = new Mail_h_Received("a", "b", DateTime.MinValue); - - MIME_Reader r = new MIME_Reader(name_value[1]); - - while (true) - { - string word = r.Word(); - // We processed all data. - if (word == null && r.Available == 0) - { - break; - } - // We have comment, just eat it. - else if (r.StartsWith("(")) - { - r.ReadParenthesized(); - } - // We have date-time. - else if (r.StartsWith(";")) - { - // Eat ';' - r.Char(false); - - retVal.m_Time = MIME_Utils.ParseRfc2822DateTime(r.ToEnd()); - } - else - { - // We have some unexpected char like: .,= ... . Just eat it. - if (word == null) - { - r.Char(true); - continue; - } - - word = word.ToUpperInvariant(); - - if (word == "FROM") - { - retVal.m_From = r.DotAtom(); - - r.ToFirstChar(); - if (r.StartsWith("(")) - { - string[] parts = r.ReadParenthesized().Split(' '); - if (parts.Length == 1) - { - if (Net_Utils.IsIPAddress(parts[0])) - { - retVal.m_pFrom_TcpInfo = new Mail_t_TcpInfo(IPAddress.Parse(parts[0]), - null); - } - } - else if (parts.Length == 2) - { - if (Net_Utils.IsIPAddress(parts[1])) - { - retVal.m_pFrom_TcpInfo = new Mail_t_TcpInfo(IPAddress.Parse(parts[1]), - parts[0]); - } - } - } - } - else if (word == "BY") - { - retVal.m_By = r.DotAtom(); - - r.ToFirstChar(); - if (r.StartsWith("(")) - { - string[] parts = r.ReadParenthesized().Split(' '); - if (parts.Length == 1) - { - if (Net_Utils.IsIPAddress(parts[0])) - { - retVal.m_pBy_TcpInfo = new Mail_t_TcpInfo(IPAddress.Parse(parts[0]), null); - } - } - else if (parts.Length == 2) - { - if (Net_Utils.IsIPAddress(parts[1])) - { - retVal.m_pBy_TcpInfo = new Mail_t_TcpInfo(IPAddress.Parse(parts[1]), - parts[0]); - } - } - } - } - else if (word == "VIA") - { - retVal.m_Via = r.Word(); - } - else if (word == "WITH") - { - retVal.m_With = r.Word(); - } - else if (word == "ID") - { - // msg-id = [CFWS] "<" id-left "@" id-right ">" [CFWS] - - if (r.StartsWith("<")) - { - retVal.m_ID = r.ReadParenthesized(); - } - else - { - retVal.m_ID = r.Atom(); - } - } - else if (word == "FOR") - { - r.ToFirstChar(); - - // path / angle-address - if (r.StartsWith("<")) - { - retVal.m_For = r.ReadParenthesized(); - } - else - { - string mailbox = Mail_Utils.SMTP_Mailbox(r); - if (mailbox == null) - { - throw new ParseException("Invalid Received: For parameter value '" + r.ToEnd() + - "'."); - } - retVal.m_For = mailbox; - } - } - // Unknown, just eat value. - else - { - r.Word(); - } - } - } - - retVal.m_ParseValue = value; - - return retVal; - } - - /// - /// Returns header field as string. - /// - /// 8-bit words ecnoder. Value null means that words are not encoded. - /// Charset to use to encode 8-bit characters. Value null means parameters not encoded. - /// Returns header field as string. - public override string ToString(MIME_Encoding_EncodedWord wordEncoder, Encoding parmetersCharset) - { - if (IsModified) - { - StringBuilder retVal = new StringBuilder(); - - retVal.Append("Received: "); - retVal.Append("FROM " + m_From); - if (m_pFrom_TcpInfo != null) - { - retVal.Append(" (" + m_pFrom_TcpInfo + ")"); - } - retVal.Append(" BY " + m_By); - if (m_pBy_TcpInfo != null) - { - retVal.Append(" (" + m_pBy_TcpInfo + ")"); - } - if (!string.IsNullOrEmpty(m_Via)) - { - retVal.Append(" VIA " + m_Via); - } - if (!string.IsNullOrEmpty(m_With)) - { - retVal.Append(" WITH " + m_With); - } - if (!string.IsNullOrEmpty(m_ID)) - { - retVal.Append(" ID " + m_ID); - } - if (!string.IsNullOrEmpty(m_For)) - { - retVal.Append(" FOR " + m_For); - } - retVal.Append("; " + MIME_Utils.DateTimeToRfc2822(m_Time)); - retVal.Append("\r\n"); - - return retVal.ToString(); - } - else - { - return m_ParseValue; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_h_ReturnPath.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_h_ReturnPath.cs deleted file mode 100644 index 1e5aba4b3..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_h_ReturnPath.cs +++ /dev/null @@ -1,150 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mail -{ - #region usings - - using System; - using System.Text; - using MIME; - - #endregion - - /// - /// Represents "Return-Path:" header. Defined in RFC 5322 3.6.7. - /// - /// - /// - /// RFC 5322 3.6.7. - /// return = "Return-Path:" path CRLF - /// path = angle-addr / ([CFWS] "<" [CFWS] ">" [CFWS]) - /// angle-addr = [CFWS] "<" addr-spec ">" [CFWS] - /// - /// - public class Mail_h_ReturnPath : MIME_h - { - #region Members - - private string m_Address; - private bool m_IsModified; - - #endregion - - #region Properties - - /// - /// Gets if this header field is modified since it has loaded. - /// - /// All new added header fields has IsModified = true. - /// Is riased when this class is disposed and this property is accessed. - public override bool IsModified - { - get { return m_IsModified; } - } - - /// - /// Gets header field name. For example "Sender". - /// - public override string Name - { - get { return "Return-Path"; } - } - - /// - /// Gets mailbox address. Value null means null-path. - /// - public string Address - { - get { return m_Address; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Address. Value null means null-path. - public Mail_h_ReturnPath(string address) - { - m_Address = address; - } - - #endregion - - #region Methods - - /// - /// Parses header field from the specified value. - /// - /// Header field value. Header field name must be included. For example: 'Return-Path: <jhon.doe@domain.com>'. - /// Returns parsed header field. - /// Is raised when value is null reference. - /// Is raised when header field parsing errors. - public static Mail_h_ReturnPath Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - string[] name_value = value.Split(new[] {':'}, 2); - if (name_value.Length != 2) - { - throw new ParseException("Invalid header field value '" + value + "'."); - } - - Mail_h_ReturnPath retVal = new Mail_h_ReturnPath(null); - - MIME_Reader r = new MIME_Reader(name_value[1]); - r.ToFirstChar(); - // Return-Path missing <>, some server won't be honor RFC. - if (!r.StartsWith("<")) - { - retVal.m_Address = r.ToEnd(); - } - else - { - retVal.m_Address = r.ReadParenthesized(); - } - - return retVal; - } - - /// - /// Returns header field as string. - /// - /// 8-bit words ecnoder. Value null means that words are not encoded. - /// Charset to use to encode 8-bit characters. Value null means parameters not encoded. - /// Returns header field as string. - public override string ToString(MIME_Encoding_EncodedWord wordEncoder, Encoding parmetersCharset) - { - if (string.IsNullOrEmpty(m_Address)) - { - return "Return-Path: <>\r\n"; - } - else - { - return "Return-Path: <" + m_Address + ">\r\n"; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_t_Address.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_t_Address.cs deleted file mode 100644 index 97890a965..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_t_Address.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mail -{ - #region usings - - using MIME; - - #endregion - - /// - /// This class represents RFC 5322 3.4 Address class. - /// This class is base class for mailbox address and group address. - /// - public abstract class Mail_t_Address - { - #region Methods - - /// - /// Returns address as string value. - /// - /// 8-bit words ecnoder. Value null means that words are not encoded. - /// Returns address as string value. - public abstract string ToString(MIME_Encoding_EncodedWord wordEncoder); - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_t_AddressList.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_t_AddressList.cs deleted file mode 100644 index 36893cfee..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_t_AddressList.cs +++ /dev/null @@ -1,303 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mail -{ - #region usings - - using System; - using System.Collections; - using System.Collections.Generic; - using System.Text; - using System.Linq; - using MIME; - - #endregion - - /// - /// This class represents address-list. Defined in RFC 5322 3.4. - /// - public class Mail_t_AddressList : IEnumerable - { - #region Members - - private readonly List m_pList; - private bool m_IsModified; - private static System.Text.RegularExpressions.Regex m_regParser = new System.Text.RegularExpressions.Regex(@"^[\w!#$%&'*+\-/=?\^_`{|}~]+(\.[\w!#$%&'*+\-/=?\^_`{|}~]+)*" - + "@" - + @"((([\-\w]+\.)+[a-zA-Z]{2,4})|(([0-9]{1,3}\.){3}[0-9]{1,3}))$");//"^([0-9a-zA-Z]([-.\\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\\w]*[0-9a-zA-Z]\\.)+[a-zA-Z]{2,9})$"); - - #endregion - - #region Properties - - /// - /// Gets if list has modified since it was loaded. - /// - public bool IsModified - { - get { return m_IsModified; } - } - - /// - /// Gets number of items in the collection. - /// - public int Count - { - get { return m_pList.Count; } - } - - /// - /// Gets the element at the specified index. - /// - /// The zero-based index of the element to get. - /// Returns the element at the specified index. - /// Is raised when index is out of range. - public Mail_t_Address this[int index] - { - get - { - if (index < 0 || index >= m_pList.Count) - { - throw new ArgumentOutOfRangeException("index"); - } - - return m_pList[index]; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public Mail_t_AddressList() - { - m_pList = new List(); - } - - #endregion - - #region Methods - - /// - /// Inserts a address into the collection at the specified location. - /// - /// The location in the collection where you want to add the item. - /// Address to insert. - /// Is raised when index is out of range. - /// Is raised when value is null reference. - public void Insert(int index, Mail_t_Address value) - { - if (index < 0 || index > m_pList.Count) - { - throw new ArgumentOutOfRangeException("index"); - } - if (value == null) - { - throw new ArgumentNullException("value"); - } - - m_pList.Insert(index, value); - m_IsModified = true; - } - - /// - /// Adds specified address to the end of the collection. - /// - /// Address to add. - /// Is raised when value is null reference value. - public void Add(Mail_t_Address value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - m_pList.Add(value); - m_IsModified = true; - } - - /// - /// Removes specified item from the collection. - /// - /// Address to remove. - /// Is raised when value is null reference value. - public void Remove(Mail_t_Address value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - m_pList.Remove(value); - } - - /// - /// Removes all items from the collection. - /// - public void Clear() - { - m_pList.Clear(); - m_IsModified = true; - } - - /// - /// Copies addresses to new array. - /// - /// Returns addresses array. - public Mail_t_Address[] ToArray() - { - return m_pList.ToArray(); - } - - /// - /// Returns address-list as string. - /// - /// Returns address-list as string. - public override string ToString() - { - StringBuilder retVal = new StringBuilder(); - for (int i = 0; i < m_pList.Count; i++) - { - if (i == (m_pList.Count - 1)) - { - retVal.Append(m_pList[i].ToString()); - } - else - { - retVal.Append(m_pList[i] + ","); - } - } - - return retVal.ToString(); - } - - /// - /// Gets enumerator. - /// - /// - public IEnumerator GetEnumerator() - { - return m_pList.GetEnumerator(); - } - - #endregion - - #region Internal methods - - /// - /// Resets IsModified property to false. - /// - internal void AcceptChanges() - { - m_IsModified = false; - } - - #endregion - - public static Mail_t_AddressList ParseAddressList(string value) - { - MIME_Reader r = new MIME_Reader(value); - - /* RFC 5322 3.4. - address = mailbox / group - - mailbox = name-addr / addr-spec - - name-addr = [display-name] angle-addr - - angle-addr = [CFWS] "<" addr-spec ">" [CFWS] / obs-angle-addr - - group = display-name ":" [group-list] ";" [CFWS] - - display-name = phrase - - mailbox-list = (mailbox *("," mailbox)) / obs-mbox-list - - address-list = (address *("," address)) / obs-addr-list - - group-list = mailbox-list / CFWS / obs-group-list - */ - - Mail_t_AddressList retVal = new Mail_t_AddressList(); - - while (true) - { - string word = r.QuotedReadToDelimiter(new[] { ',', '<', ':' }); - // We processed all data. - if (word == null && r.Available == 0) - { - if (retVal.Count == 0) - { - if (CheckEmail(value)) - { - retVal.Add(new Mail_t_Mailbox(null, value)); - } - } - - break; - } - // skip old group address format - else if (r.Peek(true) == ':') - { - // Consume ':' - r.Char(true); - } - // name-addr - else if (r.Peek(true) == '<') - { - string address = r.ReadParenthesized(); - - if (CheckEmail(address)) - { - retVal.Add( - new Mail_t_Mailbox( - word != null - ? MIME_Encoding_EncodedWord.DecodeS(TextUtils.UnQuoteString(word)) - : null, - address)); - } - } - // addr-spec - else - { - if (CheckEmail(word)) - { - retVal.Add(new Mail_t_Mailbox(null, word)); - } - } - - // We have more addresses. - if (r.Peek(true) == ',') - { - r.Char(false); - } - } - - return retVal; - } - - private static bool CheckEmail(string EmailAddress) - { - return !string.IsNullOrEmpty(EmailAddress) ? m_regParser.IsMatch(EmailAddress.Trim()) : false; - } - - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_t_Group.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_t_Group.cs deleted file mode 100644 index b6c5918a9..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_t_Group.cs +++ /dev/null @@ -1,126 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mail -{ - #region usings - - using System.Collections.Generic; - using System.Text; - using MIME; - - #endregion - - /// - /// Defined in RFC 2822 3.4. - /// - public class Mail_t_Group : Mail_t_Address - { - #region Members - - private readonly List m_pList; - private string m_DisplayName; - - #endregion - - #region Properties - - /// - /// Gets or sets diplay name. Value null means not specified. - /// - public string DisplayName - { - get { return m_DisplayName; } - - set { m_DisplayName = value; } - } - - /// - /// Gets groiup address members collection. - /// - public List Members - { - get { return m_pList; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Display name. Value null means not specified. - public Mail_t_Group(string displayName) - { - m_DisplayName = displayName; - - m_pList = new List(); - } - - #endregion - - #region Methods - - /// - /// Returns mailbox as string. - /// - /// Returns mailbox as string. - public override string ToString() - { - return ToString(null); - } - - /// - /// Returns address as string value. - /// - /// 8-bit words ecnoder. Value null means that words are not encoded. - /// Returns address as string value. - public override string ToString(MIME_Encoding_EncodedWord wordEncoder) - { - StringBuilder retVal = new StringBuilder(); - if (string.IsNullOrEmpty(m_DisplayName)) - { - retVal.Append(":"); - } - else - { - if (MIME_Encoding_EncodedWord.MustEncode(m_DisplayName)) - { - retVal.Append(wordEncoder.Encode(m_DisplayName) + ":"); - } - else - { - retVal.Append(TextUtils.QuoteString(m_DisplayName) + ":"); - } - } - for (int i = 0; i < m_pList.Count; i++) - { - retVal.Append(m_pList[i].ToString(wordEncoder)); - if (i < (m_pList.Count - 1)) - { - retVal.Append(","); - } - } - retVal.Append(";"); - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_t_Mailbox.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_t_Mailbox.cs deleted file mode 100644 index db82c2064..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_t_Mailbox.cs +++ /dev/null @@ -1,127 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mail -{ - #region usings - - using System; - using MIME; - - #endregion - - /// - /// This class represents "mailbox" address. Defined in RFC 5322 3.4. - /// - /// - /// - /// RFC 5322 3.4. - /// mailbox = name-addr / addr-spec - /// name-addr = [display-name] angle-addr - /// angle-addr = [CFWS] "<" addr-spec ">" [CFWS] - /// - /// - public class Mail_t_Mailbox : Mail_t_Address - { - #region Members - - private readonly string m_Address; - private readonly string m_DisplayName; - - #endregion - - #region Properties - - /// - /// Gets display name. Value null means not specified. - /// - public string DisplayName - { - get { return m_DisplayName; } - } - - /// - /// Gets address. - /// - public string Address - { - get { return m_Address; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Display name. Value null means not specified. - /// Email address. - /// Is raised when address is null reference. - public Mail_t_Mailbox(string displayName, string address) - { - if (address == null) - { - throw new ArgumentNullException("address"); - } - - m_DisplayName = displayName; - m_Address = address; - } - - #endregion - - #region Methods - - /// - /// Returns mailbox as string. - /// - /// Returns mailbox as string. - public override string ToString() - { - return ToString(null).Trim(); - } - - /// - /// Returns address as string value. - /// - /// 8-bit words ecnoder. Value null means that words are not encoded. - /// Returns address as string value. - public override string ToString(MIME_Encoding_EncodedWord wordEncoder) - { - if (string.IsNullOrEmpty(m_DisplayName)) - { - return "<" + m_Address.Trim() + ">"; - } - else - { - if (MIME_Encoding_EncodedWord.MustEncode(m_DisplayName)) - { - return wordEncoder == null - ? (m_DisplayName + " " + "<" + m_Address.Trim() + ">") - : (wordEncoder.Encode(m_DisplayName) + " " + "<" + m_Address.Trim() + ">"); - } - else - { - return TextUtils.QuoteString(m_DisplayName) + " " + "<" + m_Address.Trim() + ">"; - } - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_t_MailboxList.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_t_MailboxList.cs deleted file mode 100644 index 519da5ccb..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_t_MailboxList.cs +++ /dev/null @@ -1,206 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mail -{ - #region usings - - using System; - using System.Collections; - using System.Collections.Generic; - using System.Text; - - #endregion - - /// - /// This class represents mailbox-list. Defined in RFC 5322 3.4. - /// - public class Mail_t_MailboxList : IEnumerable - { - #region Members - - private readonly List m_pList; - private bool m_IsModified; - - #endregion - - #region Properties - - /// - /// Gets if list has modified since it was loaded. - /// - public bool IsModified - { - get { return m_IsModified; } - } - - /// - /// Gets number of items in the collection. - /// - public int Count - { - get { return m_pList.Count; } - } - - /// - /// Gets the element at the specified index. - /// - /// The zero-based index of the element to get. - /// Returns the element at the specified index. - /// Is raised when index is out of range. - public Mail_t_Mailbox this[int index] - { - get - { - if (index < 0 || index >= m_pList.Count) - { - throw new ArgumentOutOfRangeException("index"); - } - - return m_pList[index]; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public Mail_t_MailboxList() - { - m_pList = new List(); - } - - #endregion - - #region Methods - - /// - /// Inserts a address into the collection at the specified location. - /// - /// The location in the collection where you want to add the item. - /// Address to insert. - /// Is raised when index is out of range. - /// Is raised when value is null reference. - public void Insert(int index, Mail_t_Mailbox value) - { - if (index < 0 || index > m_pList.Count) - { - throw new ArgumentOutOfRangeException("index"); - } - if (value == null) - { - throw new ArgumentNullException("value"); - } - - m_pList.Insert(index, value); - m_IsModified = true; - } - - /// - /// Adds specified address to the end of the collection. - /// - /// Address to add. - /// Is raised when value is null reference value. - public void Add(Mail_t_Mailbox value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - m_pList.Add(value); - m_IsModified = true; - } - - /// - /// Removes specified item from the collection. - /// - /// Address to remove. - /// Is raised when value is null reference value. - public void Remove(Mail_t_Mailbox value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - m_pList.Remove(value); - } - - /// - /// Removes all items from the collection. - /// - public void Clear() - { - m_pList.Clear(); - m_IsModified = true; - } - - /// - /// Copies addresses to new array. - /// - /// Returns addresses array. - public Mail_t_Mailbox[] ToArray() - { - return m_pList.ToArray(); - } - - /// - /// Gets enumerator. - /// - /// - public IEnumerator GetEnumerator() - { - return m_pList.GetEnumerator(); - } - - public override string ToString() - { - StringBuilder retVal = new StringBuilder(); - for (int i = 0; i < m_pList.Count; i++) - { - if (i == (m_pList.Count - 1)) - { - retVal.Append(m_pList[i].ToString().Trim()); - } - else - { - retVal.Append(m_pList[i].ToString().Trim() + ","); - } - } - - return retVal.ToString(); - } - - #endregion - - #region Internal methods - - /// - /// Resets IsModified property to false. - /// - internal void AcceptChanges() - { - m_IsModified = false; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_t_TcpInfo.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_t_TcpInfo.cs deleted file mode 100644 index 00cf0dbb2..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Mail/Mail_t_TcpInfo.cs +++ /dev/null @@ -1,107 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mail -{ - #region usings - - using System; - using System.Net; - - #endregion - - /// - /// Represents Received: header "TCP-info" value. Defined in RFC 5321. 4.4. - /// - /// - /// - /// RFC 5321 4.4. - /// TCP-info = address-literal / ( Domain FWS address-literal ) - /// address-literal = "[" ( IPv4-address-literal / IPv6-address-literal / General-address-literal ) "]" - /// - /// - public class Mail_t_TcpInfo - { - #region Members - - private readonly string m_HostName; - private readonly IPAddress m_pIP; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// IP address. - /// Host name. - /// Is raised when ip is null reference. - public Mail_t_TcpInfo(IPAddress ip, string hostName) - { - if (ip == null) - { - throw new ArgumentNullException("ip"); - } - - m_pIP = ip; - m_HostName = hostName; - } - - #endregion - - #region Properties - - /// - /// Gets host value. Value null means not specified. - /// - public string HostName - { - get { return m_HostName; } - } - - /// - /// Gets IP address. - /// - public IPAddress IP - { - get { return m_pIP; } - } - - #endregion - - #region Methods - - /// - /// Returns this as string. - /// - /// Returns this as string. - public override string ToString() - { - if (string.IsNullOrEmpty(m_HostName)) - { - return "[" + m_pIP + "]"; - } - else - { - return m_HostName + " [" + m_pIP + "]"; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/CMLangConvertCharset.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/CMLangConvertCharset.cs deleted file mode 100644 index 34ebd0887..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/CMLangConvertCharset.cs +++ /dev/null @@ -1,26 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System.Runtime.InteropServices; - - [ComImport, Guid("D66D6F98-CDAA-11D0-B822-00C04FC9B31F"), CoClass(typeof(CMLangConvertCharsetClass))] - public interface CMLangConvertCharset : IMLangConvertCharset - { - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/CMLangConvertCharsetClass.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/CMLangConvertCharsetClass.cs deleted file mode 100644 index eb9e44890..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/CMLangConvertCharsetClass.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Security; - - [ComImport, ClassInterface((short) 0), TypeLibType((short) 2), Guid("D66D6F99-CDAA-11D0-B822-00C04FC9B31F")] - public class CMLangConvertCharsetClass : IMLangConvertCharset, CMLangConvertCharset - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void DoConversion([In] ref byte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref byte pDstStr, [In, Out] ref uint pcDstSize); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void DoConversionFromUnicode([In] ref ushort pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref sbyte pDstStr, [In, Out] ref uint pcDstSize); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void DoConversionToUnicode([In] ref sbyte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref ushort pDstStr, [In, Out] ref uint pcDstSize); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void GetDestinationCodePage(out uint puiDstCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void GetProperty(out uint pdwProperty); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void GetSourceCodePage(out uint puiSrcCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void Initialize([In] uint uiSrcCodePage, [In] uint uiDstCodePage, [In] uint dwProperty); - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/CMLangString.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/CMLangString.cs deleted file mode 100644 index ae264a3fe..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/CMLangString.cs +++ /dev/null @@ -1,26 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System.Runtime.InteropServices; - - [ComImport, CoClass(typeof(CMLangStringClass)), Guid("C04D65CE-B70D-11D0-B188-00AA0038C969")] - public interface CMLangString : IMLangString - { - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/CMLangStringClass.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/CMLangStringClass.cs deleted file mode 100644 index 45b256e23..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/CMLangStringClass.cs +++ /dev/null @@ -1,85 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Security; - - [ComImport, Guid("C04D65CF-B70D-11D0-B188-00AA0038C969"), ComConversionLoss, ClassInterface((short) 0), TypeLibType((short) 2)] - public class CMLangStringClass : IMLangString, CMLangString, IMLangStringWStr, IMLangStringAStr - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void GetAStr([In] int lSrcPos, [In] int lSrcLen, [In] uint uCodePageIn, out uint puCodePageOut, out sbyte pszDest, [In] int cchDest, out int pcchActual, out int plActualLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern int GetLength(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void GetLocale([In] int lSrcPos, [In] int lSrcMaxLen, out uint plocale, out int plLocalePos, out int plLocaleLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void GetMLStr([In] int lSrcPos, [In] int lSrcLen, [In, MarshalAs(UnmanagedType.IUnknown)] object pUnkOuter, [In] uint dwClsContext, [In] ref Guid piid, [MarshalAs(UnmanagedType.IUnknown)] out object ppDestMLStr, out int plDestPos, out int plDestLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void GetStrBufA([In] int lSrcPos, [In] int lSrcMaxLen, out uint puDestCodePage, [MarshalAs(UnmanagedType.Interface)] out IMLangStringBufA ppDestBuf, out int plDestLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void GetStrBufW([In] int lSrcPos, [In] int lSrcMaxLen, [MarshalAs(UnmanagedType.Interface)] out IMLangStringBufW ppDestBuf, out int plDestLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void GetWStr([In] int lSrcPos, [In] int lSrcLen, out ushort pszDest, [In] int cchDest, out int pcchActual, out int plActualLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern int IMLangStringAStr_GetLength(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMLangStringAStr_GetLocale([In] int lSrcPos, [In] int lSrcMaxLen, out uint plocale, out int plLocalePos, out int plLocaleLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMLangStringAStr_GetMLStr([In] int lSrcPos, [In] int lSrcLen, [In, MarshalAs(UnmanagedType.IUnknown)] object pUnkOuter, [In] uint dwClsContext, [In] ref Guid piid, [MarshalAs(UnmanagedType.IUnknown)] out object ppDestMLStr, out int plDestPos, out int plDestLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMLangStringAStr_SetLocale([In] int lDestPos, [In] int lDestLen, [In] uint locale); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMLangStringAStr_SetMLStr([In] int lDestPos, [In] int lDestLen, [In, MarshalAs(UnmanagedType.IUnknown)] object pSrcMLStr, [In] int lSrcPos, [In] int lSrcLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMLangStringAStr_Sync([In] int fNoAccess); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern int IMLangStringWStr_GetLength(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMLangStringWStr_GetMLStr([In] int lSrcPos, [In] int lSrcLen, [In, MarshalAs(UnmanagedType.IUnknown)] object pUnkOuter, [In] uint dwClsContext, [In] ref Guid piid, [MarshalAs(UnmanagedType.IUnknown)] out object ppDestMLStr, out int plDestPos, out int plDestLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMLangStringWStr_SetMLStr([In] int lDestPos, [In] int lDestLen, [In, MarshalAs(UnmanagedType.IUnknown)] object pSrcMLStr, [In] int lSrcPos, [In] int lSrcLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMLangStringWStr_Sync([In] int fNoAccess); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void LockAStr([In] int lSrcPos, [In] int lSrcLen, [In] int lFlags, [In] uint uCodePageIn, [In] int cchRequest, out uint puCodePageOut, [Out] IntPtr ppszDest, out int pcchDest, out int plDestLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void LockWStr([In] int lSrcPos, [In] int lSrcLen, [In] int lFlags, [In] int cchRequest, [Out] IntPtr ppszDest, out int pcchDest, out int plDestLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void SetAStr([In] int lDestPos, [In] int lDestLen, [In] uint uCodePage, [In] ref sbyte pszSrc, [In] int cchSrc, out int pcchActual, out int plActualLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void SetLocale([In] int lDestPos, [In] int lDestLen, [In] uint locale); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void SetMLStr([In] int lDestPos, [In] int lDestLen, [In, MarshalAs(UnmanagedType.IUnknown)] object pSrcMLStr, [In] int lSrcPos, [In] int lSrcLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void SetStrBufA([In] int lDestPos, [In] int lDestLen, [In] uint uCodePage, [In, MarshalAs(UnmanagedType.Interface)] IMLangStringBufA pSrcBuf, out int pcchActual, out int plActualLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void SetStrBufW([In] int lDestPos, [In] int lDestLen, [In, MarshalAs(UnmanagedType.Interface)] IMLangStringBufW pSrcBuf, out int pcchActual, out int plActualLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void SetWStr([In] int lDestPos, [In] int lDestLen, [In] ref ushort pszSrc, [In] int cchSrc, out int pcchActual, out int plActualLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void Sync([In] int fNoAccess); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void UnlockAStr([In] ref sbyte pszSrc, [In] int cchSrc, out int pcchActual, out int plActualLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void UnlockWStr([In] ref ushort pszSrc, [In] int cchSrc, out int pcchActual, out int plActualLen); - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/CMultiLanguage.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/CMultiLanguage.cs deleted file mode 100644 index 5ff32a656..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/CMultiLanguage.cs +++ /dev/null @@ -1,26 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System.Runtime.InteropServices; - - [ComImport, Guid("275C23E1-3747-11D0-9FEA-00AA003F8646"), CoClass(typeof(CMultiLanguageClass))] - public interface CMultiLanguage : IMultiLanguage - { - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/CMultiLanguageClass.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/CMultiLanguageClass.cs deleted file mode 100644 index c26283090..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/CMultiLanguageClass.cs +++ /dev/null @@ -1,237 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Security; - - [ComImport, TypeLibType((short) 2), ClassInterface((short) 0), Guid("275C23E2-3747-11D0-9FEA-00AA003F8646")] - public class CMultiLanguageClass : IMultiLanguage, CMultiLanguage, IMLangCodePages, IMLangFontLink, IMLangLineBreakConsole, IMultiLanguage2, IMLangFontLink2, IMultiLanguage3 - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void BreakLineA([In] uint locale, [In] uint uCodePage, [In] ref sbyte pszSrc, [In] int cchSrc, [In] int cMaxColumns, out int pcchLine, out int pcchSkip); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void BreakLineML([In, MarshalAs(UnmanagedType.Interface)] CMLangString pSrcMLStr, [In] int lSrcPos, [In] int lSrcLen, [In] int cMinColumns, [In] int cMaxColumns, out int plLineLen, out int plSkipLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void BreakLineW([In] uint locale, [In] ref ushort pszSrc, [In] int cchSrc, [In] int cMaxColumns, out int pcchLine, out int pcchSkip); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void CodePagesToCodePage([In] uint dwCodePages, [In] uint uDefaultCodePage, out uint puCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void CodePageToCodePages([In] uint uCodePage, out uint pdwCodePages); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void CodePageToScriptID([In] uint uiCodePage, out byte pSid); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void ConvertString([In, Out] ref uint pdwMode, [In] uint dwSrcEncoding, [In] uint dwDstEncoding, [In] ref byte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref byte pDstStr, [In, Out] ref uint pcDstSize); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void ConvertStringFromUnicode([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref ushort pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref sbyte pDstStr, [In, Out] ref uint pcDstSize); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void ConvertStringFromUnicodeEx([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref ushort pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref sbyte pDstStr, [In, Out] ref uint pcDstSize, [In] uint dwFlag, [In] ref ushort lpFallBack); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void ConvertStringInIStream([In, Out] ref uint pdwMode, [In] uint dwFlag, [In] ref ushort lpFallBack, [In] uint dwSrcEncoding, [In] uint dwDstEncoding, [In, MarshalAs(UnmanagedType.Interface)] IStream pstmIn, [In, MarshalAs(UnmanagedType.Interface)] IStream pstmOut); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void ConvertStringReset(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void ConvertStringToUnicode([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref sbyte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref ushort pDstStr, [In, Out] ref uint pcDstSize); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void ConvertStringToUnicodeEx([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref sbyte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref ushort pDstStr, [In, Out] ref uint pcDstSize, [In] uint dwFlag, [In] ref ushort lpFallBack); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void CreateConvertCharset([In] uint uiSrcCodePage, [In] uint uiDstCodePage, [In] uint dwProperty, [MarshalAs(UnmanagedType.Interface)] out CMLangConvertCharset ppMLangConvertCharset); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void DetectCodepageInIStream([In] MLDETECTCP flags, - [In] uint dwPrefWinCodePage, - [In, MarshalAs(UnmanagedType.Interface)] IStream pstmIn, - [In, Out] ref DetectEncodingInfo lpEncoding, - [In, Out] ref int pnScores); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void DetectInputCodepage([In] MLDETECTCP flags, [In] uint dwPrefWinCodePage, - [In] ref byte pSrcStr, [In, Out] ref int pcSrcSize, - [In, Out] ref DetectEncodingInfo lpEncoding, - [In, Out] ref int pnScores); - - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void DetectOutboundCodePage([In] MLCPF dwFlags, [In, MarshalAs(UnmanagedType.LPWStr)] string lpWideCharStr, [In] uint cchWideChar, -[In] IntPtr puiPreferredCodePages, [In] uint nPreferredCodePages, [In] IntPtr puiDetectedCodePages, [In, Out] ref uint pnDetectedCodePages, [In] ref ushort lpSpecialChar); - - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void DetectOutboundCodePageInIStream([In] uint dwFlags, [In, MarshalAs(UnmanagedType.Interface)] IStream pStrIn, [In] ref uint puiPreferredCodePages, [In] uint nPreferredCodePages, [In] ref uint puiDetectedCodePages, [In, Out] ref uint pnDetectedCodePages, [In] ref ushort lpSpecialChar); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void EnumCodePages([In] uint grfFlags, [MarshalAs(UnmanagedType.Interface)] out IEnumCodePage ppEnumCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void EnumCodePages([In] uint grfFlags, [In] ushort LangId, [MarshalAs(UnmanagedType.Interface)] out IEnumCodePage ppEnumCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void EnumRfc1766([MarshalAs(UnmanagedType.Interface)] out IEnumRfc1766 ppEnumRfc1766); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void EnumRfc1766([In] ushort LangId, [MarshalAs(UnmanagedType.Interface)] out IEnumRfc1766 ppEnumRfc1766); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void EnumScripts([In] uint dwFlags, [In] ushort LangId, [MarshalAs(UnmanagedType.Interface)] out IEnumScript ppEnumScript); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void GetCharCodePages([In] ushort chSrc, out uint pdwCodePages); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void GetCharsetInfo([In, MarshalAs(UnmanagedType.BStr)] string Charset, out tagMIMECSETINFO pCharsetInfo); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void GetCodePageDescription([In] uint uiCodePage, [In] uint lcid, [In, Out, MarshalAs(UnmanagedType.LPWStr)] string lpWideCharStr, [In] int cchWideChar); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void GetCodePageInfo([In] uint uiCodePage, out tagMIMECPINFO pCodePageInfo); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void GetCodePageInfo([In] uint uiCodePage, [In] ushort LangId, out tagMIMECPINFO pCodePageInfo); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void GetFamilyCodePage([In] uint uiCodePage, out uint puiFamilyCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void GetFontCodePages([In, ComAliasName("MultiLanguage.wireHDC")] ref _RemotableHandle hDC, [In, ComAliasName("MultiLanguage.wireHFONT")] ref _RemotableHandle hFont, out uint pdwCodePages); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void GetFontUnicodeRanges([In, ComAliasName("MultiLanguage.wireHDC")] ref _RemotableHandle hDC, [In, Out] ref uint puiRanges, out tagUNICODERANGE pUranges); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void GetLcidFromRfc1766(out uint plocale, [In, MarshalAs(UnmanagedType.BStr)] string bstrRfc1766); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void GetNumberOfCodePageInfo(out uint pcCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void GetNumberOfScripts(out uint pnScripts); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void GetRfc1766FromLcid([In] uint locale, [MarshalAs(UnmanagedType.BStr)] out string pbstrRfc1766); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void GetRfc1766Info([In] uint locale, out tagRFC1766INFO pRfc1766Info); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void GetRfc1766Info([In] uint locale, [In] ushort LangId, out tagRFC1766INFO pRfc1766Info); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void GetScriptFontInfo([In] byte sid, [In] uint dwFlags, [In, Out] ref uint puiFonts, out tagSCRIPFONTINFO pScriptFont); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void GetStrCodePages([In] ref ushort pszSrc, [In] int cchSrc, [In] uint dwPriorityCodePages, out uint pdwCodePages, out int pcchCodePages); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMLangFontLink_CodePagesToCodePage([In] uint dwCodePages, [In] uint uDefaultCodePage, out uint puCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMLangFontLink_CodePageToCodePages([In] uint uCodePage, out uint pdwCodePages); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMLangFontLink_GetCharCodePages([In] ushort chSrc, out uint pdwCodePages); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMLangFontLink_GetStrCodePages([In] ref ushort pszSrc, [In] int cchSrc, [In] uint dwPriorityCodePages, out uint pdwCodePages, out int pcchCodePages); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMLangFontLink2_CodePagesToCodePage([In] uint dwCodePages, [In] uint uDefaultCodePage, out uint puCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMLangFontLink2_CodePageToCodePages([In] uint uCodePage, out uint pdwCodePages); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMLangFontLink2_GetCharCodePages([In] ushort chSrc, out uint pdwCodePages); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMLangFontLink2_GetFontCodePages([In, ComAliasName("MultiLanguage.wireHDC")] ref _RemotableHandle hDC, [In, ComAliasName("MultiLanguage.wireHFONT")] ref _RemotableHandle hFont, out uint pdwCodePages); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMLangFontLink2_GetStrCodePages([In] ref ushort pszSrc, [In] int cchSrc, [In] uint dwPriorityCodePages, out uint pdwCodePages, out int pcchCodePages); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMLangFontLink2_ReleaseFont([In, ComAliasName("MultiLanguage.wireHFONT")] ref _RemotableHandle hFont); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMLangFontLink2_ResetFontMapping(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage2_ConvertString([In, Out] ref uint pdwMode, [In] uint dwSrcEncoding, [In] uint dwDstEncoding, [In] ref byte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref byte pDstStr, [In, Out] ref uint pcDstSize); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage2_ConvertStringFromUnicode([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref ushort pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref sbyte pDstStr, [In, Out] ref uint pcDstSize); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage2_ConvertStringReset(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage2_ConvertStringToUnicode([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref sbyte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref ushort pDstStr, [In, Out] ref uint pcDstSize); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage2_CreateConvertCharset([In] uint uiSrcCodePage, [In] uint uiDstCodePage, [In] uint dwProperty, [MarshalAs(UnmanagedType.Interface)] out CMLangConvertCharset ppMLangConvertCharset); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage2_GetCharsetInfo([In, MarshalAs(UnmanagedType.BStr)] string Charset, out tagMIMECSETINFO pCharsetInfo); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage2_GetFamilyCodePage([In] uint uiCodePage, out uint puiFamilyCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage2_GetLcidFromRfc1766(out uint plocale, [In, MarshalAs(UnmanagedType.BStr)] string bstrRfc1766); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage2_GetNumberOfCodePageInfo(out uint pcCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage2_GetRfc1766FromLcid([In] uint locale, [MarshalAs(UnmanagedType.BStr)] out string pbstrRfc1766); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage2_IsConvertible([In] uint dwSrcEncoding, [In] uint dwDstEncoding); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage3_ConvertString([In, Out] ref uint pdwMode, [In] uint dwSrcEncoding, [In] uint dwDstEncoding, [In] ref byte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref byte pDstStr, [In, Out] ref uint pcDstSize); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage3_ConvertStringFromUnicode([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref ushort pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref sbyte pDstStr, [In, Out] ref uint pcDstSize); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage3_ConvertStringFromUnicodeEx([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref ushort pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref sbyte pDstStr, [In, Out] ref uint pcDstSize, [In] uint dwFlag, [In] ref ushort lpFallBack); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage3_ConvertStringInIStream([In, Out] ref uint pdwMode, [In] uint dwFlag, [In] ref ushort lpFallBack, [In] uint dwSrcEncoding, [In] uint dwDstEncoding, [In, MarshalAs(UnmanagedType.Interface)] IStream pstmIn, [In, MarshalAs(UnmanagedType.Interface)] IStream pstmOut); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage3_ConvertStringReset(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage3_ConvertStringToUnicode([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref sbyte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref ushort pDstStr, [In, Out] ref uint pcDstSize); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage3_ConvertStringToUnicodeEx([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref sbyte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref ushort pDstStr, [In, Out] ref uint pcDstSize, [In] uint dwFlag, [In] ref ushort lpFallBack); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage3_CreateConvertCharset([In] uint uiSrcCodePage, [In] uint uiDstCodePage, [In] uint dwProperty, [MarshalAs(UnmanagedType.Interface)] out CMLangConvertCharset ppMLangConvertCharset); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage3_DetectCodepageInIStream([In] uint dwFlag, [In] uint dwPrefWinCodePage, [In, MarshalAs(UnmanagedType.Interface)] IStream pstmIn, [In, Out] ref DetectEncodingInfo lpEncoding, [In, Out] ref int pnScores); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage3_DetectInputCodepage([In] uint dwFlag, [In] uint dwPrefWinCodePage, [In] ref sbyte pSrcStr, [In, Out] ref int pcSrcSize, [In, Out] ref DetectEncodingInfo lpEncoding, [In, Out] ref int pnScores); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage3_EnumCodePages([In] uint grfFlags, [In] ushort LangId, [MarshalAs(UnmanagedType.Interface)] out IEnumCodePage ppEnumCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage3_EnumRfc1766([In] ushort LangId, [MarshalAs(UnmanagedType.Interface)] out IEnumRfc1766 ppEnumRfc1766); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage3_EnumScripts([In] uint dwFlags, [In] ushort LangId, [MarshalAs(UnmanagedType.Interface)] out IEnumScript ppEnumScript); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage3_GetCharsetInfo([In, MarshalAs(UnmanagedType.BStr)] string Charset, out tagMIMECSETINFO pCharsetInfo); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage3_GetCodePageDescription([In] uint uiCodePage, [In] uint lcid, [In, Out, MarshalAs(UnmanagedType.LPWStr)] string lpWideCharStr, [In] int cchWideChar); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage3_GetCodePageInfo([In] uint uiCodePage, [In] ushort LangId, out tagMIMECPINFO pCodePageInfo); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage3_GetFamilyCodePage([In] uint uiCodePage, out uint puiFamilyCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage3_GetLcidFromRfc1766(out uint plocale, [In, MarshalAs(UnmanagedType.BStr)] string bstrRfc1766); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage3_GetNumberOfCodePageInfo(out uint pcCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage3_GetNumberOfScripts(out uint pnScripts); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage3_GetRfc1766FromLcid([In] uint locale, [MarshalAs(UnmanagedType.BStr)] out string pbstrRfc1766); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage3_GetRfc1766Info([In] uint locale, [In] ushort LangId, out tagRFC1766INFO pRfc1766Info); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage3_IsCodePageInstallable([In] uint uiCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage3_IsConvertible([In] uint dwSrcEncoding, [In] uint dwDstEncoding); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage3_SetMimeDBSource([In] tagMIMECONTF dwSource); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage3_ValidateCodePage([In] uint uiCodePage, [In, ComAliasName("MultiLanguage.wireHWND")] ref _RemotableHandle hwnd); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IMultiLanguage3_ValidateCodePageEx([In] uint uiCodePage, [In, ComAliasName("MultiLanguage.wireHWND")] ref _RemotableHandle hwnd, [In] uint dwfIODControl); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IsCodePageInstallable([In] uint uiCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void IsConvertible([In] uint dwSrcEncoding, [In] uint dwDstEncoding); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void MapFont([In, ComAliasName("MultiLanguage.wireHDC")] ref _RemotableHandle hDC, [In] uint dwCodePages, [In] ushort chSrc, [Out, ComAliasName("MultiLanguage.wireHFONT")] IntPtr pFont); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void MapFont([In, ComAliasName("MultiLanguage.wireHDC")] ref _RemotableHandle hDC, [In] uint dwCodePages, [In, ComAliasName("MultiLanguage.wireHFONT")] ref _RemotableHandle hSrcFont, [Out, ComAliasName("MultiLanguage.wireHFONT")] IntPtr phDestFont); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void ReleaseFont([In, ComAliasName("MultiLanguage.wireHFONT")] ref _RemotableHandle hFont); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void ResetFontMapping(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void SetMimeDBSource([In] tagMIMECONTF dwSource); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void ValidateCodePage([In] uint uiCodePage, [In, ComAliasName("MultiLanguage.wireHWND")] ref _RemotableHandle hwnd); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - public virtual extern void ValidateCodePageEx([In] uint uiCodePage, [In, ComAliasName("MultiLanguage.wireHWND")] ref _RemotableHandle hwnd, [In] uint dwfIODControl); - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IEnumCodePage.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IEnumCodePage.cs deleted file mode 100644 index 16b7fd6ac..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IEnumCodePage.cs +++ /dev/null @@ -1,37 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Security; - - [ComImport, Guid("275C23E3-3747-11D0-9FEA-00AA003F8646"), InterfaceType((short) 1)] - public interface IEnumCodePage - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void Clone([MarshalAs(UnmanagedType.Interface)] out IEnumCodePage ppEnum); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void Next([In] uint celt, out tagMIMECPINFO rgelt, out uint pceltFetched); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void Reset(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void Skip([In] uint celt); - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IEnumRfc1766.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IEnumRfc1766.cs deleted file mode 100644 index c81eb52da..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IEnumRfc1766.cs +++ /dev/null @@ -1,37 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Security; - - [ComImport, Guid("3DC39D1D-C030-11D0-B81B-00C04FC9B31F"), InterfaceType((short) 1)] - public interface IEnumRfc1766 - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void Clone([MarshalAs(UnmanagedType.Interface)] out IEnumRfc1766 ppEnum); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void Next([In] uint celt, out tagRFC1766INFO rgelt, out uint pceltFetched); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void Reset(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void Skip([In] uint celt); - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IEnumScript.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IEnumScript.cs deleted file mode 100644 index f893a3d0c..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IEnumScript.cs +++ /dev/null @@ -1,37 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Security; - - [ComImport, Guid("AE5F1430-388B-11D2-8380-00C04F8F5DA1"), InterfaceType((short) 1)] - public interface IEnumScript - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void Clone([MarshalAs(UnmanagedType.Interface)] out IEnumScript ppEnum); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void Next([In] uint celt, out tagSCRIPTINFO rgelt, out uint pceltFetched); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void Reset(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void Skip([In] uint celt); - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangCodePages.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangCodePages.cs deleted file mode 100644 index 831399ebe..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangCodePages.cs +++ /dev/null @@ -1,37 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Security; - - [ComImport, Guid("359F3443-BD4A-11D0-B188-00AA0038C969"), InterfaceType((short) 1)] - public interface IMLangCodePages - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetCharCodePages([In] ushort chSrc, out uint pdwCodePages); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetStrCodePages([In] ref ushort pszSrc, [In] int cchSrc, [In] uint dwPriorityCodePages, out uint pdwCodePages, out int pcchCodePages); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void CodePageToCodePages([In] uint uCodePage, out uint pdwCodePages); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void CodePagesToCodePage([In] uint dwCodePages, [In] uint uDefaultCodePage, out uint puCodePage); - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangConvertCharset.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangConvertCharset.cs deleted file mode 100644 index f9ca86fa0..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangConvertCharset.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Security; - - [ComImport, Guid("D66D6F98-CDAA-11D0-B822-00C04FC9B31F"), InterfaceType((short) 1)] - public interface IMLangConvertCharset - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void Initialize([In] uint uiSrcCodePage, [In] uint uiDstCodePage, [In] uint dwProperty); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetSourceCodePage(out uint puiSrcCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetDestinationCodePage(out uint puiDstCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetProperty(out uint pdwProperty); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void DoConversion([In] ref byte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref byte pDstStr, [In, Out] ref uint pcDstSize); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void DoConversionToUnicode([In] ref sbyte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref ushort pDstStr, [In, Out] ref uint pcDstSize); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void DoConversionFromUnicode([In] ref ushort pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref sbyte pDstStr, [In, Out] ref uint pcDstSize); - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangFontLink.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangFontLink.cs deleted file mode 100644 index 3b313361a..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangFontLink.cs +++ /dev/null @@ -1,47 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -#pragma warning disable 0108 - -namespace MultiLanguage -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Security; - - [ComImport, InterfaceType((short) 1), ComConversionLoss, Guid("359F3441-BD4A-11D0-B188-00AA0038C969")] - public interface IMLangFontLink : IMLangCodePages - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetCharCodePages([In] ushort chSrc, out uint pdwCodePages); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetStrCodePages([In] ref ushort pszSrc, [In] int cchSrc, [In] uint dwPriorityCodePages, out uint pdwCodePages, out int pcchCodePages); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void CodePageToCodePages([In] uint uCodePage, out uint pdwCodePages); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void CodePagesToCodePage([In] uint dwCodePages, [In] uint uDefaultCodePage, out uint puCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetFontCodePages([In, ComAliasName("MultiLanguage.wireHDC")] ref _RemotableHandle hDC, [In, ComAliasName("MultiLanguage.wireHFONT")] ref _RemotableHandle hFont, out uint pdwCodePages); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void MapFont([In, ComAliasName("MultiLanguage.wireHDC")] ref _RemotableHandle hDC, [In] uint dwCodePages, [In, ComAliasName("MultiLanguage.wireHFONT")] ref _RemotableHandle hSrcFont, [Out, ComAliasName("MultiLanguage.wireHFONT")] IntPtr phDestFont); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void ReleaseFont([In, ComAliasName("MultiLanguage.wireHFONT")] ref _RemotableHandle hFont); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void ResetFontMapping(); - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangFontLink2.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangFontLink2.cs deleted file mode 100644 index 4c4029988..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangFontLink2.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -#pragma warning disable 0108 - -namespace MultiLanguage -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Security; - - [ComImport, ComConversionLoss, InterfaceType((short) 1), Guid("DCCFC162-2B38-11D2-B7EC-00C04F8F5D9A")] - public interface IMLangFontLink2 : IMLangCodePages - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetCharCodePages([In] ushort chSrc, out uint pdwCodePages); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetStrCodePages([In] ref ushort pszSrc, [In] int cchSrc, [In] uint dwPriorityCodePages, out uint pdwCodePages, out int pcchCodePages); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void CodePageToCodePages([In] uint uCodePage, out uint pdwCodePages); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void CodePagesToCodePage([In] uint dwCodePages, [In] uint uDefaultCodePage, out uint puCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetFontCodePages([In, ComAliasName("MultiLanguage.wireHDC")] ref _RemotableHandle hDC, [In, ComAliasName("MultiLanguage.wireHFONT")] ref _RemotableHandle hFont, out uint pdwCodePages); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void ReleaseFont([In, ComAliasName("MultiLanguage.wireHFONT")] ref _RemotableHandle hFont); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void ResetFontMapping(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void MapFont([In, ComAliasName("MultiLanguage.wireHDC")] ref _RemotableHandle hDC, [In] uint dwCodePages, [In] ushort chSrc, [Out, ComAliasName("MultiLanguage.wireHFONT")] IntPtr pFont); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetFontUnicodeRanges([In, ComAliasName("MultiLanguage.wireHDC")] ref _RemotableHandle hDC, [In, Out] ref uint puiRanges, out tagUNICODERANGE pUranges); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetScriptFontInfo([In] byte sid, [In] uint dwFlags, [In, Out] ref uint puiFonts, out tagSCRIPFONTINFO pScriptFont); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void CodePageToScriptID([In] uint uiCodePage, out byte pSid); - } -} - -#pragma warning restore 0108 \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangLineBreakConsole.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangLineBreakConsole.cs deleted file mode 100644 index 7db5b3155..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangLineBreakConsole.cs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Security; - - [ComImport, InterfaceType((short) 1), Guid("F5BE2EE1-BFD7-11D0-B188-00AA0038C969")] - public interface IMLangLineBreakConsole - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void BreakLineML([In, MarshalAs(UnmanagedType.Interface)] CMLangString pSrcMLStr, [In] int lSrcPos, [In] int lSrcLen, [In] int cMinColumns, [In] int cMaxColumns, out int plLineLen, out int plSkipLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void BreakLineW([In] uint locale, [In] ref ushort pszSrc, [In] int cchSrc, [In] int cMaxColumns, out int pcchLine, out int pcchSkip); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void BreakLineA([In] uint locale, [In] uint uCodePage, [In] ref sbyte pszSrc, [In] int cchSrc, [In] int cMaxColumns, out int pcchLine, out int pcchSkip); - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangString.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangString.cs deleted file mode 100644 index 69da9bdc6..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangString.cs +++ /dev/null @@ -1,37 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Security; - - [ComImport, Guid("C04D65CE-B70D-11D0-B188-00AA0038C969"), InterfaceType((short) 1)] - public interface IMLangString - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void Sync([In] int fNoAccess); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - int GetLength(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void SetMLStr([In] int lDestPos, [In] int lDestLen, [In, MarshalAs(UnmanagedType.IUnknown)] object pSrcMLStr, [In] int lSrcPos, [In] int lSrcLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetMLStr([In] int lSrcPos, [In] int lSrcLen, [In, MarshalAs(UnmanagedType.IUnknown)] object pUnkOuter, [In] uint dwClsContext, [In] ref Guid piid, [MarshalAs(UnmanagedType.IUnknown)] out object ppDestMLStr, out int plDestPos, out int plDestLen); - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangStringAStr.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangStringAStr.cs deleted file mode 100644 index d02a066fc..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangStringAStr.cs +++ /dev/null @@ -1,57 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -#pragma warning disable 0108 - -namespace MultiLanguage -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Security; - - [ComImport, Guid("C04D65D2-B70D-11D0-B188-00AA0038C969"), ComConversionLoss, InterfaceType((short) 1)] - public interface IMLangStringAStr : IMLangString - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void Sync([In] int fNoAccess); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - int GetLength(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void SetMLStr([In] int lDestPos, [In] int lDestLen, [In, MarshalAs(UnmanagedType.IUnknown)] object pSrcMLStr, [In] int lSrcPos, [In] int lSrcLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetMLStr([In] int lSrcPos, [In] int lSrcLen, [In, MarshalAs(UnmanagedType.IUnknown)] object pUnkOuter, [In] uint dwClsContext, [In] ref Guid piid, [MarshalAs(UnmanagedType.IUnknown)] out object ppDestMLStr, out int plDestPos, out int plDestLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void SetAStr([In] int lDestPos, [In] int lDestLen, [In] uint uCodePage, [In] ref sbyte pszSrc, [In] int cchSrc, out int pcchActual, out int plActualLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void SetStrBufA([In] int lDestPos, [In] int lDestLen, [In] uint uCodePage, [In, MarshalAs(UnmanagedType.Interface)] IMLangStringBufA pSrcBuf, out int pcchActual, out int plActualLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetAStr([In] int lSrcPos, [In] int lSrcLen, [In] uint uCodePageIn, out uint puCodePageOut, out sbyte pszDest, [In] int cchDest, out int pcchActual, out int plActualLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetStrBufA([In] int lSrcPos, [In] int lSrcMaxLen, out uint puDestCodePage, [MarshalAs(UnmanagedType.Interface)] out IMLangStringBufA ppDestBuf, out int plDestLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void LockAStr([In] int lSrcPos, [In] int lSrcLen, [In] int lFlags, [In] uint uCodePageIn, [In] int cchRequest, out uint puCodePageOut, [Out] IntPtr ppszDest, out int pcchDest, out int plDestLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void UnlockAStr([In] ref sbyte pszSrc, [In] int cchSrc, out int pcchActual, out int plActualLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void SetLocale([In] int lDestPos, [In] int lDestLen, [In] uint locale); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetLocale([In] int lSrcPos, [In] int lSrcMaxLen, out uint plocale, out int plLocalePos, out int plLocaleLen); - } -} - -#pragma warning restore 0108 \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangStringBufA.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangStringBufA.cs deleted file mode 100644 index 87662ca12..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangStringBufA.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Security; - - [ComImport, Guid("D24ACD23-BA72-11D0-B188-00AA0038C969"), InterfaceType((short) 1), ComConversionLoss] - public interface IMLangStringBufA - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetStatus(out int plFlags, out int pcchBuf); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void LockBuf([In] int cchOffset, [In] int cchMaxLock, [Out] IntPtr ppszBuf, out int pcchBuf); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void UnlockBuf([In] ref sbyte pszBuf, [In] int cchOffset, [In] int cchWrite); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void Insert([In] int cchOffset, [In] int cchMaxInsert, out int pcchActual); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void Delete([In] int cchOffset, [In] int cchDelete); - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangStringBufW.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangStringBufW.cs deleted file mode 100644 index 4265867f0..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangStringBufW.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Security; - - [ComImport, InterfaceType((short) 1), Guid("D24ACD21-BA72-11D0-B188-00AA0038C969"), ComConversionLoss] - public interface IMLangStringBufW - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetStatus(out int plFlags, out int pcchBuf); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void LockBuf([In] int cchOffset, [In] int cchMaxLock, [Out] IntPtr ppszBuf, out int pcchBuf); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void UnlockBuf([In] ref ushort pszBuf, [In] int cchOffset, [In] int cchWrite); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void Insert([In] int cchOffset, [In] int cchMaxInsert, out int pcchActual); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void Delete([In] int cchOffset, [In] int cchDelete); - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangStringWStr.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangStringWStr.cs deleted file mode 100644 index 8abfbeb10..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMLangStringWStr.cs +++ /dev/null @@ -1,57 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -#pragma warning disable 0108 - -namespace MultiLanguage -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Security; - - [ComImport, InterfaceType((short) 1), ComConversionLoss, Guid("C04D65D0-B70D-11D0-B188-00AA0038C969")] - public interface IMLangStringWStr : IMLangString - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void Sync([In] int fNoAccess); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - int GetLength(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void SetMLStr([In] int lDestPos, [In] int lDestLen, [In, MarshalAs(UnmanagedType.IUnknown)] object pSrcMLStr, [In] int lSrcPos, [In] int lSrcLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetMLStr([In] int lSrcPos, [In] int lSrcLen, [In, MarshalAs(UnmanagedType.IUnknown)] object pUnkOuter, [In] uint dwClsContext, [In] ref Guid piid, [MarshalAs(UnmanagedType.IUnknown)] out object ppDestMLStr, out int plDestPos, out int plDestLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void SetWStr([In] int lDestPos, [In] int lDestLen, [In] ref ushort pszSrc, [In] int cchSrc, out int pcchActual, out int plActualLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void SetStrBufW([In] int lDestPos, [In] int lDestLen, [In, MarshalAs(UnmanagedType.Interface)] IMLangStringBufW pSrcBuf, out int pcchActual, out int plActualLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetWStr([In] int lSrcPos, [In] int lSrcLen, out ushort pszDest, [In] int cchDest, out int pcchActual, out int plActualLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetStrBufW([In] int lSrcPos, [In] int lSrcMaxLen, [MarshalAs(UnmanagedType.Interface)] out IMLangStringBufW ppDestBuf, out int plDestLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void LockWStr([In] int lSrcPos, [In] int lSrcLen, [In] int lFlags, [In] int cchRequest, [Out] IntPtr ppszDest, out int pcchDest, out int plDestLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void UnlockWStr([In] ref ushort pszSrc, [In] int cchSrc, out int pcchActual, out int plActualLen); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void SetLocale([In] int lDestPos, [In] int lDestLen, [In] uint locale); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetLocale([In] int lSrcPos, [In] int lSrcMaxLen, out uint plocale, out int plLocalePos, out int plLocaleLen); - } -} - -#pragma warning restore 0108 \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMultiLanguage.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMultiLanguage.cs deleted file mode 100644 index efb21f9f9..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMultiLanguage.cs +++ /dev/null @@ -1,59 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Security; - - [ComImport, Guid("275C23E1-3747-11D0-9FEA-00AA003F8646"), InterfaceType((short) 1)] - public interface IMultiLanguage - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetNumberOfCodePageInfo(out uint pcCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetCodePageInfo([In] uint uiCodePage, out tagMIMECPINFO pCodePageInfo); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetFamilyCodePage([In] uint uiCodePage, out uint puiFamilyCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void EnumCodePages([In] uint grfFlags, [MarshalAs(UnmanagedType.Interface)] out IEnumCodePage ppEnumCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetCharsetInfo([In, MarshalAs(UnmanagedType.BStr)] string Charset, out tagMIMECSETINFO pCharsetInfo); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void IsConvertible([In] uint dwSrcEncoding, [In] uint dwDstEncoding); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void ConvertString([In, Out] ref uint pdwMode, [In] uint dwSrcEncoding, [In] uint dwDstEncoding, [In] ref byte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref byte pDstStr, [In, Out] ref uint pcDstSize); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void ConvertStringToUnicode([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref sbyte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref ushort pDstStr, [In, Out] ref uint pcDstSize); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void ConvertStringFromUnicode([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref ushort pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref sbyte pDstStr, [In, Out] ref uint pcDstSize); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void ConvertStringReset(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetRfc1766FromLcid([In] uint locale, [MarshalAs(UnmanagedType.BStr)] out string pbstrRfc1766); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetLcidFromRfc1766(out uint plocale, [In, MarshalAs(UnmanagedType.BStr)] string bstrRfc1766); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void EnumRfc1766([MarshalAs(UnmanagedType.Interface)] out IEnumRfc1766 ppEnumRfc1766); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetRfc1766Info([In] uint locale, out tagRFC1766INFO pRfc1766Info); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void CreateConvertCharset([In] uint uiSrcCodePage, [In] uint uiDstCodePage, [In] uint dwProperty, [MarshalAs(UnmanagedType.Interface)] out CMLangConvertCharset ppMLangConvertCharset); - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMultiLanguage2.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMultiLanguage2.cs deleted file mode 100644 index 2c1be2453..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMultiLanguage2.cs +++ /dev/null @@ -1,96 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Security; - - [ComImport, InterfaceType((short) 1), Guid("DCCFC164-2B38-11D2-B7EC-00C04F8F5D9A")] - public interface IMultiLanguage2 - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetNumberOfCodePageInfo(out uint pcCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetCodePageInfo([In] uint uiCodePage, [In] ushort LangId, out tagMIMECPINFO pCodePageInfo); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetFamilyCodePage([In] uint uiCodePage, out uint puiFamilyCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void EnumCodePages([In] uint grfFlags, [In] ushort LangId, [MarshalAs(UnmanagedType.Interface)] out IEnumCodePage ppEnumCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetCharsetInfo([In, MarshalAs(UnmanagedType.BStr)] string Charset, out tagMIMECSETINFO pCharsetInfo); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void IsConvertible([In] uint dwSrcEncoding, [In] uint dwDstEncoding); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void ConvertString([In, Out] ref uint pdwMode, [In] uint dwSrcEncoding, [In] uint dwDstEncoding, [In] ref byte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref byte pDstStr, [In, Out] ref uint pcDstSize); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void ConvertStringToUnicode([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref sbyte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref ushort pDstStr, [In, Out] ref uint pcDstSize); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void ConvertStringFromUnicode([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref ushort pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref sbyte pDstStr, [In, Out] ref uint pcDstSize); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void ConvertStringReset(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetRfc1766FromLcid([In] uint locale, [MarshalAs(UnmanagedType.BStr)] out string pbstrRfc1766); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetLcidFromRfc1766(out uint plocale, [In, MarshalAs(UnmanagedType.BStr)] string bstrRfc1766); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void EnumRfc1766([In] ushort LangId, [MarshalAs(UnmanagedType.Interface)] out IEnumRfc1766 ppEnumRfc1766); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetRfc1766Info([In] uint locale, [In] ushort LangId, out tagRFC1766INFO pRfc1766Info); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void CreateConvertCharset([In] uint uiSrcCodePage, [In] uint uiDstCodePage, [In] uint dwProperty, [MarshalAs(UnmanagedType.Interface)] out CMLangConvertCharset ppMLangConvertCharset); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void ConvertStringInIStream([In, Out] ref uint pdwMode, [In] uint dwFlag, [In] ref ushort lpFallBack, [In] uint dwSrcEncoding, [In] uint dwDstEncoding, [In, MarshalAs(UnmanagedType.Interface)] IStream pstmIn, [In, MarshalAs(UnmanagedType.Interface)] IStream pstmOut); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void ConvertStringToUnicodeEx([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref sbyte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref ushort pDstStr, [In, Out] ref uint pcDstSize, [In] uint dwFlag, [In] ref ushort lpFallBack); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void ConvertStringFromUnicodeEx([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref ushort pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref sbyte pDstStr, [In, Out] ref uint pcDstSize, [In] uint dwFlag, [In] ref ushort lpFallBack); - - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void DetectCodepageInIStream([In] MLDETECTCP flags, - [In] uint dwPrefWinCodePage, - [In, MarshalAs(UnmanagedType.Interface)] IStream pstmIn, - [In, Out] ref DetectEncodingInfo lpEncoding, - [In, Out] ref int pnScores); - - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void DetectInputCodepage([In] MLDETECTCP flags, [In] uint dwPrefWinCodePage, - [In] ref byte pSrcStr, [In, Out] ref int pcSrcSize, - [In, Out] ref DetectEncodingInfo lpEncoding, - [In, Out] ref int pnScores); - - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void ValidateCodePage([In] uint uiCodePage, [In, ComAliasName("MultiLanguage.wireHWND")] ref _RemotableHandle hwnd); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetCodePageDescription([In] uint uiCodePage, [In] uint lcid, [In, Out, MarshalAs(UnmanagedType.LPWStr)] string lpWideCharStr, [In] int cchWideChar); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void IsCodePageInstallable([In] uint uiCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void SetMimeDBSource([In] tagMIMECONTF dwSource); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetNumberOfScripts(out uint pnScripts); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void EnumScripts([In] uint dwFlags, [In] ushort LangId, [MarshalAs(UnmanagedType.Interface)] out IEnumScript ppEnumScript); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void ValidateCodePageEx([In] uint uiCodePage, [In, ComAliasName("MultiLanguage.wireHWND")] ref _RemotableHandle hwnd, [In] uint dwfIODControl); - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMultiLanguage3.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMultiLanguage3.cs deleted file mode 100644 index 9c9e66806..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IMultiLanguage3.cs +++ /dev/null @@ -1,112 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -#pragma warning disable 0108 - -namespace MultiLanguage -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Security; - - [ComImport, InterfaceType((short) 1), Guid("4E5868AB-B157-4623-9ACC-6A1D9CAEBE04")] - public interface IMultiLanguage3 : IMultiLanguage2 - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetNumberOfCodePageInfo(out uint pcCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetCodePageInfo([In] uint uiCodePage, [In] ushort LangId, out tagMIMECPINFO pCodePageInfo); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetFamilyCodePage([In] uint uiCodePage, out uint puiFamilyCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void EnumCodePages([In] uint grfFlags, [In] ushort LangId, [MarshalAs(UnmanagedType.Interface)] out IEnumCodePage ppEnumCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetCharsetInfo([In, MarshalAs(UnmanagedType.BStr)] string Charset, out tagMIMECSETINFO pCharsetInfo); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void IsConvertible([In] uint dwSrcEncoding, [In] uint dwDstEncoding); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void ConvertString([In, Out] ref uint pdwMode, [In] uint dwSrcEncoding, [In] uint dwDstEncoding, [In] ref byte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref byte pDstStr, [In, Out] ref uint pcDstSize); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void ConvertStringToUnicode([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref sbyte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref ushort pDstStr, [In, Out] ref uint pcDstSize); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void ConvertStringFromUnicode([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref ushort pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref sbyte pDstStr, [In, Out] ref uint pcDstSize); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void ConvertStringReset(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetRfc1766FromLcid([In] uint locale, [MarshalAs(UnmanagedType.BStr)] out string pbstrRfc1766); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetLcidFromRfc1766(out uint plocale, [In, MarshalAs(UnmanagedType.BStr)] string bstrRfc1766); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void EnumRfc1766([In] ushort LangId, [MarshalAs(UnmanagedType.Interface)] out IEnumRfc1766 ppEnumRfc1766); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetRfc1766Info([In] uint locale, [In] ushort LangId, out tagRFC1766INFO pRfc1766Info); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void CreateConvertCharset([In] uint uiSrcCodePage, [In] uint uiDstCodePage, [In] uint dwProperty, [MarshalAs(UnmanagedType.Interface)] out CMLangConvertCharset ppMLangConvertCharset); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void ConvertStringInIStream([In, Out] ref uint pdwMode, [In] uint dwFlag, [In] ref ushort lpFallBack, [In] uint dwSrcEncoding, [In] uint dwDstEncoding, [In, MarshalAs(UnmanagedType.Interface)] IStream pstmIn, [In, MarshalAs(UnmanagedType.Interface)] IStream pstmOut); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void ConvertStringToUnicodeEx([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref sbyte pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref ushort pDstStr, [In, Out] ref uint pcDstSize, [In] uint dwFlag, [In] ref ushort lpFallBack); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void ConvertStringFromUnicodeEx([In, Out] ref uint pdwMode, [In] uint dwEncoding, [In] ref ushort pSrcStr, [In, Out] ref uint pcSrcSize, [In] ref sbyte pDstStr, [In, Out] ref uint pcDstSize, [In] uint dwFlag, [In] ref ushort lpFallBack); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void DetectCodepageInIStream([In] MLDETECTCP flags, - [In] uint dwPrefWinCodePage, - [In, MarshalAs(UnmanagedType.Interface)] IStream pstmIn, - [In, Out] ref DetectEncodingInfo lpEncoding, - [In, Out] ref int pnScores); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] - void DetectInputCodepage([In] MLDETECTCP flags, [In] uint dwPrefWinCodePage, - [In] ref byte pSrcStr, [In, Out] ref int pcSrcSize, - [In, Out] ref DetectEncodingInfo lpEncoding, - [In, Out] ref int pnScores); - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void ValidateCodePage([In] uint uiCodePage, [In, ComAliasName("MultiLanguage.wireHWND")] ref _RemotableHandle hwnd); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetCodePageDescription([In] uint uiCodePage, [In] uint lcid, [In, Out, MarshalAs(UnmanagedType.LPWStr)] string lpWideCharStr, [In] int cchWideChar); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void IsCodePageInstallable([In] uint uiCodePage); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void SetMimeDBSource([In] tagMIMECONTF dwSource); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void GetNumberOfScripts(out uint pnScripts); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void EnumScripts([In] uint dwFlags, [In] ushort LangId, [MarshalAs(UnmanagedType.Interface)] out IEnumScript ppEnumScript); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void ValidateCodePageEx([In] uint uiCodePage, [In, ComAliasName("MultiLanguage.wireHWND")] ref _RemotableHandle hwnd, [In] uint dwfIODControl); - - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void DetectOutboundCodePage([In] MLCPF dwFlags, - [In, MarshalAs(UnmanagedType.LPWStr)] string lpWideCharStr, - [In] uint cchWideChar, - [In] IntPtr puiPreferredCodePages, - [In] uint nPreferredCodePages, - [In] IntPtr puiDetectedCodePages, - [In, Out] ref uint pnDetectedCodePages, - [In] ref ushort lpSpecialChar); - - - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void DetectOutboundCodePageInIStream([In] uint dwFlags, [In, MarshalAs(UnmanagedType.Interface)] IStream pStrIn, [In] ref uint puiPreferredCodePages, [In] uint nPreferredCodePages, [In] ref uint puiDetectedCodePages, [In, Out] ref uint pnDetectedCodePages, [In] ref ushort lpSpecialChar); - } -} - -#pragma warning restore 0108 \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/ISequentialStream.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/ISequentialStream.cs deleted file mode 100644 index f81dc0ea2..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/ISequentialStream.cs +++ /dev/null @@ -1,33 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Security; - - [ComImport, Guid("0C733A30-2A1C-11CE-ADE5-00AA0044773D"), InterfaceType((short) 1)] - public interface ISequentialStream - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void RemoteRead(IntPtr pv, uint cb, ref uint pcbRead); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void RemoteWrite([In] ref byte pv, [In] uint cb, ref uint pcbWritten); - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IStream.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IStream.cs deleted file mode 100644 index f1d3cefca..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/IStream.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -#pragma warning disable 0108 - -namespace MultiLanguage -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - using System.Security; - - [ComImport, Guid("0000000C-0000-0000-C000-000000000046"), InterfaceType((short) 1)] - public interface IStream : ISequentialStream - { - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void RemoteRead(IntPtr pv, uint cb, ref uint pcbRead); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void RemoteWrite([In] ref byte pv, [In] uint cb, ref uint pcbWritten); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void RemoteSeek([In] _LARGE_INTEGER dlibMove, [In] uint dwOrigin, IntPtr plibNewPosition); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void SetSize([In] _ULARGE_INTEGER libNewSize); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void RemoteCopyTo([In, MarshalAs(UnmanagedType.Interface)] IStream pstm, [In] _ULARGE_INTEGER cb, out _ULARGE_INTEGER pcbRead, out _ULARGE_INTEGER pcbWritten); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void Commit([In] uint grfCommitFlags); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void Revert(); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void LockRegion([In] _ULARGE_INTEGER libOffset, [In] _ULARGE_INTEGER cb, [In] uint dwLockType); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void UnlockRegion([In] _ULARGE_INTEGER libOffset, [In] _ULARGE_INTEGER cb, [In] uint dwLockType); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void Stat(out tagSTATSTG pstatstg, [In] uint grfStatFlag); - [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)] - void Clone([MarshalAs(UnmanagedType.Interface)] out IStream ppstm); - } -} - -#pragma warning restore 0108 \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/_FILETIME.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/_FILETIME.cs deleted file mode 100644 index b7037941d..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/_FILETIME.cs +++ /dev/null @@ -1,30 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Runtime.InteropServices; - using System.Security; - - [StructLayout(LayoutKind.Sequential, Pack=4)] - public struct _FILETIME - { - public uint dwLowDateTime; - public uint dwHighDateTime; - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/_LARGE_INTEGER.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/_LARGE_INTEGER.cs deleted file mode 100644 index c15662542..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/_LARGE_INTEGER.cs +++ /dev/null @@ -1,29 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Runtime.InteropServices; - using System.Security; - - [StructLayout(LayoutKind.Sequential, Pack=8)] - public struct _LARGE_INTEGER - { - public long QuadPart; - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/_RemotableHandle.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/_RemotableHandle.cs deleted file mode 100644 index 9277b789f..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/_RemotableHandle.cs +++ /dev/null @@ -1,30 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Runtime.InteropServices; - using System.Security; - - [StructLayout(LayoutKind.Sequential, Pack=4)] - public struct _RemotableHandle - { - public int fContext; - public __MIDL_IWinTypes_0009 u; - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/_ULARGE_INTEGER.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/_ULARGE_INTEGER.cs deleted file mode 100644 index 1c4240421..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/_ULARGE_INTEGER.cs +++ /dev/null @@ -1,29 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Runtime.InteropServices; - using System.Security; - - [StructLayout(LayoutKind.Sequential, Pack=8)] - public struct _ULARGE_INTEGER - { - public ulong QuadPart; - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/__MIDL_IWinTypes_0009.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/__MIDL_IWinTypes_0009.cs deleted file mode 100644 index 68c2326a4..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/__MIDL_IWinTypes_0009.cs +++ /dev/null @@ -1,32 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Runtime.InteropServices; - using System.Security; - - [StructLayout(LayoutKind.Explicit, Pack=4)] - public struct __MIDL_IWinTypes_0009 - { - [FieldOffset(0)] - public int hInproc; - [FieldOffset(0)] - public int hRemote; - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagDetectEncodingInfo.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagDetectEncodingInfo.cs deleted file mode 100644 index 3e1b15b4b..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagDetectEncodingInfo.cs +++ /dev/null @@ -1,34 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Runtime.InteropServices; - using System.Security; - - /// Thanks to jannewe for finding the fix! - /// http://www.codeproject.com/KB/recipes/DetectEncoding.aspx?msg=3247475#xx3247475xx - [StructLayout(LayoutKind.Sequential)] - public struct DetectEncodingInfo - { - public uint nLangID; - public uint nCodePage; - public int nDocPercent; - public int nConfidence; - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagMIMECONTF.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagMIMECONTF.cs deleted file mode 100644 index 87a44f7ef..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagMIMECONTF.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Security; - - public enum tagMIMECONTF - { - MIMECONTF_BROWSER = 2, - MIMECONTF_EXPORT = 0x400, - MIMECONTF_IMPORT = 8, - MIMECONTF_MAILNEWS = 1, - MIMECONTF_MIME_IE4 = 0x10000000, - MIMECONTF_MIME_LATEST = 0x20000000, - MIMECONTF_MIME_REGISTRY = 0x40000000, - MIMECONTF_MINIMAL = 4, - MIMECONTF_PRIVCONVERTER = 0x10000, - MIMECONTF_SAVABLE_BROWSER = 0x200, - MIMECONTF_SAVABLE_MAILNEWS = 0x100, - MIMECONTF_VALID = 0x20000, - MIMECONTF_VALID_NLS = 0x40000 - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagMIMECPINFO.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagMIMECPINFO.cs deleted file mode 100644 index ad211fe73..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagMIMECPINFO.cs +++ /dev/null @@ -1,44 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Runtime.InteropServices; - using System.Security; - - [StructLayout(LayoutKind.Sequential, Pack=4)] - public struct tagMIMECPINFO - { - public uint dwFlags; - public uint uiCodePage; - public uint uiFamilyCodePage; - [MarshalAs(UnmanagedType.ByValArray, SizeConst=0x40)] - public ushort[] wszDescription; - [MarshalAs(UnmanagedType.ByValArray, SizeConst=50)] - public ushort[] wszWebCharset; - [MarshalAs(UnmanagedType.ByValArray, SizeConst=50)] - public ushort[] wszHeaderCharset; - [MarshalAs(UnmanagedType.ByValArray, SizeConst=50)] - public ushort[] wszBodyCharset; - [MarshalAs(UnmanagedType.ByValArray, SizeConst=0x20)] - public ushort[] wszFixedWidthFont; - [MarshalAs(UnmanagedType.ByValArray, SizeConst=0x20)] - public ushort[] wszProportionalFont; - public byte bGDICharset; - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagMIMECSETINFO.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagMIMECSETINFO.cs deleted file mode 100644 index 5620ab609..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagMIMECSETINFO.cs +++ /dev/null @@ -1,32 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Runtime.InteropServices; - using System.Security; - - [StructLayout(LayoutKind.Sequential, Pack=4)] - public struct tagMIMECSETINFO - { - public uint uiCodePage; - public uint uiInternetEncoding; - [MarshalAs(UnmanagedType.ByValArray, SizeConst=50)] - public ushort[] wszCharset; - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagMLCPF.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagMLCPF.cs deleted file mode 100644 index 1771179df..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagMLCPF.cs +++ /dev/null @@ -1,54 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.Text; - -namespace MultiLanguage -{ - [Flags] - public enum MLCPF - { - // Not currently supported. - MLDETECTF_MAILNEWS = 0x0001, - - // Not currently supported. - MLDETECTF_BROWSER = 0x0002, - - // Detection result must be valid for conversion and text rendering. - MLDETECTF_VALID = 0x0004, - - // Detection result must be valid for conversion. - MLDETECTF_VALID_NLS = 0x0008, - - //Preserve preferred code page order. - //This is meaningful only if you have set the puiPreferredCodePages parameter in IMultiLanguage3::DetectOutboundCodePage or IMultiLanguage3::DetectOutboundCodePageInIStream. - MLDETECTF_PRESERVE_ORDER = 0x0010, - - // Only return one of the preferred code pages as the detection result. - // This is meaningful only if you have set the puiPreferredCodePages parameter in IMultiLanguage3::DetectOutboundCodePage or IMultiLanguage3::DetectOutboundCodePageInIStream. - MLDETECTF_PREFERRED_ONLY = 0x0020, - - // Filter out graphical symbols and punctuation. - MLDETECTF_FILTER_SPECIALCHAR = 0x0040, - - // Return only Unicode codepages if the euro character is detected. - MLDETECTF_EURO_UTF8 = 0x0080 - } - -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagMLDETECTCP.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagMLDETECTCP.cs deleted file mode 100644 index 4a3e65229..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagMLDETECTCP.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.Text; - -namespace MultiLanguage -{ - public enum MLDETECTCP { - // Default setting will be used. - MLDETECTCP_NONE = 0, - - // Input stream consists of 7-bit data. - MLDETECTCP_7BIT = 1, - - // Input stream consists of 8-bit data. - MLDETECTCP_8BIT = 2, - - // Input stream consists of double-byte data. - MLDETECTCP_DBCS = 4, - - // Input stream is an HTML page. - MLDETECTCP_HTML = 8, - - //Not currently supported. - MLDETECTCP_NUMBER = 16 - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagMLSTR_FLAGS.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagMLSTR_FLAGS.cs deleted file mode 100644 index 79e7c6531..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagMLSTR_FLAGS.cs +++ /dev/null @@ -1,28 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Security; - - public enum tagMLSTR_FLAGS - { - MLSTR_READ = 1, - MLSTR_WRITE = 2 - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagRFC1766INFO.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagRFC1766INFO.cs deleted file mode 100644 index 56ec8bf43..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagRFC1766INFO.cs +++ /dev/null @@ -1,33 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Runtime.InteropServices; - using System.Security; - - [StructLayout(LayoutKind.Sequential, Pack=4)] - public struct tagRFC1766INFO - { - public uint lcid; - [MarshalAs(UnmanagedType.ByValArray, SizeConst=6)] - public ushort[] wszRfc1766; - [MarshalAs(UnmanagedType.ByValArray, SizeConst=0x20)] - public ushort[] wszLocaleName; - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagSCRIPFONTINFO.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagSCRIPFONTINFO.cs deleted file mode 100644 index 0dbef754d..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagSCRIPFONTINFO.cs +++ /dev/null @@ -1,31 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Runtime.InteropServices; - using System.Security; - - [StructLayout(LayoutKind.Sequential, Pack=8)] - public struct tagSCRIPFONTINFO - { - public long scripts; - [MarshalAs(UnmanagedType.ByValArray, SizeConst=0x20)] - public ushort[] wszFont; - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagSCRIPTINFO.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagSCRIPTINFO.cs deleted file mode 100644 index 0eb7ba42a..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagSCRIPTINFO.cs +++ /dev/null @@ -1,36 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Runtime.InteropServices; - using System.Security; - - [StructLayout(LayoutKind.Sequential, Pack=4)] - public struct tagSCRIPTINFO - { - public byte ScriptId; - public uint uiCodePage; - [MarshalAs(UnmanagedType.ByValArray, SizeConst=0x30)] - public ushort[] wszDescription; - [MarshalAs(UnmanagedType.ByValArray, SizeConst=0x20)] - public ushort[] wszFixedWidthFont; - [MarshalAs(UnmanagedType.ByValArray, SizeConst=0x20)] - public ushort[] wszProportionalFont; - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagSTATSTG.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagSTATSTG.cs deleted file mode 100644 index 0bd4c0f59..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagSTATSTG.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Runtime.InteropServices; - using System.Security; - - [StructLayout(LayoutKind.Sequential, Pack=8)] - public struct tagSTATSTG - { - [MarshalAs(UnmanagedType.LPWStr)] - public string pwcsName; - public uint type; - public _ULARGE_INTEGER cbSize; - public _FILETIME mtime; - public _FILETIME ctime; - public _FILETIME atime; - public uint grfMode; - public uint grfLocksSupported; - public Guid clsid; - public uint grfStateBits; - public uint reserved; - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagUNICODERANGE.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagUNICODERANGE.cs deleted file mode 100644 index f9d966f4f..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Multilang/tagUNICODERANGE.cs +++ /dev/null @@ -1,30 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace MultiLanguage -{ - using System; - using System.Runtime.InteropServices; - using System.Security; - - [StructLayout(LayoutKind.Sequential, Pack=2)] - public struct tagUNICODERANGE - { - public ushort wcFrom; - public ushort wcTo; - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/NNTP/Client/NNTP_Client.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/NNTP/Client/NNTP_Client.cs deleted file mode 100644 index f3011a493..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/NNTP/Client/NNTP_Client.cs +++ /dev/null @@ -1,221 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.NNTP.Client -{ - #region usings - - using System; - using System.Collections.Generic; - using System.IO; - using TCP; - - #endregion - - /// - /// NNTP client. Defined in RFC 977. - /// - public class NNTP_Client : TCP_Client - { - #region Methods - - /// - /// Closes connection to NNTP server. - /// - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when NNTP client is not connected. - public override void Disconnect() - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("NNTP client is not connected."); - } - - try - { - // Send QUIT command to server. - WriteLine("QUIT"); - } - catch {} - - try - { - base.Disconnect(); - } - catch {} - } - - /// - /// Gets NNTP newsgoups. - /// - /// - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when NNTP client is not connected. - public string[] GetNewsGroups() - { - /* RFC 977 3.6.1. LIST - - Returns a list of valid newsgroups and associated information. Each - newsgroup is sent as a line of text in the following format: - - group last first p - - where is the name of the newsgroup, is the number of - the last known article currently in that newsgroup, is the - number of the first article currently in the newsgroup, and

    is - either 'y' or 'n' indicating whether posting to this newsgroup is - allowed ('y') or prohibited ('n'). - - The and fields will always be numeric. They may have - leading zeros. If the field evaluates to less than the - field, there are no articles currently on file in the - newsgroup. - - Example: - C: LIST - S: 215 list of newsgroups follows - S: net.wombats 00543 00501 y - S: net.unix-wizards 10125 10011 y - S: . - */ - - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("NNTP client is not connected."); - } - - // Send LIST command - WriteLine("LIST"); - - // Read server response - string responseLine = ReadLine(); - if (!responseLine.StartsWith("215")) - { - throw new Exception(responseLine); - } - - List newsGroups = new List(); - responseLine = ReadLine(); - while (responseLine != ".") - { - newsGroups.Add(responseLine.Split(' ')[0]); - - responseLine = ReadLine(); - } - - return newsGroups.ToArray(); - } - - ///

    - /// Posts specified message to the specified newsgroup. - /// - /// Newsgroup where to post message. - /// Message to post. Message is taken from stream current position. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when NNTP client is not connected. - public void PostMessage(string newsgroup, Stream message) - { - /* RFC 977 3.10.1. POST - - If posting is allowed, response code 340 is returned to indicate that - the article to be posted should be sent. Response code 440 indicates - that posting is prohibited for some installation-dependent reason. - - If posting is permitted, the article should be presented in the - format specified by RFC850, and should include all required header - lines. After the article's header and body have been completely sent - by the client to the server, a further response code will be returned - to indicate success or failure of the posting attempt. - - The text forming the header and body of the message to be posted - should be sent by the client using the conventions for text received - from the news server: A single period (".") on a line indicates the - end of the text, with lines starting with a period in the original - text having that period doubled during transmission. - - No attempt shall be made by the server to filter characters, fold or - limit lines, or otherwise process incoming text. It is our intent - that the server just pass the incoming message to be posted to the - server installation's news posting software, which is separate from - this specification. See RFC850 for more details. - - Example: - C: POST - S: 340 Continue posting; Period on a line by itself to end - C: (transmits news article in RFC850 format) - C: . - S: 240 Article posted successfully. - */ - - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("NNTP client is not connected."); - } - - // Send POST command - WriteLine("POST"); - - // Read server response - string responseLine = ReadLine(); - if (!responseLine.StartsWith("340")) - { - throw new Exception(responseLine); - } - - // POST message - TcpStream.WritePeriodTerminated(message); - - // Read server response - responseLine = ReadLine(); - if (!responseLine.StartsWith("240")) - { - throw new Exception(responseLine); - } - } - - #endregion - - #region Overrides - - /// - /// This method is called after TCP client has sucessfully connected. - /// - protected override void OnConnected() - { - // Read first line of reply, check if it's ok. - string responseLine = ReadLine(); - if (!responseLine.StartsWith("200")) - { - throw new Exception(responseLine); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Net_Core.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Net_Core.cs deleted file mode 100644 index eff744b8a..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Net_Core.cs +++ /dev/null @@ -1,1364 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System.Globalization; - -namespace ASC.Mail.Net -{ - #region usings - - using System; - using System.IO; - using System.Net; - using System.Net.Sockets; - using System.Security.Cryptography; - using System.Text; - using Dns.Client; - using System.Text.RegularExpressions; - - #endregion - - #region enum AuthType - - /// - /// Authentication type. - /// - public enum AuthType - { - /// - /// Plain username/password authentication. - /// - Plain = 0, - - /// - /// APOP - /// - APOP = 1, - - /// - /// Not implemented. - /// - LOGIN = 2, - - /// - /// Cram-md5 authentication. - /// - CRAM_MD5 = 3, - - /// - /// DIGEST-md5 authentication. - /// - DIGEST_MD5 = 4, - } - - #endregion - - /// - /// Provides net core utility methods. - /// - public class Core - { - #region Methods - - /// - /// Gets host name. If fails returns ip address. - /// - /// IP address which to reverse lookup. - /// Returns host name of specified IP address. - /// Is raised when ip is null. - public static string GetHostName(IPAddress ip) - { - if (ip == null) - { - throw new ArgumentNullException("ip"); - } - - string retVal = ip.ToString(); - try - { - Dns_Client dns = new Dns_Client(); - DnsServerResponse response = dns.Query(ip.ToString(), QTYPE.PTR); - if (response.ResponseCode == RCODE.NO_ERROR) - { - DNS_rr_PTR[] ptrs = response.GetPTRRecords(); - if (ptrs.Length > 0) - { - retVal = ptrs[0].DomainName; - } - } - } - catch {} - - return retVal; - } - - /// - /// Gets argument part of command text. - /// - /// Input srting from where to remove value. - /// Command text which to remove. - /// - public static string GetArgsText(string input, string cmdTxtToRemove) - { - string buff = input.Trim(); - if (buff.Length >= cmdTxtToRemove.Length) - { - buff = buff.Substring(cmdTxtToRemove.Length); - } - buff = buff.Trim(); - - return buff; - } - - /// - /// Checks if specified string is number(long). - /// - /// - /// - public static bool IsNumber(string str) - { - try - { - Convert.ToInt64(str); - return true; - } - catch - { - return false; - } - } - - /// - /// Reverses the specified array elements. - /// - /// Array elements to reverse. - /// Returns array with reversed items. - /// Is raised when array is null. - public static Array ReverseArray(Array array) - { - if (array == null) - { - throw new ArgumentNullException("array"); - } - - Array.Reverse(array); - - return array; - } - - /// - /// Encodes specified data with base64 encoding. - /// - /// Data to encode. - /// - public static byte[] Base64Encode(byte[] data) - { - return Base64EncodeEx(data, null, true); - } - - /// - /// Encodes specified data with bas64 encoding. - /// - /// Data to to encode. - /// Custom base64 chars (64 chars) or null if default chars used. - /// Padd missing block chars. Normal base64 must be 4 bytes blocks, if not 4 bytes in block, - /// missing bytes must be padded with '='. Modified base64 just skips missing bytes. - /// - public static byte[] Base64EncodeEx(byte[] data, char[] base64Chars, bool padd) - { - /* RFC 2045 6.8. Base64 Content-Transfer-Encoding - - Base64 is processed from left to right by 4 6-bit byte block, 4 6-bit byte block - are converted to 3 8-bit bytes. - If base64 4 byte block doesn't have 3 8-bit bytes, missing bytes are marked with =. - - - Value Encoding Value Encoding Value Encoding Value Encoding - 0 A 17 R 34 i 51 z - 1 B 18 S 35 j 52 0 - 2 C 19 T 36 k 53 1 - 3 D 20 U 37 l 54 2 - 4 E 21 V 38 m 55 3 - 5 F 22 W 39 n 56 4 - 6 G 23 X 40 o 57 5 - 7 H 24 Y 41 p 58 6 - 8 I 25 Z 42 q 59 7 - 9 J 26 a 43 r 60 8 - 10 K 27 b 44 s 61 9 - 11 L 28 c 45 t 62 + - 12 M 29 d 46 u 63 / - 13 N 30 e 47 v - 14 O 31 f 48 w (pad) = - 15 P 32 g 49 x - 16 Q 33 h 50 y - - NOTE: 4 base64 6-bit bytes = 3 8-bit bytes - // | 6-bit | 6-bit | 6-bit | 6-bit | - // | 1 2 3 4 5 6 | 1 2 3 4 5 6 | 1 2 3 4 5 6 | 1 2 3 4 5 6 | - // | 8-bit | 8-bit | 8-bit | - */ - - if (base64Chars != null && base64Chars.Length != 64) - { - throw new Exception("There must be 64 chars in base64Chars char array !"); - } - - if (base64Chars == null) - { - base64Chars = new[] - { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', - 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', - 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', - 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', '+', '/' - }; - } - - // Convert chars to bytes - byte[] base64LoockUpTable = new byte[64]; - for (int i = 0; i < 64; i++) - { - base64LoockUpTable[i] = (byte) base64Chars[i]; - } - - int encodedDataLength = (int) Math.Ceiling((data.Length*8)/(double) 6); - // Retrun value won't be interegral 4 block, but has less. Padding requested, padd missing with '=' - if (padd && (encodedDataLength/(double) 4 != Math.Ceiling(encodedDataLength/(double) 4))) - { - encodedDataLength += (int) (Math.Ceiling(encodedDataLength/(double) 4)*4) - encodedDataLength; - } - - // See how many line brakes we need - int numberOfLineBreaks = 0; - if (encodedDataLength > 76) - { - numberOfLineBreaks = (int) Math.Ceiling(encodedDataLength/(double) 76) - 1; - } - - // Construc return valu buffer - byte[] retVal = new byte[encodedDataLength + (numberOfLineBreaks*2)]; // * 2 - CRLF - - int lineBytes = 0; - // Loop all 3 bye blocks - int position = 0; - for (int i = 0; i < data.Length; i += 3) - { - // Do line splitting - if (lineBytes >= 76) - { - retVal[position + 0] = (byte) '\r'; - retVal[position + 1] = (byte) '\n'; - position += 2; - lineBytes = 0; - } - - // Full 3 bytes data block - if ((data.Length - i) >= 3) - { - retVal[position + 0] = base64LoockUpTable[data[i + 0] >> 2]; - retVal[position + 1] = base64LoockUpTable[(data[i + 0] & 0x3) << 4 | data[i + 1] >> 4]; - retVal[position + 2] = base64LoockUpTable[(data[i + 1] & 0xF) << 2 | data[i + 2] >> 6]; - retVal[position + 3] = base64LoockUpTable[data[i + 2] & 0x3F]; - position += 4; - lineBytes += 4; - } - // 2 bytes data block, left (last block) - else if ((data.Length - i) == 2) - { - retVal[position + 0] = base64LoockUpTable[data[i + 0] >> 2]; - retVal[position + 1] = base64LoockUpTable[(data[i + 0] & 0x3) << 4 | data[i + 1] >> 4]; - retVal[position + 2] = base64LoockUpTable[(data[i + 1] & 0xF) << 2]; - if (padd) - { - retVal[position + 3] = (byte) '='; - } - } - // 1 bytes data block, left (last block) - else if ((data.Length - i) == 1) - { - retVal[position + 0] = base64LoockUpTable[data[i + 0] >> 2]; - retVal[position + 1] = base64LoockUpTable[(data[i + 0] & 0x3) << 4]; - if (padd) - { - retVal[position + 2] = (byte) '='; - retVal[position + 3] = (byte) '='; - } - } - } - - return retVal; - } - - /// - /// Decodes base64 data. Defined in RFC 2045 6.8. Base64 Content-Transfer-Encoding. - /// - /// Base64 decoded data. - /// - public static byte[] Base64Decode(byte[] base64Data) - { - return Base64DecodeEx(base64Data, null); - } - - /// - /// Decodes base64 data. Defined in RFC 2045 6.8. Base64 Content-Transfer-Encoding. - /// - /// Base64 decoded data. - /// Custom base64 chars (64 chars) or null if default chars used. - /// - public static byte[] Base64DecodeEx(byte[] base64Data, char[] base64Chars) - { - /* RFC 2045 6.8. Base64 Content-Transfer-Encoding - - Base64 is processed from left to right by 4 6-bit byte block, 4 6-bit byte block - are converted to 3 8-bit bytes. - If base64 4 byte block doesn't have 3 8-bit bytes, missing bytes are marked with =. - - - Value Encoding Value Encoding Value Encoding Value Encoding - 0 A 17 R 34 i 51 z - 1 B 18 S 35 j 52 0 - 2 C 19 T 36 k 53 1 - 3 D 20 U 37 l 54 2 - 4 E 21 V 38 m 55 3 - 5 F 22 W 39 n 56 4 - 6 G 23 X 40 o 57 5 - 7 H 24 Y 41 p 58 6 - 8 I 25 Z 42 q 59 7 - 9 J 26 a 43 r 60 8 - 10 K 27 b 44 s 61 9 - 11 L 28 c 45 t 62 + - 12 M 29 d 46 u 63 / - 13 N 30 e 47 v - 14 O 31 f 48 w (pad) = - 15 P 32 g 49 x - 16 Q 33 h 50 y - - NOTE: 4 base64 6-bit bytes = 3 8-bit bytes - // | 6-bit | 6-bit | 6-bit | 6-bit | - // | 1 2 3 4 5 6 | 1 2 3 4 5 6 | 1 2 3 4 5 6 | 1 2 3 4 5 6 | - // | 8-bit | 8-bit | 8-bit | - */ - - if (base64Chars != null && base64Chars.Length != 64) - { - throw new Exception("There must be 64 chars in base64Chars char array !"); - } - - if (base64Chars == null) - { - base64Chars = new[] - { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', - 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', - 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', - 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', '+', '/' - }; - } - - //--- Create decode table ---------------------// - byte[] decodeTable = new byte[128]; - for (int i = 0; i < 128; i++) - { - int mappingIndex = -1; - for (int bc = 0; bc < base64Chars.Length; bc++) - { - if (i == base64Chars[bc]) - { - mappingIndex = bc; - break; - } - } - - if (mappingIndex > -1) - { - decodeTable[i] = (byte) mappingIndex; - } - else - { - decodeTable[i] = 0xFF; - } - } - //---------------------------------------------// - - byte[] decodedDataBuffer = new byte[((base64Data.Length*6)/8) + 4]; - int decodedBytesCount = 0; - int nByteInBase64Block = 0; - byte[] decodedBlock = new byte[3]; - byte[] base64Block = new byte[4]; - - for (int i = 0; i < base64Data.Length; i++) - { - byte b = base64Data[i]; - - // Read 4 byte base64 block and process it - // Any characters outside of the base64 alphabet are to be ignored in base64-encoded data. - - // Padding char - if (b == '=') - { - base64Block[nByteInBase64Block] = 0xFF; - } - else - { - byte decodeByte = decodeTable[b & 0x7F]; - if (decodeByte != 0xFF) - { - base64Block[nByteInBase64Block] = decodeByte; - nByteInBase64Block++; - } - } - - /* Check if we can decode some bytes. - * We must have full 4 byte base64 block or reached at the end of data. - */ - int encodedBytesCount = -1; - // We have full 4 byte base64 block - if (nByteInBase64Block == 4) - { - encodedBytesCount = 3; - } - // We have reached at the end of base64 data, there may be some bytes left - else if (i == base64Data.Length - 1) - { - // Invalid value, we can't have only 6 bit, just skip - if (nByteInBase64Block == 1) - { - encodedBytesCount = 0; - } - // There is 1 byte in two base64 bytes (6 + 2 bit) - else if (nByteInBase64Block == 2) - { - encodedBytesCount = 1; - } - // There are 2 bytes in two base64 bytes ([6 + 2],[4 + 4] bit) - else if (nByteInBase64Block == 3) - { - encodedBytesCount = 2; - } - } - - // We have some bytes available to decode, decode them - if (encodedBytesCount > -1) - { - decodedDataBuffer[decodedBytesCount + 0] = - (byte) (base64Block[0] << 2 | base64Block[1] >> 4); - decodedDataBuffer[decodedBytesCount + 1] = - (byte) ((base64Block[1] & 0xF) << 4 | base64Block[2] >> 2); - decodedDataBuffer[decodedBytesCount + 2] = - (byte) ((base64Block[2] & 0x3) << 6 | base64Block[3] >> 0); - - // Increase decoded bytes count - decodedBytesCount += encodedBytesCount; - - // Reset this block, reade next if there is any - nByteInBase64Block = 0; - } - } - - // There is some decoded bytes, construct return value - if (decodedBytesCount > -1) - { - byte[] retVal = new byte[decodedBytesCount]; - Array.Copy(decodedDataBuffer, 0, retVal, 0, decodedBytesCount); - return retVal; - } - // There is no decoded bytes - else - { - return new byte[0]; - } - } - - /// - /// Encodes data with quoted-printable encoding. - /// - /// Data to encode. - /// - public static byte[] QuotedPrintableEncode(byte[] data) - { - /* Rfc 2045 6.7. Quoted-Printable Content-Transfer-Encoding - - (2) (Literal representation) Octets with decimal values of 33 through 60 inclusive, - and 62 through 126, inclusive, MAY be represented as the US-ASCII characters which - correspond to those octets (EXCLAMATION POINT through LESS THAN, and GREATER THAN - through TILDE, respectively). - - (3) (White Space) Octets with values of 9 and 32 MAY be represented as US-ASCII TAB (HT) and - SPACE characters, respectively, but MUST NOT be so represented at the end of an encoded line. - You must encode it =XX. - - (5) Encoded lines must not be longer than 76 characters, not counting the trailing CRLF. - If longer lines are to be encoded with the Quoted-Printable encoding, "soft" line breaks - must be used. An equal sign as the last character on a encoded line indicates such - a non-significant ("soft") line break in the encoded text. - - *) If binary data is encoded in quoted-printable, care must be taken to encode - CR and LF characters as "=0D" and "=0A", respectively. - - */ - - int lineLength = 0; - // Encode bytes <= 33 , >= 126 and 61 (=) - MemoryStream retVal = new MemoryStream(); - foreach (byte b in data) - { - // Suggested line length is exceeded, add soft line break - if (lineLength > 75) - { - retVal.Write(new[] {(byte) '=', (byte) '\r', (byte) '\n'}, 0, 3); - lineLength = 0; - } - - // We need to encode that byte - if (b <= 33 || b >= 126 || b == 61) - { - retVal.Write(new[] {(byte) '='}, 0, 1); - retVal.Write(ToHex(b), 0, 2); - lineLength += 3; - } - // We don't need to encode that byte, just write it to stream - else - { - retVal.WriteByte(b); - lineLength++; - } - } - - return retVal.ToArray(); - } - - - private static readonly Regex QuotedRegex = new Regex(@"(=[A-F0-9][A-F0-9])+", RegexOptions.Compiled | RegexOptions.Multiline); - /// - /// quoted-printable decoder. Defined in RFC 2045 6.7. - /// - /// Data which to encode. - /// - public static string QuotedPrintableDecode(string data, Encoding encoding) - { - /* RFC 2045 6.7. Quoted-Printable Content-Transfer-Encoding - - (1) (General 8bit representation) Any octet, except a CR or - LF that is part of a CRLF line break of the canonical - (standard) form of the data being encoded, may be - represented by an "=" followed by a two digit - hexadecimal representation of the octet's value. The - digits of the hexadecimal alphabet, for this purpose, - are "0123456789ABCDEF". Uppercase letters must be - used; lowercase letters are not allowed. - - (2) (Literal representation) Octets with decimal values of - 33 through 60 inclusive, and 62 through 126, inclusive, - MAY be represented as the US-ASCII characters which - correspond to those octets (EXCLAMATION POINT through - LESS THAN, and GREATER THAN through TILDE, respectively). - - (3) (White Space) Octets with values of 9 and 32 MAY be - represented as US-ASCII TAB (HT) and SPACE characters, - respectively, but MUST NOT be so represented at the end - of an encoded line. Any TAB (HT) or SPACE characters - on an encoded line MUST thus be followed on that line - by a printable character. In particular, an "=" at the - end of an encoded line, indicating a soft line break - (see rule #5) may follow one or more TAB (HT) or SPACE - characters. It follows that an octet with decimal - value 9 or 32 appearing at the end of an encoded line - must be represented according to Rule #1. This rule is - necessary because some MTAs (Message Transport Agents, - programs which transport messages from one user to - another, or perform a portion of such transfers) are - known to pad lines of text with SPACEs, and others are - known to remove "white space" characters from the end - of a line. Therefore, when decoding a Quoted-Printable - body, any trailing white space on a line must be - deleted, as it will necessarily have been added by - intermediate transport agents. - - (4) (Line Breaks) A line break in a text body, represented - as a CRLF sequence in the text canonical form, must be - represented by a (RFC 822) line break, which is also a - CRLF sequence, in the Quoted-Printable encoding. Since - the canonical representation of media types other than - text do not generally include the representation of - line breaks as CRLF sequences, no hard line breaks - (i.e. line breaks that are intended to be meaningful - and to be displayed to the user) can occur in the - quoted-printable encoding of such types. Sequences - like "=0D", "=0A", "=0A=0D" and "=0D=0A" will routinely - appear in non-text data represented in quoted- - printable, of course. - - (5) (Soft Line Breaks) The Quoted-Printable encoding - REQUIRES that encoded lines be no more than 76 - characters long. If longer lines are to be encoded - with the Quoted-Printable encoding, "soft" line breaks - */ - - var result = QuotedRegex.Replace(data, new MatchEvaluator(x=>EvalQuoted(x,encoding))); - return result; - } - - private static string EvalQuoted(Match match, Encoding encoding) - { - try - { - if (match.Success) - { - var values = match.Value.Split(new[]{'='},StringSplitOptions.RemoveEmptyEntries); - var buffer = new byte[values.Length]; - for (int index = 0; index < values.Length; index++) - { - var value = values[index]; - byte parsed; - if (byte.TryParse(value, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out parsed)) - { - buffer[index] = parsed; - } - } - return encoding.GetString(buffer); - } - } - catch (Exception) - { - - } - return match.Value; - } - - /// - /// "Q" decoder. This is same as quoted-printable, except '_' is converted to ' '. - /// Defined in RFC 2047 4.2. - /// - /// Input string encoding. - /// String which to encode. - /// Returns decoded string. - public static string QDecode(Encoding encoding, string data) - { - //It's simple - - - return QuotedPrintableDecode(data.Replace("_", " "),encoding); - - // REMOVEME: - // 15.09.2004 - replace must be done before encoding - // return QuotedPrintableDecode(encoding,System.Text.Encoding.ASCII.GetBytes(data)).Replace("_"," "); - } - - /// - /// Canonical decoding. Decodes all canonical encoding occurences in specified text. - /// Usually mime message header unicode/8bit values are encoded as Canonical. - /// Format: =?charSet?type[Q or B]?encoded_string?= . - /// Defined in RFC 2047. - /// - /// Text to decode. - /// - [Obsolete("Use MimeUtils.DecodeWords method instead.")] - public static string CanonicalDecode(string text) - { - /* RFC 2047 - Generally, an "encoded-word" is a sequence of printable ASCII - characters that begins with "=?", ends with "?=", and has two "?"s in - between. - - Syntax: =?charSet?type[Q or B]?encoded_string?= - - Examples: - =?utf-8?q?Buy a Rolex?= - =?iso-8859-1?B?bORs5D8=?= - */ - - StringBuilder retVal = new StringBuilder(); - int offset = 0; - while (offset < text.Length) - { - // Search start and end of canonical entry - int iStart = text.IndexOf("=?", offset); - int iEnd = -1; - if (iStart > -1) - { - // End index must be over start index position - iEnd = text.IndexOf("?=", iStart + 2); - } - - if (iStart > -1 && iEnd > -1) - { - // Add left side non encoded text of encoded text, if there is any - if ((iStart - offset) > 0) - { - retVal.Append(text.Substring(offset, iStart - offset)); - } - - while (true) - { - // Check if it is encoded entry - string[] charset_type_text = text.Substring(iStart + 2, iEnd - iStart - 2).Split('?'); - if (charset_type_text.Length == 3) - { - // Try to parse encoded text - try - { - Encoding enc = EncodingTools.GetEncodingByCodepageName_Throws(charset_type_text[0]); - // QEncoded text - if (charset_type_text[1].ToLower() == "q") - { - retVal.Append(QDecode(enc, charset_type_text[2])); - } - // Base64 encoded text - else - { - retVal.Append( - enc.GetString( - Base64Decode(Encoding.Default.GetBytes(charset_type_text[2])))); - } - } - catch - { - // Parsing failed, just leave text as is. - retVal.Append(text.Substring(iStart, iEnd - iStart + 2)); - } - - // Move current offset in string - offset = iEnd + 2; - break; - } - // This isn't right end tag, try next - else if (charset_type_text.Length < 3) - { - // Try next end tag - iEnd = text.IndexOf("?=", iEnd + 2); - - // No suitable end tag for active start tag, move offset over start tag. - if (iEnd == -1) - { - retVal.Append("=?"); - offset = iStart + 2; - break; - } - } - // Illegal start tag or start tag is just in side some text, move offset over start tag. - else - { - retVal.Append("=?"); - offset = iStart + 2; - break; - } - } - } - // There are no more entries - else - { - // Add remaining non encoded text, if there is any. - if (text.Length > offset) - { - retVal.Append(text.Substring(offset)); - offset = text.Length; - } - } - } - - return retVal.ToString(); - } - - /// - /// Canonical encoding. - /// - /// String to encode. - /// With what charset to encode string. If you aren't sure about it, utf-8 is suggested. - /// Returns encoded text. - public static string CanonicalEncode(string str, string charSet) - { - /* RFC 2049 2. (9),(10) - =?encodedWord?= - encodedWord -> charset?encoding?encodedText - encoding -> Q(Q encode) or B(base64) - */ - - // Contains non ascii chars, must to encode. - if (!IsAscii(str)) - { - string retVal = "=?" + charSet + "?" + "B?"; - retVal += Convert.ToBase64String(EncodingTools.GetEncodingByCodepageName_Throws(charSet).GetBytes(str)); - retVal += "?="; - - return retVal; - } - - return str; - } - - /// - /// Encodes specified data with IMAP modified UTF7 encoding. Defined in RFC 3501 5.1.3. Mailbox International Naming Convention. - /// Example: �� is encoded to &APYA9g-. - /// - /// Text to encode. - /// - public static string Encode_IMAP_UTF7_String(string text) - { - /* RFC 3501 5.1.3. Mailbox International Naming Convention - In modified UTF-7, printable US-ASCII characters, except for "&", - represent themselves; that is, characters with octet values 0x20-0x25 - and 0x27-0x7e. The character "&" (0x26) is represented by the - two-octet sequence "&-". - - All other characters (octet values 0x00-0x1f and 0x7f-0xff) are - represented in modified BASE64, with a further modification from - [UTF-7] that "," is used instead of "/". Modified BASE64 MUST NOT be - used to represent any printing US-ASCII character which can represent - itself. - - "&" is used to shift to modified BASE64 and "-" to shift back to - US-ASCII. There is no implicit shift from BASE64 to US-ASCII, and - null shifts ("-&" while in BASE64; note that "&-" while in US-ASCII - means "&") are not permitted. However, all names start in US-ASCII, - and MUST end in US-ASCII; that is, a name that ends with a non-ASCII - ISO-10646 character MUST end with a "-"). - */ - - // Base64 chars, except '/' is replaced with ',' - char[] base64Chars = new[] - { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', - 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', - 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', - 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', - '4', '5', '6', '7', '8', '9', '+', ',' - }; - - MemoryStream retVal = new MemoryStream(); - for (int i = 0; i < text.Length; i++) - { - char c = text[i]; - - // The character "&" (0x26) is represented by the two-octet sequence "&-". - if (c == '&') - { - retVal.Write(new[] {(byte) '&', (byte) '-'}, 0, 2); - } - // It is allowed char, don't need to encode - else if (c >= 0x20 && c <= 0x25 || c >= 0x27 && c <= 0x7E) - { - retVal.WriteByte((byte) c); - } - // Not allowed char, encode it - else - { - // Superfluous shifts are not allowed. - // For example: �� may not encoded as &APY-&APY-, but must be &APYA9g-. - - // Get all continuous chars that need encoding and encode them as one block - MemoryStream encodeBlock = new MemoryStream(); - for (int ic = i; ic < text.Length; ic++) - { - char cC = text[ic]; - - // Allowed char - if (cC >= 0x20 && cC <= 0x25 || cC >= 0x27 && cC <= 0x7E) - { - break; - } - else - { - encodeBlock.WriteByte((byte) ((cC & 0xFF00) >> 8)); - encodeBlock.WriteByte((byte) (cC & 0xFF)); - i = ic; - } - } - - // Ecode block - byte[] encodedData = Base64EncodeEx(encodeBlock.ToArray(), base64Chars, false); - retVal.WriteByte((byte) '&'); - retVal.Write(encodedData, 0, encodedData.Length); - retVal.WriteByte((byte) '-'); - } - } - - return Encoding.Default.GetString(retVal.ToArray()); - } - - /// - /// Decodes IMAP modified UTF7 encoded data. Defined in RFC 3501 5.1.3. Mailbox International Naming Convention. - /// Example: &APYA9g- is decoded to ��. - /// - /// Text to encode. - /// - public static string Decode_IMAP_UTF7_String(string text) - { - /* RFC 3501 5.1.3. Mailbox International Naming Convention - In modified UTF-7, printable US-ASCII characters, except for "&", - represent themselves; that is, characters with octet values 0x20-0x25 - and 0x27-0x7e. The character "&" (0x26) is represented by the - two-octet sequence "&-". - - All other characters (octet values 0x00-0x1f and 0x7f-0xff) are - represented in modified BASE64, with a further modification from - [UTF-7] that "," is used instead of "/". Modified BASE64 MUST NOT be - used to represent any printing US-ASCII character which can represent - itself. - - "&" is used to shift to modified BASE64 and "-" to shift back to - US-ASCII. There is no implicit shift from BASE64 to US-ASCII, and - null shifts ("-&" while in BASE64; note that "&-" while in US-ASCII - means "&") are not permitted. However, all names start in US-ASCII, - and MUST end in US-ASCII; that is, a name that ends with a non-ASCII - ISO-10646 character MUST end with a "-"). - */ - - // Base64 chars, except '/' is replaced with ',' - char[] base64Chars = new[] - { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', - 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', - 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', - 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', - '4', '5', '6', '7', '8', '9', '+', ',' - }; - - StringBuilder retVal = new StringBuilder(); - for (int i = 0; i < text.Length; i++) - { - char c = text[i]; - - // Encoded block or escaped & - if (c == '&') - { - int endingPos = -1; - // Read encoded block - for (int b = i + 1; b < text.Length; b++) - { - // - marks block end - if (text[b] == '-') - { - endingPos = b; - break; - } - // Invalid & sequence, just treat it as '&' char and not like shift. - // &....&, but must be &....- - else if (text[b] == '&') - { - break; - } - } - - // If no ending -, invalid encoded block. Treat it like it is - if (endingPos == -1) - { - // Just let main for to handle other chars after & - retVal.Append(c); - } - // If empty block, then escaped & - else if (endingPos - i == 1) - { - retVal.Append(c); - // Move i over '-' - i++; - } - // Decode block - else - { - // Get encoded block - byte[] encodedBlock = - Encoding.Default.GetBytes(text.Substring(i + 1, endingPos - i - 1)); - - // Convert to UTF-16 char - byte[] decodedData = Base64DecodeEx(encodedBlock, base64Chars); - char[] decodedChars = new char[decodedData.Length/2]; - for (int iC = 0; iC < decodedChars.Length; iC++) - { - decodedChars[iC] = (char) (decodedData[iC*2] << 8 | decodedData[(iC*2) + 1]); - } - - // Decode data - retVal.Append(decodedChars); - - // Move i over '-' - i += encodedBlock.Length + 1; - } - } - // Normal byte - else - { - retVal.Append(c); - } - } - - return retVal.ToString(); - } - - /// - /// Checks if specified string data is acii data. - /// - /// - /// - public static bool IsAscii(string data) - { - foreach (char c in data) - { - if (c > 127) - { - return false; - } - } - - return true; - } - - /// - /// Gets file name from path. - /// - /// File file path with file name. For examples: c:\fileName.xxx, aaa\fileName.xxx. - /// - public static string GetFileNameFromPath(string filePath) - { - return Path.GetFileName(filePath); - } - - /// - /// Gets if specified value is IP address. - /// - /// String value. - /// Returns true if specified value is IP address. - public static bool IsIP(string value) - { - try - { - IPAddress.Parse(value); - return true; - } - catch - { - return false; - } - } - - /// - /// Compares 2 IP addresses. Returns 0 if IPs are equal, - /// returns positive value if destination IP is bigger than source IP, - /// returns negative value if destination IP is smaller than source IP. - /// - /// Source IP address. - /// Destination IP address. - /// - public static int CompareIP(IPAddress source, IPAddress destination) - { - byte[] sourceIpBytes = source.GetAddressBytes(); - byte[] destinationIpBytes = destination.GetAddressBytes(); - - // IPv4 and IPv6 - if (sourceIpBytes.Length < destinationIpBytes.Length) - { - return 1; - } - // IPv6 and IPv4 - else if (sourceIpBytes.Length > destinationIpBytes.Length) - { - return -1; - } - // IPv4 and IPv4 OR IPv6 and IPv6 - else - { - for (int i = 0; i < sourceIpBytes.Length; i++) - { - if (sourceIpBytes[i] < destinationIpBytes[i]) - { - return 1; - } - else if (sourceIpBytes[i] > destinationIpBytes[i]) - { - return -1; - } - } - - return 0; - } - } - - /// - /// Gets if specified IP address is private LAN IP address. For example 192.168.x.x is private ip. - /// - /// IP address to check. - /// Is raised when ip is null reference. - /// Returns true if IP is private IP. - public static bool IsPrivateIP(string ip) - { - if (ip == null) - { - throw new ArgumentNullException("ip"); - } - - return IsPrivateIP(IPAddress.Parse(ip)); - } - - /// - /// Gets if specified IP address is private LAN IP address. For example 192.168.x.x is private ip. - /// - /// IP address to check. - /// Returns true if IP is private IP. - /// Is raised when ip is null reference. - public static bool IsPrivateIP(IPAddress ip) - { - if (ip == null) - { - throw new ArgumentNullException("ip"); - } - - if (ip.AddressFamily == AddressFamily.InterNetwork) - { - byte[] ipBytes = ip.GetAddressBytes(); - - /* Private IPs: - First Octet = 192 AND Second Octet = 168 (Example: 192.168.X.X) - First Octet = 172 AND (Second Octet >= 16 AND Second Octet <= 31) (Example: 172.16.X.X - 172.31.X.X) - First Octet = 10 (Example: 10.X.X.X) - First Octet = 169 AND Second Octet = 254 (Example: 169.254.X.X) - - */ - - if (ipBytes[0] == 192 && ipBytes[1] == 168) - { - return true; - } - if (ipBytes[0] == 172 && ipBytes[1] >= 16 && ipBytes[1] <= 31) - { - return true; - } - if (ipBytes[0] == 10) - { - return true; - } - if (ipBytes[0] == 169 && ipBytes[1] == 254) - { - return true; - } - } - - return false; - } - - /// - /// Creates new socket for the specified end point. - /// - /// Local end point. - /// Protocol type. - /// Retruns newly created socket. - public static Socket CreateSocket(IPEndPoint localEP, ProtocolType protocolType) - { - SocketType socketType = SocketType.Dgram; - if (protocolType == ProtocolType.Udp) - { - socketType = SocketType.Dgram; - } - - if (localEP.AddressFamily == AddressFamily.InterNetwork) - { - Socket socket = new Socket(AddressFamily.InterNetwork, socketType, protocolType); - socket.Bind(localEP); - - return socket; - } - else if (localEP.AddressFamily == AddressFamily.InterNetworkV6) - { - Socket socket = new Socket(AddressFamily.InterNetworkV6, socketType, protocolType); - socket.Bind(localEP); - - return socket; - } - else - { - throw new ArgumentException("Invalid IPEndPoint address family."); - } - } - - /// - /// Converts string to hex string. - /// - /// String to convert. - /// Returns data as hex string. - public static string ToHexString(string data) - { - return Encoding.Default.GetString(ToHex(Encoding.Default.GetBytes(data))); - } - - /// - /// Converts string to hex string. - /// - /// Data to convert. - /// Returns data as hex string. - public static string ToHexString(byte[] data) - { - return Encoding.Default.GetString(ToHex(data)); - } - - /// - /// Convert byte to hex data. - /// - /// Byte to convert. - /// - public static byte[] ToHex(byte byteValue) - { - return ToHex(new[] {byteValue}); - } - - /// - /// Converts data to hex data. - /// - /// Data to convert. - /// - public static byte[] ToHex(byte[] data) - { - char[] hexChars = new[] - { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', - 'F' - }; - - MemoryStream retVal = new MemoryStream(data.Length*2); - foreach (byte b in data) - { - byte[] hexByte = new byte[2]; - - // left 4 bit of byte - hexByte[0] = (byte) hexChars[(b & 0xF0) >> 4]; - - // right 4 bit of byte - hexByte[1] = (byte) hexChars[b & 0x0F]; - - retVal.Write(hexByte, 0, 2); - } - - return retVal.ToArray(); - } - - /// - /// Converts hex byte data to normal byte data. Hex data must be in two bytes pairs, for example: 0F,FF,A3,... . - /// - /// Hex data. - /// - public static byte[] FromHex(byte[] hexData) - { - if (hexData.Length < 2 || (hexData.Length/(double) 2 != Math.Floor(hexData.Length/(double) 2))) - { - throw new Exception( - "Illegal hex data, hex data must be in two bytes pairs, for example: 0F,FF,A3,... ."); - } - - MemoryStream retVal = new MemoryStream(hexData.Length/2); - // Loop hex value pairs - for (int i = 0; i < hexData.Length; i += 2) - { - byte[] hexPairInDecimal = new byte[2]; - // We need to convert hex char to decimal number, for example F = 15 - for (int h = 0; h < 2; h++) - { - if (((char) hexData[i + h]) == '0') - { - hexPairInDecimal[h] = 0; - } - else if (((char) hexData[i + h]) == '1') - { - hexPairInDecimal[h] = 1; - } - else if (((char) hexData[i + h]) == '2') - { - hexPairInDecimal[h] = 2; - } - else if (((char) hexData[i + h]) == '3') - { - hexPairInDecimal[h] = 3; - } - else if (((char) hexData[i + h]) == '4') - { - hexPairInDecimal[h] = 4; - } - else if (((char) hexData[i + h]) == '5') - { - hexPairInDecimal[h] = 5; - } - else if (((char) hexData[i + h]) == '6') - { - hexPairInDecimal[h] = 6; - } - else if (((char) hexData[i + h]) == '7') - { - hexPairInDecimal[h] = 7; - } - else if (((char) hexData[i + h]) == '8') - { - hexPairInDecimal[h] = 8; - } - else if (((char) hexData[i + h]) == '9') - { - hexPairInDecimal[h] = 9; - } - else if (((char) hexData[i + h]) == 'A' || ((char) hexData[i + h]) == 'a') - { - hexPairInDecimal[h] = 10; - } - else if (((char) hexData[i + h]) == 'B' || ((char) hexData[i + h]) == 'b') - { - hexPairInDecimal[h] = 11; - } - else if (((char) hexData[i + h]) == 'C' || ((char) hexData[i + h]) == 'c') - { - hexPairInDecimal[h] = 12; - } - else if (((char) hexData[i + h]) == 'D' || ((char) hexData[i + h]) == 'd') - { - hexPairInDecimal[h] = 13; - } - else if (((char) hexData[i + h]) == 'E' || ((char) hexData[i + h]) == 'e') - { - hexPairInDecimal[h] = 14; - } - else if (((char) hexData[i + h]) == 'F' || ((char) hexData[i + h]) == 'f') - { - hexPairInDecimal[h] = 15; - } - } - - // Join hex 4 bit(left hex cahr) + 4bit(right hex char) in bytes 8 it - retVal.WriteByte((byte) ((hexPairInDecimal[0] << 4) | hexPairInDecimal[1])); - } - - return retVal.ToArray(); - } - - /// - /// Computes md5 hash. - /// - /// Text to hash. - /// Specifies if md5 value is returned as hex string. - /// Resturns md5 value or md5 hex value. - public static string ComputeMd5(string text, bool hex) - { - MD5 md5 = new MD5CryptoServiceProvider(); - byte[] hash = md5.ComputeHash(Encoding.Default.GetBytes(text)); - - if (hex) - { - return ToHexString(Encoding.Default.GetString(hash)).ToLower(); - } - else - { - return Encoding.Default.GetString(hash); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Net_Utils.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Net_Utils.cs deleted file mode 100644 index 5af9de9bf..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Net_Utils.cs +++ /dev/null @@ -1,259 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - #region usings - - using System; - using System.IO; - using System.Net; - using System.Net.Sockets; - using System.Text; - - #endregion - - /// - /// Common utility methods. - /// - public class Net_Utils - { - #region Methods - - /// - /// Gets local host name or argument hostName value if it's specified. - /// - /// Host name or null. - /// Returns local host name or argument hostName value if it's specified. - public static string GetLocalHostName(string hostName) - { - if (string.IsNullOrEmpty(hostName)) - { - return System.Net.Dns.GetHostName(); - } - else - { - return hostName; - } - } - - /// - /// Compares if specified array itmes equals. - /// - /// Array 1. - /// Array 2 - /// Returns true if both arrays are equal. - public static bool CompareArray(Array array1, Array array2) - { - return CompareArray(array1, array2, array2.Length); - } - - /// - /// Compares if specified array itmes equals. - /// - /// Array 1. - /// Array 2 - /// Number of bytes in array 2 used for compare. - /// Returns true if both arrays are equal. - public static bool CompareArray(Array array1, Array array2, int array2Count) - { - if (array1 == null && array2 == null) - { - return true; - } - if (array1 == null && array2 != null) - { - return false; - } - if (array1 != null && array2 == null) - { - return false; - } - if (array1.Length != array2Count) - { - return false; - } - else - { - for (int i = 0; i < array1.Length; i++) - { - if (!array1.GetValue(i).Equals(array2.GetValue(i))) - { - return false; - } - } - } - - return true; - } - - /// - /// Copies source stream data to target stream. - /// - /// Source stream. Reading starts from stream current position. - /// Target stream. Writing starts from stream current position. - /// Specifies transfer block size in bytes. - /// Returns number of bytes copied. - public static long StreamCopy(Stream source, Stream target, int blockSize) - { - if (source == null) - { - throw new ArgumentNullException("source"); - } - if (target == null) - { - throw new ArgumentNullException("target"); - } - if (blockSize < 1024) - { - throw new ArgumentException("Argument 'blockSize' value must be >= 1024."); - } - - byte[] buffer = new byte[blockSize]; - long totalReaded = 0; - while (true) - { - int readedCount = source.Read(buffer, 0, buffer.Length); - // We reached end of stream, we readed all data sucessfully. - if (readedCount == 0) - { - return totalReaded; - } - else - { - target.Write(buffer, 0, readedCount); - totalReaded += readedCount; - } - } - } - - /// - /// Gets if the specified string value is IP address. - /// - /// Value to check. - /// Returns true if specified value is IP address. - /// Is raised when value is null reference. - public static bool IsIPAddress(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - IPAddress ip = null; - - return IPAddress.TryParse(value, out ip); - } - - /// - /// Gets if the specified IP address is multicast address. - /// - /// IP address. - /// Returns true if ip is muticast address, otherwise false. - /// Is raised when ip s null reference. - public static bool IsMulticastAddress(IPAddress ip) - { - if (ip == null) - { - throw new ArgumentNullException("ip"); - } - - // IPv4 multicast 224.0.0.0 to 239.255.255.255 - - if (ip.IsIPv6Multicast) - { - return true; - } - else if (ip.AddressFamily == AddressFamily.InterNetwork) - { - byte[] bytes = ip.GetAddressBytes(); - if (bytes[0] >= 224 && bytes[0] <= 239) - { - return true; - } - } - - return false; - } - - /// - /// Parses IPEndPoint from the specified string value. - /// - /// IPEndPoint string value. - /// Returns parsed IPEndPoint. - /// Is raised when value is null reference. - /// Is raised when any of the arguments has invalid value. - public static IPEndPoint ParseIPEndPoint(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - try - { - string[] ip_port = value.Split(':'); - - return new IPEndPoint(IPAddress.Parse(ip_port[0]), Convert.ToInt32(ip_port[1])); - } - catch (Exception x) - { - throw new ArgumentException("Invalid IPEndPoint value.", "value", x); - } - } - - /// - /// Gets if IO completion ports supported by OS. - /// - /// - public static bool IsIoCompletionPortsSupported() - { - Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - try - { - SocketAsyncEventArgs e = new SocketAsyncEventArgs(); - e.SetBuffer(new byte[0], 0, 0); - e.RemoteEndPoint = new IPEndPoint(IPAddress.Loopback, 111); - s.SendToAsync(e); - - return true; - } - catch (NotSupportedException nX) - { - string dummy = nX.Message; - - return false; - } - finally - { - s.Close(); - } - } - - /// - /// Converts specified string to HEX string. - /// - /// String to convert. - /// Returns hex string. - public static string Hex(string text) - { - return BitConverter.ToString(Encoding.Default.GetBytes(text)).ToLower().Replace("-", ""); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Client/POP3_Client.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Client/POP3_Client.cs deleted file mode 100644 index 1681bbf91..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Client/POP3_Client.cs +++ /dev/null @@ -1,1205 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System.Globalization; - -namespace ASC.Mail.Net.POP3.Client -{ - #region usings - - using System; - using System.Collections.Generic; - using System.IO; - using System.Security.Principal; - using IO; - using TCP; - - #endregion - - /// - /// POP3 Client. Defined in RFC 1939. - /// - /// - /// - /// - /// /* - /// To make this code to work, you need to import following namespaces: - /// using LumiSoft.Net.Mime; - /// using LumiSoft.Net.POP3.Client; - /// */ - /// - /// using(POP3_Client c = new POP3_Client()){ - /// c.Connect("ivx",WellKnownPorts.POP3); - /// c.Authenticate("test","test",true); - /// - /// // Get first message if there is any - /// if(c.Messages.Count > 0){ - /// // Do your suff - /// - /// // Parse message - /// Mime m = Mime.Parse(c.Messages[0].MessageToByte()); - /// string from = m.MainEntity.From; - /// string subject = m.MainEntity.Subject; - /// // ... - /// } - /// } - /// - /// - public class POP3_Client : TCP_Client - { - #region Nested type: AuthenticateDelegate - - /// - /// Internal helper method for asynchronous Authenticate method. - /// - private delegate void AuthenticateDelegate(string userName, string password, bool tryApop); - - #endregion - - #region Nested type: NoopDelegate - - /// - /// Internal helper method for asynchronous Noop method. - /// - private delegate void NoopDelegate(); - - #endregion - - #region Nested type: ResetDelegate - - /// - /// Internal helper method for asynchronous Reset method. - /// - private delegate void ResetDelegate(); - - #endregion - - #region Nested type: StartTLSDelegate - - /// - /// Internal helper method for asynchronous StartTLS method. - /// - private delegate void StartTLSDelegate(); - - #endregion - - #region Members - - private readonly List m_pExtCapabilities; - private string m_ApopHashKey = ""; - private string m_GreetingText = ""; - private bool m_IsUidlSupported; - private GenericIdentity m_pAuthdUserIdentity; - private POP3_ClientMessageCollection m_pMessages; - - #endregion - - #region Properties - - /// - /// Gets greeting text which was sent by POP3 server. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when this property is accessed and POP3 client is not connected. - public string GreetingText - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - - return m_GreetingText; - } - } - - /// - /// Gets POP3 exteneded capabilities supported by POP3 server. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when this property is accessed and POP3 client is not connected. - [Obsolete("USe ExtendedCapabilities instead !")] - public string[] ExtenededCapabilities - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - - return m_pExtCapabilities.ToArray(); - } - } - - /// - /// Gets POP3 exteneded capabilities supported by POP3 server. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when this property is accessed and POP3 client is not connected. - public string[] ExtendedCapabilities - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - - return m_pExtCapabilities.ToArray(); - } - } - - /// - /// Gets if POP3 server supports UIDL command. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when this property is accessed and - /// POP3 client is not connected and authenticated. - public bool IsUidlSupported - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException("You must authenticate first."); - } - - return m_IsUidlSupported; - } - } - - /// - /// Gets messages collection. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when this property is accessed and - /// POP3 client is not connected and authenticated. - public POP3_ClientMessageCollection Messages - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException("You must authenticate first."); - } - - return m_pMessages; - } - } - - /// - /// Gets session authenticated user identity, returns null if not authenticated. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when this property is accessed and POP3 client is not connected. - public override GenericIdentity AuthenticatedUserIdentity - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - - return m_pAuthdUserIdentity; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public POP3_Client(int default_login_delay) - { - m_pExtCapabilities = new List(); - this.default_login_delay = default_login_delay; - AuthSucceed += OnAuthSucceed; - } - - #endregion - - #region Methods - - /// - /// Clean up any resources being used. - /// - public override void Dispose() - { - base.Dispose(); - } - - /// - /// Closes connection to POP3 server. - /// - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when POP3 client is not connected. - public override void Disconnect() - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("POP3 client is not connected."); - } - - try - { - // Send QUIT command to server. - WriteLine("QUIT"); - } - catch {} - - try - { - base.Disconnect(); - } - catch {} - - if (m_pMessages != null) - { - m_pMessages.Dispose(); - m_pMessages = null; - } - m_ApopHashKey = ""; - m_pExtCapabilities.Clear(); - m_IsUidlSupported = false; - } - - /// - /// Starts switching to SSL. - /// - /// An IAsyncResult that references the asynchronous operation. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when POP3 client is not connected or is authenticated or is already secure connection. - public IAsyncResult BeginStartTLS(AsyncCallback callback, object state) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (IsAuthenticated) - { - throw new InvalidOperationException( - "The STLS command is only valid in non-authenticated state."); - } - if (IsSecureConnection) - { - throw new InvalidOperationException("Connection is already secure."); - } - - StartTLSDelegate asyncMethod = StartTLS; - AsyncResultState asyncState = new AsyncResultState(this, asyncMethod, callback, state); - asyncState.SetAsyncResult(asyncMethod.BeginInvoke(asyncState.CompletedCallback, null)); - - return asyncState; - } - - /// - /// Ends a pending asynchronous StartTLS request. - /// - /// An IAsyncResult that stores state information and any user defined data for this asynchronous operation. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when asyncResult is null. - /// Is raised when invalid asyncResult passed to this method. - /// Is raised when POP3 server returns error. - public void EndStartTLS(IAsyncResult asyncResult) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (asyncResult == null) - { - throw new ArgumentNullException("asyncResult"); - } - - AsyncResultState castedAsyncResult = asyncResult as AsyncResultState; - if (castedAsyncResult == null || castedAsyncResult.AsyncObject != this) - { - throw new ArgumentException( - "Argument asyncResult was not returned by a call to the BeginReset method."); - } - if (castedAsyncResult.IsEndCalled) - { - throw new InvalidOperationException( - "BeginReset was previously called for the asynchronous connection."); - } - - castedAsyncResult.IsEndCalled = true; - if (castedAsyncResult.AsyncDelegate is StartTLSDelegate) - { - ((StartTLSDelegate) castedAsyncResult.AsyncDelegate).EndInvoke(castedAsyncResult.AsyncResult); - } - else - { - throw new ArgumentException( - "Argument asyncResult was not returned by a call to the BeginReset method."); - } - } - - /// - /// Switches POP3 connection to SSL. - /// - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when POP3 client is not connected or is authenticated or is already secure connection. - /// Is raised when POP3 server returns error. - public void StartTLS() - { - /* RFC 2595 4. POP3 STARTTLS extension. - Arguments: none - - Restrictions: - Only permitted in AUTHORIZATION state. - - Possible Responses: - +OK -ERR - - Examples: - C: STLS - S: +OK Begin TLS negotiation - - ... - C: STLS - S: -ERR Command not permitted when TLS active - */ - - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (IsAuthenticated) - { - throw new InvalidOperationException( - "The STLS command is only valid in non-authenticated state."); - } - if (IsSecureConnection) - { - throw new InvalidOperationException("Connection is already secure."); - } - - WriteLine("STLS"); - - string line = ReadLine(); - if (!line.ToUpper().StartsWith("+OK")) - { - throw new POP3_ClientException(line); - } - - SwitchToSecure(); - } - - /// - /// Starts authentication. - /// - /// User login name. - /// Password. - /// If true and POP3 server supports APOP, then APOP is used, otherwise normal login used. - /// Callback to call when the asynchronous operation is complete. - /// User data. - /// An IAsyncResult that references the asynchronous operation. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when POP3 client is not connected or is already authenticated. - public IAsyncResult BeginAuthenticate(string userName, - string password, - bool tryApop, - AsyncCallback callback, - object state) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (IsAuthenticated) - { - throw new InvalidOperationException("Session is already authenticated."); - } - - AuthenticateDelegate asyncMethod = Authenticate; - AsyncResultState asyncState = new AsyncResultState(this, asyncMethod, callback, state); - asyncState.SetAsyncResult(asyncMethod.BeginInvoke(userName, - password, - tryApop, - asyncState.CompletedCallback, - null)); - - return asyncState; - } - - /// - /// Ends a pending asynchronous authentication request. - /// - /// An IAsyncResult that stores state information and any user defined data for this asynchronous operation. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when asyncResult is null. - /// Is raised when invalid asyncResult passed to this method. - /// Is raised when POP3 server returns error. - public void EndAuthenticate(IAsyncResult asyncResult) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (asyncResult == null) - { - throw new ArgumentNullException("asyncResult"); - } - - AsyncResultState castedAsyncResult = asyncResult as AsyncResultState; - if (castedAsyncResult == null || castedAsyncResult.AsyncObject != this) - { - throw new ArgumentException( - "Argument asyncResult was not returned by a call to the BeginAuthenticate method."); - } - if (castedAsyncResult.IsEndCalled) - { - throw new InvalidOperationException( - "BeginAuthenticate was previously called for the asynchronous connection."); - } - - castedAsyncResult.IsEndCalled = true; - if (castedAsyncResult.AsyncDelegate is AuthenticateDelegate) - { - ((AuthenticateDelegate) castedAsyncResult.AsyncDelegate).EndInvoke( - castedAsyncResult.AsyncResult); - } - else - { - throw new ArgumentException( - "Argument asyncResult was not returned by a call to the BeginAuthenticate method."); - } - } - - public delegate void AuthSucceedDelegate(); - public event AuthSucceedDelegate AuthSucceed; - - public delegate void AuthFailedDelegate(string response_line); - public event AuthFailedDelegate AuthFailed; - - /// - /// Authenticates user. - /// - /// User login name. - /// Password. - /// If true and POP3 server supports APOP, then APOP is used, otherwise normal login used. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when POP3 client is not connected or is already authenticated. - /// Is raised when POP3 server returns error. - public void Authenticate(string userName, string password, bool tryApop) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (IsAuthenticated) - { - throw new InvalidOperationException("Session is already authenticated."); - } - - // Supports APOP, use it. - if (tryApop && m_ApopHashKey.Length > 0) - { - string hexHash = Core.ComputeMd5(m_ApopHashKey + password, true); - - int countWritten = TcpStream.WriteLine("APOP " + userName + " " + hexHash); - LogAddWrite(countWritten, "APOP " + userName + " " + hexHash); - - string line = ReadLine(); - if (line.StartsWith("+OK")) - { - m_pAuthdUserIdentity = new GenericIdentity(userName, "apop"); - } - else - { - if (AuthFailed != null) - { - AuthFailed.Invoke(line); - } - throw new POP3_ClientException(line); - } - } - // Use normal LOGIN, don't support APOP. - else - { - int countWritten = TcpStream.WriteLine("USER " + userName); - LogAddWrite(countWritten, "USER " + userName); - - string line = ReadLine(); - if (line.StartsWith("+OK")) - { - countWritten = TcpStream.WriteLine("PASS " + password); - LogAddWrite(countWritten, "PASS <***REMOVED***>"); - - line = ReadLine(); - if (line.StartsWith("+OK")) - { - m_pAuthdUserIdentity = new GenericIdentity(userName, "pop3-user/pass"); - } - else - { - if (AuthFailed != null) - { - AuthFailed.Invoke(line); - } - throw new POP3_ClientException(line); - } - } - else - { - if (AuthFailed != null) - { - AuthFailed.Invoke(line); - } - throw new POP3_ClientException(line); - } - } - - if (IsAuthenticated) - { - if (AuthSucceed != null) - { - AuthSucceed.Invoke(); - } - FillMessages(); - } - } - - private void OnAuthSucceed() - { - if (need_precise_login_delay) - { - GetCAPA_Parameters(); - LoginDelay = ObtainLoginDelay(default_login_delay); - } - } - - /// - /// Starts sending NOOP command to server. This method can be used for keeping connection alive(not timing out). - /// - /// Callback to call when the asynchronous operation is complete. - /// User data. - /// An IAsyncResult that references the asynchronous operation. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when POP3 client is not connected. - public IAsyncResult BeginNoop(AsyncCallback callback, object state) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - - NoopDelegate asyncMethod = Noop; - AsyncResultState asyncState = new AsyncResultState(this, asyncMethod, callback, state); - asyncState.SetAsyncResult(asyncMethod.BeginInvoke(asyncState.CompletedCallback, null)); - - return asyncState; - } - - /// - /// Ends a pending asynchronous Noop request. - /// - /// An IAsyncResult that stores state information and any user defined data for this asynchronous operation. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when asyncResult is null. - /// Is raised when invalid asyncResult passed to this method. - /// Is raised when POP3 server returns error. - public void EndNoop(IAsyncResult asyncResult) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (asyncResult == null) - { - throw new ArgumentNullException("asyncResult"); - } - - AsyncResultState castedAsyncResult = asyncResult as AsyncResultState; - if (castedAsyncResult == null || castedAsyncResult.AsyncObject != this) - { - throw new ArgumentException( - "Argument asyncResult was not returned by a call to the BeginNoop method."); - } - if (castedAsyncResult.IsEndCalled) - { - throw new InvalidOperationException( - "BeginNoop was previously called for the asynchronous connection."); - } - - castedAsyncResult.IsEndCalled = true; - if (castedAsyncResult.AsyncDelegate is NoopDelegate) - { - ((NoopDelegate) castedAsyncResult.AsyncDelegate).EndInvoke(castedAsyncResult.AsyncResult); - } - else - { - throw new ArgumentException( - "Argument asyncResult was not returned by a call to the BeginNoop method."); - } - } - - /// - /// Send NOOP command to server. This method can be used for keeping connection alive(not timing out). - /// - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when POP3 client is not connected. - /// Is raised when POP3 server returns error. - public void Noop() - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException("The NOOP command is only valid in TRANSACTION state."); - } - - /* RFC 1939 5 NOOP. - Arguments: none - - Restrictions: - may only be given in the TRANSACTION state - - Discussion: - The POP3 server does nothing, it merely replies with a - positive response. - - Possible Responses: - +OK - - Examples: - C: NOOP - S: +OK - */ - - WriteLine("NOOP"); - - string line = ReadLine(); - if (!line.ToUpper().StartsWith("+OK")) - { - throw new POP3_ClientException(line); - } - } - - /// - /// Starts resetting session. Messages marked for deletion will be unmarked. - /// - /// Callback to call when the asynchronous operation is complete. - /// User data. - /// An IAsyncResult that references the asynchronous operation. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when POP3 client is not connected and authenticated. - public IAsyncResult BeginReset(AsyncCallback callback, object state) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException("The RSET command is only valid in authenticated state."); - } - - ResetDelegate asyncMethod = Reset; - AsyncResultState asyncState = new AsyncResultState(this, asyncMethod, callback, state); - asyncState.SetAsyncResult(asyncMethod.BeginInvoke(asyncState.CompletedCallback, null)); - - return asyncState; - } - - /// - /// Ends a pending asynchronous reset request. - /// - /// An IAsyncResult that stores state information and any user defined data for this asynchronous operation. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when asyncResult is null. - /// Is raised when invalid asyncResult passed to this method. - /// Is raised when POP3 server returns error. - public void EndReset(IAsyncResult asyncResult) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (asyncResult == null) - { - throw new ArgumentNullException("asyncResult"); - } - - AsyncResultState castedAsyncResult = asyncResult as AsyncResultState; - if (castedAsyncResult == null || castedAsyncResult.AsyncObject != this) - { - throw new ArgumentException( - "Argument asyncResult was not returned by a call to the BeginReset method."); - } - if (castedAsyncResult.IsEndCalled) - { - throw new InvalidOperationException( - "BeginReset was previously called for the asynchronous connection."); - } - - castedAsyncResult.IsEndCalled = true; - if (castedAsyncResult.AsyncDelegate is ResetDelegate) - { - ((ResetDelegate) castedAsyncResult.AsyncDelegate).EndInvoke(castedAsyncResult.AsyncResult); - } - else - { - throw new ArgumentException( - "Argument asyncResult was not returned by a call to the BeginReset method."); - } - } - - /// - /// Resets session. Messages marked for deletion will be unmarked. - /// - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when POP3 client is not connected and authenticated. - /// Is raised when POP3 server returns error. - public void Reset() - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!IsAuthenticated) - { - throw new InvalidOperationException("The RSET command is only valid in TRANSACTION state."); - } - - /* RFC 1939 5. RSET. - Arguments: none - - Restrictions: - may only be given in the TRANSACTION state - - Discussion: - If any messages have been marked as deleted by the POP3 - server, they are unmarked. The POP3 server then replies - with a positive response. - - Possible Responses: - +OK - - Examples: - C: RSET - S: +OK maildrop has 2 messages (320 octets) - */ - - WriteLine("RSET"); - - // Read first line of reply, check if it's ok. - string line = ReadLine(); - if (!line.StartsWith("+OK")) - { - throw new POP3_ClientException(line); - } - - foreach (POP3_ClientMessage message in m_pMessages) - { - message.SetMarkedForDeletion(false); - } - } - - #endregion - - #region Overrides - - /// - /// This method is called after TCP client has sucessfully connected. - /// - protected override void OnConnected() - { - // Read first line of reply, check if it's ok. - string line = ReadLine(); - if (line.ToUpper().StartsWith("+OK")) - { - m_GreetingText = line.Substring(3).Trim(); - - // Try to read APOP hash key, if supports APOP. - if (line.IndexOf("<") > -1 && line.IndexOf(">") > -1) - { - m_ApopHashKey = line.Substring(line.IndexOf("<"), - line.LastIndexOf(">") - line.IndexOf("<") + 1); - } - } - else - { - throw new POP3_ClientException(line); - } - - // Try to get POP3 server supported capabilities, if command not supported, just skip tat command. - GetCAPA_Parameters(); - LoginDelay = ObtainLoginDelay(default_login_delay); - } - - private static int GetIntParam(string capa, int position /* 1-based */, int unspecifiedValue) - { - var capaParams = capa.Split(' '); - if (capaParams.Length >= position) - { - int param; - if (int.TryParse(capaParams[position-1], NumberStyles.Integer, CultureInfo.InvariantCulture, out param)) - { - return param; - } - } - return unspecifiedValue; - } - - public int LoginDelay { get; set; } - private bool need_precise_login_delay = false; - private int default_login_delay; - - #endregion - - #region Utility methods - - /// - /// Fills messages info. - /// - private void FillMessages() - { - m_pMessages = new POP3_ClientMessageCollection(this); - - /* - First make messages info, then try to add UIDL if server supports. - */ - - /* NOTE: If reply is +OK, this is multiline respone and is terminated with '.'. - Examples: - C: LIST - S: +OK 2 messages (320 octets) - S: 1 120 - S: 2 200 - S: . - ... - C: LIST 3 - S: -ERR no such message, only 2 messages in maildrop - */ - - WriteLine("LIST"); - - // Read first line of reply, check if it's ok. - string line = ReadLine(); - if (!string.IsNullOrEmpty(line) && line.StartsWith("+OK")) - { - // Read lines while get only '.' on line itshelf. - while (true) - { - line = ReadLine(); - - // End of data - if (line.Trim() == ".") - { - break; - } - else - { - string[] no_size = line.Trim().Split(new[] {' '}); - m_pMessages.Add(Convert.ToInt32(no_size[1])); - } - } - } - else - { - throw new POP3_ClientException(line); - } - - // Try to fill messages UIDs. - /* NOTE: If reply is +OK, this is multiline respone and is terminated with '.'. - Examples: - C: UIDL - S: +OK - S: 1 whqtswO00WBw418f9t5JxYwZ - S: 2 QhdPYR:00WBw1Ph7x7 - S: . - ... - C: UIDL 3 - S: -ERR no such message - */ - - WriteLine("UIDL"); - - // Read first line of reply, check if it's ok - line = ReadLine(); - if (line.StartsWith("+OK")) - { - m_IsUidlSupported = true; - - // Read lines while get only '.' on line itshelf. - while (true) - { - line = ReadLine(); - - // End of data - if (line.Trim() == ".") - { - break; - } - else - { - string[] no_uid = line.Trim().Split(new[] {' '}); - m_pMessages[Convert.ToInt32(no_uid[0]) - 1].UIDL = no_uid[1]; - } - } - } - else - { - m_IsUidlSupported = false; - } - } - - /// - /// Executes CAPA command and reads its parameters. - /// - /// RFC 2449 CAPA - /// Arguments: - /// none - /// - /// Restrictions: - /// none - /// - /// Discussion: - /// An -ERR response indicates the capability command is not - /// implemented and the client will have to probe for - /// capabilities as before. - /// - /// An +OK response is followed by a list of capabilities, one - /// per line. Each capability name MAY be followed by a single - /// space and a space-separated list of parameters. Each - /// capability line is limited to 512 octets (including the - /// CRLF). The capability list is terminated by a line - /// containing a termination octet (".") and a CRLF pair. - /// - /// Possible Responses: - /// +OK -ERR - /// - /// Examples: - /// C: CAPA - /// S: +OK Capability list follows - /// S: TOP - /// S: USER - /// S: SASL CRAM-MD5 KERBEROS_V4 - /// S: RESP-CODES - /// S: LOGIN-DELAY 900 - /// S: PIPELINING - /// S: EXPIRE 60 - /// S: UIDL - /// S: IMPLEMENTATION Shlemazle-Plotz-v302 - /// S: . - /// - /// Note: beware that parameters list may be different in authentication step and in transaction step - /// - private void GetCAPA_Parameters() - { - m_pExtCapabilities.Clear(); - - WriteLine("CAPA"); - - // CAPA command supported, read capabilities. - if (ReadLine().ToUpper().StartsWith("+OK")) - { - string line; - while ((line = ReadLine()) != ".") - { - m_pExtCapabilities.Add(line.ToUpper()); - } - } - } - - /// - /// Obtains LOOGIN-DELAY tag from CAPA parameters - /// - /// login delay read or 'default_value' if the parameter is absent - private int ObtainLoginDelay(int default_value) - { - need_precise_login_delay = m_pExtCapabilities.Count != 0; - foreach (string capa_line in m_pExtCapabilities) - { - if (capa_line.StartsWith("LOGIN-DELAY")) - { - string[] capaParams = capa_line.Split(' '); - if (capaParams.Length > 1) - { - int delay; - if (int.TryParse(capaParams[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out delay)) - { - need_precise_login_delay = capaParams.Length > 2 && capaParams[2].ToUpper() == "USER"; - return delay; - } - } - } - } - return default_value; - } - - #endregion - - #region Internal methods - - /// - /// Marks specified message for deletion. - /// - /// Message sequence number. - internal void MarkMessageForDeletion(int sequenceNumber) - { - WriteLine("DELE " + sequenceNumber); - - // Read first line of reply, check if it's ok. - string line = ReadLine(); - if (!line.StartsWith("+OK")) - { - throw new POP3_ClientException(line); - } - } - - /// - /// Stores specified message to the specified stream. - /// - /// Message 1 based sequence number. - /// Stream where to store message. - internal void GetMessage(int sequenceNumber, Stream stream) - { - WriteLine("RETR " + sequenceNumber); - - // Read first line of reply, check if it's ok. - string line = ReadLine(); - if (line.StartsWith("+OK")) - { - SmartStream.ReadPeriodTerminatedAsyncOP readTermOP = - new SmartStream.ReadPeriodTerminatedAsyncOP(stream, - 999999999, - SizeExceededAction.ThrowException); - TcpStream.ReadPeriodTerminated(readTermOP, false); - if (readTermOP.Error != null) - { - throw readTermOP.Error; - } - LogAddWrite(readTermOP.BytesStored, readTermOP.BytesStored + " bytes read."); - } - else - { - throw new POP3_ClientException(line); - } - } - - /// - /// Stores specified message header + specified lines of body to the specified stream. - /// - /// Message 1 based sequence number. - /// Stream where to store data. - /// Number of lines of message body to get. - internal void GetTopOfMessage(int sequenceNumber, Stream stream, int lineCount) - { - TcpStream.WriteLine("TOP " + sequenceNumber + " " + lineCount); - - // Read first line of reply, check if it's ok. - string line = ReadLine(); - if (line.StartsWith("+OK")) - { - SmartStream.ReadPeriodTerminatedAsyncOP readTermOP = - new SmartStream.ReadPeriodTerminatedAsyncOP(stream, - 999999999, - SizeExceededAction.ThrowException); - TcpStream.ReadPeriodTerminated(readTermOP, false); - if (readTermOP.Error != null) - { - throw readTermOP.Error; - } - LogAddWrite(readTermOP.BytesStored, readTermOP.BytesStored + " bytes read."); - } - else - { - throw new POP3_ClientException(line); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Client/POP3_ClientException.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Client/POP3_ClientException.cs deleted file mode 100644 index 7b35d10b6..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Client/POP3_ClientException.cs +++ /dev/null @@ -1,83 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.POP3.Client -{ - #region usings - - using System; - - #endregion - - /// - /// POP3 client exception. - /// - public class POP3_ClientException : Exception - { - #region Members - - private readonly string m_ResponseText = ""; - private readonly string m_StatusCode = ""; - - #endregion - - #region Properties - - /// - /// Gets POP3 server error status code. - /// - public string StatusCode - { - get { return m_StatusCode; } - } - - /// - /// Gets POP3 server response text after status code. - /// - public string ResponseText - { - get { return m_ResponseText; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// IMAP server response line. - /// Is raised when responseLine is null. - public POP3_ClientException(string responseLine) : base(responseLine) - { - if (responseLine == null) - { - throw new ArgumentNullException("responseLine"); - } - - // SP - string[] code_text = responseLine.Split(new char[] {}, 2); - m_StatusCode = code_text[0]; - if (code_text.Length == 2) - { - m_ResponseText = code_text[1]; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Client/POP3_ClientMessage.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Client/POP3_ClientMessage.cs deleted file mode 100644 index 517940d7f..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Client/POP3_ClientMessage.cs +++ /dev/null @@ -1,380 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.POP3.Client -{ - #region usings - - using System; - using System.IO; - using System.Text; - using System.Security.Cryptography; - using System.Globalization; - - #endregion - - /// - /// This class represents POP3 client message. - /// - public class POP3_ClientMessage - { - #region Members - - private readonly int m_SequenceNumber = 1; - private readonly int m_Size; - private bool m_IsDisposed; - private bool m_IsMarkedForDeletion; - private POP3_Client m_Pop3Client; - - #endregion - - #region Properties - - /// - /// Gets if POP3 message is Disposed. - /// - public bool IsDisposed - { - get { return m_IsDisposed; } - } - - /// - /// Gets message 1 based sequence number. - /// - /// Is raised when this object is disposed and this property is accessed. - public int SequenceNumber - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_SequenceNumber; - } - } - - /// - /// Gets message UID. NOTE: Before accessing this property, check that server supports UIDL command. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when POP3 server doesnt support UIDL command. - public string UIDL {get; set;} - - /// - /// Gets message size in bytes. - /// - /// Is raised when this object is disposed and this property is accessed. - public int Size - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_Size; - } - } - - /// - /// Gets if message is marked for deletion. - /// - /// Is raised when this object is disposed and this property is accessed. - public bool IsMarkedForDeletion - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_IsMarkedForDeletion; - } - } - - - public string MD5 { get; set; } - public bool IsNew { get; set; } - - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner POP3 client. - /// Message 1 based sequence number. - /// Message size in bytes. - internal POP3_ClientMessage(POP3_Client pop3, int seqNumber, int size) - { - m_Pop3Client = pop3; - m_SequenceNumber = seqNumber; - m_Size = size; - } - - #endregion - - #region Methods - - /// - /// Marks message as deleted. - /// - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when POP3 serveer returns error. - public void MarkForDeletion() - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (IsMarkedForDeletion) - { - return; - } - m_IsMarkedForDeletion = true; - - m_Pop3Client.MarkMessageForDeletion(SequenceNumber); - } - - /// - /// Gets message header as string. - /// - /// Returns message header as string. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when message is marked for deletion and this method is accessed. - /// Is raised when POP3 serveer returns error. - public string HeaderToString() - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (IsMarkedForDeletion) - { - throw new InvalidOperationException("Can't access message, it's marked for deletion."); - } - - return Encoding.Default.GetString(HeaderToByte()); - } - - /// - /// Gets message header as byte[] data. - /// - /// Returns message header as byte[] data. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when message is marked for deletion and this method is accessed. - /// Is raised when POP3 serveer returns error. - public byte[] HeaderToByte() - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (IsMarkedForDeletion) - { - throw new InvalidOperationException("Can't access message, it's marked for deletion."); - } - - MemoryStream retVal = new MemoryStream(); - MessageTopLinesToStream(retVal, 0); - - return retVal.ToArray(); - } - - /// - /// Stores message header to the specified stream. - /// - /// Stream where to store data. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when argument stream value is null. - /// Is raised when POP3 serveer returns error. - public void HeaderToStream(Stream stream) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (stream == null) - { - throw new ArgumentNullException("Argument 'stream' value can't be null."); - } - if (IsMarkedForDeletion) - { - throw new InvalidOperationException("Can't access message, it's marked for deletion."); - } - - MessageTopLinesToStream(stream, 0); - } - - /// - /// Gets message as byte[] data. - /// - /// Returns message as byte[] data. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when message is marked for deletion and this method is accessed. - /// Is raised when POP3 serveer returns error. - public byte[] MessageToByte() - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (IsMarkedForDeletion) - { - throw new InvalidOperationException("Can't access message, it's marked for deletion."); - } - - MemoryStream retVal = new MemoryStream(); - MessageToStream(retVal); - - return retVal.ToArray(); - } - - /// - /// Stores message to specified stream. - /// - /// Stream where to store message. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when argument stream value is null. - /// Is raised when message is marked for deletion and this method is accessed. - /// Is raised when POP3 serveer returns error. - public void MessageToStream(Stream stream) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (stream == null) - { - throw new ArgumentNullException("Argument 'stream' value can't be null."); - } - if (IsMarkedForDeletion) - { - throw new InvalidOperationException("Can't access message, it's marked for deletion."); - } - - m_Pop3Client.GetMessage(SequenceNumber, stream); - } - - /// - /// Gets message header + specified number lines of message body. - /// - /// Number of lines to get from message body. - /// Returns message header + specified number lines of message body. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when numberOfLines is negative value. - /// Is raised when message is marked for deletion and this method is accessed. - /// Is raised when POP3 serveer returns error. - public byte[] MessageTopLinesToByte(int lineCount) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (lineCount < 0) - { - throw new ArgumentException("Argument 'lineCount' value must be >= 0."); - } - if (IsMarkedForDeletion) - { - throw new InvalidOperationException("Can't access message, it's marked for deletion."); - } - - MemoryStream retVal = new MemoryStream(); - MessageTopLinesToStream(retVal, lineCount); - - return retVal.ToArray(); - } - - /// - /// Stores message header + specified number lines of message body to the specified stream. - /// - /// Stream where to store data. - /// Number of lines to get from message body. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when argument stream value is null. - /// Is raised when message is marked for deletion and this method is accessed. - /// Is raised when POP3 serveer returns error. - public void MessageTopLinesToStream(Stream stream, int lineCount) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (stream == null) - { - throw new ArgumentNullException("Argument 'stream' value can't be null."); - } - if (IsMarkedForDeletion) - { - throw new InvalidOperationException("Can't access message, it's marked for deletion."); - } - - m_Pop3Client.GetTopOfMessage(SequenceNumber, stream, lineCount); - } - - #endregion - - #region Internal methods - - /// - /// Disposes message. - /// - internal void Dispose() - { - if (m_IsDisposed) - { - return; - } - - m_IsDisposed = true; - m_Pop3Client = null; - } - - /// - /// Sets IsMarkedForDeletion flag value. - /// - /// New IsMarkedForDeletion value. - internal void SetMarkedForDeletion(bool isMarkedForDeletion) - { - m_IsMarkedForDeletion = isMarkedForDeletion; - } - - - public void CalculateMD5() - { - if (!string.IsNullOrEmpty(MD5)) return; - MD5 hash = System.Security.Cryptography.MD5.Create(); - byte[] data = hash.ComputeHash(HeaderToByte()); - string result_md5 = ""; - for (int i = 0; i < data.Length; i++) - { - result_md5 += data[i].ToString("x2", CultureInfo.InvariantCulture); - } - MD5 = result_md5; - } - - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Client/POP3_ClientMessageCollection.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Client/POP3_ClientMessageCollection.cs deleted file mode 100644 index a9c9be235..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Client/POP3_ClientMessageCollection.cs +++ /dev/null @@ -1,206 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.POP3.Client -{ - #region usings - - using System; - using System.Collections; - using System.Collections.Generic; - - #endregion - - /// - /// This class represents POP3 client messages collection. - /// - public class POP3_ClientMessageCollection : IEnumerable, IDisposable - { - #region Members - - private readonly POP3_Client m_pPop3Client; - private bool m_IsDisposed; - private List m_pMessages; - - #endregion - - #region Properties - - /// - /// Gets total size of messages, messages marked for deletion are included. - /// - /// Is raised when this object is disposed and this property is accessed. - public long TotalSize - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - long size = 0; - foreach (POP3_ClientMessage message in m_pMessages) - { - size += message.Size; - } - - return size; - } - } - - /// - /// Gets number of messages in the collection, messages marked for deletion are included. - /// - /// Is raised when this object is disposed and this property is accessed. - public int Count - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pMessages.Count; - } - } - - /// - /// Gets message from specified index. - /// - /// Message zero based index in the collection. - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when index is out of range. - public POP3_ClientMessage this[int index] - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (index < 0 || index > m_pMessages.Count) - { - throw new ArgumentOutOfRangeException(); - } - - return m_pMessages[index]; - } - } - - /// - /// Gets message with specified UID value. - /// - /// Message UID value. - /// Returns message or null if message doesn't exist. - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when POP3 server doesn't support UIDL. - public POP3_ClientMessage this[string uidl] - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!m_pPop3Client.IsUidlSupported) - { - throw new NotSupportedException(); - } - - foreach (POP3_ClientMessage message in m_pMessages) - { - if (message.UIDL == uidl) - { - return message; - } - } - - return null; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner POP3 client. - internal POP3_ClientMessageCollection(POP3_Client pop3) - { - m_pPop3Client = pop3; - - m_pMessages = new List(); - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public void Dispose() - { - if (m_IsDisposed) - { - return; - } - m_IsDisposed = true; - - // Release messages. - foreach (POP3_ClientMessage message in m_pMessages) - { - message.Dispose(); - } - m_pMessages = null; - } - - /// - /// Gets enumerator. - /// - /// Returns IEnumerator interface. - /// Is raised when this object is disposed and this property is accessed. - public IEnumerator GetEnumerator() - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pMessages.GetEnumerator(); - } - - #endregion - - #region Internal methods - - /// - /// Adds new message to messages collection. - /// - /// Message size in bytes. - internal void Add(int size) - { - m_pMessages.Add(new POP3_ClientMessage(m_pPop3Client, m_pMessages.Count + 1, size)); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/POP3_ExtendedCapabilities.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/POP3_ExtendedCapabilities.cs deleted file mode 100644 index 567253bd4..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/POP3_ExtendedCapabilities.cs +++ /dev/null @@ -1,78 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.Text; - -namespace LumiSoft.Net.POP3 -{ - /// - /// This class holds known POP3 extended capabilities. Defined in http://www.iana.org/assignments/pop3-extension-mechanism. - /// - public class POP3_ExtendedCapabilities - { - /// - /// The TOP capability indicates the optional TOP command is available. Defined in RFC 2449. - /// - public static readonly string TOP = "TOP"; - - /// - /// The USER capability indicates that the USER and PASS commands are supported. Defined in RFC 2449. - /// - public static readonly string USER = "USER"; - - /// - /// The SASL capability indicates that the AUTH command is available and that it supports an optional base64 - /// encoded second argument for an initial client response as described in the SASL specification. Defined in RFC 2449. - /// - public static readonly string SASL = "SASL"; - - /// - /// The RESP-CODES capability indicates that any response text issued by this server which begins with an open - /// square bracket ("[") is an extended response code. Defined in RFC 2449. - /// - public static readonly string RESP_CODES = "RESP-CODES"; - - /// - /// LOGIN-DELAY capability. Defined in RFC 2449. - /// - public static readonly string LOGIN_DELAY = "LOGIN-DELAY"; - - /// - /// The PIPELINING capability indicates the server is capable of accepting multiple commands at a time; - /// the client does not have to wait for the response to a command before issuing a subsequent command. - /// Defined in RFC 2449. - /// - public static readonly string PIPELINING = "PIPELINING"; - - /// - /// EXPIRE capability. Defined in RFC 2449. - /// - public static readonly string EXPIRE = "EXPIRE"; - - /// - /// UIDL command is supported. Defined in RFC 2449. - /// - public static readonly string UIDL = "UIDL"; - - /// - /// STLS(start TLS) command supported. Defined in RFC 2449. - /// - public static readonly string STLS = "STLS"; - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/AuthUser_EventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/AuthUser_EventArgs.cs deleted file mode 100644 index 27125b6b5..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/AuthUser_EventArgs.cs +++ /dev/null @@ -1,134 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.POP3.Server -{ - public class AuthUser_EventArgsBase { - protected AuthType m_AuthType; - protected string m_Data = ""; - protected string m_PasswData = ""; - protected string m_UserName = ""; - private string m_ReturnData = ""; - private bool m_Validated = true; - - /// - /// User name. - /// - public string UserName - { - get { return m_UserName; } - } - - /// - /// Password data. eg. for AUTH=PLAIN it's password and for AUTH=APOP it's md5HexHash. - /// - public string PasswData - { - get { return m_PasswData; } - } - - /// - /// Authentication specific data(as tag). - /// - public string AuthData - { - get { return m_Data; } - } - - /// - /// Authentication type. - /// - public AuthType AuthType - { - get { return m_AuthType; } - } - - /// - /// Gets or sets if user is valid. - /// - public bool Validated - { - get { return m_Validated; } - - set { m_Validated = value; } - } - - /// - /// Gets or sets authentication data what must be returned for connected client. - /// - public string ReturnData - { - get { return m_ReturnData; } - - set { m_ReturnData = value; } - } - - /// - /// Gets or sets error text returned to connected client. - /// - public string ErrorText { get; set; } - } - - /// - /// Provides data for the AuthUser event for POP3_Server. - /// - public class AuthUser_EventArgs : AuthUser_EventArgsBase - { - #region Members - - private readonly POP3_Session m_pSession; - - #endregion - - #region Properties - - /// - /// Gets reference to pop3 session. - /// - public POP3_Session Session - { - get { return m_pSession; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Reference to pop3 session. - /// Username. - /// Password data. - /// Authentication specific data(as tag). - /// Authentication type. - public AuthUser_EventArgs(POP3_Session session, - string userName, - string passwData, - string data, - AuthType authType) - { - m_pSession = session; - m_UserName = userName; - m_PasswData = passwData; - m_Data = data; - m_AuthType = authType; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/GetMessagesInfo_EventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/GetMessagesInfo_EventArgs.cs deleted file mode 100644 index d9907728d..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/GetMessagesInfo_EventArgs.cs +++ /dev/null @@ -1,86 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.POP3.Server -{ - /// - /// Provides data for the GetMessgesList event. - /// - public class GetMessagesInfo_EventArgs - { - #region Members - - private readonly POP3_MessageCollection m_pPOP3_Messages; - private readonly POP3_Session m_pSession; - private readonly string m_UserName = ""; - - #endregion - - #region Properties - - /// - /// Gets reference to pop3 session. - /// - public POP3_Session Session - { - get { return m_pSession; } - } - - /// - /// Gets referance to POP3 messages info. - /// - public POP3_MessageCollection Messages - { - get { return m_pPOP3_Messages; } - } - - /// - /// User Name. - /// - public string UserName - { - get { return m_UserName; } - } - - /// - /// Mailbox name. - /// - public string Mailbox - { - get { return m_UserName; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Reference to pop3 session. - /// - /// Mailbox name. - public GetMessagesInfo_EventArgs(POP3_Session session, POP3_MessageCollection messages, string mailbox) - { - m_pSession = session; - m_pPOP3_Messages = messages; - m_UserName = mailbox; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/POP3_Message.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/POP3_Message.cs deleted file mode 100644 index 3d0529058..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/POP3_Message.cs +++ /dev/null @@ -1,91 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.POP3.Server -{ - /// - /// Holds POP3_Message info (ID,Size,...). - /// - public class POP3_Message - { - #region Members - - private string m_ID = ""; // Holds message ID. - private POP3_MessageCollection m_pOwner; - private string m_UID = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets message ID. - /// - public string ID - { - get { return m_ID; } - - set { m_ID = value; } - } - - /// - /// Gets or sets message UID. This UID is reported in UIDL command. - /// - public string UID - { - get { return m_UID; } - - set { m_UID = value; } - } - - /// - /// Gets or sets message size in bytes. - /// - public long Size { get; set; } - - /// - /// Gets or sets message state flag. - /// - public bool MarkedForDelete { get; set; } - - /// - /// Gets or sets user data for message. - /// - public object Tag { get; set; } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner collection. - /// Message ID. - /// Message UID. - /// Message size in bytes. - internal POP3_Message(POP3_MessageCollection onwer, string id, string uid, long size) - { - m_pOwner = onwer; - m_ID = id; - m_UID = uid; - Size = size; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/POP3_MessageCollection.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/POP3_MessageCollection.cs deleted file mode 100644 index 8e82d73ac..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/POP3_MessageCollection.cs +++ /dev/null @@ -1,193 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.POP3.Server -{ - #region usings - - using System.Collections; - using System.Collections.Generic; - - #endregion - - /// - /// POP3 messages info collection. - /// - public class POP3_MessageCollection : IEnumerable - { - #region Members - - private readonly List m_pMessages; - - #endregion - - #region Properties - - /// - /// Gets number of messages in the collection. - /// - public int Count - { - get { return m_pMessages.Count; } - } - - /// - /// Gets a POP3_Message object in the collection by index number. - /// - /// An Int32 value that specifies the position of the POP3_Message object in the POP3_MessageCollection collection. - public POP3_Message this[int index] - { - get { return m_pMessages[index]; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public POP3_MessageCollection() - { - m_pMessages = new List(); - } - - #endregion - - #region Methods - - /// - /// Adds new message info to the collection. - /// - /// Message ID. - /// Message UID. - /// Message size in bytes. - public POP3_Message Add(string id, string uid, long size) - { - return Add(id, uid, size, null); - } - - /// - /// Adds new message info to the collection. - /// - /// Message ID. - /// Message UID. - /// Message size in bytes. - /// Message user data. - public POP3_Message Add(string id, string uid, long size, object tag) - { - POP3_Message message = new POP3_Message(this, id, uid, size); - m_pMessages.Add(message); - message.Tag = tag; - - return message; - } - - /// - /// Removes specified message from the collection. - /// - /// Message to remove. - public void Remove(POP3_Message message) - { - m_pMessages.Remove(message); - } - - /// - /// Gets if collection contains message with the specified UID. - /// - /// Message UID to check. - /// - public bool ContainsUID(string uid) - { - foreach (POP3_Message message in m_pMessages) - { - if (message.UID.ToLower() == uid.ToLower()) - { - return true; - } - } - - return false; - } - - /// - /// Removes all messages from the collection. - /// - public void Clear() - { - m_pMessages.Clear(); - } - - /// - /// Checks if message exists. NOTE marked for delete messages returns false. - /// - /// Message 1 based sequence number. - /// - public bool MessageExists(int sequenceNo) - { - if (sequenceNo > 0 && sequenceNo <= m_pMessages.Count) - { - if (!m_pMessages[sequenceNo - 1].MarkedForDelete) - { - return true; - } - } - - return false; - } - - /// - /// Gets messages total sizes. NOTE messages marked for deletion is excluded. - /// - /// - public long GetTotalMessagesSize() - { - long totalSize = 0; - foreach (POP3_Message msg in m_pMessages) - { - if (!msg.MarkedForDelete) - { - totalSize += msg.Size; - } - } - - return totalSize; - } - - /// - /// Resets deleted flags on all messages. - /// - public void ResetDeletedFlag() - { - foreach (POP3_Message msg in m_pMessages) - { - msg.MarkedForDelete = false; - } - } - - /// - /// Gets enumerator. - /// - /// - public IEnumerator GetEnumerator() - { - return m_pMessages.GetEnumerator(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/POP3_Message_EventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/POP3_Message_EventArgs.cs deleted file mode 100644 index 8e18bd930..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/POP3_Message_EventArgs.cs +++ /dev/null @@ -1,142 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.POP3.Server -{ - #region usings - - using System.Net.Sockets; - - #endregion - - /// - /// Provides data for the GetMailEvent,DeleteMessage,GetTopLines event. - /// - public class POP3_Message_EventArgs - { - #region Members - - private readonly int m_Lines; - private readonly POP3_Message m_pMessage; - private readonly POP3_Session m_pSession; - private Socket m_pSocket; - - #endregion - - #region Properties - - /// - /// Gets reference to pop3 session. - /// - public POP3_Session Session - { - get { return m_pSession; } - } - - /// - /// Gets reference to message, which to get. - /// - public POP3_Message Message - { - get { return m_pMessage; } - } - - /// - /// ID of message which to retrieve. - /// - public string MessageID - { - get { return m_pMessage.ID; } - } - - /// - /// UID of message which to retrieve. - /// - public string MessageUID - { - get { return m_pMessage.UID; } - } - - /* - /// - /// Gets direct access to connected socket. - /// This is meant for advanced users only. - /// Just write message to this socket. - /// NOTE: Message must be period handled and doesn't(MAY NOT) contain message terminator at end. - /// - public Socket ConnectedSocket - { - get{ return m_pSocket; } - } -*/ - - /// - /// Mail message which is delivered to user. NOTE: may be full message or top lines of message. - /// - public byte[] MessageData { get; set; } - - /// - /// Number of lines to get. - /// - public int Lines - { - get { return m_Lines; } - } - - /// - /// User Name. - /// - public string UserName - { - get { return m_pSession.UserName; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Reference to pop3 session. - /// Message which to get. - /// Connected socket. - public POP3_Message_EventArgs(POP3_Session session, POP3_Message message, Socket socket) - { - m_pSession = session; - m_pMessage = message; - m_pSocket = socket; - } - - /// - /// TopLines constructor. - /// - /// Reference to pop3 session. - /// Message which to get. - /// Connected socket. - /// Number of lines to get. - public POP3_Message_EventArgs(POP3_Session session, POP3_Message message, Socket socket, int nLines) - { - m_pSession = session; - m_pMessage = message; - m_pSocket = socket; - m_Lines = nLines; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/POP3_Server.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/POP3_Server.cs deleted file mode 100644 index cb798695d..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/POP3_Server.cs +++ /dev/null @@ -1,354 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using ASC.Mail.Net.TCP; - -namespace ASC.Mail.Net.POP3.Server -{ - #region usings - - using System; - using System.Net; - using System.Net.Sockets; - using System.Text; - using AUTH; - - #endregion - - #region Event delegates - - /// - /// Represents the method that will handle the AuthUser event for POP3_Server. - /// - /// The source of the event. - /// A AuthUser_EventArgs that contains the event data. - public delegate void AuthUserEventHandler(object sender, AuthUser_EventArgs e); - - /// - /// Represents the method that will handle the GetMessgesList event for POP3_Server. - /// - /// The source of the event. - /// A GetMessagesInfo_EventArgs that contains the event data. - public delegate void GetMessagesInfoHandler(object sender, GetMessagesInfo_EventArgs e); - - /// - /// Represents the method that will handle the GetMessage,DeleteMessage,GetTopLines event for POP3_Server. - /// - /// The source of the event. - /// A GetMessage_EventArgs that contains the event data. - public delegate void MessageHandler(object sender, POP3_Message_EventArgs e); - - /// - /// Represents the method that will handle the GetMessageStream event for POP3_Server. - /// - /// The source of the event. - /// Event data. - public delegate void GetMessageStreamHandler(object sender, POP3_eArgs_GetMessageStream e); - - #endregion - - /// - /// POP3 server component. - /// - public class POP3_Server : TCP_Server - { - #region Events - - /// - /// Occurs when connected user tryes to authenticate. - /// - public event AuthUserEventHandler AuthUser = null; - - /// - /// Occurs when user requests delete message. - /// - public event MessageHandler DeleteMessage = null; - - /// - /// Occurs when user requests to get specified message. - /// - public event GetMessageStreamHandler GetMessageStream = null; - - /// - /// Occurs when server needs to know logged in user's maibox messages. - /// - public event GetMessagesInfoHandler GetMessgesList = null; - - /// - /// Occurs when user requests specified message TOP lines. - /// - public event MessageHandler GetTopLines = null; - - /// - /// Occurs user session ends. This is place for clean up. - /// - public event EventHandler SessionEnd = null; - - /// - /// Occurs when POP3 session has finished and session log is available. - /// - public event LogEventHandler SessionLog = null; - - /// - /// Occurs user session resetted. Messages marked for deletion are unmarked. - /// - public event EventHandler SessionResetted = null; - - /// - /// Occurs when new computer connected to POP3 server. - /// - public event ValidateIPHandler ValidateIPAddress = null; - - #endregion - - #region Members - - private string m_GreetingText = ""; - - private int m_MaxConnectionsPerIP; - private SaslAuthTypes m_SupportedAuth = SaslAuthTypes.All; - - #endregion - - #region Properties - - /// - /// Gets or sets server supported authentication types. - /// - public SaslAuthTypes SupportedAuthentications - { - get { return m_SupportedAuth; } - - set { m_SupportedAuth = value; } - } - - /// - /// Gets or sets server greeting text. - /// - public string GreetingText - { - get { return m_GreetingText; } - - set { m_GreetingText = value; } - } - - /// - /// Gets or sets maximum allowed conncurent connections from 1 IP address. Value 0 means unlimited connections. - /// - public new int MaxConnectionsPerIP - { - get { return m_MaxConnectionsPerIP; } - - set { m_MaxConnectionsPerIP = value; } - } - - public int MaxBadCommands { get; set; } - - #endregion - - #region Constructor - - /// - /// Defalut constructor. - /// - public POP3_Server() - { - - } - - #endregion - - #region Overrides - - - #endregion - - #region Virtual methods - - - protected override void OnMaxConnectionsPerIPExceeded(POP3_Session session) - { - base.OnMaxConnectionsPerIPExceeded(session); - session.TcpStream.WriteLine("-ERR Maximum connections from your IP address is exceeded, try again later!"); - } - - protected override void OnMaxConnectionsExceeded(POP3_Session session) - { - session.TcpStream.WriteLine("-ERR Maximum connections exceeded, try again later!"); - } - /// - /// Raises event ValidateIP event. - /// - /// Server IP. - /// Connected client IP. - /// Returns true if connection allowed. - internal virtual bool OnValidate_IpAddress(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint) - { - ValidateIP_EventArgs oArg = new ValidateIP_EventArgs(localEndPoint, remoteEndPoint); - if (ValidateIPAddress != null) - { - ValidateIPAddress(this, oArg); - } - - return oArg.Validated; - } - - /// - /// Authenticates user. - /// - /// Reference to current pop3 session. - /// User name. - /// - /// - /// - /// - internal virtual AuthUser_EventArgs OnAuthUser(POP3_Session session, - string userName, - string passwData, - string data, - AuthType authType) - { - AuthUser_EventArgs oArg = new AuthUser_EventArgs(session, userName, passwData, data, authType); - if (AuthUser != null) - { - AuthUser(this, oArg); - } - - return oArg; - } - - /// - /// Gest pop3 messages info. - /// - /// - /// - internal virtual void OnGetMessagesInfo(POP3_Session session, POP3_MessageCollection messages) - { - GetMessagesInfo_EventArgs oArg = new GetMessagesInfo_EventArgs(session, messages, session.UserName); - if (GetMessgesList != null) - { - GetMessgesList(this, oArg); - } - } - - /// - /// Raises delete message event. - /// - /// - /// Message which to delete. - /// - internal virtual bool OnDeleteMessage(POP3_Session session, POP3_Message message) - { - POP3_Message_EventArgs oArg = new POP3_Message_EventArgs(session, message, null); - if (DeleteMessage != null) - { - DeleteMessage(this, oArg); - } - - return true; - } - - #endregion - - #region Internal methods - - /// - /// Checks if user is logged in. - /// - /// User name. - /// - internal bool IsUserLoggedIn(string userName) - { - lock (Sessions) - { - foreach (POP3_Session sess in Sessions) - { - if (sess.AuthenticatedUserIdentity!=null && sess.AuthenticatedUserIdentity.Name.ToLower() == userName.ToLower()) - { - return true; - } - } - } - - return false; - } - - /// - /// Raises event 'GetMessageStream'. - /// - /// Reference to POP3 session. - /// Message info what message stream to get. - /// - internal POP3_eArgs_GetMessageStream OnGetMessageStream(POP3_Session session, POP3_Message messageInfo) - { - POP3_eArgs_GetMessageStream eArgs = new POP3_eArgs_GetMessageStream(session, messageInfo); - if (GetMessageStream != null) - { - GetMessageStream(this, eArgs); - } - return eArgs; - } - - /// - /// Raises event GetTopLines. - /// - /// - /// Message wich top lines to get. - /// Header + number of body lines to get. - /// - internal byte[] OnGetTopLines(POP3_Session session, POP3_Message message, int nLines) - { - POP3_Message_EventArgs oArgs = new POP3_Message_EventArgs(session, message, null, nLines); - if (GetTopLines != null) - { - GetTopLines(this, oArgs); - } - return oArgs.MessageData; - } - - /// - /// Raises SessionEnd event. - /// - /// Session which is ended. - internal void OnSessionEnd(object session) - { - if (SessionEnd != null) - { - SessionEnd(session, new EventArgs()); - } - } - - /// - /// Raises SessionResetted event. - /// - /// Session which is resetted. - internal void OnSessionResetted(object session) - { - if (SessionResetted != null) - { - SessionResetted(session, new EventArgs()); - } - } - - #endregion - - public void OnSysError(string s, Exception exception) - { - OnError(exception); - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/POP3_Session.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/POP3_Session.cs deleted file mode 100644 index d5e7b3532..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/POP3_Session.cs +++ /dev/null @@ -1,1712 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System.Security.Principal; -using ASC.Mail.Net.IO; -using ASC.Mail.Net.TCP; - -namespace ASC.Mail.Net.POP3.Server -{ - #region usings - - using System; - using System.IO; - using System.Net.Sockets; - using System.Text; - using AUTH; - - #endregion - - /// - /// POP3 Session. - /// - public class POP3_Session : TCP_ServerSession - { - #region Members - - private readonly POP3_MessageCollection m_POP3_Messages; - private readonly POP3_Server m_pServer; - private int m_BadCmdCount; // Holds number of bad commands. - private string m_MD5_prefix = ""; // Session MD5 prefix for APOP command - private GenericIdentity m_pUser; - - #endregion - - #region Constructor - - - public string UserName - { - get - { - if (AuthenticatedUserIdentity!=null) - { - return AuthenticatedUserIdentity.Name; - } - return string.Empty; - } - } - - #endregion - - #region Methods - - - #endregion - - #region Overrides - - protected override void OnTimeout() - { - base.OnTimeout(); - WriteLine("-ERR Session timeout, closing transmission channel"); - } - - private POP3_Server Pop3Server { get { return Server as POP3_Server; } } - - private void AddWriteEntry(string cmd) - { - // Log - if (Pop3Server.Logger != null) - { - Pop3Server.Logger.AddWrite(ID, AuthenticatedUserIdentity, cmd.Length, cmd, LocalEndPoint, RemoteEndPoint); - } - } - - protected void WriteLine(string line) - { - try - { - AddWriteEntry(line); - TcpStream.WriteLine(line); - } - catch (Exception x) - { - OnError(x); - } - } - - #endregion - - #region Utility methods - - protected internal override void Start() - { - base.Start(); - try - { - // Check if ip is allowed to connect this computer - if (m_pServer.OnValidate_IpAddress(LocalEndPoint, RemoteEndPoint)) - { - // Notify that server is ready - m_MD5_prefix = "<" + Guid.NewGuid().ToString().ToLower() + ">"; - if (m_pServer.GreetingText == "") - { - WriteLine(string.Format("+OK {0} POP3 Server ready {1}", LocalHostName, m_MD5_prefix)); - } - else - { - WriteLine(string.Format("+OK {0} {1}", m_pServer.GreetingText, m_MD5_prefix)); - } - - BeginRecieveCmd(); - } - else - { - EndSession(); - } - } - catch (Exception x) - { - OnError(x); - } - } - - /// - /// Starts session. - /// - //private void StartSession() - //{ - // // Add session to session list - // m_pServer.AddSession(this); - - // try - // { - // // Check if ip is allowed to connect this computer - // if (m_pServer.OnValidate_IpAddress(LocalEndPoint, RemoteEndPoint)) - // { - // //--- Dedicated SSL connection, switch to SSL -----------------------------------// - // if (BindInfo.SslMode == SslMode.SSL) - // { - // try - // { - // Socket.SwitchToSSL(BindInfo.Certificate); - - // if (Socket.Logger != null) - // { - // Socket.Logger.AddTextEntry("SSL negotiation completed successfully."); - // } - // } - // catch (Exception x) - // { - // if (Socket.Logger != null) - // { - // Socket.Logger.AddTextEntry("SSL handshake failed ! " + x.Message); - - // EndSession(); - // return; - // } - // } - // } - // //-------------------------------------------------------------------------------// - - // // Notify that server is ready - // m_MD5_prefix = "<" + Guid.NewGuid().ToString().ToLower() + ">"; - // if (m_pServer.GreetingText == "") - // { - // Socket.WriteLine("+OK " + Net_Utils.GetLocalHostName(BindInfo.HostName) + - // " POP3 Server ready " + m_MD5_prefix); - // } - // else - // { - // Socket.WriteLine("+OK " + m_pServer.GreetingText + " " + m_MD5_prefix); - // } - - // BeginRecieveCmd(); - // } - // else - // { - // EndSession(); - // } - // } - // catch (Exception x) - // { - // OnError(x); - // } - //} - - /// - /// Ends session, closes socket. - /// - private void EndSession() - { - Disconnect(); - } - - /// - /// Is called when error occures. - /// - /// - private new void OnError(Exception x) - { - try - { - // We must see InnerException too, SocketException may be as inner exception. - SocketException socketException = null; - if (x is SocketException) - { - socketException = (SocketException) x; - } - else if (x.InnerException != null && x.InnerException is SocketException) - { - socketException = (SocketException) x.InnerException; - } - - if (socketException != null) - { - // Client disconnected without shutting down - if (socketException.ErrorCode == 10054 || socketException.ErrorCode == 10053) - { - EndSession(); - // Exception handled, return - return; - } - } - - m_pServer.OnSysError("", x); - } - catch (Exception ex) - { - m_pServer.OnSysError("", ex); - } - } - - /// - /// Starts recieveing command. - /// - private void BeginRecieveCmd() - { - ReadAsync(ReciveCmdComleted); - } - - private void AddReadEntry(string cmd) - { - // Log - if (Pop3Server.Logger != null) - { - Pop3Server.Logger.AddRead(ID, AuthenticatedUserIdentity, cmd.Length, cmd, LocalEndPoint, RemoteEndPoint); - } - } - - internal void BeginWriteLine(string line) - { - if (!line.EndsWith("\r\n")) - { - line += "\r\n"; - } - byte[] buffer = Encoding.Default.GetBytes(line); - TcpStream.BeginWrite(buffer, 0, buffer.Length, EndSend, null); - } - - private void EndSend(IAsyncResult ar) - { - try - { - TcpStream.EndWrite(ar); - BeginRecieveCmd(); - } - catch (Exception x) - { - OnError(x); - } - } - - internal void ReadAsync(EventHandler> completed) - { - SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength], - SizeExceededAction. - JunkAndThrowException); - - args.Completed += completed; - TcpStream.ReadLine(args, true); - } - - private void ReciveCmdComleted(object sender, EventArgs e) - { - if (e.Value.IsCompleted && e.Value.Error == null) - { - //Call - bool sessionEnd = false; - try - { - string cmdLine = e.Value.LineUtf8; - AddReadEntry(cmdLine); - // Exceute command - sessionEnd = SwitchCommand(cmdLine); - if (sessionEnd) - { - // Session end, close session - EndSession(); - } - } - catch (Exception ex) - { - WriteLine("-ERR " + ex.Message); - if (!sessionEnd) - { - BeginRecieveCmd(); - } - } - } - else if (e.Value.Error != null) - { - OnError(e.Value.Error); - } - } - - /// - /// Parses and executes POP3 commmand. - /// - /// POP3 command text. - /// Returns true,if session must be terminated. - private bool SwitchCommand(string POP3_commandTxt) - { - //---- Parse command --------------------------------------------------// - string[] cmdParts = POP3_commandTxt.TrimStart().Split(new[] {' '}); - string POP3_command = cmdParts[0].ToUpper().Trim(); - string argsText = Core.GetArgsText(POP3_commandTxt, POP3_command); - //---------------------------------------------------------------------// - - bool getNextCmd = true; - - switch (POP3_command) - { - case "USER": - USER(argsText); - getNextCmd = false; - break; - - case "PASS": - PASS(argsText); - getNextCmd = false; - break; - - case "STAT": - STAT(); - getNextCmd = false; - break; - - case "LIST": - LIST(argsText); - getNextCmd = false; - break; - - case "RETR": - RETR(argsText); - getNextCmd = false; - break; - - case "DELE": - DELE(argsText); - getNextCmd = false; - break; - - case "NOOP": - NOOP(); - getNextCmd = false; - break; - - case "RSET": - RSET(); - getNextCmd = false; - break; - - case "QUIT": - QUIT(); - getNextCmd = false; - return true; - - //----- Optional commands ----- // - case "UIDL": - UIDL(argsText); - getNextCmd = false; - break; - - case "APOP": - APOP(argsText); - getNextCmd = false; - break; - - case "TOP": - TOP(argsText); - getNextCmd = false; - break; - - case "AUTH": - AUTH(argsText); - getNextCmd = false; - break; - - case "CAPA": - CAPA(argsText); - getNextCmd = false; - break; - - case "STLS": - STLS(argsText); - getNextCmd = false; - break; - - default: - WriteLine("-ERR Invalid command"); - - //---- Check that maximum bad commands count isn't exceeded ---------------// - if (m_BadCmdCount > m_pServer.MaxBadCommands - 1) - { - WriteLine("-ERR Too many bad commands, closing transmission channel"); - return true; - } - m_BadCmdCount++; - //-------------------------------------------------------------------------// - break; - } - - if (getNextCmd) - { - BeginRecieveCmd(); - } - - return false; - } - - public override GenericIdentity AuthenticatedUserIdentity - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pUser; - } - } - - private void SetUserName(string userName) - { - m_pUser = new GenericIdentity(UserName); - - } - - private void USER(string argsText) - { - /* RFC 1939 7. USER - Arguments: - a string identifying a mailbox (required), which is of - significance ONLY to the server - - NOTE: - If the POP3 server responds with a positive - status indicator ("+OK"), then the client may issue - either the PASS command to complete the authentication, - or the QUIT command to terminate the POP3 session. - - */ - - if (IsAuthenticated) - { - BeginWriteLine("-ERR You are already authenticated"); - return; - } - if (UserName.Length > 0) - { - BeginWriteLine("-ERR username is already specified, please specify password"); - return; - } - if ((m_pServer.SupportedAuthentications & SaslAuthTypes.Plain) == 0) - { - BeginWriteLine("-ERR USER/PASS command disabled"); - return; - } - - string[] param = TextUtils.SplitQuotedString(argsText, ' ', true); - - // There must be only one parameter - userName - if (argsText.Length > 0 && param.Length == 1) - { - string userName = param[0]; - - // Check if user isn't logged in already - if (!m_pServer.IsUserLoggedIn(userName)) - { - SetUserName(userName); - - // Send this line last, because it issues a new command and any assignments done - // after this method may not become wisible to next command. - BeginWriteLine("+OK User:'" + userName + "' ok"); - } - else - { - BeginWriteLine("-ERR User:'" + userName + "' already logged in"); - } - } - else - { - BeginWriteLine("-ERR Syntax error. Syntax:{USER username}"); - } - } - - private void PASS(string argsText) - { - /* RFC 7. PASS - Arguments: - a server/mailbox-specific password (required) - - Restrictions: - may only be given in the AUTHORIZATION state immediately - after a successful USER command - - NOTE: - When the client issues the PASS command, the POP3 server - uses the argument pair from the USER and PASS commands to - determine if the client should be given access to the - appropriate maildrop. - - Possible Responses: - +OK maildrop locked and ready - -ERR invalid password - -ERR unable to lock maildrop - - */ - - if (IsAuthenticated) - { - BeginWriteLine("-ERR You are already authenticated"); - return; - } - if (UserName.Length == 0) - { - BeginWriteLine("-ERR please specify username first"); - return; - } - if ((m_pServer.SupportedAuthentications & SaslAuthTypes.Plain) == 0) - { - BeginWriteLine("-ERR USER/PASS command disabled"); - return; - } - - string[] param = TextUtils.SplitQuotedString(argsText, ' ', true); - - // There may be only one parameter - password - if (param.Length == 1) - { - string password = param[0]; - - // Authenticate user - AuthUser_EventArgs aArgs = m_pServer.OnAuthUser(this, UserName, password, "", AuthType.Plain); - - // There is custom error, return it - if (aArgs.ErrorText != null) - { - BeginWriteLine("-ERR " + aArgs.ErrorText); - return; - } - - if (aArgs.Validated) - { - SetUserName(UserName); - - // Get user messages info. - m_pServer.OnGetMessagesInfo(this, m_POP3_Messages); - - BeginWriteLine("+OK Password ok"); - } - else - { - BeginWriteLine("-ERR UserName or Password is incorrect"); - SetUserName(""); // Reset userName !!! - } - } - else - { - BeginWriteLine("-ERR Syntax error. Syntax:{PASS userName}"); - } - } - - private void STAT() - { - /* RFC 1939 5. STAT - NOTE: - The positive response consists of "+OK" followed by a single - space, the number of messages in the maildrop, a single - space, and the size of the maildrop in octets. - - Note that messages marked as deleted are not counted in - either total. - - Example: - C: STAT - S: +OK 2 320 - */ - - if (!IsAuthenticated) - { - BeginWriteLine("-ERR You must authenticate first"); - return; - } - - BeginWriteLine( - string.Format("+OK {0} {1}", m_POP3_Messages.Count, m_POP3_Messages.GetTotalMessagesSize())); - } - - private void LIST(string argsText) - { - /* RFC 1939 5. LIST - Arguments: - a message-number (optional), which, if present, may NOT - refer to a message marked as deleted - - NOTE: - If an argument was given and the POP3 server issues a - positive response with a line containing information for - that message. - - If no argument was given and the POP3 server issues a - positive response, then the response given is multi-line. - - Note that messages marked as deleted are not listed. - - Examples: - C: LIST - S: +OK 2 messages (320 octets) - S: 1 120 - S: 2 200 - S: . - ... - C: LIST 2 - S: +OK 2 200 - ... - C: LIST 3 - S: -ERR no such message, only 2 messages in maildrop - - */ - - if (!IsAuthenticated) - { - BeginWriteLine("-ERR You must authenticate first"); - return; - } - - string[] param = TextUtils.SplitQuotedString(argsText, ' ', true); - - // Argument isn't specified, multiline response. - if (argsText.Length == 0) - { - StringBuilder reply = new StringBuilder(); - reply.Append("+OK " + m_POP3_Messages.Count + " messages\r\n"); - - // Send message number and size for each message - for (int i = 0; i < m_POP3_Messages.Count; i++) - { - POP3_Message message = m_POP3_Messages[i]; - if (!message.MarkedForDelete) - { - reply.Append((i + 1) + " " + message.Size + "\r\n"); - } - } - - // "." - means end of list - reply.Append(".\r\n"); - - BeginWriteLine(reply.ToString()); - } - else - { - // If parameters specified,there may be only one parameter - messageNr - if (param.Length == 1) - { - // Check if messageNr is valid - if (Core.IsNumber(param[0])) - { - int messageNr = Convert.ToInt32(param[0]); - if (m_POP3_Messages.MessageExists(messageNr)) - { - POP3_Message msg = m_POP3_Messages[messageNr - 1]; - - BeginWriteLine(string.Format("+OK {0} {1}", messageNr, msg.Size)); - } - else - { - BeginWriteLine("-ERR no such message, or marked for deletion"); - } - } - else - { - BeginWriteLine("-ERR message-number is invalid"); - } - } - else - { - BeginWriteLine("-ERR Syntax error. Syntax:{LIST [messageNr]}"); - } - } - } - - private void RETR(string argsText) - { - /* RFC 1939 5. RETR - Arguments: - a message-number (required) which may NOT refer to a - message marked as deleted - - NOTE: - If the POP3 server issues a positive response, then the - response given is multi-line. After the initial +OK, the - POP3 server sends the message corresponding to the given - message-number, being careful to byte-stuff the termination - character (as with all multi-line responses). - - Example: - C: RETR 1 - S: +OK 120 octets - S: - S: . - - */ - - if (!IsAuthenticated) - { - BeginWriteLine("-ERR You must authenticate first"); - } - - string[] param = TextUtils.SplitQuotedString(argsText, ' ', true); - - // There must be only one parameter - messageNr - if (argsText.Length > 0 && param.Length == 1) - { - // Check if messageNr is valid - if (Core.IsNumber(param[0])) - { - int messageNr = Convert.ToInt32(param[0]); - if (m_POP3_Messages.MessageExists(messageNr)) - { - POP3_Message msg = m_POP3_Messages[messageNr - 1]; - - // Raise Event, request message - POP3_eArgs_GetMessageStream eArgs = m_pServer.OnGetMessageStream(this, msg); - if (eArgs.MessageExists && eArgs.MessageStream != null) - { - WriteLine("+OK " + eArgs.MessageSize + " octets"); - - // Send message asynchronously to client - TcpStream.WritePeriodTerminated(eArgs.MessageStream); - BeginRecieveCmd(); - } - else - { - BeginWriteLine("-ERR no such message"); - } - } - else - { - BeginWriteLine("-ERR no such message"); - } - } - else - { - BeginWriteLine("-ERR message-number is invalid"); - } - } - else - { - BeginWriteLine("-ERR Syntax error. Syntax:{RETR messageNr}"); - } - } - - private void DELE(string argsText) - { - /* RFC 1939 5. DELE - Arguments: - a message-number (required) which may NOT refer to a - message marked as deleted - - NOTE: - The POP3 server marks the message as deleted. Any future - reference to the message-number associated with the message - in a POP3 command generates an error. The POP3 server does - not actually delete the message until the POP3 session - enters the UPDATE state. - */ - - if (!IsAuthenticated) - { - BeginWriteLine("-ERR You must authenticate first"); - return; - } - - string[] param = TextUtils.SplitQuotedString(argsText, ' ', true); - - // There must be only one parameter - messageNr - if (argsText.Length > 0 && param.Length == 1) - { - // Check if messageNr is valid - if (Core.IsNumber(param[0])) - { - int nr = Convert.ToInt32(param[0]); - if (m_POP3_Messages.MessageExists(nr)) - { - POP3_Message msg = m_POP3_Messages[nr - 1]; - msg.MarkedForDelete = true; - - BeginWriteLine("+OK marked for delete"); - } - else - { - BeginWriteLine("-ERR no such message"); - } - } - else - { - BeginWriteLine("-ERR message-number is invalid"); - } - } - else - { - BeginWriteLine("-ERR Syntax error. Syntax:{DELE messageNr}"); - } - } - - private void NOOP() - { - /* RFC 1939 5. NOOP - NOTE: - The POP3 server does nothing, it merely replies with a - positive response. - */ - - if (!IsAuthenticated) - { - BeginWriteLine("-ERR You must authenticate first"); - return; - } - - BeginWriteLine("+OK"); - } - - private void RSET() - { - /* RFC 1939 5. RSET - Discussion: - If any messages have been marked as deleted by the POP3 - server, they are unmarked. The POP3 server then replies - with a positive response. - */ - - if (!IsAuthenticated) - { - BeginWriteLine("-ERR You must authenticate first"); - return; - } - - Reset(); - - // Raise SessionResetted event - m_pServer.OnSessionResetted(this); - - BeginWriteLine("+OK"); - } - - private void QUIT() - { - /* RFC 1939 6. QUIT - NOTE: - The POP3 server removes all messages marked as deleted - from the maildrop and replies as to the status of this - operation. If there is an error, such as a resource - shortage, encountered while removing messages, the - maildrop may result in having some or none of the messages - marked as deleted be removed. In no case may the server - remove any messages not marked as deleted. - - Whether the removal was successful or not, the server - then releases any exclusive-access lock on the maildrop - and closes the TCP connection. - */ - Update(); - - WriteLine("+OK POP3 server signing off"); - } - - //--- Optional commands - - private void TOP(string argsText) - { - /* RFC 1939 7. TOP - Arguments: - a message-number (required) which may NOT refer to to a - message marked as deleted, and a non-negative number - of lines (required) - - NOTE: - If the POP3 server issues a positive response, then the - response given is multi-line. After the initial +OK, the - POP3 server sends the headers of the message, the blank - line separating the headers from the body, and then the - number of lines of the indicated message's body, being - careful to byte-stuff the termination character (as with - all multi-line responses). - - Examples: - C: TOP 1 10 - S: +OK - S: - S: . - ... - C: TOP 100 3 - S: -ERR no such message - - */ - - if (!IsAuthenticated) - { - BeginWriteLine("-ERR You must authenticate first"); - } - - string[] param = TextUtils.SplitQuotedString(argsText, ' ', true); - - // There must be at two parameters - messageNr and nrLines - if (param.Length == 2) - { - // Check if messageNr and nrLines is valid - if (Core.IsNumber(param[0]) && Core.IsNumber(param[1])) - { - int messageNr = Convert.ToInt32(param[0]); - if (m_POP3_Messages.MessageExists(messageNr)) - { - POP3_Message msg = m_POP3_Messages[messageNr - 1]; - - byte[] lines = m_pServer.OnGetTopLines(this, msg, Convert.ToInt32(param[1])); - if (lines != null) - { - WriteLine("+OK " + lines.Length + " octets"); - - // Send message asynchronously to client - TcpStream.WritePeriodTerminated(new MemoryStream(lines)); - BeginRecieveCmd(); - } - else - { - BeginWriteLine("-ERR no such message"); - } - } - else - { - BeginWriteLine("-ERR no such message"); - } - } - else - { - BeginWriteLine("-ERR message-number or number of lines is invalid"); - } - } - else - { - BeginWriteLine("-ERR Syntax error. Syntax:{TOP messageNr nrLines}"); - } - } - - private void UIDL(string argsText) - { - /* RFC 1939 UIDL [msg] - Arguments: - a message-number (optional), which, if present, may NOT - refer to a message marked as deleted - - NOTE: - If an argument was given and the POP3 server issues a positive - response with a line containing information for that message. - - If no argument was given and the POP3 server issues a positive - response, then the response given is multi-line. After the - initial +OK, for each message in the maildrop, the POP3 server - responds with a line containing information for that message. - - Examples: - C: UIDL - S: +OK - S: 1 whqtswO00WBw418f9t5JxYwZ - S: 2 QhdPYR:00WBw1Ph7x7 - S: . - ... - C: UIDL 2 - S: +OK 2 QhdPYR:00WBw1Ph7x7 - ... - C: UIDL 3 - S: -ERR no such message - */ - - if (!IsAuthenticated) - { - BeginWriteLine("-ERR You must authenticate first"); - return; - } - - string[] param = TextUtils.SplitQuotedString(argsText, ' ', true); - - // Argument isn't specified, multiline response. - if (argsText.Length == 0) - { - StringBuilder reply = new StringBuilder(); - reply.Append("+OK\r\n"); - - // Send message number and size for each message - for (int i = 0; i < m_POP3_Messages.Count; i++) - { - POP3_Message message = m_POP3_Messages[i]; - if (!message.MarkedForDelete) - { - reply.Append((i + 1) + " " + message.UID + "\r\n"); - } - } - - // "." - means end of list - reply.Append(".\r\n"); - - BeginWriteLine(reply.ToString()); - } - else - { - // If parameters specified,there may be only one parameter - messageID - if (param.Length == 1) - { - // Check if messageNr is valid - if (Core.IsNumber(param[0])) - { - int messageNr = Convert.ToInt32(param[0]); - if (m_POP3_Messages.MessageExists(messageNr)) - { - POP3_Message msg = m_POP3_Messages[messageNr - 1]; - - BeginWriteLine(string.Format("+OK {0} {1}", messageNr, msg.UID)); - } - else - { - BeginWriteLine("-ERR no such message"); - } - } - else - { - BeginWriteLine("-ERR message-number is invalid"); - } - } - else - { - BeginWriteLine("-ERR Syntax error. Syntax:{UIDL [messageNr]}"); - } - } - } - - private void APOP(string argsText) - { - /* RFC 1939 7. APOP - Arguments: - a string identifying a mailbox and a MD5 digest string - (both required) - - NOTE: - A POP3 server which implements the APOP command will - include a timestamp in its banner greeting. The syntax of - the timestamp corresponds to the `msg-id' in [RFC822], and - MUST be different each time the POP3 server issues a banner - greeting. - - Examples: - S: +OK POP3 server ready <1896.697170952@dbc.mtview.ca.us> - C: APOP mrose c4c9334bac560ecc979e58001b3e22fb - S: +OK maildrop has 1 message (369 octets) - - In this example, the shared secret is the string `tan- - staaf'. Hence, the MD5 algorithm is applied to the string - - <1896.697170952@dbc.mtview.ca.us>tanstaaf - - which produces a digest value of - c4c9334bac560ecc979e58001b3e22fb - - */ - - if (IsAuthenticated) - { - BeginWriteLine("-ERR You are already authenticated"); - return; - } - - string[] param = TextUtils.SplitQuotedString(argsText, ' ', true); - - // There must be two params - if (param.Length == 2) - { - string userName = param[0]; - string md5HexHash = param[1]; - - // Check if user isn't logged in already - if (m_pServer.IsUserLoggedIn(userName)) - { - BeginWriteLine(string.Format("-ERR User:'{0}' already logged in", userName)); - return; - } - - // Authenticate user - AuthUser_EventArgs aArgs = m_pServer.OnAuthUser(this, - userName, - md5HexHash, - m_MD5_prefix, - AuthType.APOP); - if (aArgs.Validated) - { - SetUserName(userName); - - // Get user messages info. - m_pServer.OnGetMessagesInfo(this, m_POP3_Messages); - - BeginWriteLine("+OK authentication was successful"); - } - else - { - BeginWriteLine("-ERR authentication failed"); - } - } - else - { - BeginWriteLine("-ERR syntax error. Syntax:{APOP userName md5HexHash}"); - } - } - - private string ReadLine() - { - SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength], - SizeExceededAction. - JunkAndThrowException); - if (TcpStream.ReadLine(args, false)) - { - return args.LineUtf8; - } - return string.Empty; - } - - private void AUTH(string argsText) - { - /* Rfc 1734 - - AUTH mechanism - - Arguments: - a string identifying an IMAP4 authentication mechanism, - such as defined by [IMAP4-AUTH]. Any use of the string - "imap" used in a server authentication identity in the - definition of an authentication mechanism is replaced with - the string "pop". - - Possible Responses: - +OK maildrop locked and ready - -ERR authentication exchange failed - - Restrictions: - may only be given in the AUTHORIZATION state - - Discussion: - The AUTH command indicates an authentication mechanism to - the server. If the server supports the requested - authentication mechanism, it performs an authentication - protocol exchange to authenticate and identify the user. - Optionally, it also negotiates a protection mechanism for - subsequent protocol interactions. If the requested - authentication mechanism is not supported, the server - should reject the AUTH command by sending a negative - response. - - The authentication protocol exchange consists of a series - of server challenges and client answers that are specific - to the authentication mechanism. A server challenge, - otherwise known as a ready response, is a line consisting - of a "+" character followed by a single space and a BASE64 - encoded string. The client answer consists of a line - containing a BASE64 encoded string. If the client wishes - to cancel an authentication exchange, it should issue a - line with a single "*". If the server receives such an - answer, it must reject the AUTH command by sending a - negative response. - - A protection mechanism provides integrity and privacy - protection to the protocol session. If a protection - mechanism is negotiated, it is applied to all subsequent - data sent over the connection. The protection mechanism - takes effect immediately following the CRLF that concludes - the authentication exchange for the client, and the CRLF of - the positive response for the server. Once the protection - mechanism is in effect, the stream of command and response - octets is processed into buffers of ciphertext. Each - buffer is transferred over the connection as a stream of - octets prepended with a four octet field in network byte - order that represents the length of the following data. - The maximum ciphertext buffer length is defined by the - protection mechanism. - - The server is not required to support any particular - authentication mechanism, nor are authentication mechanisms - required to support any protection mechanisms. If an AUTH - command fails with a negative response, the session remains - in the AUTHORIZATION state and client may try another - authentication mechanism by issuing another AUTH command, - or may attempt to authenticate by using the USER/PASS or - APOP commands. In other words, the client may request - authentication types in decreasing order of preference, - with the USER/PASS or APOP command as a last resort. - - Should the client successfully complete the authentication - exchange, the POP3 server issues a positive response and - the POP3 session enters the TRANSACTION state. - - Examples: - S: +OK POP3 server ready - C: AUTH KERBEROS_V4 - S: + AmFYig== - C: BAcAQU5EUkVXLkNNVS5FRFUAOCAsho84kLN3/IJmrMG+25a4DT - +nZImJjnTNHJUtxAA+o0KPKfHEcAFs9a3CL5Oebe/ydHJUwYFd - WwuQ1MWiy6IesKvjL5rL9WjXUb9MwT9bpObYLGOKi1Qh - S: + or//EoAADZI= - C: DiAF5A4gA+oOIALuBkAAmw== - S: +OK Kerberos V4 authentication successful - ... - C: AUTH FOOBAR - S: -ERR Unrecognized authentication type - - */ - if (IsAuthenticated) - { - BeginWriteLine("-ERR already authenticated"); - return; - } - - //------ Parse parameters -------------------------------------// - string userName = ""; - string password = ""; - AuthUser_EventArgs aArgs = null; - - string[] param = TextUtils.SplitQuotedString(argsText, ' ', true); - switch (param[0].ToUpper()) - { - case "PLAIN": - BeginWriteLine("-ERR Unrecognized authentication type."); - break; - - case "LOGIN": - - #region LOGIN authentication - - //---- AUTH = LOGIN ------------------------------ - /* Login - C: AUTH LOGIN-MD5 - S: + VXNlcm5hbWU6 - C: username_in_base64 - S: + UGFzc3dvcmQ6 - C: password_in_base64 - - VXNlcm5hbWU6 base64_decoded= USERNAME - UGFzc3dvcmQ6 base64_decoded= PASSWORD - */ - // Note: all strings are base64 strings eg. VXNlcm5hbWU6 = UserName. - - // Query UserName - WriteLine("+ VXNlcm5hbWU6"); - - string userNameLine = ReadLine(); - // Encode username from base64 - if (userNameLine.Length > 0) - { - userName = Encoding.Default.GetString(Convert.FromBase64String(userNameLine)); - } - - // Query Password - WriteLine("+ UGFzc3dvcmQ6"); - - string passwordLine = ReadLine(); - // Encode password from base64 - if (passwordLine.Length > 0) - { - password = Encoding.Default.GetString(Convert.FromBase64String(passwordLine)); - } - - aArgs = m_pServer.OnAuthUser(this, userName, password, "", AuthType.Plain); - - // There is custom error, return it - if (aArgs.ErrorText != null) - { - BeginWriteLine("-ERR " + aArgs.ErrorText); - return; - } - - if (aArgs.Validated) - { - - SetUserName(userName); - - // Get user messages info. - m_pServer.OnGetMessagesInfo(this, m_POP3_Messages); - BeginWriteLine("+OK Authentication successful."); - } - else - { - BeginWriteLine("-ERR Authentication failed"); - } - - #endregion - - break; - - case "CRAM-MD5": - - #region CRAM-MD5 authentication - - /* Cram-M5 - C: AUTH CRAM-MD5 - S: + - C: base64(decoded:username password_hash) - */ - - string md5Hash = "<" + Guid.NewGuid().ToString().ToLower() + ">"; - WriteLine("+ " + Convert.ToBase64String(Encoding.ASCII.GetBytes(md5Hash))); - - string reply = ReadLine(); - - reply = Encoding.Default.GetString(Convert.FromBase64String(reply)); - string[] replyArgs = reply.Split(' '); - userName = replyArgs[0]; - - aArgs = m_pServer.OnAuthUser(this, userName, replyArgs[1], md5Hash, AuthType.CRAM_MD5); - - // There is custom error, return it - if (aArgs.ErrorText != null) - { - BeginWriteLine("-ERR " + aArgs.ErrorText); - return; - } - - if (aArgs.Validated) - { - - SetUserName(userName); - - // Get user messages info. - m_pServer.OnGetMessagesInfo(this, m_POP3_Messages); - BeginWriteLine("+OK Authentication successful."); - } - else - { - BeginWriteLine("-ERR Authentication failed"); - } - - #endregion - - break; - - case "DIGEST-MD5": - - #region DIGEST-MD5 authentication - - /* RFC 2831 AUTH DIGEST-MD5 - * - * Example: - * - * C: AUTH DIGEST-MD5 - * S: + base64(realm="elwood.innosoft.com",nonce="OA6MG9tEQGm2hh",qop="auth",algorithm=md5-sess) - * C: base64(username="chris",realm="elwood.innosoft.com",nonce="OA6MG9tEQGm2hh", - * nc=00000001,cnonce="OA6MHXh6VqTrRk",digest-uri="imap/elwood.innosoft.com", - * response=d388dad90d4bbd760a152321f2143af7,qop=auth) - * S: + base64(rspauth=ea40f60335c427b5527b84dbabcdfffd) - * C: - * S: +OK Authentication successful. - */ - - string realm = LocalHostName; - string nonce = AuthHelper.GenerateNonce(); - - WriteLine("+ " + - AuthHelper.Base64en(AuthHelper.Create_Digest_Md5_ServerResponse(realm, - nonce))); - - string clientResponse = AuthHelper.Base64de(ReadLine()); - // Check that realm and nonce in client response are same as we specified - if (clientResponse.IndexOf("realm=\"" + realm + "\"") > - 1 && - clientResponse.IndexOf("nonce=\"" + nonce + "\"") > - 1) - { - // Parse user name and password compare value - // string userName = ""; - string passwData = ""; - string cnonce = ""; - foreach (string clntRespParam in clientResponse.Split(',')) - { - if (clntRespParam.StartsWith("username=")) - { - userName = clntRespParam.Split(new[] {'='}, 2)[1].Replace("\"", ""); - } - else if (clntRespParam.StartsWith("response=")) - { - passwData = clntRespParam.Split(new[] {'='}, 2)[1]; - } - else if (clntRespParam.StartsWith("cnonce=")) - { - cnonce = clntRespParam.Split(new[] {'='}, 2)[1].Replace("\"", ""); - } - } - - aArgs = m_pServer.OnAuthUser(this, - userName, - passwData, - clientResponse, - AuthType.DIGEST_MD5); - - // There is custom error, return it - if (aArgs.ErrorText != null) - { - BeginWriteLine("-ERR " + aArgs.ErrorText); - return; - } - - if (aArgs.Validated) - { - // Send server computed password hash - WriteLine("+ " + AuthHelper.Base64en("rspauth=" + aArgs.ReturnData)); - - // We must got empty line here - clientResponse = ReadLine(); - if (clientResponse == "") - { - - SetUserName(userName); - m_pServer.OnGetMessagesInfo(this, m_POP3_Messages); - BeginWriteLine("+OK Authentication successful."); - } - else - { - BeginWriteLine("-ERR Authentication failed"); - } - } - else - { - BeginWriteLine("-ERR Authentication failed"); - } - } - else - { - BeginWriteLine("-ERR Authentication failed"); - } - - #endregion - - break; - - default: - BeginWriteLine("-ERR Unrecognized authentication type."); - break; - } - //-----------------------------------------------------------------// - } - - private void CAPA(string argsText) - { - /* Rfc 2449 5. The CAPA Command - - The POP3 CAPA command returns a list of capabilities supported by the - POP3 server. It is available in both the AUTHORIZATION and - TRANSACTION states. - - A capability description MUST document in which states the capability - is announced, and in which states the commands are valid. - - Capabilities available in the AUTHORIZATION state MUST be announced - in both states. - - If a capability is announced in both states, but the argument might - differ after authentication, this possibility MUST be stated in the - capability description. - - (These requirements allow a client to issue only one CAPA command if - it does not use any TRANSACTION-only capabilities, or any - capabilities whose values may differ after authentication.) - - If the authentication step negotiates an integrity protection layer, - the client SHOULD reissue the CAPA command after authenticating, to - check for active down-negotiation attacks. - - Each capability may enable additional protocol commands, additional - parameters and responses for existing commands, or describe an aspect - of server behavior. These details are specified in the description - of the capability. - - Section 3 describes the CAPA response using [ABNF]. When a - capability response describes an optional command, the - SHOULD be identical to the command keyword. CAPA response tags are - case-insensitive. - - CAPA - - Arguments: - none - - Restrictions: - none - - Discussion: - An -ERR response indicates the capability command is not - implemented and the client will have to probe for - capabilities as before. - - An +OK response is followed by a list of capabilities, one - per line. Each capability name MAY be followed by a single - space and a space-separated list of parameters. Each - capability line is limited to 512 octets (including the - CRLF). The capability list is terminated by a line - containing a termination octet (".") and a CRLF pair. - - Possible Responses: - +OK -ERR - - Examples: - C: CAPA - S: +OK Capability list follows - S: TOP - S: USER - S: SASL CRAM-MD5 KERBEROS_V4 - S: RESP-CODES - S: LOGIN-DELAY 900 - S: PIPELINING - S: EXPIRE 60 - S: UIDL - S: IMPLEMENTATION Shlemazle-Plotz-v302 - S: . - */ - - string capaResponse = ""; - capaResponse += "+OK Capability list follows\r\n"; - capaResponse += "PIPELINING\r\n"; - capaResponse += "UIDL\r\n"; - capaResponse += "TOP\r\n"; - if ((m_pServer.SupportedAuthentications & SaslAuthTypes.Plain) != 0) - { - capaResponse += "USER\r\n"; - } - capaResponse += "SASL"; - if ((m_pServer.SupportedAuthentications & SaslAuthTypes.Cram_md5) != 0) - { - capaResponse += " CRAM-MD5"; - } - if ((m_pServer.SupportedAuthentications & SaslAuthTypes.Digest_md5) != 0) - { - capaResponse += " DIGEST-MD5"; - } - if ((m_pServer.SupportedAuthentications & SaslAuthTypes.Login) != 0) - { - capaResponse += " LOGIN"; - } - capaResponse += "\r\n"; - if (Certificate != null) - { - capaResponse += "STLS\r\n"; - } - capaResponse += ".\r\n"; - - BeginWriteLine(capaResponse); - } - - private void STLS(string argsText) - { - /* RFC 2595 4. POP3 STARTTLS extension. - Arguments: none - - Restrictions: - Only permitted in AUTHORIZATION state. - - Discussion: - A TLS negotiation begins immediately after the CRLF at the - end of the +OK response from the server. A -ERR response - MAY result if a security layer is already active. Once a - client issues a STLS command, it MUST NOT issue further - commands until a server response is seen and the TLS - negotiation is complete. - - The STLS command is only permitted in AUTHORIZATION state - and the server remains in AUTHORIZATION state, even if - client credentials are supplied during the TLS negotiation. - The AUTH command [POP-AUTH] with the EXTERNAL mechanism - [SASL] MAY be used to authenticate once TLS client - credentials are successfully exchanged, but servers - supporting the STLS command are not required to support the - EXTERNAL mechanism. - - Once TLS has been started, the client MUST discard cached - information about server capabilities and SHOULD re-issue - the CAPA command. This is necessary to protect against - man-in-the-middle attacks which alter the capabilities list - prior to STLS. The server MAY advertise different - capabilities after STLS. - - Possible Responses: - +OK -ERR - - Examples: - C: STLS - S: +OK Begin TLS negotiation - - ... - C: STLS - S: -ERR Command not permitted when TLS active - */ - - if (IsAuthenticated) - { - WriteLine("-ERR STLS command is only permitted in AUTHORIZATION state !"); - return; - } - - if (Certificate == null) - { - WriteLine("-ERR TLS not available, SSL certificate isn't specified !"); - return; - } - - WriteLine("+OK Ready to start TLS"); - - try - { - SwitchToSecure(); - } - catch (Exception x) - { - WriteLine("-ERR TLS handshake failed ! " + x.Message); - } - - Reset(); - - BeginRecieveCmd(); - } - - private void Reset() - { - /* RFC 1939 5. RSET - Discussion: - If any messages have been marked as deleted by the POP3 - server, they are unmarked. - */ - m_POP3_Messages.ResetDeletedFlag(); - } - - private void Update() - { - /* RFC 1939 6. - NOTE: - When the client issues the QUIT command from the TRANSACTION state, - the POP3 session enters the UPDATE state. (Note that if the client - issues the QUIT command from the AUTHORIZATION state, the POP3 - session terminates but does NOT enter the UPDATE state.) - - If a session terminates for some reason other than a client-issued - QUIT command, the POP3 session does NOT enter the UPDATE state and - MUST not remove any messages from the maildrop. - */ - - if (IsAuthenticated) - { - lock (m_POP3_Messages) - { - // Delete all message which are marked for deletion ---// - foreach (POP3_Message msg in m_POP3_Messages) - { - if (msg.MarkedForDelete) - { - m_pServer.OnDeleteMessage(this, msg); - } - } - //-----------------------------------------------------// - } - } - } - - /// - /// Is called when asynchronous send completes. - /// - /// If true, then send was successfull. - /// Count sended. - /// Exception happend on send. NOTE: available only is result=false. - /// User data. - - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/POP3_eArgs_GetMessageStream.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/POP3_eArgs_GetMessageStream.cs deleted file mode 100644 index 5268794c0..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/POP3/Server/POP3_eArgs_GetMessageStream.cs +++ /dev/null @@ -1,147 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.POP3.Server -{ - #region usings - - using System; - using System.IO; - - #endregion - - /// - /// Provides data to POP3 server event GetMessageStream. - /// - public class POP3_eArgs_GetMessageStream - { - #region Members - - private readonly POP3_Message m_pMessageInfo; - private readonly POP3_Session m_pSession; - private bool m_CloseMessageStream = true; - private bool m_MessageExists = true; - private long m_MessageStartOffset; - private Stream m_MessageStream; - - #endregion - - #region Properties - - /// - /// Gets reference to current POP3 session. - /// - public POP3_Session Session - { - get { return m_pSession; } - } - - /// - /// Gets message info what message stream to get. - /// - public POP3_Message MessageInfo - { - get { return m_pMessageInfo; } - } - - /// - /// Gets or sets if message stream is closed automatically if all actions on it are completed. - /// Default value is true. - /// - public bool CloseMessageStream - { - get { return m_CloseMessageStream; } - - set { m_CloseMessageStream = value; } - } - - /// - /// Gets or sets message stream. When setting this property Stream position must be where message begins. - /// - public Stream MessageStream - { - get - { - if (m_MessageStream != null) - { - m_MessageStream.Position = m_MessageStartOffset; - } - return m_MessageStream; - } - - set - { - if (value == null) - { - throw new ArgumentNullException("Property MessageStream value can't be null !"); - } - if (!value.CanSeek) - { - throw new Exception("Stream must support seeking !"); - } - - m_MessageStream = value; - m_MessageStartOffset = m_MessageStream.Position; - } - } - - /// - /// Gets message size in bytes. - /// - public long MessageSize - { - get - { - if (m_MessageStream == null) - { - throw new Exception("You must set MessageStream property first to use this property !"); - } - else - { - return m_MessageStream.Length - m_MessageStream.Position; - } - } - } - - /// - /// Gets or sets if message exists. Set this false, if message actually doesn't exist any more. - /// - public bool MessageExists - { - get { return m_MessageExists; } - - set { m_MessageExists = value; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Reference to current POP3 session. - /// Message info what message items to get. - public POP3_eArgs_GetMessageStream(POP3_Session session, POP3_Message messageInfo) - { - m_pSession = session; - m_pMessageInfo = messageInfo; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ParseException.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ParseException.cs deleted file mode 100644 index c5f743d73..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ParseException.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - #region usings - - using System; - - #endregion - - /// - /// This exception is thrown when parse errors are encountered. - /// - public class ParseException : Exception - { - #region Constructor - - /// - /// Default constructor. - /// - /// - public ParseException(string message) : base(message) {} - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/PortRange.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/PortRange.cs deleted file mode 100644 index b5c7923af..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/PortRange.cs +++ /dev/null @@ -1,88 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - #region usings - - using System; - - #endregion - - /// - /// This class holds UDP or TCP port range. - /// - public class PortRange - { - #region Members - - private readonly int m_End = 1100; - private readonly int m_Start = 1000; - - #endregion - - #region Properties - - /// - /// Gets start port. - /// - public int Start - { - get { return m_Start; } - } - - /// - /// Gets end port. - /// - public int End - { - get { return m_End; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Start port. - /// End port. - /// Is raised when any of the aruments value is out of range. - public PortRange(int start, int end) - { - if (start < 1 || start > 0xFFFF) - { - throw new ArgumentOutOfRangeException("Argument 'start' value must be > 0 and << 65 535."); - } - if (end < 1 || end > 0xFFFF) - { - throw new ArgumentOutOfRangeException("Argument 'end' value must be > 0 and << 65 535."); - } - if (start > end) - { - throw new ArgumentOutOfRangeException( - "Argumnet 'start' value must be >= argument 'end' value."); - } - - m_Start = start; - m_End = end; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Properties/AssemblyInfo.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Properties/AssemblyInfo.cs deleted file mode 100644 index 35ecdf3f2..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System.Reflection; - -// -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -// - -[assembly: AssemblyTitle("ASC.Mail")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Ascensio System SIA")] -[assembly: AssemblyProduct("ASC.Mail")] -[assembly: AssemblyCopyright("(c) Ascensio System SIA. All rights reserved")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Revision and Build Numbers -// by using the '*' as shown below: - -[assembly: AssemblyVersion("1.0.0.0")] - -// -// In order to sign your assembly you must specify a key to use. Refer to the -// Microsoft .NET Framework documentation for more information on assembly signing. -// -// Use the attributes below to control which key is used for signing. -// -// Notes: -// (*) If no key is specified, the assembly is not signed. -// (*) KeyName refers to a key that has been installed in the Crypto Service -// Provider (CSP) on your machine. KeyFile refers to a file which contains -// a key. -// (*) If the KeyFile and the KeyName values are both specified, the -// following processing occurs: -// (1) If the KeyName can be found in the CSP, that key is used. -// (2) If the KeyName does not exist and the KeyFile does exist, the key -// in the KeyFile is installed into the CSP and used. -// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. -// When specifying the KeyFile, the location of the KeyFile should be -// relative to the project output directory which is -// %Project Directory%\obj\. For example, if your KeyFile is -// located in the project directory, you would specify the AssemblyKeyFile -// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] -// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework -// documentation for more information on this. -// diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/Debug/wfrm_RTP_Debug.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/Debug/wfrm_RTP_Debug.cs deleted file mode 100644 index 1715141ad..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/Debug/wfrm_RTP_Debug.cs +++ /dev/null @@ -1,1252 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP.Debug -{ - #region usings - - using System; - using System.Drawing; - using System.Net; - using System.Windows.Forms; - - #endregion - - /// - /// This class implements RTP multimedia session debugger/monitoring UI. - /// - public class wfrm_RTP_Debug : Form - { - #region Nested type: ComboBoxItem - - /// - /// This class implements ComboBaox item. - /// - private class ComboBoxItem - { - #region Members - - private readonly object m_pTag; - private readonly string m_Text = ""; - - #endregion - - #region Properties - - /// - /// Gets text. - /// - public string Text - { - get { return m_Text; } - } - - /// - /// Gets user data. - /// - public object Tag - { - get { return m_pTag; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Text. - /// User data. - public ComboBoxItem(string text, object tag) - { - m_Text = text; - m_pTag = tag; - } - - #endregion - - #region Methods - - /// - /// Returns ComboBox text. - /// - /// eturns ComboBox text. - public override string ToString() - { - return m_Text; - } - - #endregion - } - - #endregion - - #region Nested type: RTP_ParticipantInfo - - /// - /// This class provides data for RTP participant property grid. - /// - private class RTP_ParticipantInfo - { - #region Members - - private readonly RTP_Participant m_pParticipant; - - #endregion - - #region Properties - - /// - /// Gets or sets the real name, eg. "John Doe". Value null means not specified. - /// - public string Name - { - get - { - if (m_pParticipant is RTP_Participant_Local) - { - return ((RTP_Participant_Local) m_pParticipant).Name; - } - else - { - return ((RTP_Participant_Remote) m_pParticipant).Name; - } - } - } - - /// - /// Gets or sets email address. For example "John.Doe@example.com". Value null means not specified. - /// - public string Email - { - get - { - if (m_pParticipant is RTP_Participant_Local) - { - return ((RTP_Participant_Local) m_pParticipant).Email; - } - else - { - return ((RTP_Participant_Remote) m_pParticipant).Email; - } - } - } - - /// - /// Gets or sets phone number. For example "+1 908 555 1212". Value null means not specified. - /// - public string Phone - { - get - { - if (m_pParticipant is RTP_Participant_Local) - { - return ((RTP_Participant_Local) m_pParticipant).Phone; - } - else - { - return ((RTP_Participant_Remote) m_pParticipant).Phone; - } - } - } - - /// - /// Gets or sets location string. It may be geographic address or for example chat room name. - /// Value null means not specified. - /// - public string Location - { - get - { - if (m_pParticipant is RTP_Participant_Local) - { - return ((RTP_Participant_Local) m_pParticipant).Location; - } - else - { - return ((RTP_Participant_Remote) m_pParticipant).Location; - } - } - } - - /// - /// Gets or sets streaming application name/version. - /// Value null means not specified. - /// - public string Tool - { - get - { - if (m_pParticipant is RTP_Participant_Local) - { - return ((RTP_Participant_Local) m_pParticipant).Tool; - } - else - { - return ((RTP_Participant_Remote) m_pParticipant).Tool; - } - } - } - - /// - /// Gets or sets note text. The NOTE item is intended for transient messages describing the current state - /// of the source, e.g., "on the phone, can't talk". Value null means not specified. - /// - public string Note - { - get - { - if (m_pParticipant is RTP_Participant_Local) - { - return ((RTP_Participant_Local) m_pParticipant).Note; - } - else - { - return ((RTP_Participant_Remote) m_pParticipant).Note; - } - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// RTP local participant. - /// Is raised when participant null reference. - public RTP_ParticipantInfo(RTP_Participant participant) - { - if (participant == null) - { - throw new ArgumentNullException("participant"); - } - - m_pParticipant = participant; - } - - #endregion - } - - #endregion - - #region Nested type: RTP_ReceiveStreamInfo - - /// - /// This class provides data for RTP "receive stream" property grid. - /// - private class RTP_ReceiveStreamInfo - { - #region Members - - private readonly RTP_ReceiveStream m_pStream; - - #endregion - - #region Properties - - /// - /// Gets stream owner RTP session. - /// - /// Is raised when this class is Disposed and this property is accessed. - public int Session - { - get { return m_pStream.Session.GetHashCode(); } - } - - /// - /// Gets number of times SeqNo has wrapped around. - /// - /// Is raised when this class is Disposed and this property is accessed. - public int SeqNoWrapCount - { - get { return m_pStream.SeqNoWrapCount; } - } - - /// - /// Gets first sequence number what this stream got. - /// - /// Is raised when this class is Disposed and this property is accessed. - public int FirstSeqNo - { - get { return m_pStream.FirstSeqNo; } - } - - /// - /// Gets maximum sequnce number that stream has got. - /// - /// Is raised when this class is Disposed and this property is accessed. - public int MaxSeqNo - { - get { return m_pStream.MaxSeqNo; } - } - - /// - /// Gets how many RTP packets has received by this stream. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long PacketsReceived - { - get { return m_pStream.PacketsReceived; } - } - - /// - /// Gets how many RTP misorder packets has received by this stream. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long PacketsMisorder - { - get { return m_pStream.PacketsMisorder; } - } - - /// - /// Gets how many RTP data has received by this stream. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long BytesReceived - { - get { return m_pStream.BytesReceived; } - } - - /// - /// Gets how many RTP packets has lost during transmission. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long PacketsLost - { - get { return m_pStream.PacketsLost; } - } - - /// - /// Gets inter arrival jitter. - /// - /// Is raised when this class is Disposed and this property is accessed. - public double Jitter - { - get { return m_pStream.Jitter; } - } - - /// - /// Gets time when last SR(sender report) was received. Returns DateTime.MinValue if no SR received. - /// - public string LastSRTime - { - get { return m_pStream.LastSRTime.ToString("HH:mm:ss"); } - } - - /// - /// Gets delay between las SR(sender report) and now in seconds. - /// - public int DelaySinceLastSR - { - get { return m_pStream.DelaySinceLastSR/1000; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// RTP receive stream. - public RTP_ReceiveStreamInfo(RTP_ReceiveStream stream) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - m_pStream = stream; - } - - #endregion - } - - #endregion - - #region Nested type: RTP_SendStreamInfo - - /// - /// This class provides data for RTP "send stream" property grid. - /// - private class RTP_SendStreamInfo - { - #region Members - - private readonly RTP_SendStream m_pStream; - - #endregion - - #region Properties - - /// - /// Gets stream owner RTP session. - /// - /// Is raised when this class is Disposed and this property is accessed. - public int Session - { - get { return m_pStream.Session.GetHashCode(); } - } - - /// - /// Gets number of times SeqNo has wrapped around. - /// - /// Is raised when this class is Disposed and this property is accessed. - public int SeqNoWrapCount - { - get { return m_pStream.SeqNoWrapCount; } - } - - /// - /// Gets next packet sequence number. - /// - /// Is raised when this class is Disposed and this property is accessed. - public int SeqNo - { - get { return m_pStream.SeqNo; } - } - - /// - /// Gets last packet send time. - /// - /// Is raised when this class is Disposed and this property is accessed. - public string LastPacketTime - { - get { return m_pStream.LastPacketTime.ToString("HH:mm:ss"); } - } - - /// - /// Gets last sent RTP packet RTP timestamp header value. - /// - /// Is raised when this class is Disposed and this property is accessed. - public uint LastPacketRtpTimestamp - { - get { return m_pStream.LastPacketRtpTimestamp; } - } - - /// - /// Gets how many RTP packets has sent by this stream. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long RtpPacketsSent - { - get { return m_pStream.RtpPacketsSent; } - } - - /// - /// Gets how many RTP bytes has sent by this stream. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long RtpBytesSent - { - get { return m_pStream.RtpBytesSent; } - } - - /// - /// Gets how many RTP data(no RTP header included) bytes has sent by this stream. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long RtpDataBytesSent - { - get { return m_pStream.RtpDataBytesSent; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// RTP send stream. - /// Is raised when stream is null reference. - public RTP_SendStreamInfo(RTP_SendStream stream) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - m_pStream = stream; - } - - #endregion - } - - #endregion - - #region Nested type: RTP_SessionStatistics - - /// - /// This class provides data for RTP global statistic property grid. - /// - private class RTP_SessionStatistics - { - #region Members - - private readonly RTP_Session m_pSession; - - #endregion - - #region Properties - - /// - /// Gets total members count. - /// - public long Members - { - get { return m_pSession.Members.Length; } - } - - /// - /// Gets total members who send RPT data. - /// - public long Senders - { - get { return m_pSession.Senders.Length; } - } - - /// - /// Gets total of RTP packets sent by this session. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long RtpPacketsSent - { - get { return m_pSession.RtpPacketsSent; } - } - - /// - /// Gets total of RTP bytes(RTP headers included) sent by this session. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long RtpBytesSent - { - get { return m_pSession.RtpBytesSent; } - } - - /// - /// Gets total of RTP packets received by this session. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long RtpPacketsReceived - { - get { return m_pSession.RtpPacketsReceived; } - } - - /// - /// Gets total of RTP bytes(RTP headers included) received by this session. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long RtpBytesReceived - { - get { return m_pSession.RtpBytesReceived; } - } - - /// - /// Gets number of times RTP packet sending has failed. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long RtpFailedTransmissions - { - get { return m_pSession.RtpFailedTransmissions; } - } - - /// - /// Gets total of RTCP packets sent by this session. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long RtcpPacketsSent - { - get { return m_pSession.RtcpPacketsSent; } - } - - /// - /// Gets total of RTCP bytes(RTCP headers included) sent by this session. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long RtcpBytesSent - { - get { return m_pSession.RtcpBytesSent; } - } - - /// - /// Gets total of RTCP packets received by this session. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long RtcpPacketsReceived - { - get { return m_pSession.RtcpPacketsReceived; } - } - - /// - /// Gets total of RTCP bytes(RTCP headers included) received by this session. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long RtcpBytesReceived - { - get { return m_pSession.RtcpBytesReceived; } - } - - /// - /// Gets number of times RTCP packet sending has failed. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long RtcpFailedTransmissions - { - get { return m_pSession.RtcpFailedTransmissions; } - } - - /// - /// Current RTCP reporting interval in seconds. - /// - /// Is raised when this class is Disposed and this property is accessed. - public int RtcpInterval - { - get { return m_pSession.RtcpInterval; } - } - - /// - /// Gets time when last RTCP report was sent. - /// - /// Is raised when this class is Disposed and this property is accessed. - public string RtcpLastTransmission - { - get { return m_pSession.RtcpLastTransmission.ToString("HH:mm:ss"); } - } - - /// - /// Gets number of times local SSRC collision dedected. - /// - public long LocalCollisions - { - get { return m_pSession.LocalCollisions; } - } - - /// - /// Gets number of times remote SSRC collision dedected. - /// - public long RemoteCollisions - { - get { return m_pSession.RemoteCollisions; } - } - - /// - /// Gets number of times local packets loop dedected. - /// - public long LocalPacketsLooped - { - get { return m_pSession.LocalPacketsLooped; } - } - - /// - /// Gets number of times remote packets loop dedected. - /// - public long RemotePacketsLooped - { - get { return m_pSession.RemotePacketsLooped; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// RTP session. - /// Is raised when session - public RTP_SessionStatistics(RTP_Session session) - { - if (session == null) - { - throw new ArgumentNullException("session"); - } - - m_pSession = session; - } - - #endregion - } - - #endregion - - #region Nested type: RTP_SourceInfo - - /// - /// This class provides data for RTP "source" property grid. - /// - private class RTP_SourceInfo - { - #region Members - - private readonly RTP_Source m_pSource; - - #endregion - - #region Properties - - /// - /// Gets source state. - /// - public RTP_SourceState State - { - get { return m_pSource.State; } - } - - /// - /// Gets owner RTP session. - /// - /// Is raised when this class is Disposed and this property is accessed. - public int Session - { - get { return m_pSource.Session.GetHashCode(); } - } - - /// - /// Gets synchronization source ID. - /// - /// Is raised when this class is Disposed and this property is accessed. - public uint SSRC - { - get { return m_pSource.SSRC; } - } - - /// - /// Gets source RTCP end point. Value null means source haven't sent any RTCP packet. - /// - /// Is raised when this class is Disposed and this property is accessed. - public IPEndPoint RtcpEP - { - get { return m_pSource.RtcpEP; } - } - - /// - /// Gets source RTP end point. Value null means source haven't sent any RTCP packet. - /// - /// Is raised when this class is Disposed and this property is accessed. - public IPEndPoint RtpEP - { - get { return m_pSource.RtpEP; } - } - - /// - /// Gets last time when source sent RTP or RCTP packet. - /// - /// Is raised when this class is Disposed and this property is accessed. - public string LastActivity - { - get { return m_pSource.LastActivity.ToString("HH:mm:ss"); } - } - - /// - /// Gets last time when source sent RTCP packet. - /// - /// Is raised when this class is Disposed and this property is accessed. - public string LastRtcpPacket - { - get { return m_pSource.LastRtcpPacket.ToString("HH:mm:ss"); } - } - - /// - /// Gets last time when source sent RTP packet. - /// - /// Is raised when this class is Disposed and this property is accessed. - public string LastRtpPacket - { - get { return m_pSource.LastRtpPacket.ToString("HH:mm:ss"); } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// RTP source. - /// Is raised when source is null reference. - public RTP_SourceInfo(RTP_Source source) - { - if (source == null) - { - throw new ArgumentNullException("source"); - } - - m_pSource = source; - } - - #endregion - } - - #endregion - - #region Members - - private readonly RTP_MultimediaSession m_pSession; - private readonly Timer m_pTimer; - private bool m_IsDisposed; - private ListView m_pErrors; - private PropertyGrid m_pGlobalSessionInfo; - private PropertyGrid m_pParticipantData; - private TreeView m_pParticipants; - private SplitContainer m_pParticipantsSplitter; - private ComboBox m_pSessions; - private TabControl m_pTab; - - #endregion - - #region Properties - - /// - /// Gets RTP session what UI debugs. - /// - public RTP_MultimediaSession Session - { - get { return m_pSession; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// RTP multimedia session. - public wfrm_RTP_Debug(RTP_MultimediaSession session) - { - if (session == null) - { - throw new ArgumentNullException("session"); - } - - m_pSession = session; - - InitUI(); - - m_pSession.Error += m_pSession_Error; - m_pSession.SessionCreated += m_pSession_SessionCreated; - m_pSession.NewParticipant += m_pSession_NewParticipant; - m_pSession.LocalParticipant.SourceAdded += Participant_SourceAdded; - m_pSession.LocalParticipant.SourceRemoved += Participant_SourceRemoved; - - m_pTimer = new Timer(); - m_pTimer.Interval = 1000; - m_pTimer.Tick += m_pTimer_Tick; - m_pTimer.Enabled = true; - - TreeNode nodeParticipant = new TreeNode(session.LocalParticipant.CNAME); - nodeParticipant.Tag = new RTP_ParticipantInfo(session.LocalParticipant); - nodeParticipant.Nodes.Add("Sources"); - m_pParticipants.Nodes.Add(nodeParticipant); - } - - #endregion - - #region Event handlers - - private void m_pParticipants_AfterSelect(object sender, TreeViewEventArgs e) - { - m_pParticipantData.SelectedObject = e.Node.Tag; - } - - private void m_pSessions_SelectedIndexChanged(object sender, EventArgs e) - { - m_pGlobalSessionInfo.SelectedObject = ((ComboBoxItem) m_pSessions.SelectedItem).Tag; - } - - private void m_pErrors_DoubleClick(object sender, EventArgs e) - { - if (m_pErrors.SelectedItems.Count > 0) - { - MessageBox.Show(this, - "Error: " + (m_pErrors.SelectedItems[0].Tag), - "Error:", - MessageBoxButtons.OK, - MessageBoxIcon.Error); - } - } - - /// - /// Is called when RTP session gets unhandled error. - /// - /// Sender. - /// Event data. - private void m_pSession_Error(object sender, ExceptionEventArgs e) - { - if (m_IsDisposed) - { - return; - } - - // Move processing to UI thread. - BeginInvoke(new MethodInvoker(delegate - { - ListViewItem item = new ListViewItem(e.Exception.Message); - item.Tag = e.Exception; - m_pErrors.Items.Add(item); - })); - } - - /// - /// Is called when RTP multimedia session creates new session. - /// - /// Sender. - /// Event data. - private void m_pSession_SessionCreated(object sender, EventArgs e) - { - if (m_IsDisposed) - { - return; - } - - // Move processing to UI thread. - BeginInvoke(new MethodInvoker(delegate - { - ComboBoxItem item = - new ComboBoxItem("Session: " + e.Value.GetHashCode(), - new RTP_SessionStatistics(e.Value)); - m_pSessions.Items.Add(item); - - if (m_pSessions.SelectedIndex == -1) - { - m_pSessions.SelectedIndex = 0; - } - })); - } - - /// - /// This method is called when RTP session sees new remote participant. - /// - /// Sender. - /// Event data. - private void m_pSession_NewParticipant(object sender, RTP_ParticipantEventArgs e) - { - if (m_IsDisposed) - { - return; - } - - e.Participant.Removed += Participant_Removed; - e.Participant.SourceAdded += Participant_SourceAdded; - e.Participant.SourceRemoved += Participant_SourceRemoved; - - // Move processing to UI thread. - BeginInvoke(new MethodInvoker(delegate - { - TreeNode nodeParticipant = new TreeNode(e.Participant.CNAME); - nodeParticipant.Tag = new RTP_ParticipantInfo(e.Participant); - nodeParticipant.Nodes.Add("Sources"); - m_pParticipants.Nodes.Add(nodeParticipant); - })); - } - - /// - /// This method is called when RTP remote participant has disjoined the multimedia session. - /// - /// Sender. - /// Event data. - private void Participant_Removed(object sender, EventArgs e) - { - if (m_IsDisposed) - { - return; - } - - // Move processing to UI thread. - BeginInvoke(new MethodInvoker(delegate - { - TreeNode nodeParticipant = - FindParticipantNode((RTP_Participant) sender); - if (nodeParticipant != null) - { - nodeParticipant.Remove(); - } - })); - } - - /// - /// This method is called when participant creates new source. - /// - /// Sender. - /// Event data. - private void Participant_SourceAdded(object sender, RTP_SourceEventArgs e) - { - if (m_IsDisposed) - { - return; - } - - e.Source.StateChanged += Source_StateChanged; - - // Move processing to UI thread. - BeginInvoke(new MethodInvoker(delegate - { - TreeNode nodeParticipant = null; - if (e.Source is RTP_Source_Remote) - { - nodeParticipant = - FindParticipantNode( - ((RTP_Source_Remote) e.Source).Participant); - } - else - { - nodeParticipant = - FindParticipantNode( - ((RTP_Source_Local) e.Source).Participant); - } - TreeNode nodeSource = - nodeParticipant.Nodes[0].Nodes.Add( - e.Source.SSRC.ToString()); - nodeSource.Tag = new RTP_SourceInfo(e.Source); - - if (e.Source.State == RTP_SourceState.Active) - { - TreeNode nodeSourceStream = - nodeSource.Nodes.Add("RTP Stream"); - if (e.Source is RTP_Source_Local) - { - nodeSourceStream.Tag = - new RTP_SendStreamInfo( - ((RTP_Source_Local) e.Source).Stream); - } - else - { - nodeSourceStream.Tag = - new RTP_ReceiveStreamInfo( - ((RTP_Source_Remote) e.Source).Stream); - } - } - })); - } - - /// - /// This method is called when participant source state changes. - /// - /// Sender. - /// Event data. - private void Source_StateChanged(object sender, EventArgs e) - { - if (m_IsDisposed) - { - return; - } - - RTP_Source source = (RTP_Source) sender; - if (source.State == RTP_SourceState.Disposed) - { - return; - } - - // Move processing to UI thread. - BeginInvoke(new MethodInvoker(delegate - { - TreeNode nodeParticipant = null; - if (source is RTP_Source_Remote) - { - nodeParticipant = - FindParticipantNode( - ((RTP_Source_Remote) source).Participant); - } - else - { - nodeParticipant = - FindParticipantNode( - ((RTP_Source_Local) source).Participant); - } - if (nodeParticipant != null) - { - foreach ( - TreeNode nodeSource in - nodeParticipant.Nodes[0].Nodes) - { - if (nodeSource.Text == source.SSRC.ToString()) - { - if (source.State == RTP_SourceState.Active) - { - TreeNode nodeSourceStream = - nodeSource.Nodes.Add("RTP Stream"); - if (source is RTP_Source_Local) - { - nodeSourceStream.Tag = - new RTP_SendStreamInfo( - ((RTP_Source_Local) source). - Stream); - } - else - { - nodeSourceStream.Tag = - new RTP_ReceiveStreamInfo( - ((RTP_Source_Remote) source). - Stream); - } - } - - break; - } - } - } - })); - } - - /// - /// This method is called when participant closes source. - /// - /// Sender. - /// Event data. - private void Participant_SourceRemoved(object sender, RTP_SourceEventArgs e) - { - if (m_IsDisposed) - { - return; - } - - // Get SSRC here, BeginInvoke is anynchronous and source may dispose at same time. - uint ssrc = e.Source.SSRC; - - // Move processing to UI thread. - BeginInvoke(new MethodInvoker(delegate - { - TreeNode nodeParticipant = - FindParticipantNode((RTP_Participant) sender); - if (nodeParticipant != null) - { - foreach ( - TreeNode nodeSource in - nodeParticipant.Nodes[0].Nodes) - { - if (nodeSource.Text == ssrc.ToString()) - { - nodeSource.Remove(); - break; - } - } - } - })); - } - - private void m_pTimer_Tick(object sender, EventArgs e) - { - m_pParticipantData.Refresh(); - m_pGlobalSessionInfo.Refresh(); - } - - private void wfrm_RTP_Debug_FormClosing(object sender, FormClosingEventArgs e) - { - m_IsDisposed = true; - - m_pSession.Error -= m_pSession_Error; - m_pSession.SessionCreated -= m_pSession_SessionCreated; - m_pSession.NewParticipant -= m_pSession_NewParticipant; - m_pSession.LocalParticipant.SourceAdded -= Participant_SourceAdded; - m_pSession.LocalParticipant.SourceRemoved -= Participant_SourceRemoved; - } - - #endregion - - #region Utility methods - - /// - /// Creates and initializes UI. - /// - private void InitUI() - { - ClientSize = new Size(400, 450); - Text = "RTP debug"; - //this.Icon = ; TODO: - FormClosing += wfrm_RTP_Debug_FormClosing; - - m_pTab = new TabControl(); - m_pTab.Dock = DockStyle.Fill; - - m_pTab.TabPages.Add("participants", "Participants"); - - m_pParticipantsSplitter = new SplitContainer(); - m_pParticipantsSplitter.Dock = DockStyle.Fill; - m_pParticipantsSplitter.Orientation = Orientation.Vertical; - m_pParticipantsSplitter.SplitterDistance = 60; - m_pTab.TabPages["participants"].Controls.Add(m_pParticipantsSplitter); - - m_pParticipants = new TreeView(); - m_pParticipants.Dock = DockStyle.Fill; - m_pParticipants.BorderStyle = BorderStyle.None; - m_pParticipants.FullRowSelect = true; - m_pParticipants.HideSelection = false; - m_pParticipants.AfterSelect += m_pParticipants_AfterSelect; - m_pParticipantsSplitter.Panel1.Controls.Add(m_pParticipants); - - m_pParticipantData = new PropertyGrid(); - m_pParticipantData.Dock = DockStyle.Fill; - m_pParticipantsSplitter.Panel2.Controls.Add(m_pParticipantData); - - m_pTab.TabPages.Add("global_statistics", "Global statistics"); - - m_pGlobalSessionInfo = new PropertyGrid(); - m_pGlobalSessionInfo.Dock = DockStyle.Fill; - m_pTab.TabPages["global_statistics"].Controls.Add(m_pGlobalSessionInfo); - - m_pSessions = new ComboBox(); - m_pSessions.Size = new Size(200, 20); - m_pSessions.Location = new Point(100, 2); - m_pSessions.DropDownStyle = ComboBoxStyle.DropDownList; - m_pSessions.SelectedIndexChanged += m_pSessions_SelectedIndexChanged; - m_pTab.TabPages["global_statistics"].Controls.Add(m_pSessions); - m_pSessions.BringToFront(); - - m_pTab.TabPages.Add("errors", "Errors"); - - m_pErrors = new ListView(); - m_pErrors.Dock = DockStyle.Fill; - m_pErrors.View = View.Details; - m_pErrors.FullRowSelect = true; - m_pErrors.HideSelection = false; - m_pErrors.Columns.Add("Message", 300); - m_pErrors.DoubleClick += m_pErrors_DoubleClick; - m_pTab.TabPages["errors"].Controls.Add(m_pErrors); - - Controls.Add(m_pTab); - } - - /// - /// Searches specified participant tree node. - /// - /// RTP participant. - /// Returns specified participant tree node or null if no matching node. - private TreeNode FindParticipantNode(RTP_Participant participant) - { - if (participant == null) - { - throw new ArgumentNullException("participant"); - } - - foreach (TreeNode node in m_pParticipants.Nodes) - { - if (node.Text == participant.CNAME) - { - return node; - } - } - - return null; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_CompoundPacket.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_CompoundPacket.cs deleted file mode 100644 index 0a375d233..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_CompoundPacket.cs +++ /dev/null @@ -1,223 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - using System.Collections.Generic; - - #endregion - - /// - /// This class represents RTCP compound packet. - /// - public class RTCP_CompoundPacket - { - #region Members - - private readonly List m_pPackets; - - #endregion - - #region Properties - - /// - /// Gets compound packets. - /// - public List Packets - { - get { return m_pPackets; } - } - - /// - /// Gets total packets size in bytes which is needed for this compound packet. - /// - internal int TotalSize - { - get - { - int size = 0; - foreach (RTCP_Packet packet in m_pPackets) - { - size += packet.Size; - } - - return size; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - internal RTCP_CompoundPacket() - { - m_pPackets = new List(); - } - - #endregion - - #region Methods - - /// - /// Parses RTP compound packet. - /// - /// Data buffer.. - /// Number of bytes in the buffer. - /// Returns parsed RTP packet. - public static RTCP_CompoundPacket Parse(byte[] buffer, int count) - { - /* Compound packet stucture: - Encryption prefix - If and only if the compound packet is to be encrypted, it is prefixed by a - random 32-bit quantity redrawn for every compound packet transmitted. - - SR or RR - The first RTCP packet in the compound packet must always be a report - packet to facilitate header validation as described in Appendix A.2. - This is true even if no data has been sent nor received, in which case an - empty RR is sent, and even if the only other RTCP packet in the compound packet is a BYE. - - Additional RRs - If the number of sources for which reception statistics are being reported - exceeds 31, the number that will fit into one SR or RR packet, then additional - RR packets should follow the initial report packet. - - SDES - An SDES packet containing a CNAME item must be included in each compound RTCP packet. - Other source description items may optionally be included if required by a particular - application, subject to bandwidth constraints (see Section 6.2.2). - - BYE or APP - Other RTCP packet types, including those yet to be defined, may follow in any order, - except that BYE should be the last packet sent with a given SSRC/CSRC. - Packet types may appear more than once. - */ - - int offset = 0; - - RTCP_CompoundPacket packet = new RTCP_CompoundPacket(); - while (offset < count) - { - try - { - packet.m_pPackets.Add(RTCP_Packet.Parse(buffer, ref offset)); - } - catch - { - // Skip invalid or unknown packets. - } - } - - return packet; - } - - /// - /// Gets RTCP compound packet as raw byte data. - /// - /// Returns compound packet as raw byte data. - public byte[] ToByte() - { - byte[] retVal = new byte[TotalSize]; - int offset = 0; - ToByte(retVal, ref offset); - - return retVal; - } - - /// - /// Stores this compund packet to specified buffer. - /// - /// Buffer where to store data. - /// Offset in buffer. - /// Is raised when buffer is null. - /// Is raised when any of the arguments has invalid value. - public void ToByte(byte[] buffer, ref int offset) - { - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (offset < 0) - { - throw new ArgumentException("Argument 'offset' value must be >= 0."); - } - - foreach (RTCP_Packet packet in m_pPackets) - { - packet.ToByte(buffer, ref offset); - } - } - - /// - /// Validates RTCP compound packet. - /// - /// Is raised when invalid RTCP compound packet. - public void Validate() - { - /* RFC 3550 A.2 RTCP Header Validity Checks. - The following checks should be applied to RTCP packets. - - o RTP version field must equal 2. - - o The payload type field of the first RTCP packet in a compound - packet must be equal to SR or RR. - - o The padding bit (P) should be zero for the first packet of a - compound RTCP packet because padding should only be applied, if it - is needed, to the last packet. - - o The length fields of the individual RTCP packets must add up to - the overall length of the compound RTCP packet as received. This - is a fairly strong check. - */ - - if (m_pPackets.Count == 0) - { - throw new ArgumentException("No RTCP packets."); - } - - // Check version and padding. - for (int i = 0; i < m_pPackets.Count; i++) - { - RTCP_Packet packet = m_pPackets[i]; - if (packet.Version != 2) - { - throw new ArgumentException("RTP version field must equal 2."); - } - if (i < (m_pPackets.Count - 1) && packet.IsPadded) - { - throw new ArgumentException("Only the last packet in RTCP compound packet may be padded."); - } - } - - // The first RTCP packet in a compound packet must be equal to SR or RR. - if (m_pPackets[0].Type != RTCP_PacketType.SR || m_pPackets[0].Type != RTCP_PacketType.RR) - { - throw new ArgumentException( - "The first RTCP packet in a compound packet must be equal to SR or RR."); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet.cs deleted file mode 100644 index 29cbad8f1..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet.cs +++ /dev/null @@ -1,194 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - - #endregion - - /// - /// This is base class for RTCP packets. - /// - public abstract class RTCP_Packet - { - #region Members - - private int m_PaddBytesCount; - - #endregion - - #region Properties - - /// - /// Gets RTCP version. - /// - public abstract int Version { get; } - - /// - /// Gets if packet is padded to some bytes boundary. - /// - public bool IsPadded - { - get - { - if (m_PaddBytesCount > 0) - { - return true; - } - else - { - return false; - } - } - } - - /// - /// Gets RTCP packet type. - /// - public abstract int Type { get; } - - /// - /// Gets or sets number empty bytes to add at the end of packet. - /// - public int PaddBytesCount - { - get { return m_PaddBytesCount; } - - set - { - if (value < 0) - { - throw new ArgumentException("Property 'PaddBytesCount' value must be >= 0."); - } - - m_PaddBytesCount = value; - } - } - - /// - /// Gets number of bytes needed for this packet. - /// - public abstract int Size { get; } - - #endregion - - #region Methods - - /// - /// Parses 1 RTCP packet from the specified buffer. - /// - /// Buffer which contains RTCP packet. - /// Offset in buffer. - /// Returns parsed RTCP packet. - /// Is raised when buffer is null. - /// Is raised when any of the arguments has invalid value. - public static RTCP_Packet Parse(byte[] buffer, ref int offset) - { - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (offset < 0) - { - throw new ArgumentException("Argument 'offset' value must be >= 0."); - } - - /* RFC 3550 RTCP header. - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - header |V=2|P| XX | type | length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - int type = buffer[offset + 1]; - - // SR - if (type == RTCP_PacketType.SR) - { - RTCP_Packet_SR packet = new RTCP_Packet_SR(); - packet.ParseInternal(buffer, ref offset); - - return packet; - } - // RR - else if (type == RTCP_PacketType.RR) - { - RTCP_Packet_RR packet = new RTCP_Packet_RR(); - packet.ParseInternal(buffer, ref offset); - - return packet; - } - // SDES - else if (type == RTCP_PacketType.SDES) - { - RTCP_Packet_SDES packet = new RTCP_Packet_SDES(); - packet.ParseInternal(buffer, ref offset); - - return packet; - } - // BYE - else if (type == RTCP_PacketType.BYE) - { - RTCP_Packet_BYE packet = new RTCP_Packet_BYE(); - packet.ParseInternal(buffer, ref offset); - - return packet; - } - // APP - else if (type == RTCP_PacketType.APP) - { - RTCP_Packet_APP packet = new RTCP_Packet_APP(); - packet.ParseInternal(buffer, ref offset); - - return packet; - } - else - { - // We need to move offset. - offset += 2; - int length = buffer[offset++] << 8 | buffer[offset++]; - offset += length; - - throw new ArgumentException("Unknown RTCP packet type '" + type + "'."); - } - } - - /// - /// Stores this packet to the specified buffer. - /// - /// Buffer where to store packet. - /// Offset in buffer. - public abstract void ToByte(byte[] buffer, ref int offset); - - #endregion - - #region Abstract methods - - /// - /// Parses RTCP packet from the specified buffer. - /// - /// Buffer which contains packet. - /// Offset in buffer. - protected abstract void ParseInternal(byte[] buffer, ref int offset); - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_PacketType.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_PacketType.cs deleted file mode 100644 index 81ac53902..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_PacketType.cs +++ /dev/null @@ -1,54 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - /// - /// This class holds known RTCP packet types. - /// - public class RTCP_PacketType - { - #region Members - - /// - /// Application specifiec data. - /// - public const int APP = 204; - - /// - /// BYE. - /// - public const int BYE = 203; - - /// - /// Receiver report. - /// - public const int RR = 201; - - /// - /// Session description. - /// - public const int SDES = 202; - - /// - /// Sender report. - /// - public const int SR = 200; - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_APP.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_APP.cs deleted file mode 100644 index 5b0f18e31..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_APP.cs +++ /dev/null @@ -1,232 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - - #endregion - - /// - /// This class represents Application-Defined RTCP Packet. - /// - public class RTCP_Packet_APP : RTCP_Packet - { - #region Members - - private byte[] m_Data; - private string m_Name = ""; - private uint m_Source; - private int m_SubType; - private int m_Version = 2; - - #endregion - - #region Properties - - /// - /// Gets RTCP version. - /// - public override int Version - { - get { return m_Version; } - } - - /// - /// Gets RTCP packet type. - /// - public override int Type - { - get { return RTCP_PacketType.APP; } - } - - /// - /// Gets subtype value. - /// - public int SubType - { - get { return m_SubType; } - } - - /// - /// Gets sender synchronization(SSRC) or contributing(CSRC) source identifier. - /// - public uint Source - { - get { return m_Source; } - - set { m_Source = value; } - } - - /// - /// Gets 4 ASCII char packet name. - /// - public string Name - { - get { return m_Name; } - } - - /// - /// Gets application-dependent data. - /// - public byte[] Data - { - get { return m_Data; } - } - - /// - /// Gets number of bytes needed for this packet. - /// - public override int Size - { - get { return 12 + m_Data.Length; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - internal RTCP_Packet_APP() - { - m_Name = "xxxx"; - m_Data = new byte[0]; - } - - #endregion - - #region Methods - - /// - /// Stores APP packet to the specified buffer. - /// - /// Buffer where to store APP packet. - /// Offset in buffer. - /// Is raised when buffer is null. - /// Is raised when any of the arguments has invalid value. - public override void ToByte(byte[] buffer, ref int offset) - { - /* RFC 3550 6.7 APP: Application-Defined RTCP Packet. - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |V=2|P| subtype | PT=APP=204 | length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | SSRC/CSRC | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | name (ASCII) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | application-dependent data ... - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (offset < 0) - { - throw new ArgumentException("Argument 'offset' value must be >= 0."); - } - - int length = 8 + m_Data.Length; - - // V P subtype - buffer[offset++] = (byte) (2 << 6 | 0 << 5 | m_SubType & 0x1F); - // PT=APP=204 - buffer[offset++] = 204; - // length - buffer[offset++] = (byte) ((length >> 8) | 0xFF); - buffer[offset++] = (byte) ((length) | 0xFF); - // SSRC/CSRC - buffer[offset++] = (byte) ((m_Source >> 24) | 0xFF); - buffer[offset++] = (byte) ((m_Source >> 16) | 0xFF); - buffer[offset++] = (byte) ((m_Source >> 8) | 0xFF); - buffer[offset++] = (byte) ((m_Source) | 0xFF); - // name - buffer[offset++] = (byte) m_Name[0]; - buffer[offset++] = (byte) m_Name[1]; - buffer[offset++] = (byte) m_Name[2]; - buffer[offset++] = (byte) m_Name[2]; - // application-dependent data - Array.Copy(m_Data, 0, buffer, offset, m_Data.Length); - offset += m_Data.Length; - } - - #endregion - - #region Overrides - - /// - /// Parses APP packet from the specified buffer. - /// - /// Buffer what conatins APP packet. - /// Offset in buffer. - /// Is raised when buffer is null. - /// Is raised when any of the arguments has invalid value. - protected override void ParseInternal(byte[] buffer, ref int offset) - { - /* RFC 3550 6.7 APP: Application-Defined RTCP Packet. - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |V=2|P| subtype | PT=APP=204 | length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | SSRC/CSRC | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | name (ASCII) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | application-dependent data ... - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (offset < 0) - { - throw new ArgumentException("Argument 'offset' value must be >= 0."); - } - - m_Version = buffer[offset++] >> 6; - bool isPadded = Convert.ToBoolean((buffer[offset] >> 5) & 0x1); - int subType = buffer[offset++] & 0x1F; - int type = buffer[offset++]; - int length = buffer[offset++] << 8 | buffer[offset++]; - if (isPadded) - { - PaddBytesCount = buffer[offset + length]; - } - - m_SubType = subType; - m_Source = - (uint) - (buffer[offset++] << 24 | buffer[offset++] << 16 | buffer[offset++] << 8 | buffer[offset++]); - m_Name = ((char) buffer[offset++]) + ((char) buffer[offset++]).ToString() + - ((char) buffer[offset++]) + ((char) buffer[offset++]); - m_Data = new byte[length - 8]; - Array.Copy(buffer, offset, m_Data, 0, m_Data.Length); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_BYE.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_BYE.cs deleted file mode 100644 index e7cb9251b..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_BYE.cs +++ /dev/null @@ -1,252 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// This class represents BYE: Goodbye RTCP Packet. - /// - public class RTCP_Packet_BYE : RTCP_Packet - { - #region Members - - private string m_LeavingReason = ""; - private uint[] m_Sources; - private int m_Version = 2; - - #endregion - - #region Properties - - /// - /// Gets RTCP version. - /// - public override int Version - { - get { return m_Version; } - } - - /// - /// Gets RTCP packet type. - /// - public override int Type - { - get { return RTCP_PacketType.BYE; } - } - - /// - /// Gets or sets SSRC/CSRC identifiers included in this BYE packet. - /// - /// Is raised when invalid value is passed. - public uint[] Sources - { - get { return m_Sources; } - - set - { - if (value.Length > 31) - { - throw new ArgumentException("Property 'Sources' can accomodate only 31 entries."); - } - - m_Sources = value; - } - } - - /// - /// Gets leaving reason. - /// - public string LeavingReason - { - get { return m_LeavingReason; } - - set { m_LeavingReason = value; } - } - - /// - /// Gets number of bytes needed for this packet. - /// - public override int Size - { - get - { - int size = 4; - if (m_Sources != null) - { - size += 4*m_Sources.Length; - } - if (!string.IsNullOrEmpty(m_LeavingReason)) - { - size += Encoding.UTF8.GetByteCount(m_LeavingReason); - } - - return size; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - internal RTCP_Packet_BYE() {} - - #endregion - - #region Methods - - /// - /// Stores BYE packet to the specified buffer. - /// - /// Buffer where to store BYE packet. - /// Offset in buffer. - /// Is raised when buffer is null. - /// Is raised when any of the arguments has invalid value. - public override void ToByte(byte[] buffer, ref int offset) - { - /* RFC 3550.6.6 BYE: Goodbye RTCP Packet. - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |V=2|P| SC | PT=BYE=203 | length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | SSRC/CSRC | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - : ... : - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - (opt) | length | reason for leaving ... - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (offset < 0) - { - throw new ArgumentException("Argument 'offset' value must be >= 0."); - } - - // Calculate packet body size in bytes. - int length = 0; - length += m_Sources.Length*4; - if (!string.IsNullOrEmpty(m_LeavingReason)) - { - length += Encoding.UTF8.GetByteCount(m_LeavingReason); - } - - // V=2 P SC - buffer[offset++] = (byte) (2 << 6 | 0 << 5 | m_Sources.Length & 0x1F); - // PT=BYE=203 - buffer[offset++] = 203; - // length - buffer[offset++] = (byte) ((length >> 8) & 0xFF); - buffer[offset++] = (byte) (length & 0xFF); - // SSRC/CSRC's - foreach (int source in m_Sources) - { - buffer[offset++] = (byte) ((source & 0xFF000000) >> 24); - buffer[offset++] = (byte) ((source & 0x00FF0000) >> 16); - buffer[offset++] = (byte) ((source & 0x0000FF00) >> 8); - buffer[offset++] = (byte) ((source & 0x000000FF)); - } - // reason for leaving - if (!string.IsNullOrEmpty(m_LeavingReason)) - { - byte[] reasonBytes = Encoding.UTF8.GetBytes(m_LeavingReason); - buffer[offset++] = (byte) reasonBytes.Length; - Array.Copy(reasonBytes, 0, buffer, offset, reasonBytes.Length); - offset += reasonBytes.Length; - } - } - - #endregion - - #region Overrides - - /// - /// Parses BYE packet from raw byte[] bye packet. - /// - /// Buffer what contains BYE packet. - /// Offset in buffer. - /// Is raised when buffer is null. - /// Is raised when any of the arguments has invalid value. - protected override void ParseInternal(byte[] buffer, ref int offset) - { - /* RFC 3550.6.6 BYE: Goodbye RTCP Packet. - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |V=2|P| SC | PT=BYE=203 | length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | SSRC/CSRC | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - : ... : - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - (opt) | length | reason for leaving ... - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (offset < 0) - { - throw new ArgumentException("Argument 'offset' value must be >= 0."); - } - - m_Version = buffer[offset] >> 6; - bool isPadded = Convert.ToBoolean((buffer[offset] >> 5) & 0x1); - int sourceCount = buffer[offset++] & 0x1F; - int type = buffer[offset++]; - int length = buffer[offset++] << 8 | buffer[offset++]; - if (isPadded) - { - PaddBytesCount = buffer[offset + length]; - } - - m_Sources = new uint[sourceCount]; - for (int i = 0; i < sourceCount; i++) - { - m_Sources[i] = - (uint) - (buffer[offset++] << 24 | buffer[offset++] << 16 | buffer[offset++] << 8 | - buffer[offset++]); - } - - // See if we have optional reason text. - if (length > m_Sources.Length*4) - { - int reasonLength = buffer[offset++]; - m_LeavingReason = Encoding.UTF8.GetString(buffer, offset, reasonLength); - offset += reasonLength; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_RR.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_RR.cs deleted file mode 100644 index 7a9fbf412..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_RR.cs +++ /dev/null @@ -1,268 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - using System.Collections.Generic; - using System.Text; - - #endregion - - /// - /// This class represents RR: Receiver Report RTCP Packet. - /// - public class RTCP_Packet_RR : RTCP_Packet - { - #region Members - - private readonly List m_pReportBlocks; - private uint m_SSRC; - private int m_Version = 2; - - #endregion - - #region Properties - - /// - /// Gets RTCP version. - /// - public override int Version - { - get { return m_Version; } - } - - /// - /// Gets RTCP packet type. - /// - public override int Type - { - get { return RTCP_PacketType.RR; } - } - - /// - /// Gets or sets sender(local reporting) synchronization source identifier. - /// - public uint SSRC - { - get { return m_SSRC; } - - set { m_SSRC = value; } - } - - /// - /// Gets reports blocks. - /// - public List ReportBlocks - { - get { return m_pReportBlocks; } - } - - /// - /// Gets number of bytes needed for this packet. - /// - public override int Size - { - get { return 8 + (24*m_pReportBlocks.Count); } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - internal RTCP_Packet_RR() - { - m_pReportBlocks = new List(); - } - - /// - /// Default constructor. - /// - /// SSRC of this packet sender. - internal RTCP_Packet_RR(uint ssrc) - { - m_pReportBlocks = new List(); - } - - #endregion - - #region Methods - - /// - /// Stores receiver report(RR) packet to the specified buffer. - /// - /// Buffer where to store RR packet. - /// Offset in buffer. - /// Is raised when buffer is null. - /// Is raised when any of the arguments has invalid value. - public override void ToByte(byte[] buffer, ref int offset) - { - /* RFC 3550 6.4.2 RR: Receiver Report RTCP Packet. - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - header |V=2|P| RC | PT=RR=201 | length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | SSRC of packet sender | - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - report | SSRC_1 (SSRC of first source) | - block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - 1 | fraction lost | cumulative number of packets lost | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | extended highest sequence number received | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | interarrival jitter | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | last SR (LSR) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | delay since last SR (DLSR) | - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - report | SSRC_2 (SSRC of second source) | - block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - 2 : ... : - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - | profile-specific extensions | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (offset < 0) - { - throw new ArgumentException("Argument 'offset' value must be >= 0."); - } - - // NOTE: Size in 32-bit boundary, header not included. - int length = (4 + (m_pReportBlocks.Count*24))/4; - - // V P RC - buffer[offset++] = (byte) (2 << 6 | 0 << 5 | (m_pReportBlocks.Count & 0x1F)); - // PT=RR=201 - buffer[offset++] = 201; - // length - buffer[offset++] = (byte) ((length >> 8) & 0xFF); - buffer[offset++] = (byte) ((length) & 0xFF); - // SSRC - buffer[offset++] = (byte) ((m_SSRC >> 24) & 0xFF); - buffer[offset++] = (byte) ((m_SSRC >> 16) & 0xFF); - buffer[offset++] = (byte) ((m_SSRC >> 8) & 0xFF); - buffer[offset++] = (byte) ((m_SSRC) & 0xFF); - // Report blocks - foreach (RTCP_Packet_ReportBlock block in m_pReportBlocks) - { - block.ToByte(buffer, ref offset); - } - } - - /// - /// Returns RR packet as string. - /// - /// Returns RR packet as string. - public override string ToString() - { - StringBuilder retVal = new StringBuilder(); - retVal.AppendLine("Type: RR"); - retVal.AppendLine("Version: " + m_Version); - retVal.AppendLine("SSRC: " + m_SSRC); - retVal.AppendLine("Report blocks: " + m_pReportBlocks.Count); - - return retVal.ToString(); - } - - #endregion - - #region Overrides - - /// - /// Parses receiver report(RR) from byte buffer. - /// - /// Buffer wihich contains receiver report. - /// Offset in buufer. - /// Is raised when buffer is null. - /// Is raised when any of the arguments has invalid value. - protected override void ParseInternal(byte[] buffer, ref int offset) - { - /* RFC 3550 6.4.2 RR: Receiver Report RTCP Packet. - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - header |V=2|P| RC | PT=RR=201 | length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | SSRC of packet sender | - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - report | SSRC_1 (SSRC of first source) | - block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - 1 | fraction lost | cumulative number of packets lost | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | extended highest sequence number received | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | interarrival jitter | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | last SR (LSR) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | delay since last SR (DLSR) | - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - report | SSRC_2 (SSRC of second source) | - block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - 2 : ... : - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - | profile-specific extensions | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (offset < 0) - { - throw new ArgumentException("Argument 'offset' value must be >= 0."); - } - - m_Version = buffer[offset] >> 6; - bool isPadded = Convert.ToBoolean((buffer[offset] >> 5) & 0x1); - int reportBlockCount = buffer[offset++] & 0x1F; - int type = buffer[offset++]; - int length = buffer[offset++] << 8 | buffer[offset++]; - if (isPadded) - { - PaddBytesCount = buffer[offset + length]; - } - - m_SSRC = - (uint) - (buffer[offset++] << 24 | buffer[offset++] << 16 | buffer[offset++] << 8 | buffer[offset++]); - for (int i = 0; i < reportBlockCount; i++) - { - RTCP_Packet_ReportBlock reportBlock = new RTCP_Packet_ReportBlock(); - reportBlock.Parse(buffer, offset); - m_pReportBlocks.Add(reportBlock); - offset += 24; - } - // TODO: profile-specific extensions - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_ReportBlock.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_ReportBlock.cs deleted file mode 100644 index 2e71b1e58..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_ReportBlock.cs +++ /dev/null @@ -1,268 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - - #endregion - - /// - /// This class represents RTCP sender report(SR) or reciver report(RR) packet report block. - /// - public class RTCP_Packet_ReportBlock - { - #region Members - - private uint m_CumulativePacketsLost; - private uint m_DelaySinceLastSR; - private uint m_ExtHighestSeqNumber; - private uint m_FractionLost; - private uint m_Jitter; - private uint m_LastSR; - private uint m_SSRC; - - #endregion - - #region Properties - - /// - /// Gets the SSRC identifier of the source to which the information in this reception report block pertains. - /// - public uint SSRC - { - get { return m_SSRC; } - } - - /// - /// Gets or sets the fraction of RTP data packets from source SSRC lost since the previous SR or - /// RR packet was sent. - /// - public uint FractionLost - { - get { return m_FractionLost; } - - set { m_FractionLost = value; } - } - - /// - /// Gets or sets total number of RTP data packets from source SSRC that have - /// been lost since the beginning of reception. - /// - public uint CumulativePacketsLost - { - get { return m_CumulativePacketsLost; } - - set { m_CumulativePacketsLost = value; } - } - - /// - /// Gets or sets extended highest sequence number received. - /// - public uint ExtendedHighestSeqNo - { - get { return m_ExtHighestSeqNumber; } - - set { m_ExtHighestSeqNumber = value; } - } - - /// - /// Gets or sets an estimate of the statistical variance of the RTP data packet - /// interarrival time, measured in timestamp units and expressed as an unsigned integer. - /// - public uint Jitter - { - get { return m_Jitter; } - - set { m_Jitter = value; } - } - - /// - /// Gets or sets The middle 32 bits out of 64 in the NTP timestamp (as explained in Section 4) received as part of - /// the most recent RTCP sender report (SR) packet from source SSRC_n. If no SR has been received yet, the field is set to zero. - /// - public uint LastSR - { - get { return m_LastSR; } - - set { m_LastSR = value; } - } - - /// - /// Gets or sets the delay, expressed in units of 1/65536 seconds, between receiving the last SR packet from - /// source SSRC_n and sending this reception report block. If no SR packet has been received yet from SSRC_n, - /// the DLSR field is set to zero. - /// - public uint DelaySinceLastSR - { - get { return m_DelaySinceLastSR; } - - set { m_DelaySinceLastSR = value; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Source ID. - internal RTCP_Packet_ReportBlock(uint ssrc) - { - m_SSRC = ssrc; - } - - /// - /// Default constructor. - /// - internal RTCP_Packet_ReportBlock() {} - - #endregion - - #region Methods - - /// - /// Parses RTCP report block (part of SR or RR packet) from specified buffer. - /// - /// Buffer from where to read report block. - /// Offset in buffer. - /// Is raised when buffer is null. - /// Is raised when any of the arguments has invalid value. - public void Parse(byte[] buffer, int offset) - { - /* RFC 3550 6.4.1. - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - report | SSRC_1 (SSRC of first source) | - block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - 1 | fraction lost | cumulative number of packets lost | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | extended highest sequence number received | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | interarrival jitter | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | last SR (LSR) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | delay since last SR (DLSR) | - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - */ - - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (offset < 0) - { - throw new ArgumentException("Argument 'offset' value must be >= 0."); - } - - m_SSRC = - (uint) - (buffer[offset++] << 24 | buffer[offset++] << 16 | buffer[offset++] << 8 | buffer[offset++]); - m_FractionLost = buffer[offset++]; - m_CumulativePacketsLost = - (uint) (buffer[offset++] << 16 | buffer[offset++] << 8 | buffer[offset++]); - m_ExtHighestSeqNumber = - (uint) - (buffer[offset++] << 24 | buffer[offset++] << 16 | buffer[offset++] << 8 | buffer[offset++]); - m_Jitter = - (uint) - (buffer[offset++] << 24 | buffer[offset++] << 16 | buffer[offset++] << 8 | buffer[offset++]); - m_LastSR = - (uint) - (buffer[offset++] << 24 | buffer[offset++] << 16 | buffer[offset++] << 8 | buffer[offset++]); - m_DelaySinceLastSR = - (uint) - (buffer[offset++] << 24 | buffer[offset++] << 16 | buffer[offset++] << 8 | buffer[offset++]); - } - - /// - /// Stores report block to the specified buffer. - /// - /// Buffer where to store data. - /// Offset in buffer. - /// Is raised when buffer is null. - /// Is raised when any of the arguments has invalid value. - public void ToByte(byte[] buffer, ref int offset) - { - /* RFC 3550 6.4.1. - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - report | SSRC_1 (SSRC of first source) | - block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - 1 | fraction lost | cumulative number of packets lost | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | extended highest sequence number received | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | interarrival jitter | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | last SR (LSR) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | delay since last SR (DLSR) | - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - */ - - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (offset < 0) - { - throw new ArgumentException("Argument 'offset' must be >= 0."); - } - if (offset + 24 > buffer.Length) - { - throw new ArgumentException("Argument 'buffer' has not enough room to store report block."); - } - - // SSRC - buffer[offset++] = (byte) ((m_SSRC >> 24) | 0xFF); - buffer[offset++] = (byte) ((m_SSRC >> 16) | 0xFF); - buffer[offset++] = (byte) ((m_SSRC >> 8) | 0xFF); - buffer[offset++] = (byte) ((m_SSRC) | 0xFF); - // fraction lost - buffer[offset++] = (byte) m_FractionLost; - // cumulative packets lost - buffer[offset++] = (byte) ((m_CumulativePacketsLost >> 16) | 0xFF); - buffer[offset++] = (byte) ((m_CumulativePacketsLost >> 8) | 0xFF); - buffer[offset++] = (byte) ((m_CumulativePacketsLost) | 0xFF); - // extended highest sequence number - buffer[offset++] = (byte) ((m_ExtHighestSeqNumber >> 24) | 0xFF); - buffer[offset++] = (byte) ((m_ExtHighestSeqNumber >> 16) | 0xFF); - buffer[offset++] = (byte) ((m_ExtHighestSeqNumber >> 8) | 0xFF); - buffer[offset++] = (byte) ((m_ExtHighestSeqNumber) | 0xFF); - // jitter - buffer[offset++] = (byte) ((m_Jitter >> 24) | 0xFF); - buffer[offset++] = (byte) ((m_Jitter >> 16) | 0xFF); - buffer[offset++] = (byte) ((m_Jitter >> 8) | 0xFF); - buffer[offset++] = (byte) ((m_Jitter) | 0xFF); - // last SR - buffer[offset++] = (byte) ((m_LastSR >> 24) | 0xFF); - buffer[offset++] = (byte) ((m_LastSR >> 16) | 0xFF); - buffer[offset++] = (byte) ((m_LastSR >> 8) | 0xFF); - buffer[offset++] = (byte) ((m_LastSR) | 0xFF); - // delay since last SR - buffer[offset++] = (byte) ((m_DelaySinceLastSR >> 24) | 0xFF); - buffer[offset++] = (byte) ((m_DelaySinceLastSR >> 16) | 0xFF); - buffer[offset++] = (byte) ((m_DelaySinceLastSR >> 8) | 0xFF); - buffer[offset++] = (byte) ((m_DelaySinceLastSR) | 0xFF); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_SDES.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_SDES.cs deleted file mode 100644 index 33c1cd9fc..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_SDES.cs +++ /dev/null @@ -1,197 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - using System.Collections.Generic; - - #endregion - - /// - /// This class represents SDES: Source Description RTCP Packet. - /// - public class RTCP_Packet_SDES : RTCP_Packet - { - #region Members - - private readonly List m_pChunks; - private int m_Version = 2; - - #endregion - - #region Properties - - /// - /// Gets RTCP version. - /// - public override int Version - { - get { return m_Version; } - } - - /// - /// Gets RTCP packet type. - /// - public override int Type - { - get { return RTCP_PacketType.SDES; } - } - - /// - /// Gets session description(SDES) chunks. - /// - public List Chunks - { - get { return m_pChunks; } - } - - /// - /// Gets number of bytes needed for this packet. - /// - public override int Size - { - get - { - int size = 4; - foreach (RTCP_Packet_SDES_Chunk chunk in m_pChunks) - { - size += chunk.Size; - } - - return size; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - internal RTCP_Packet_SDES() - { - m_pChunks = new List(); - } - - #endregion - - #region Methods - - /// - /// Stores SDES packet to the specified buffer. - /// - /// Buffer where to store SDES packet. - /// Offset in buffer. - public override void ToByte(byte[] buffer, ref int offset) - { - /* RFC 3550 6.5 SDES: Source Description RTCP Packet. - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - header |V=2|P| SC | PT=SDES=202 | length | - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - chunk | SSRC/CSRC_1 | - 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | SDES items | - | ... | - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - chunk | SSRC/CSRC_2 | - 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | SDES items | - | ... | - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - */ - - // V=2 P SC - buffer[offset++] = (byte) (2 << 6 | 0 << 5 | m_pChunks.Count & 0x1F); - // PT=SDES=202 - buffer[offset++] = 202; - // length - int lengthOffset = offset; - buffer[offset++] = 0; // We fill it at last, when length is known. - buffer[offset++] = 0; // We fill it at last, when length is known. - - int chunksStartOffset = offset; - - // Add chunks. - foreach (RTCP_Packet_SDES_Chunk chunk in m_pChunks) - { - chunk.ToByte(buffer, ref offset); - } - - // NOTE: Size in 32-bit boundary, header not included. - int length = (offset - chunksStartOffset)/4; - // length - buffer[lengthOffset] = (byte) ((length >> 8) & 0xFF); - buffer[lengthOffset + 1] = (byte) ((length) & 0xFF); - } - - #endregion - - #region Overrides - - /// - /// Parses Source Description(SDES) packet from data buffer. - /// - /// Buffer what contains SDES packet. - /// Offset in buffer. - protected override void ParseInternal(byte[] buffer, ref int offset) - { - /* RFC 3550 6.5 SDES: Source Description RTCP Packet. - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - header |V=2|P| SC | PT=SDES=202 | length | - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - chunk | SSRC/CSRC_1 | - 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | SDES items | - | ... | - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - chunk | SSRC/CSRC_2 | - 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | SDES items | - | ... | - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - */ - - m_Version = buffer[offset] >> 6; - bool isPadded = Convert.ToBoolean((buffer[offset] >> 5) & 0x1); - int sourceCount = buffer[offset++] & 0x1F; - int type = buffer[offset++]; - int length = buffer[offset++] << 8 | buffer[offset++]; - if (isPadded) - { - PaddBytesCount = buffer[offset + length]; - } - - // Read chunks - for (int i = 0; i < sourceCount; i++) - { - RTCP_Packet_SDES_Chunk chunk = new RTCP_Packet_SDES_Chunk(); - chunk.Parse(buffer, ref offset); - m_pChunks.Add(chunk); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_SDES_Chunk.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_SDES_Chunk.cs deleted file mode 100644 index ae8a3089e..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_SDES_Chunk.cs +++ /dev/null @@ -1,453 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// This class implements RTCP SDES packet one "chunk". - /// - public class RTCP_Packet_SDES_Chunk - { - #region Members - - private string m_CName; - private string m_Email; - private string m_Location; - private string m_Name; - private string m_Note; - private string m_Phone; - private uint m_Source; - private string m_Tool; - - #endregion - - #region Properties - - /// - /// Gets SSRC or CSRC identifier. - /// - public uint Source - { - get { return m_Source; } - } - - /// - /// Gets Canonical End-Point Identifier. - /// - public string CName - { - get { return m_CName; } - } - - /// - /// Gets or sets the real name, eg. "John Doe". Value null means not specified. - /// - /// Is raised when invalid value is passed. - public string Name - { - get { return m_Name; } - - set - { - if (Encoding.UTF8.GetByteCount(value) > 255) - { - throw new ArgumentException("Property 'Name' value must be <= 255 bytes."); - } - - m_Name = value; - } - } - - /// - /// Gets or sets email address. For example "John.Doe@example.com". Value null means not specified. - /// - /// Is raised when invalid value is passed. - public string Email - { - get { return m_Email; } - - set - { - if (Encoding.UTF8.GetByteCount(value) > 255) - { - throw new ArgumentException("Property 'Email' value must be <= 255 bytes."); - } - - m_Email = value; - } - } - - /// - /// Gets or sets phone number. For example "+1 908 555 1212". Value null means not specified. - /// - /// Is raised when invalid value is passed. - public string Phone - { - get { return m_Phone; } - - set - { - if (Encoding.UTF8.GetByteCount(value) > 255) - { - throw new ArgumentException("Property 'Phone' value must be <= 255 bytes."); - } - - m_Phone = value; - } - } - - /// - /// Gets or sets location string. It may be geographic address or for example chat room name. - /// Value null means not specified. - /// - /// Is raised when invalid value is passed. - public string Location - { - get { return m_Location; } - - set - { - if (Encoding.UTF8.GetByteCount(value) > 255) - { - throw new ArgumentException("Property 'Location' value must be <= 255 bytes."); - } - - m_Location = value; - } - } - - /// - /// Gets or sets streaming application name/version. - /// Value null means not specified. - /// - /// Is raised when invalid value is passed. - public string Tool - { - get { return m_Tool; } - - set - { - if (Encoding.UTF8.GetByteCount(value) > 255) - { - throw new ArgumentException("Property 'Tool' value must be <= 255 bytes."); - } - - m_Tool = value; - } - } - - /// - /// Gets or sets note text. The NOTE item is intended for transient messages describing the current state - /// of the source, e.g., "on the phone, can't talk". Value null means not specified. - /// - /// Is raised when invalid value is passed. - public string Note - { - get { return m_Note; } - - set - { - if (Encoding.UTF8.GetByteCount(value) > 255) - { - throw new ArgumentException("Property 'Note' value must be <= 255 bytes."); - } - - m_Note = value; - } - } - - /// - /// Gets number of bytes needed for this SDES chunk. - /// - public int Size - { - get - { - int size = 4; - if (!string.IsNullOrEmpty(m_CName)) - { - size += 2; - size += Encoding.UTF8.GetByteCount(m_CName); - } - if (!string.IsNullOrEmpty(m_Name)) - { - size += 2; - size += Encoding.UTF8.GetByteCount(m_Name); - } - if (!string.IsNullOrEmpty(m_Email)) - { - size += 2; - size += Encoding.UTF8.GetByteCount(m_Email); - } - if (!string.IsNullOrEmpty(m_Phone)) - { - size += 2; - size += Encoding.UTF8.GetByteCount(m_Phone); - } - if (!string.IsNullOrEmpty(m_Location)) - { - size += 2; - size += Encoding.UTF8.GetByteCount(m_Location); - } - if (!string.IsNullOrEmpty(m_Tool)) - { - size += 2; - size += Encoding.UTF8.GetByteCount(m_Tool); - } - if (!string.IsNullOrEmpty(m_Note)) - { - size += 2; - size += Encoding.UTF8.GetByteCount(m_Note); - } - - // Add terminate byte and padding bytes. - size++; - size += size%4; - - return size; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// SSRC or CSRC identifier. - /// Canonical End-Point Identifier. - /// Is raised when invalid value is passed. - public RTCP_Packet_SDES_Chunk(uint source, string cname) - { - if (source == 0) - { - throw new ArgumentException("Argument 'source' value must be > 0."); - } - if (string.IsNullOrEmpty(cname)) - { - throw new ArgumentException("Argument 'cname' value may not be null or empty."); - } - - m_Source = source; - m_CName = cname; - } - - /// - /// Parser constructor. - /// - internal RTCP_Packet_SDES_Chunk() {} - - #endregion - - #region Methods - - /// - /// Parses SDES chunk from the specified buffer. - /// - /// Buffer which contains SDES chunk. - /// Offset in buffer. - /// Is raised when buffer is null. - /// Is raised when any of the arguments has invalid value. - public void Parse(byte[] buffer, ref int offset) - { - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (offset < 0) - { - throw new ArgumentException("Argument 'offset' value must be >= 0."); - } - - /* RFC 3550 6.5. - The list of items in each chunk - MUST be terminated by one or more null octets, the first of which is - interpreted as an item type of zero to denote the end of the list. - No length octet follows the null item type octet, but additional null - octets MUST be included if needed to pad until the next 32-bit - boundary. Note that this padding is separate from that indicated by - the P bit in the RTCP header. A chunk with zero items (four null - octets) is valid but useless. - */ - - int startOffset = offset; - - // Read SSRC/CSRC - m_Source = - (uint) - (buffer[offset++] << 24 | buffer[offset++] << 16 | buffer[offset++] << 8 | buffer[offset++]); - - // Read SDES items while reach end of buffer or we get chunk terminator(\0). - while (offset < buffer.Length && buffer[offset] != 0) - { - int type = buffer[offset++]; - int length = buffer[offset++]; - - // CNAME - if (type == 1) - { - m_CName = Encoding.UTF8.GetString(buffer, offset, length); - } - // NAME - else if (type == 2) - { - m_Name = Encoding.UTF8.GetString(buffer, offset, length); - } - // EMAIL - else if (type == 3) - { - m_Email = Encoding.UTF8.GetString(buffer, offset, length); - } - // PHONE - else if (type == 4) - { - m_Phone = Encoding.UTF8.GetString(buffer, offset, length); - } - // LOC - else if (type == 5) - { - m_Location = Encoding.UTF8.GetString(buffer, offset, length); - } - // TOOL - else if (type == 6) - { - m_Tool = Encoding.UTF8.GetString(buffer, offset, length); - } - // NOTE - else if (type == 7) - { - m_Note = Encoding.UTF8.GetString(buffer, offset, length); - } - // PRIV - else if (type == 8) - { - // TODO: - } - offset += length; - } - - // Terminator 0. - offset++; - - // Pad to 32-bit boundary, if it isn't. See not above. - offset += (offset - startOffset)%4; - } - - /// - /// Stores SDES junk to the specified buffer. - /// - /// Buffer where to store data. - /// Offset in buffer. - /// Is raised when buffer is null. - /// Is raised when any of the arguments has invalid value. - public void ToByte(byte[] buffer, ref int offset) - { - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (offset < 0) - { - throw new ArgumentException("Argument 'offset' value must be >= 0."); - } - - int startOffset = offset; - - // SSRC/SDES - buffer[offset++] = (byte) ((m_Source >> 24) & 0xFF); - buffer[offset++] = (byte) ((m_Source >> 16) & 0xFF); - buffer[offset++] = (byte) ((m_Source >> 8) & 0xFF); - buffer[offset++] = (byte) ((m_Source) & 0xFF); - - //--- SDES items ----------------------------------- - if (!string.IsNullOrEmpty(m_CName)) - { - byte[] b = Encoding.UTF8.GetBytes(m_CName); - buffer[offset++] = 1; - buffer[offset++] = (byte) b.Length; - Array.Copy(b, 0, buffer, offset, b.Length); - offset += b.Length; - } - if (!string.IsNullOrEmpty(m_Name)) - { - byte[] b = Encoding.UTF8.GetBytes(m_Name); - buffer[offset++] = 2; - buffer[offset++] = (byte) b.Length; - Array.Copy(b, 0, buffer, offset, b.Length); - offset += b.Length; - } - if (!string.IsNullOrEmpty(m_Email)) - { - byte[] b = Encoding.UTF8.GetBytes(m_Email); - buffer[offset++] = 3; - buffer[offset++] = (byte) b.Length; - Array.Copy(b, 0, buffer, offset, b.Length); - offset += b.Length; - } - if (!string.IsNullOrEmpty(m_Phone)) - { - byte[] b = Encoding.UTF8.GetBytes(m_Phone); - buffer[offset++] = 4; - buffer[offset++] = (byte) b.Length; - Array.Copy(b, 0, buffer, offset, b.Length); - offset += b.Length; - } - if (!string.IsNullOrEmpty(m_Location)) - { - byte[] b = Encoding.UTF8.GetBytes(m_Location); - buffer[offset++] = 5; - buffer[offset++] = (byte) b.Length; - Array.Copy(b, 0, buffer, offset, b.Length); - offset += b.Length; - } - if (!string.IsNullOrEmpty(m_Tool)) - { - byte[] b = Encoding.UTF8.GetBytes(m_Tool); - buffer[offset++] = 6; - buffer[offset++] = (byte) b.Length; - Array.Copy(b, 0, buffer, offset, b.Length); - offset += b.Length; - } - if (!string.IsNullOrEmpty(m_Note)) - { - byte[] b = Encoding.UTF8.GetBytes(m_Note); - buffer[offset++] = 7; - buffer[offset++] = (byte) b.Length; - Array.Copy(b, 0, buffer, offset, b.Length); - offset += b.Length; - } - // Terminate chunk - buffer[offset++] = 0; - - // Pad to 4(32-bit) bytes boundary. - while ((offset - startOffset)%4 > 0) - { - buffer[offset++] = 0; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_SR.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_SR.cs deleted file mode 100644 index ee0996fbe..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_SR.cs +++ /dev/null @@ -1,354 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - using System.Collections.Generic; - - #endregion - - /// - /// This class represents SR: Sender Report RTCP Packet. - /// - public class RTCP_Packet_SR : RTCP_Packet - { - #region Members - - private readonly List m_pReportBlocks; - private ulong m_NtpTimestamp; - private uint m_RtpTimestamp; - private uint m_SenderOctetCount; - private uint m_SenderPacketCount; - private uint m_SSRC; - private int m_Version = 2; - - #endregion - - #region Properties - - /// - /// Gets RTCP version. - /// - public override int Version - { - get { return m_Version; } - } - - /// - /// Gets RTCP packet type. - /// - public override int Type - { - get { return RTCP_PacketType.SR; } - } - - /// - /// Gets sender synchronization source identifier. - /// - public uint SSRC - { - get { return m_SSRC; } - } - - /// - /// Gets or sets the wallclock time (see Section 4) when this report was sent. - /// - public ulong NtpTimestamp - { - get { return m_NtpTimestamp; } - - set { m_NtpTimestamp = value; } - } - - /// - /// Gets RTP timestamp. - /// - public uint RtpTimestamp - { - get { return m_RtpTimestamp; } - - set { m_RtpTimestamp = value; } - } - - /// - /// Gets how many packets sender has sent. - /// - public uint SenderPacketCount - { - get { return m_SenderPacketCount; } - - set { m_SenderPacketCount = value; } - } - - /// - /// Gets how many bytes sender has sent. - /// - public uint SenderOctetCount - { - get { return m_SenderOctetCount; } - - set { m_SenderOctetCount = value; } - } - - /// - /// Gets reports blocks. - /// - public List ReportBlocks - { - get { return m_pReportBlocks; } - } - - /// - /// Gets number of bytes needed for this packet. - /// - public override int Size - { - get { return 28 + (24*m_pReportBlocks.Count); } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Source(sender) ID. - internal RTCP_Packet_SR(uint ssrc) - { - m_SSRC = ssrc; - - m_pReportBlocks = new List(); - } - - /// - /// Default constructor. - /// - internal RTCP_Packet_SR() - { - m_pReportBlocks = new List(); - } - - #endregion - - #region Methods - - /// - /// Stores sender report(SR) packet to the specified buffer. - /// - /// Buffer where to store SR packet. - /// Offset in buffer. - /// Is raised when buffer is null. - /// Is raised when any of the arguments has invalid value. - public override void ToByte(byte[] buffer, ref int offset) - { - /* RFC 3550 6.4.1 SR: Sender Report RTCP Packet. - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - header |V=2|P| RC | PT=SR=200 | length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | SSRC of sender | - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - sender | NTP timestamp, most significant word | - info +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | NTP timestamp, least significant word | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | RTP timestamp | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | sender's packet count | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | sender's octet count | - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - report | SSRC_1 (SSRC of first source) | - block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - 1 | fraction lost | cumulative number of packets lost | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | extended highest sequence number received | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | interarrival jitter | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | last SR (LSR) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | delay since last SR (DLSR) | - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - report | SSRC_2 (SSRC of second source) | - block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - 2 : ... : - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - | profile-specific extensions | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (offset < 0) - { - throw new ArgumentException("Argument 'offset' value must be >= 0."); - } - - // NOTE: Size in 32-bit boundary, header not included. - int length = (24 + (m_pReportBlocks.Count*24))/4; - - // V P RC - buffer[offset++] = (byte) (2 << 6 | 0 << 5 | (m_pReportBlocks.Count & 0x1F)); - // PT=SR=200 - buffer[offset++] = 200; - // length - buffer[offset++] = (byte) ((length >> 8) & 0xFF); - buffer[offset++] = (byte) ((length) & 0xFF); - // SSRC - buffer[offset++] = (byte) ((m_SSRC >> 24) & 0xFF); - buffer[offset++] = (byte) ((m_SSRC >> 16) & 0xFF); - buffer[offset++] = (byte) ((m_SSRC >> 8) & 0xFF); - buffer[offset++] = (byte) ((m_SSRC) & 0xFF); - // NTP timestamp - buffer[offset++] = (byte) ((m_NtpTimestamp >> 56) & 0xFF); - buffer[offset++] = (byte) ((m_NtpTimestamp >> 48) & 0xFF); - buffer[offset++] = (byte) ((m_NtpTimestamp >> 40) & 0xFF); - buffer[offset++] = (byte) ((m_NtpTimestamp >> 32) & 0xFF); - buffer[offset++] = (byte) ((m_NtpTimestamp >> 24) & 0xFF); - buffer[offset++] = (byte) ((m_NtpTimestamp >> 16) & 0xFF); - buffer[offset++] = (byte) ((m_NtpTimestamp >> 8) & 0xFF); - buffer[offset++] = (byte) ((m_NtpTimestamp) & 0xFF); - // RTP timestamp - buffer[offset++] = (byte) ((m_RtpTimestamp >> 24) & 0xFF); - buffer[offset++] = (byte) ((m_RtpTimestamp >> 16) & 0xFF); - buffer[offset++] = (byte) ((m_RtpTimestamp >> 8) & 0xFF); - buffer[offset++] = (byte) ((m_RtpTimestamp) & 0xFF); - // sender's packet count - buffer[offset++] = (byte) ((m_SenderPacketCount >> 24) & 0xFF); - buffer[offset++] = (byte) ((m_SenderPacketCount >> 16) & 0xFF); - buffer[offset++] = (byte) ((m_SenderPacketCount >> 8) & 0xFF); - buffer[offset++] = (byte) ((m_SenderPacketCount) & 0xFF); - // sender's octet count - buffer[offset++] = (byte) ((m_SenderOctetCount >> 24) & 0xFF); - buffer[offset++] = (byte) ((m_SenderOctetCount >> 16) & 0xFF); - buffer[offset++] = (byte) ((m_SenderOctetCount >> 8) & 0xFF); - buffer[offset++] = (byte) ((m_SenderOctetCount) & 0xFF); - // Report blocks - foreach (RTCP_Packet_ReportBlock block in m_pReportBlocks) - { - block.ToByte(buffer, ref offset); - } - } - - #endregion - - #region Overrides - - /// - /// Parses RTCP sender report(SR) from specified data buffer. - /// - /// Buffer which contains sender report. - /// Offset in buffer. - /// Is raised when buffer is null. - /// Is raised when any of the arguments has invalid value. - protected override void ParseInternal(byte[] buffer, ref int offset) - { - /* RFC 3550 6.4.1 SR: Sender Report RTCP Packet. - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - header |V=2|P| RC | PT=SR=200 | length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | SSRC of sender | - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - sender | NTP timestamp, most significant word | - info +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | NTP timestamp, least significant word | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | RTP timestamp | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | sender's packet count | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | sender's octet count | - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - report | SSRC_1 (SSRC of first source) | - block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - 1 | fraction lost | cumulative number of packets lost | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | extended highest sequence number received | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | interarrival jitter | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | last SR (LSR) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | delay since last SR (DLSR) | - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - report | SSRC_2 (SSRC of second source) | - block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - 2 : ... : - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - | profile-specific extensions | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (offset < 0) - { - throw new ArgumentException("Argument 'offset' value must be >= 0."); - } - - m_Version = buffer[offset] >> 6; - bool isPadded = Convert.ToBoolean((buffer[offset] >> 5) & 0x1); - int reportBlockCount = buffer[offset++] & 0x1F; - int type = buffer[offset++]; - int length = buffer[offset++] << 8 | buffer[offset++]; - if (isPadded) - { - PaddBytesCount = buffer[offset + length]; - } - - m_SSRC = - (uint) - (buffer[offset++] << 24 | buffer[offset++] << 16 | buffer[offset++] << 8 | buffer[offset++]); - m_NtpTimestamp = - (ulong) - (buffer[offset++] << 56 | buffer[offset++] << 48 | buffer[offset++] << 40 | - buffer[offset++] << 32 | buffer[offset++] << 24 | buffer[offset++] << 16 | - buffer[offset++] << 8 | buffer[offset++]); - m_RtpTimestamp = - (uint) - (buffer[offset++] << 24 | buffer[offset++] << 16 | buffer[offset++] << 8 | buffer[offset++]); - m_SenderPacketCount = - (uint) - (buffer[offset++] << 24 | buffer[offset++] << 16 | buffer[offset++] << 8 | buffer[offset++]); - m_SenderOctetCount = - (uint) - (buffer[offset++] << 24 | buffer[offset++] << 16 | buffer[offset++] << 8 | buffer[offset++]); - - for (int i = 0; i < reportBlockCount; i++) - { - RTCP_Packet_ReportBlock reportBlock = new RTCP_Packet_ReportBlock(); - reportBlock.Parse(buffer, offset); - m_pReportBlocks.Add(reportBlock); - offset += 24; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_Unknown.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_Unknown.cs deleted file mode 100644 index d83e68509..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Packet_Unknown.cs +++ /dev/null @@ -1,34 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - /// - /// This class represents unknown RTCP packet. - /// - public class RTCP_Packet_Unknown - { - // private int m_Type = 0: - // private byte[] m_pPacket = null; - - // TODO: - - #region Properties Implementation - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Report_Receiver.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Report_Receiver.cs deleted file mode 100644 index 7f722f8ab..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Report_Receiver.cs +++ /dev/null @@ -1,122 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - - #endregion - - /// - /// This class holds receiver report info. - /// - public class RTCP_Report_Receiver - { - #region Members - - private readonly uint m_CumulativePacketsLost; - private readonly uint m_DelaySinceLastSR; - private readonly uint m_ExtHigestSeqNumber; - private readonly uint m_FractionLost; - private readonly uint m_Jitter; - private readonly uint m_LastSR; - - #endregion - - #region Properties - - /// - /// Gets the fraction of RTP data packets from source SSRC lost since the previous SR or - /// RR packet was sent. - /// - public uint FractionLost - { - get { return m_FractionLost; } - } - - /// - /// Gets total number of RTP data packets from source SSRC that have - /// been lost since the beginning of reception. - /// - public uint CumulativePacketsLost - { - get { return m_CumulativePacketsLost; } - } - - /// - /// Gets extended highest sequence number received. - /// - public uint ExtendedSequenceNumber - { - get { return m_ExtHigestSeqNumber; } - } - - /// - /// Gets an estimate of the statistical variance of the RTP data packet - /// interarrival time, measured in timestamp units and expressed as an - /// unsigned integer. - /// - public uint Jitter - { - get { return m_Jitter; } - } - - /// - /// Gets when last sender report(SR) was recieved. - /// - public uint LastSR - { - get { return m_LastSR; } - } - - /// - /// Gets delay since last sender report(SR) was received. - /// - public uint DelaySinceLastSR - { - get { return m_DelaySinceLastSR; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// RTCP RR report. - /// Is raised when rr is null reference. - internal RTCP_Report_Receiver(RTCP_Packet_ReportBlock rr) - { - if (rr == null) - { - throw new ArgumentNullException("rr"); - } - - m_FractionLost = rr.FractionLost; - m_CumulativePacketsLost = rr.CumulativePacketsLost; - m_ExtHigestSeqNumber = rr.ExtendedHighestSeqNo; - m_Jitter = rr.Jitter; - m_LastSR = rr.LastSR; - m_DelaySinceLastSR = rr.DelaySinceLastSR; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Report_Sender.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Report_Sender.cs deleted file mode 100644 index d30175266..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTCP_Report_Sender.cs +++ /dev/null @@ -1,98 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - - #endregion - - /// - /// This class holds sender report info. - /// - public class RTCP_Report_Sender - { - #region Members - - private readonly ulong m_NtpTimestamp; - private readonly uint m_RtpTimestamp; - private readonly uint m_SenderOctetCount; - private readonly uint m_SenderPacketCount; - - #endregion - - #region Properties - - /// - /// Gets the wallclock time (see Section 4) when this report was sent. - /// - public ulong NtpTimestamp - { - get { return m_NtpTimestamp; } - } - - /// - /// Gets RTP timestamp. - /// - public uint RtpTimestamp - { - get { return m_RtpTimestamp; } - } - - /// - /// Gets how many packets sender has sent. - /// - public uint SenderPacketCount - { - get { return m_SenderPacketCount; } - } - - /// - /// Gets how many bytes sender has sent. - /// - public uint SenderOctetCount - { - get { return m_SenderOctetCount; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// RTCP SR report. - /// Is raised when sr is null reference. - internal RTCP_Report_Sender(RTCP_Packet_SR sr) - { - if (sr == null) - { - throw new ArgumentNullException("sr"); - } - - m_NtpTimestamp = sr.NtpTimestamp; - m_RtpTimestamp = sr.RtpTimestamp; - m_SenderPacketCount = sr.SenderPacketCount; - m_SenderOctetCount = sr.SenderOctetCount; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Address.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Address.cs deleted file mode 100644 index 470f3d5c2..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Address.cs +++ /dev/null @@ -1,240 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - using System.Net; - - #endregion - - /// - /// This class implements RTP session address. - /// - public class RTP_Address - { - #region Members - - private readonly int m_ControlPort; - private readonly int m_DataPort; - private readonly IPAddress m_pIP; - private readonly IPEndPoint m_pRtcpEP; - private readonly IPEndPoint m_pRtpEP; - private readonly int m_TTL; - - #endregion - - #region Properties - - /// - /// Gets if this is multicast RTP address. - /// - public bool IsMulticast - { - get { return Net_Utils.IsMulticastAddress(m_pIP); } - } - - /// - /// Gets IP address. - /// - public IPAddress IP - { - get { return m_pIP; } - } - - /// - /// Gets RTP data port. - /// - public int DataPort - { - get { return m_DataPort; } - } - - /// - /// Gets RTCP control port. - /// - public int ControlPort - { - get { return m_ControlPort; } - } - - /// - /// Gets mulicast TTL(time to live) value. - /// - public int TTL - { - get { return m_TTL; } - } - - /// - /// Gets RTP end point. - /// - public IPEndPoint RtpEP - { - get { return m_pRtpEP; } - } - - /// - /// Gets RTPCP end point. - /// - public IPEndPoint RtcpEP - { - get { return m_pRtcpEP; } - } - - #endregion - - #region Constructor - - /// - /// Unicast constructor. - /// - /// Unicast IP address. - /// RTP data port. - /// RTP control port. Usualy this is dataPort + 1. - /// Is raised when ip is null reference. - /// Is raised when any of the arguments has invalid values. - public RTP_Address(IPAddress ip, int dataPort, int controlPort) - { - if (ip == null) - { - throw new ArgumentNullException("ip"); - } - if (dataPort < IPEndPoint.MinPort || dataPort > IPEndPoint.MaxPort) - { - throw new ArgumentException("Argument 'dataPort' value must be between '" + IPEndPoint.MinPort + - "' and '" + IPEndPoint.MaxPort + "'."); - } - if (controlPort < IPEndPoint.MinPort || controlPort > IPEndPoint.MaxPort) - { - throw new ArgumentException("Argument 'controlPort' value must be between '" + - IPEndPoint.MinPort + "' and '" + IPEndPoint.MaxPort + "'."); - } - if (dataPort == controlPort) - { - throw new ArgumentException("Arguments 'dataPort' and 'controlPort' values must be different."); - } - - m_pIP = ip; - m_DataPort = dataPort; - m_ControlPort = controlPort; - - m_pRtpEP = new IPEndPoint(ip, dataPort); - m_pRtcpEP = new IPEndPoint(ip, controlPort); - } - - /// - /// Multicast constructor. - /// - /// Multicast IP address. - /// RTP data port. - /// RTP control port. Usualy this is dataPort + 1. - /// RTP control port. Usualy this is dataPort + 1. - /// Is raised when ip is null reference. - /// Is raised when any of the arguments has invalid values. - public RTP_Address(IPAddress ip, int dataPort, int controlPort, int ttl) - { - if (ip == null) - { - throw new ArgumentNullException("ip"); - } - if (!Net_Utils.IsMulticastAddress(ip)) - { - throw new ArgumentException("Argument 'ip' is not multicast ip address."); - } - if (dataPort < IPEndPoint.MinPort || dataPort > IPEndPoint.MaxPort) - { - throw new ArgumentException("Argument 'dataPort' value must be between '" + IPEndPoint.MinPort + - "' and '" + IPEndPoint.MaxPort + "'."); - } - if (controlPort < IPEndPoint.MinPort || controlPort > IPEndPoint.MaxPort) - { - throw new ArgumentException("Argument 'controlPort' value must be between '" + - IPEndPoint.MinPort + "' and '" + IPEndPoint.MaxPort + "'."); - } - if (dataPort == controlPort) - { - throw new ArgumentException("Arguments 'dataPort' and 'controlPort' values must be different."); - } - if (ttl < 0 || ttl > 255) - { - throw new ArgumentException("Argument 'ttl' value must be between '0' and '255'."); - } - - m_pIP = ip; - m_DataPort = dataPort; - m_ControlPort = controlPort; - m_TTL = ttl; - - m_pRtpEP = new IPEndPoint(ip, dataPort); - m_pRtcpEP = new IPEndPoint(ip, controlPort); - } - - #endregion - - #region Methods - - /// - /// Determines whether the specified Object is equal to the current Object. - /// - /// The Object to compare with the current Object. - /// True if the specified Object is equal to the current Object; otherwise, false. - public override bool Equals(object obj) - { - if (obj == null) - { - return false; - } - - if (obj is RTP_Address) - { - RTP_Address a = (RTP_Address) obj; - if (!a.IP.Equals(IP)) - { - return false; - } - if (a.DataPort != DataPort) - { - return false; - } - if (a.ControlPort != ControlPort) - { - return false; - } - } - else - { - return false; - } - - return true; - } - - /// - /// Gets this hash code. - /// - /// - public override int GetHashCode() - { - return base.GetHashCode(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Clock.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Clock.cs deleted file mode 100644 index e73a3230e..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Clock.cs +++ /dev/null @@ -1,98 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - - #endregion - - /// - /// Implements RTP media clock. - /// - public class RTP_Clock - { - #region Members - - private readonly int m_BaseValue; - private readonly DateTime m_CreateTime; - private readonly int m_Rate = 1; - - #endregion - - #region Properties - - /// - /// Gets clock base value from where clock started. - /// - public int BaseValue - { - get { return m_BaseValue; } - } - - /// - /// Gets current clock rate in Hz. - /// - public int Rate - { - get { return m_Rate; } - } - - /// - /// Gets current RTP timestamp. - /// - public uint RtpTimestamp - { - get - { - /* - m_Rate -> 1000ms - elapsed -> x - */ - - long elapsed = (long) ((DateTime.Now - m_CreateTime)).TotalMilliseconds; - - return (uint) (m_BaseValue + ((m_Rate*elapsed)/1000)); - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Clock base value from where clock starts. - /// Clock rate in Hz. - public RTP_Clock(int baseValue, int rate) - { - if (rate < 1) - { - throw new ArgumentException("Argument 'rate' value must be between 1 and 100 000.", "rate"); - } - - m_BaseValue = baseValue; - m_Rate = rate; - m_CreateTime = DateTime.Now; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_MultimediaSession.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_MultimediaSession.cs deleted file mode 100644 index cda60540f..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_MultimediaSession.cs +++ /dev/null @@ -1,344 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - using System.Collections.Generic; - - #endregion - - /// - /// This class represents RTP single-media and multimedia session. - /// - public class RTP_MultimediaSession : IDisposable - { - #region Events - - /// - /// Is raised when unknown error has happened. - /// - public event EventHandler Error = null; - - /// - /// Is raised when new remote participant has joined to session. - /// - public event EventHandler NewParticipant = null; - - /// - /// Is raised when new session has created. - /// - public event EventHandler> SessionCreated = null; - - #endregion - - #region Members - - private bool m_IsDisposed; - private RTP_Participant_Local m_pLocalParticipant; - private Dictionary m_pParticipants; - private List m_pSessions; - - #endregion - - #region Properties - - /// - /// Gets if this object is disposed. - /// - public bool IsDisposed - { - get { return m_IsDisposed; } - } - - /// - /// Gets media sessions. - /// - /// Is raised when this class is Disposed and this property is accessed. - public RTP_Session[] Sessions - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pSessions.ToArray(); - } - } - - /// - /// Gets local participant. - /// - /// Is raised when this class is Disposed and this property is accessed. - public RTP_Participant_Local LocalParticipant - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pLocalParticipant; - } - } - - /// - /// Gets session remote participants. - /// - /// Is raised when this class is Disposed and this property is accessed. - public RTP_Participant_Remote[] RemoteParticipants - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - lock (m_pParticipants) - { - RTP_Participant_Remote[] retVal = new RTP_Participant_Remote[m_pParticipants.Count]; - m_pParticipants.Values.CopyTo(retVal, 0); - - return retVal; - } - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Canonical name of participant. RTP_Utils.GenerateCNAME - /// can be used to create this value. - /// Is raised when cname is null reference. - /// Is raised when any of the arguments has invalid value. - public RTP_MultimediaSession(string cname) - { - if (cname == null) - { - throw new ArgumentNullException("cname"); - } - if (cname == string.Empty) - { - throw new ArgumentException("Argument 'cname' value must be specified."); - } - - m_pLocalParticipant = new RTP_Participant_Local(cname); - m_pSessions = new List(); - m_pParticipants = new Dictionary(); - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public void Dispose() - { - if (m_IsDisposed) - { - return; - } - foreach (RTP_Session session in m_pSessions.ToArray()) - { - session.Dispose(); - } - m_IsDisposed = true; - - m_pLocalParticipant = null; - m_pSessions = null; - m_pParticipants = null; - - NewParticipant = null; - Error = null; - } - - /// - /// Closes RTP multimedia session, sends BYE with optional reason text to remote targets. - /// - /// Close reason. Value null means not specified. - /// Is raised when this class is Disposed and this method is accessed. - public void Close(string closeReason) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - foreach (RTP_Session session in m_pSessions.ToArray()) - { - session.Close(closeReason); - } - - Dispose(); - } - - /// - /// Starts session. - /// - /// Is raised when this class is Disposed and this method is accessed. - public void Start() - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - // TODO: - } - - /// - /// Stops session. - /// - /// Is raised when this class is Disposed and this method is accessed. - public void Stop() - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - // TODO: - } - - /// - /// Creates new RTP session. - /// - /// Local RTP end point. - /// RTP media clock. - /// Returns created session. - /// Is raised when this class is Disposed and this method is accessed. - /// Is raised when localEP or clock is null reference. - public RTP_Session CreateSession(RTP_Address localEP, RTP_Clock clock) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (localEP == null) - { - throw new ArgumentNullException("localEP"); - } - if (clock == null) - { - throw new ArgumentNullException("clock"); - } - - RTP_Session session = new RTP_Session(this, localEP, clock); - session.Disposed += delegate(object s, EventArgs e) { m_pSessions.Remove((RTP_Session) s); }; - m_pSessions.Add(session); - - OnSessionCreated(session); - - return session; - } - - #endregion - - #region Utility methods - - /// - /// Raises SessionCreated event. - /// - /// RTP session. - private void OnSessionCreated(RTP_Session session) - { - if (session == null) - { - throw new ArgumentNullException("session"); - } - - if (SessionCreated != null) - { - SessionCreated(this, new EventArgs(session)); - } - } - - /// - /// Raises NewParticipant event. - /// - /// New participant. - private void OnNewParticipant(RTP_Participant_Remote participant) - { - if (NewParticipant != null) - { - NewParticipant(this, new RTP_ParticipantEventArgs(participant)); - } - } - - #endregion - - #region Internal methods - - /// - /// Gets or creates new participant if participant does not exist. - /// - /// Participant canonical name. - /// Returns specified participant. - internal RTP_Participant_Remote GetOrCreateParticipant(string cname) - { - if (cname == null) - { - throw new ArgumentNullException("cname"); - } - if (cname == string.Empty) - { - throw new ArgumentException("Argument 'cname' value must be specified."); - } - - lock (m_pParticipants) - { - RTP_Participant_Remote participant = null; - if (!m_pParticipants.TryGetValue(cname, out participant)) - { - participant = new RTP_Participant_Remote(cname); - participant.Removed += delegate { m_pParticipants.Remove(participant.CNAME); }; - m_pParticipants.Add(cname, participant); - - OnNewParticipant(participant); - } - - return participant; - } - } - - /// - /// Raises Error event. - /// - /// Exception. - internal void OnError(Exception exception) - { - if (Error != null) - { - Error(this, new ExceptionEventArgs(exception)); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Packet.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Packet.cs deleted file mode 100644 index ad02c88e1..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Packet.cs +++ /dev/null @@ -1,404 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// A data packet consisting of the fixed RTP header, a possibly empty list of contributing - /// sources (see below), and the payload data. Some underlying protocols may require an - /// encapsulation of the RTP packet to be defined. Typically one packet of the underlying - /// protocol contains a single RTP packet, but several RTP packets MAY be contained if - /// permitted by the encapsulation method (see Section 11). - /// - public class RTP_Packet - { - #region Members - - private uint[] m_CSRC; - private byte[] m_Data; - private bool m_IsMarker; - private int m_PayloadType; - private ushort m_SequenceNumber; - private uint m_SSRC; - private uint m_Timestamp; - private int m_Version = 2; - - #endregion - - #region Properties - - /// - /// Gets RTP version. - /// - public int Version - { - get { return m_Version; } - } - - /// - /// Gets if packet is padded to some bytes boundary. - /// - public bool IsPadded - { - get { return false; } - } - - /// - /// Gets marker bit. The usage of this bit depends on payload type. - /// - public bool IsMarker - { - get { return m_IsMarker; } - - set { m_IsMarker = value; } - } - - /// - /// Gets payload type. - /// - /// Is raised when invalid value is passed. - public int PayloadType - { - get { return m_PayloadType; } - - set - { - if (value < 0 || value > 128) - { - throw new ArgumentException("Payload value must be >= 0 and <= 128."); - } - - m_PayloadType = value; - } - } - - /// - /// Gets or sets RTP packet sequence number. - /// - /// Is raised when invalid value is passed. - public ushort SeqNo - { - get { return m_SequenceNumber; } - - set { m_SequenceNumber = value; } - } - - /// - /// Gets sets packet timestamp. - /// - /// Is raised when invalid value is passed. - public uint Timestamp - { - get { return m_Timestamp; } - - set - { - if (value < 1) - { - throw new ArgumentException("Timestamp value must be >= 1."); - } - - m_Timestamp = value; - } - } - - /// - /// Gets or sets synchronization source ID. - /// - /// Is raised when invalid value is passed. - public uint SSRC - { - get { return m_SSRC; } - - set - { - if (value < 1) - { - throw new ArgumentException("SSRC value must be >= 1."); - } - - m_SSRC = value; - } - } - - /// - /// Gets or sets the contributing sources for the payload contained in this packet. - /// Value null means none. - /// - public uint[] CSRC - { - get { return m_CSRC; } - - set { m_CSRC = value; } - } - - /// - /// Gets SSRC + CSRCs as joined array. - /// - public uint[] Sources - { - get - { - uint[] retVal = new uint[1]; - if (m_CSRC != null) - { - retVal = new uint[1 + m_CSRC.Length]; - } - retVal[0] = m_SSRC; - Array.Copy(m_CSRC, retVal, m_CSRC.Length); - - return retVal; - } - } - - /// - /// Gets or sets RTP data. Data must be encoded with PayloadType encoding. - /// - /// Is raised when null value is passed. - public byte[] Data - { - get { return m_Data; } - - set - { - if (value == null) - { - throw new ArgumentNullException("Data"); - } - - m_Data = value; - } - } - - #endregion - - #region Methods - - /// - /// Parses RTP packet. - /// - /// Buffer containing RTP packet. - /// Number of bytes used in buffer. - /// Returns parsed RTP packet. - public static RTP_Packet Parse(byte[] buffer, int size) - { - RTP_Packet packet = new RTP_Packet(); - packet.ParseInternal(buffer, size); - - return packet; - } - - /// - /// Validates RTP packet. - /// - public void Validate() - { - // TODO: Validate RTP apcket - } - - /// - /// Stores this packet to the specified buffer. - /// - /// Buffer where to store packet. - /// Offset in buffer. - public void ToByte(byte[] buffer, ref int offset) - { - /* RFC 3550.5.1 RTP Fixed Header Fields. - - The RTP header has the following format: - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |V=2|P|X| CC |M| PT | sequence number | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | timestamp | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | synchronization source (SSRC) identifier | - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - | contributing source (CSRC) identifiers | - | .... | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - - 5.3. Available if X bit filled. - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | defined by profile | length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | header extension | - | .... | - - */ - - int cc = 0; - if (m_CSRC != null) - { - cc = m_CSRC.Length; - } - - // V P X CC - buffer[offset++] = (byte) (m_Version << 6 | 0 << 5 | cc & 0xF); - // M PT - buffer[offset++] = (byte) (Convert.ToInt32(m_IsMarker) << 7 | m_PayloadType & 0x7F); - // sequence number - buffer[offset++] = (byte) (m_SequenceNumber >> 8); - buffer[offset++] = (byte) (m_SequenceNumber & 0xFF); - // timestamp - buffer[offset++] = (byte) ((m_Timestamp >> 24) & 0xFF); - buffer[offset++] = (byte) ((m_Timestamp >> 16) & 0xFF); - buffer[offset++] = (byte) ((m_Timestamp >> 8) & 0xFF); - buffer[offset++] = (byte) (m_Timestamp & 0xFF); - // SSRC - buffer[offset++] = (byte) ((m_SSRC >> 24) & 0xFF); - buffer[offset++] = (byte) ((m_SSRC >> 16) & 0xFF); - buffer[offset++] = (byte) ((m_SSRC >> 8) & 0xFF); - buffer[offset++] = (byte) (m_SSRC & 0xFF); - // CSRCs - if (m_CSRC != null) - { - foreach (int csrc in m_CSRC) - { - buffer[offset++] = (byte) ((csrc >> 24) & 0xFF); - buffer[offset++] = (byte) ((csrc >> 16) & 0xFF); - buffer[offset++] = (byte) ((csrc >> 8) & 0xFF); - buffer[offset++] = (byte) (csrc & 0xFF); - } - } - // X - Array.Copy(m_Data, 0, buffer, offset, m_Data.Length); - offset += m_Data.Length; - } - - /// - /// Returns this packet info as string. - /// - /// Returns packet info. - public override string ToString() - { - StringBuilder retVal = new StringBuilder(); - retVal.Append("----- RTP Packet\r\n"); - retVal.Append("Version: " + m_Version + "\r\n"); - retVal.Append("IsMaker: " + m_IsMarker + "\r\n"); - retVal.Append("PayloadType: " + m_PayloadType + "\r\n"); - retVal.Append("SeqNo: " + m_SequenceNumber + "\r\n"); - retVal.Append("Timestamp: " + m_Timestamp + "\r\n"); - retVal.Append("SSRC: " + m_SSRC + "\r\n"); - retVal.Append("Data: " + m_Data.Length + " bytes.\r\n"); - - return retVal.ToString(); - } - - #endregion - - #region Utility methods - - /// - /// Parses RTP packet from the specified buffer. - /// - /// Buffer containing RTP packet. - /// Number of bytes used in buffer. - private void ParseInternal(byte[] buffer, int size) - { - /* RFC 3550.5.1 RTP Fixed Header Fields. - - The RTP header has the following format: - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |V=2|P|X| CC |M| PT | sequence number | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | timestamp | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | synchronization source (SSRC) identifier | - +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ - | contributing source (CSRC) identifiers | - | .... | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - - 5.3. Available if X bit filled. - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | defined by profile | length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | header extension | - | .... | - - */ - - int offset = 0; - - // V - m_Version = buffer[offset] >> 6; - // P - bool isPadded = Convert.ToBoolean((buffer[offset] >> 5) & 0x1); - // X - bool hasExtention = Convert.ToBoolean((buffer[offset] >> 4) & 0x1); - // CC - int csrcCount = buffer[offset++] & 0xF; - // M - m_IsMarker = Convert.ToBoolean(buffer[offset] >> 7); - // PT - m_PayloadType = buffer[offset++] & 0x7F; - // sequence number - m_SequenceNumber = (ushort) (buffer[offset++] << 8 | buffer[offset++]); - // timestamp - m_Timestamp = - (uint) - (buffer[offset++] << 24 | buffer[offset++] << 16 | buffer[offset++] << 8 | buffer[offset++]); - // SSRC - m_SSRC = - (uint) - (buffer[offset++] << 24 | buffer[offset++] << 16 | buffer[offset++] << 8 | buffer[offset++]); - // CSRC - m_CSRC = new uint[csrcCount]; - for (int i = 0; i < csrcCount; i++) - { - m_CSRC[i] = - (uint) - (buffer[offset++] << 24 | buffer[offset++] << 16 | buffer[offset++] << 8 | - buffer[offset++]); - } - // X - if (hasExtention) - { - // Skip extention - offset++; - offset += buffer[offset]; - } - - // TODO: Padding - - // Data - m_Data = new byte[size - offset]; - Array.Copy(buffer, offset, m_Data, 0, m_Data.Length); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_PacketEventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_PacketEventArgs.cs deleted file mode 100644 index d1d6bd334..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_PacketEventArgs.cs +++ /dev/null @@ -1,67 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - - #endregion - - /// - /// This class provides data for RTP packet related events/methods. - /// - public class RTP_PacketEventArgs : EventArgs - { - #region Members - - private readonly RTP_Packet m_pPacket; - - #endregion - - #region Properties - - /// - /// Gets RTP packet. - /// - public RTP_Packet Packet - { - get { return m_pPacket; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// RTP packet. - public RTP_PacketEventArgs(RTP_Packet packet) - { - if (packet == null) - { - throw new ArgumentNullException("packet"); - } - - m_pPacket = packet; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Participant.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Participant.cs deleted file mode 100644 index 53c16b0e6..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Participant.cs +++ /dev/null @@ -1,208 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - using System.Collections.Generic; - - #endregion - - /// - /// This is base class for RTP_Participant_Local and RTP_Participant_Remote class. - /// - public abstract class RTP_Participant - { - #region Events - - /// - /// Is raised when participant disjoins(timeout or BYE all sources) the RTP multimedia session. - /// - public event EventHandler Removed = null; - - /// - /// Is raised when participant gets new RTP source. - /// - public event EventHandler SourceAdded = null; - - /// - /// Is raised when RTP source removed from(Timeout or BYE) participant. - /// - public event EventHandler SourceRemoved = null; - - #endregion - - #region Members - - private readonly string m_CNAME = ""; - private List m_pSources; - - #endregion - - #region Properties - - /// - /// Gets canonical name of participant. - /// - public string CNAME - { - get { return m_CNAME; } - } - - /// - /// Gets the sources what participant owns(sends). - /// - public RTP_Source[] Sources - { - get { return m_pSources.ToArray(); } - } - - /// - /// Gets or sets user data. - /// - public object Tag { get; set; } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Canonical name of participant. - /// Is raised when cname is null reference. - public RTP_Participant(string cname) - { - if (cname == null) - { - throw new ArgumentNullException("cname"); - } - if (cname == string.Empty) - { - throw new ArgumentException("Argument 'cname' value must be specified."); - } - - m_CNAME = cname; - - m_pSources = new List(); - } - - #endregion - - #region Utility methods - - /// - /// Raises Removed event. - /// - private void OnRemoved() - { - if (Removed != null) - { - Removed(this, new EventArgs()); - } - } - - /// - /// Raises SourceAdded event. - /// - /// RTP source. - private void OnSourceAdded(RTP_Source source) - { - if (source == null) - { - throw new ArgumentNullException("source"); - } - - if (SourceAdded != null) - { - SourceAdded(this, new RTP_SourceEventArgs(source)); - } - } - - /// - /// Raises SourceRemoved event. - /// - /// RTP source. - private void OnSourceRemoved(RTP_Source source) - { - if (source == null) - { - throw new ArgumentNullException("source"); - } - - if (SourceRemoved != null) - { - SourceRemoved(this, new RTP_SourceEventArgs(source)); - } - } - - #endregion - - #region Internal methods - - /// - /// Cleans up any resources being used. - /// - internal void Dispose() - { - m_pSources = null; - - Removed = null; - SourceAdded = null; - SourceRemoved = null; - } - - /// - /// Adds specified source to participant if participant doesn't contain the specified source. - /// - /// RTP source. - /// Is raised when source is null reference. - internal void EnsureSource(RTP_Source source) - { - if (source == null) - { - throw new ArgumentNullException("source"); - } - - if (!m_pSources.Contains(source)) - { - m_pSources.Add(source); - - OnSourceAdded(source); - - source.Disposing += delegate - { - if (m_pSources.Remove(source)) - { - OnSourceRemoved(source); - - // If last source removed, the participant is dead, so dispose participant. - if (m_pSources.Count == 0) - { - OnRemoved(); - Dispose(); - } - } - }; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_ParticipantEventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_ParticipantEventArgs.cs deleted file mode 100644 index 0ae4742b5..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_ParticipantEventArgs.cs +++ /dev/null @@ -1,68 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - - #endregion - - /// - /// This claass provides data for RTP_MultimediaSession.NewParticipant event. - /// - public class RTP_ParticipantEventArgs : EventArgs - { - #region Members - - private readonly RTP_Participant_Remote m_pParticipant; - - #endregion - - #region Properties - - /// - /// Gets participant. - /// - public RTP_Participant_Remote Participant - { - get { return m_pParticipant; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// RTP participant. - /// Is raised when participant is null reference. - public RTP_ParticipantEventArgs(RTP_Participant_Remote participant) - { - if (participant == null) - { - throw new ArgumentNullException("participant"); - } - - m_pParticipant = participant; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Participant_Local.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Participant_Local.cs deleted file mode 100644 index 9daf75e0c..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Participant_Local.cs +++ /dev/null @@ -1,248 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - - #endregion - - /// - /// This class represents RTP session/multimedia-session local participant. - /// - /// Term participant is not well commented/defined in RTP. In general for single media session participant - /// is RTP session itself, for multimedia sesssion participant is multimedia session(RTP sessions group). - public class RTP_Participant_Local : RTP_Participant - { - #region Members - - private readonly CircleCollection m_pOtionalItemsRoundRobin; - private string m_Email; - private string m_Location; - private string m_Name; - private string m_Note; - private string m_Phone; - private string m_Tool; - - #endregion - - #region Properties - - /// - /// Gets or sets the real name, eg. "John Doe". Value null means not specified. - /// - public string Name - { - get { return m_Name; } - - set - { - m_Name = value; - - ConstructOptionalItems(); - } - } - - /// - /// Gets or sets email address. For example "John.Doe@example.com". Value null means not specified. - /// - public string Email - { - get { return m_Email; } - - set - { - m_Email = value; - - ConstructOptionalItems(); - } - } - - /// - /// Gets or sets phone number. For example "+1 908 555 1212". Value null means not specified. - /// - public string Phone - { - get { return m_Phone; } - - set - { - m_Phone = value; - - ConstructOptionalItems(); - } - } - - /// - /// Gets or sets location string. It may be geographic address or for example chat room name. - /// Value null means not specified. - /// - public string Location - { - get { return m_Location; } - - set - { - m_Location = value; - - ConstructOptionalItems(); - } - } - - /// - /// Gets or sets streaming application name/version. - /// Value null means not specified. - /// - public string Tool - { - get { return m_Tool; } - - set - { - m_Tool = value; - - ConstructOptionalItems(); - } - } - - /// - /// Gets or sets note text. The NOTE item is intended for transient messages describing the current state - /// of the source, e.g., "on the phone, can't talk". Value null means not specified. - /// - public string Note - { - get { return m_Note; } - - set - { - m_Note = value; - - ConstructOptionalItems(); - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Canonical name of participant. - /// Is raised when cname is null reference. - public RTP_Participant_Local(string cname) : base(cname) - { - m_pOtionalItemsRoundRobin = new CircleCollection(); - } - - #endregion - - #region Utility methods - - /// - /// Constructs optional SDES items round-robin. - /// - private void ConstructOptionalItems() - { - lock (m_pOtionalItemsRoundRobin) - { - m_pOtionalItemsRoundRobin.Clear(); - - if (!string.IsNullOrEmpty(m_Note)) - { - m_pOtionalItemsRoundRobin.Add("note"); - } - if (!string.IsNullOrEmpty(m_Name)) - { - m_pOtionalItemsRoundRobin.Add("name"); - } - if (!string.IsNullOrEmpty(m_Email)) - { - m_pOtionalItemsRoundRobin.Add("email"); - } - if (!string.IsNullOrEmpty(m_Phone)) - { - m_pOtionalItemsRoundRobin.Add("phone"); - } - if (!string.IsNullOrEmpty(m_Location)) - { - m_pOtionalItemsRoundRobin.Add("location"); - } - if (!string.IsNullOrEmpty(m_Tool)) - { - m_pOtionalItemsRoundRobin.Add("tool"); - } - } - } - - #endregion - - #region Internal methods - - /// - /// Adds next(round-robined) optional SDES item to SDES chunk, if any available. - /// - /// SDES chunk where to add item. - /// Is raised when sdes is null reference. - internal void AddNextOptionalSdesItem(RTCP_Packet_SDES_Chunk sdes) - { - if (sdes == null) - { - throw new ArgumentNullException("sdes"); - } - - lock (m_pOtionalItemsRoundRobin) - { - if (m_pOtionalItemsRoundRobin.Count > 0) - { - string itemName = m_pOtionalItemsRoundRobin.Next(); - - if (itemName == "name") - { - sdes.Name = m_Name; - } - else if (itemName == "email") - { - sdes.Email = m_Email; - } - else if (itemName == "phone") - { - sdes.Phone = m_Phone; - } - else if (itemName == "location") - { - sdes.Location = m_Location; - } - else if (itemName == "tool") - { - sdes.Tool = m_Tool; - } - else if (itemName == "note") - { - sdes.Note = m_Note; - } - } - } - } - - #endregion - - // TODO: PRIV - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Participant_Remote.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Participant_Remote.cs deleted file mode 100644 index bbdee39b3..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Participant_Remote.cs +++ /dev/null @@ -1,230 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements RTP participant. Defined in RFC 3550. - /// - public class RTP_Participant_Remote : RTP_Participant - { - #region Events - - /// - /// Is raised when participant data changed. - /// - public event EventHandler Changed = null; - - #endregion - - #region Members - - private string m_Email; - private string m_Location; - private string m_Name; - private string m_Note; - private string m_Phone; - private string m_Tool; - - #endregion - - #region Properties - - /// - /// Gets the real name, eg. "John Doe". Value null means not specified. - /// - public string Name - { - get { return m_Name; } - } - - /// - /// Gets email address. For example "John.Doe@example.com". Value null means not specified. - /// - public string Email - { - get { return m_Email; } - } - - /// - /// Gets phone number. For example "+1 908 555 1212". Value null means not specified. - /// - public string Phone - { - get { return m_Phone; } - } - - /// - /// Gets location string. It may be geographic address or for example chat room name. - /// Value null means not specified. - /// - public string Location - { - get { return m_Location; } - } - - /// - /// Gets streaming application name/version. - /// Value null means not specified. - /// - public string Tool - { - get { return m_Tool; } - } - - /// - /// Gets note text. The NOTE item is intended for transient messages describing the current state - /// of the source, e.g., "on the phone, can't talk". Value null means not specified. - /// - public string Note - { - get { return m_Note; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Canonical name of participant. For example: john.doe@domain.com-randomTag. - /// Is raised when cname is null reference. - internal RTP_Participant_Remote(string cname) : base(cname) {} - - #endregion - - #region Methods - - /// - /// Returns participant as string. - /// - /// Returns participant as string. - public override string ToString() - { - StringBuilder retVal = new StringBuilder(); - - retVal.AppendLine("CNAME: " + CNAME); - if (!string.IsNullOrEmpty(m_Name)) - { - retVal.AppendLine("Name: " + m_Name); - } - if (!string.IsNullOrEmpty(m_Email)) - { - retVal.AppendLine("Email: " + m_Email); - } - if (!string.IsNullOrEmpty(m_Phone)) - { - retVal.AppendLine("Phone: " + m_Phone); - } - if (!string.IsNullOrEmpty(m_Location)) - { - retVal.AppendLine("Location: " + m_Location); - } - if (!string.IsNullOrEmpty(m_Tool)) - { - retVal.AppendLine("Tool: " + m_Tool); - } - if (!string.IsNullOrEmpty(m_Note)) - { - retVal.AppendLine("Note: " + m_Note); - } - - return retVal.ToString().TrimEnd(); - } - - #endregion - - // TODO: PRIV - - #region Utility methods - - /// - /// Raises Changed event. - /// - private void OnChanged() - { - if (Changed != null) - { - Changed(this, new RTP_ParticipantEventArgs(this)); - } - } - - #endregion - - #region Internal methods - - /// - /// Updates participant data from SDES items. - /// - /// SDES chunk. - /// Is raised when sdes is null reference value. - internal void Update(RTCP_Packet_SDES_Chunk sdes) - { - if (sdes == null) - { - throw new ArgumentNullException("sdes"); - } - - bool changed = false; - if (!string.IsNullOrEmpty(sdes.Name) && !string.Equals(m_Name, sdes.Name)) - { - m_Name = sdes.Name; - changed = true; - } - if (!string.IsNullOrEmpty(sdes.Email) && !string.Equals(m_Email, sdes.Email)) - { - m_Email = sdes.Email; - changed = true; - } - if (!string.IsNullOrEmpty(sdes.Phone) && !string.Equals(Phone, sdes.Phone)) - { - m_Phone = sdes.Phone; - changed = true; - } - if (!string.IsNullOrEmpty(sdes.Location) && !string.Equals(m_Location, sdes.Location)) - { - m_Location = sdes.Location; - changed = true; - } - if (!string.IsNullOrEmpty(sdes.Tool) && !string.Equals(m_Tool, sdes.Tool)) - { - m_Tool = sdes.Tool; - changed = true; - } - if (!string.IsNullOrEmpty(sdes.Note) && !string.Equals(m_Note, sdes.Note)) - { - m_Note = sdes.Note; - changed = true; - } - - if (changed) - { - OnChanged(); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_PayloadTypes.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_PayloadTypes.cs deleted file mode 100644 index 688c5d69c..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_PayloadTypes.cs +++ /dev/null @@ -1,145 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - /// - /// RTP payload specifies data type which is RTP packet. - /// IANA registered RTP payload types. Defined in http://www.iana.org/assignments/rtp-parameters. - /// - public class RTP_PayloadTypes - { - #region Members - - /// - /// CELB video codec. Defined in RFC 2029. - /// - public static readonly int CELB = 25; - - /// - /// DVI4 11025hz audio codec. - /// - public static readonly int DVI4_11025 = 16; - - /// - /// DVI4 16khz audio codec. Defined in RFC 3551. - /// - public static readonly int DVI4_16000 = 6; - - /// - /// DVI4 220505hz audio codec. - /// - public static readonly int DVI4_22050 = 17; - - /// - /// DVI4 8khz audio codec. Defined in RFC 3551. - /// - public static readonly int DVI4_8000 = 5; - - /// - /// G722 audio codec. Defined in RFC 3551. - /// - public static readonly int G722 = 9; - - /// - /// G723 audio codec. - /// - public static readonly int G723 = 4; - - /// - /// G728 audio codec. Defined in RFC 3551. - /// - public static readonly int G728 = 15; - - /// - /// G729 audio codec. - /// - public static readonly int G729 = 18; - - /// - /// GSM audio codec. Defined in RFC 3551. - /// - public static readonly int GSM = 3; - - /// - /// H261 video codec. Defined in RFC 2032. - /// - public static readonly int H261 = 31; - - /// - /// H263 video codec. - /// - public static readonly int H263 = 34; - - /// - /// JPEG video codec. Defined in RFC 2435. - /// - public static readonly int JPEG = 26; - - /// - /// L16 1 channel audio codec. Defined in RFC 3551. - /// - public static readonly int L16_1CH = 10; - - /// - /// L16 2 channel audio codec. Defined in RFC 3551. - /// - public static readonly int L16_2CH = 11; - - /// - /// LPC audio codec. Defined in RFC 3551. - /// - public static readonly int LPC = 7; - - /// - /// MP2T video codec. Defined in RFC 2250. - /// - public static readonly int MP2T = 33; - - /// - /// MPA audio codec. Defined in RFC 3551. - /// - public static readonly int MPA = 14; - - /// - /// H261 video codec. Defined in RFC 2250. - /// - public static readonly int MPV = 32; - - /// - /// NV video codec. Defined in RFC 3551. - /// - public static readonly int NV = 28; - - /// - /// PCMA(a-law) audio codec. Defined in RFC 3551. - /// - public static readonly int PCMA = 8; - - /// - /// PCMU8(u-law) audio codec. Defined in RFC 3551. - /// - public static readonly int PCMU; - - /// - /// QCELP audio codec. - /// - public static readonly int QCELP = 12; - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_ReceiveStream.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_ReceiveStream.cs deleted file mode 100644 index e210027c8..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_ReceiveStream.cs +++ /dev/null @@ -1,703 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - - #endregion - - /// - /// Implements RTP session receive stream. - /// - public class RTP_ReceiveStream - { - #region Events - - /// - /// Is raised when stream is closed by remote party (remote party sent BYE). - /// - public event EventHandler Closed = null; - - /// - /// Is raised when new RTP packet received. - /// - public event EventHandler PacketReceived = null; - - /// - /// Is raised when steam gets new sender report from remote party. - /// - public event EventHandler SenderReport = null; - - /// - /// Is raised when receive stream has timed out by RTP session. - /// - /// After Timeout event stream will be disposed and has no longer accessible. - public event EventHandler Timeout = null; - - #endregion - - #region Members - - private readonly RTP_Source m_pSSRC; - private uint m_BaseSeq; - private long m_BytesReceived; - private long m_ExpectedPrior; - private bool m_IsDisposed; - private double m_Jitter; - private uint m_LastBadSeqPlus1; - private DateTime m_LastSRTime = DateTime.MinValue; - private ushort m_MaxSeqNo; - private long m_PacketsMisorder; - private long m_PacketsReceived; - private RTCP_Report_Sender m_pLastSR; - private RTP_Participant_Remote m_pParticipant; - private int m_Probation; - private RTP_Session m_pSession; - private long m_ReceivedPrior; - private int m_SeqNoWrapCount; - private int m_Transit; - private int MAX_DROPOUT = 3000; - private int MAX_MISORDER = 100; - private int MIN_SEQUENTIAL = 2; - private uint RTP_SEQ_MOD = (1 << 16); - - #endregion - - #region Properties - - /// - /// Gets if this object is disposed. - /// - public bool IsDisposed - { - get { return m_IsDisposed; } - } - - /// - /// Gets stream owner RTP session. - /// - /// Is raised when this class is Disposed and this property is accessed. - public RTP_Session Session - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pSession; - } - } - - /// - /// Gets stream owner synchronization source. - /// - /// Is raised when this class is Disposed and this property is accessed. - public RTP_Source SSRC - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pSSRC; - } - } - - /// - /// Gets remote participant who is owner of this stream. Returns null if this stream is not yet received RTCP SDES. - /// - /// Is raised when this class is Disposed and this property is accessed. - public RTP_Participant_Remote Participant - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pParticipant; - } - } - - /// - /// Gets number of times SeqNo has wrapped around. - /// - /// Is raised when this class is Disposed and this property is accessed. - public int SeqNoWrapCount - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_SeqNoWrapCount; - } - } - - /// - /// Gets first sequence number what this stream got. - /// - /// Is raised when this class is Disposed and this property is accessed. - public int FirstSeqNo - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return (int) m_BaseSeq; - } - } - - /// - /// Gets maximum sequnce number that stream has got. - /// - /// Is raised when this class is Disposed and this property is accessed. - public int MaxSeqNo - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_MaxSeqNo; - } - } - - /// - /// Gets how many RTP packets has received by this stream. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long PacketsReceived - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_PacketsReceived; - } - } - - /// - /// Gets how many RTP misorder packets has received by this stream. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long PacketsMisorder - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_PacketsMisorder; - } - } - - /// - /// Gets how many RTP packets has lost during transmission. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long PacketsLost - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - // RFC 3550 A.3 Determining Number of Packets Expected and Lost. - uint extHighestSeqNo = (uint) ((65536*m_SeqNoWrapCount) + m_MaxSeqNo); - uint expected = extHighestSeqNo - m_BaseSeq + 1; - long lost = expected - m_PacketsReceived; - - return lost; - } - } - - /// - /// Gets how many RTP data has received by this stream. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long BytesReceived - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_BytesReceived; - } - } - - /// - /// Gets inter arrival jitter. - /// - /// Is raised when this class is Disposed and this property is accessed. - public double Jitter - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_Jitter; - } - } - - /// - /// Gets delay between las SR(sender report) and now in milliseconds. Returns -1 if no SR received. - /// - public int DelaySinceLastSR - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return - (int) - (m_LastSRTime == DateTime.MinValue - ? -1 - : ((DateTime.Now - m_LastSRTime)).TotalMilliseconds); - } - } - - /// - /// Gets time when last SR(sender report) was received. Returns DateTime.MinValue if no SR received. - /// - public DateTime LastSRTime - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_LastSRTime; - } - } - - /// - /// Gets last received RTCP SR(sender report). Value null means no SR received. - /// - /// Is raised when this class is Disposed and this property is accessed. - public RTCP_Report_Sender LastSR - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pLastSR; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner RTP session. - /// Onwer synchronization source. - /// RTP packet SeqNo value. - /// Is riased when session or ssrc is null reference. - internal RTP_ReceiveStream(RTP_Session session, RTP_Source ssrc, ushort packetSeqNo) - { - if (session == null) - { - throw new ArgumentNullException("session"); - } - if (ssrc == null) - { - throw new ArgumentNullException("ssrc"); - } - - m_pSession = session; - m_pSSRC = ssrc; - - // RFC 3550 A.1. - InitSeq(packetSeqNo); - m_MaxSeqNo = (ushort) (packetSeqNo - 1); - m_Probation = MIN_SEQUENTIAL; - } - - #endregion - - #region Utility methods - - /// - /// Initializes new sequence number. - /// - /// Sequence number. - private void InitSeq(ushort seqNo) - { - // For more info see RFC 3550 A.1. - - m_BaseSeq = seqNo; - m_MaxSeqNo = seqNo; - m_LastBadSeqPlus1 = RTP_SEQ_MOD + 1; /* so seq == bad_seq is false */ - m_SeqNoWrapCount = 0; - m_PacketsReceived = 0; - m_ReceivedPrior = 0; - m_ExpectedPrior = 0; - } - - /// - /// Updates sequence number. - /// - /// RTP packet sequence number. - /// Returns true if sequence is valid, otherwise false. - private bool UpdateSeq(ushort seqNo) - { - // For more info see RFC 3550 A.1. - - ushort udelta = (ushort) (seqNo - m_MaxSeqNo); - - /* - * Source is not valid until MIN_SEQUENTIAL packets with - * sequential sequence numbers have been received. - */ - - // Stream not validated yet. - if (m_Probation > 0) - { - // The seqNo is in sequence. - if (seqNo == m_MaxSeqNo + 1) - { - m_Probation--; - m_MaxSeqNo = seqNo; - - // The receive stream has validated ok. - if (m_Probation == 0) - { - InitSeq(seqNo); - m_PacketsReceived++; - - // Raise NewReceiveStream event. - m_pSession.OnNewReceiveStream(this); - - return true; - } - } - else - { - m_Probation = MIN_SEQUENTIAL - 1; - m_MaxSeqNo = seqNo; - } - - return false; - } - // The seqNo is order, with permissible gap. - else if (udelta < MAX_DROPOUT) - { - // The seqNo has wrapped around. - if (seqNo < m_MaxSeqNo) - { - m_SeqNoWrapCount++; - } - m_MaxSeqNo = seqNo; - } - // The seqNo made a very large jump. - else if (udelta <= RTP_SEQ_MOD - MAX_MISORDER) - { - if (seqNo == m_LastBadSeqPlus1) - { - /* - * Two sequential packets -- assume that the other side - * restarted without telling us so just re-sync - * (i.e., pretend this was the first packet). - */ - InitSeq(seqNo); - } - else - { - m_LastBadSeqPlus1 = (uint) ((seqNo + 1) & (RTP_SEQ_MOD - 1)); - - return false; - } - } - else - { - /* duplicate or reordered packet */ - m_PacketsMisorder++; - } - - m_PacketsReceived++; - - return true; - } - - /// - /// Raises Closed event. - /// - private void OnClosed() - { - if (Closed != null) - { - Closed(this, new EventArgs()); - } - } - - /// - /// Raises SenderReport event. - /// - private void OnSenderReport() - { - if (SenderReport != null) - { - SenderReport(this, new EventArgs()); - } - } - - /// - /// Raises PacketReceived event. - /// - /// RTP packet. - private void OnPacketReceived(RTP_Packet packet) - { - if (PacketReceived != null) - { - PacketReceived(this, new RTP_PacketEventArgs(packet)); - } - } - - #endregion - - #region Internal methods - - /// - /// Cleans up any resources being used. - /// - internal void Dispose() - { - if (m_IsDisposed) - { - return; - } - m_IsDisposed = true; - - m_pSession = null; - m_pParticipant = null; - - Closed = null; - Timeout = null; - SenderReport = null; - PacketReceived = null; - } - - /// - /// Processes specified RTP packet thorugh this stream. - /// - /// RTP packet. - /// RTP packet size in bytes. - /// Is raised when packet is null reference. - internal void Process(RTP_Packet packet, int size) - { - if (packet == null) - { - throw new ArgumentNullException("packet"); - } - - m_BytesReceived += size; - - if (UpdateSeq(packet.SeqNo)) - { - OnPacketReceived(packet); - - /* RFC 3550 A.8 Estimating the Interarrival Jitter. - The code fragments below implement the algorithm given in Section - 6.4.1 for calculating an estimate of the statistical variance of the - RTP data interarrival time to be inserted in the interarrival jitter - field of reception reports. The inputs are r->ts, the timestamp from - the incoming packet, and arrival, the current time in the same units. - Here s points to state for the source; s->transit holds the relative - transit time for the previous packet, and s->jitter holds the - estimated jitter. The jitter field of the reception report is - measured in timestamp units and expressed as an unsigned integer, but - the jitter estimate is kept in a floating point. As each data packet - arrives, the jitter estimate is updated: - - int transit = arrival - r->ts; - int d = transit - s->transit; - s->transit = transit; - if (d < 0) d = -d; - s->jitter += (1./16.) * ((double)d - s->jitter); - - When a reception report block (to which rr points) is generated for - this member, the current jitter estimate is returned: - - rr->jitter = (u_int32) s->jitter; - - */ - uint arrival = RTP_Utils.DateTimeToNTP32(DateTime.Now); - int transit = (int) (arrival - packet.Timestamp); - int d = transit - m_Transit; - m_Transit = transit; - if (d < 0) - { - d = -d; - } - m_Jitter += (1.0/16.0)*(d - m_Jitter); - } - // else Packet not valid, skip it. - } - - /// - /// Sets property LastSR value. - /// - /// Sender report. - /// Is raised when report is null reference. - internal void SetSR(RTCP_Report_Sender report) - { - if (report == null) - { - throw new ArgumentNullException("report"); - } - - m_LastSRTime = DateTime.Now; - m_pLastSR = report; - - OnSenderReport(); - } - - /// - /// Creates receiver report. - /// - /// Returns created receiver report. - internal RTCP_Packet_ReportBlock CreateReceiverReport() - { - /* RFC 3550 A.3 Determining Number of Packets Expected and Lost. - In order to compute packet loss rates, the number of RTP packets - expected and actually received from each source needs to be known, - using per-source state information defined in struct source - referenced via pointer s in the code below. The number of packets - received is simply the count of packets as they arrive, including any - late or duplicate packets. The number of packets expected can be - computed by the receiver as the difference between the highest - sequence number received (s->max_seq) and the first sequence number - received (s->base_seq). Since the sequence number is only 16 bits - and will wrap around, it is necessary to extend the highest sequence - number with the (shifted) count of sequence number wraparounds - (s->cycles). Both the received packet count and the count of cycles - are maintained the RTP header validity check routine in Appendix A.1. - - extended_max = s->cycles + s->max_seq; - expected = extended_max - s->base_seq + 1; - - The number of packets lost is defined to be the number of packets - expected less the number of packets actually received: - - lost = expected - s->received; - - Since this signed number is carried in 24 bits, it should be clamped - at 0x7fffff for positive loss or 0x800000 for negative loss rather - than wrapping around. - - The fraction of packets lost during the last reporting interval - (since the previous SR or RR packet was sent) is calculated from - differences in the expected and received packet counts across the - interval, where expected_prior and received_prior are the values - saved when the previous reception report was generated: - - expected_interval = expected - s->expected_prior; - s->expected_prior = expected; - received_interval = s->received - s->received_prior; - s->received_prior = s->received; - lost_interval = expected_interval - received_interval; - if(expected_interval == 0 || lost_interval <= 0) - fraction = 0; - else - fraction = (lost_interval << 8) / expected_interval; - - The resulting fraction is an 8-bit fixed point number with the binary - point at the left edge. - */ - - uint extHighestSeqNo = (uint) (m_SeqNoWrapCount << 16 + m_MaxSeqNo); - uint expected = extHighestSeqNo - m_BaseSeq + 1; - - int expected_interval = (int) (expected - m_ExpectedPrior); - m_ExpectedPrior = expected; - int received_interval = (int) (m_PacketsReceived - m_ReceivedPrior); - m_ReceivedPrior = m_PacketsReceived; - int lost_interval = expected_interval - received_interval; - int fraction = 0; - if (expected_interval == 0 || lost_interval <= 0) - { - fraction = 0; - } - else - { - fraction = (lost_interval << 8)/expected_interval; - } - - RTCP_Packet_ReportBlock rr = new RTCP_Packet_ReportBlock(SSRC.SSRC); - rr.FractionLost = (uint) fraction; - rr.CumulativePacketsLost = (uint) PacketsLost; - rr.ExtendedHighestSeqNo = extHighestSeqNo; - rr.Jitter = (uint) m_Jitter; - rr.LastSR = (m_pLastSR == null ? 0 : ((uint) ((long) m_pLastSR.NtpTimestamp >> 8) & 0xFFFF)); - rr.DelaySinceLastSR = (uint) Math.Max(0, DelaySinceLastSR/65.536); - - return rr; - } - - /// - /// Raised Timeout event. - /// - internal void OnTimeout() - { - if (Timeout != null) - { - Timeout(this, new EventArgs()); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_ReceiveStreamEventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_ReceiveStreamEventArgs.cs deleted file mode 100644 index 36158ac71..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_ReceiveStreamEventArgs.cs +++ /dev/null @@ -1,68 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - - #endregion - - /// - /// This method provides data for RTP receive stream related events and methods. - /// - public class RTP_ReceiveStreamEventArgs : EventArgs - { - #region Members - - private readonly RTP_ReceiveStream m_pStream; - - #endregion - - #region Properties - - /// - /// Gets RTP stream. - /// - public RTP_ReceiveStream Stream - { - get { return m_pStream; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// RTP stream. - /// Is raised when stream is null reference. - public RTP_ReceiveStreamEventArgs(RTP_ReceiveStream stream) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - m_pStream = stream; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_SendStream.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_SendStream.cs deleted file mode 100644 index 5974ddeed..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_SendStream.cs +++ /dev/null @@ -1,397 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - - #endregion - - /// - /// Implements RTP session send stream. - /// - public class RTP_SendStream - { - #region Events - - /// - /// Is raised when stream is closed. - /// - public event EventHandler Closed = null; - - /// - /// Is raised when stream has disposed. - /// - public event EventHandler Disposed = null; - - #endregion - - #region Members - - private bool m_IsDisposed; - private uint m_LastPacketRtpTimestamp; - private DateTime m_LastPacketTime; - private RTP_Source_Local m_pSource; - private int m_RtcpCyclesSinceWeSent = 9999; - private long m_RtpBytesSent; - private long m_RtpDataBytesSent; - private long m_RtpPacketsSent; - private int m_SeqNo; - private int m_SeqNoWrapCount; - - #endregion - - #region Properties - - /// - /// Gets if this object is disposed. - /// - public bool IsDisposed - { - get { return m_IsDisposed; } - } - - /// - /// Gets stream owner RTP session. - /// - /// Is raised when this class is Disposed and this property is accessed. - public RTP_Session Session - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pSource.Session; - } - } - - /// - /// Gets stream owner source. - /// - /// Is raised when this class is Disposed and this property is accessed. - public RTP_Source Source - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pSource; - } - } - - /// - /// Gets number of times SeqNo has wrapped around. - /// - /// Is raised when this class is Disposed and this property is accessed. - public int SeqNoWrapCount - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_SeqNoWrapCount; - } - } - - /// - /// Gets next packet sequence number. - /// - /// Is raised when this class is Disposed and this property is accessed. - public int SeqNo - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_SeqNo; - } - } - - /// - /// Gets last packet send time. - /// - /// Is raised when this class is Disposed and this property is accessed. - public DateTime LastPacketTime - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_LastPacketTime; - } - } - - /// - /// Gets last sent RTP packet RTP timestamp header value. - /// - /// Is raised when this class is Disposed and this property is accessed. - public uint LastPacketRtpTimestamp - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_LastPacketRtpTimestamp; - } - } - - /// - /// Gets how many RTP packets has sent by this stream. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long RtpPacketsSent - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_RtpPacketsSent; - } - } - - /// - /// Gets how many RTP bytes has sent by this stream. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long RtpBytesSent - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_RtpBytesSent; - } - } - - /// - /// Gets how many RTP data(no RTP header included) bytes has sent by this stream. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long RtpDataBytesSent - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_RtpDataBytesSent; - } - } - - /// - /// Gets how many RTCP cycles has passed since we sent data. - /// - internal int RtcpCyclesSinceWeSent - { - get { return m_RtcpCyclesSinceWeSent; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner RTP source. - /// Is raised when source is null reference. - internal RTP_SendStream(RTP_Source_Local source) - { - if (source == null) - { - throw new ArgumentNullException("source"); - } - - m_pSource = source; - - /* RFC 3550 4. - The initial value of the sequence number SHOULD be random (unpredictable) to make known-plaintext - attacks on encryption more difficult. - */ - m_SeqNo = new Random().Next(1, Workaround.Definitions.MaxStreamLineLength); - } - - #endregion - - #region Methods - - /// - /// Closes this sending stream. - /// - /// Is raised when this class is Disposed and this method is accessed. - public void Close() - { - Close(null); - } - - /// - /// Closes this sending stream. - /// - /// Stream closing reason text what is reported to the remote party. Value null means not specified. - /// Is raised when this class is Disposed and this method is accessed. - public void Close(string closeReason) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - m_pSource.Close(closeReason); - - OnClosed(); - Dispose(); - } - - /// - /// Sends specified packet to the RTP session remote party. - /// - /// RTP packet. - /// Is raised when this class is Disposed and this method is accessed. - /// Is raised when packet is null reference. - /// Properties packet.SSRC,packet.SeqNo,packet.PayloadType filled by this method automatically. - public void Send(RTP_Packet packet) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (packet == null) - { - throw new ArgumentNullException("packet"); - } - - // RTP was designed around the concept of Application Level Framing (ALF), - // because of it we only allow to send packets and don't deal with breaking frames into packets. - - packet.SSRC = Source.SSRC; - packet.SeqNo = NextSeqNo(); - packet.PayloadType = Session.Payload; - - // Send RTP packet. - m_RtpBytesSent += m_pSource.SendRtpPacket(packet); - - m_RtpPacketsSent++; - m_RtpDataBytesSent += packet.Data.Length; - m_LastPacketTime = DateTime.Now; - m_LastPacketRtpTimestamp = packet.Timestamp; - m_RtcpCyclesSinceWeSent = 0; - } - - #endregion - - #region Utility methods - - /// - /// Cleans up any resources being used. - /// - private void Dispose() - { - if (m_IsDisposed) - { - return; - } - m_IsDisposed = true; - - m_pSource = null; - - OnDisposed(); - - Disposed = null; - Closed = null; - } - - /// - /// Gets next packet sequence number. - /// - /// Returns next packet sequence number. - private ushort NextSeqNo() - { - // Wrap around sequence number. - if (m_SeqNo >= ushort.MaxValue) - { - m_SeqNo = 0; - m_SeqNoWrapCount++; - } - - return (ushort) m_SeqNo++; - } - - /// - /// Raises Disposed event. - /// - private void OnDisposed() - { - if (Disposed != null) - { - Disposed(this, new EventArgs()); - } - } - - /// - /// Raises Closed event. - /// - private void OnClosed() - { - if (Closed != null) - { - Closed(this, new EventArgs()); - } - } - - #endregion - - #region Internal methods - - /// - /// Is called by RTP session if RTCP cycle compled. - /// - internal void RtcpCycle() - { - m_RtcpCyclesSinceWeSent++; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_SendStreamEventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_SendStreamEventArgs.cs deleted file mode 100644 index 5f7084972..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_SendStreamEventArgs.cs +++ /dev/null @@ -1,67 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - - #endregion - - /// - /// This method provides data for RTP send stream related events and methods. - /// - public class RTP_SendStreamEventArgs : EventArgs - { - #region Members - - private readonly RTP_SendStream m_pStream; - - #endregion - - #region Properties - - /// - /// Gets RTP stream. - /// - public RTP_SendStream Stream - { - get { return m_pStream; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// RTP send stream. - public RTP_SendStreamEventArgs(RTP_SendStream stream) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - m_pStream = stream; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Session.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Session.cs deleted file mode 100644 index b2e741d36..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Session.cs +++ /dev/null @@ -1,2143 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - using System.Collections.Generic; - using System.Net; - using System.Net.Sockets; - using System.Threading; - - #endregion - - /// - /// Implements RTP session. Defined in RFC 3550. - /// - /// RTP session can exchange 1 payload type at time. - /// For example if application wants to send audio and video, it must create two different RTP sessions. - /// Though RTP session can send multiple streams of same payload. - public class RTP_Session : IDisposable - { - #region Events - - /// - /// Is raised when RTP session has closed. - /// - public event EventHandler Closed = null; - - /// - /// Is raised when RTP session has disposed. - /// - public event EventHandler Disposed = null; - - /// - /// Is raised when new recieve stream received from remote target. - /// - public event EventHandler NewReceiveStream = null; - - /// - /// Is raised when new send stream created. - /// - public event EventHandler NewSendStream = null; - - #endregion - - #region Members - - private readonly RTP_Clock m_pRtpClock; - private int m_Bandwidth = 64000; - private bool m_IsDisposed; - private long m_LocalCollisions; - private long m_LocalPacketsLooped; - private int m_MTU = 1400; - private int m_Payload; - private Dictionary m_pConflictingEPs; - private RTP_Address m_pLocalEP; - private List m_pLocalSources; - private object m_pLock = new object(); - private Dictionary m_pMembers; - private int m_PMembersCount; - private byte[] m_pRtcpReceiveBuffer; - private Socket m_pRtcpSocket; - private RTP_Source m_pRtcpSource; - private TimerEx m_pRtcpTimer; - private byte[] m_pRtpReceiveBuffer; - private Socket m_pRtpSocket; - private Dictionary m_pSenders; - private RTP_MultimediaSession m_pSession; - private List m_pTargets; - private long m_RemoteCollisions; - private long m_RemotePacketsLooped; - private double m_RtcpAvgPacketSize; - private long m_RtcpBytesReceived; - private long m_RtcpBytesSent; - private long m_RtcpFailedTransmissions; - private DateTime m_RtcpLastTransmission = DateTime.MinValue; - private long m_RtcpPacketsReceived; - private long m_RtcpPacketsSent; - private long m_RtcpUnknownPacketsReceived; - private long m_RtpBytesReceived; - private long m_RtpBytesSent; - private long m_RtpFailedTransmissions; - private long m_RtpPacketsReceived; - private long m_RtpPacketsSent; - - #endregion - - #region Properties - - /// - /// Gets if this object is disposed. - /// - public bool IsDisposed - { - get { return m_IsDisposed; } - } - - /// - /// Gets owner RTP multimedia session. - /// - /// Is raised when this class is Disposed and this property is accessed. - public RTP_MultimediaSession Session - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pSession; - } - } - - /// - /// Gets local RTP end point. - /// - /// Is raised when this class is Disposed and this property is accessed. - public RTP_Address LocalEP - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pLocalEP; - } - } - - /// - /// Gets RTP media clock. - /// - /// Is raised when this class is Disposed and this property is accessed. - public RTP_Clock RtpClock - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pRtpClock; - } - } - - /// - /// Gets RTP session remote targets. - /// - /// Normally RTP session has only 1 remote target, for multi-unicast session, there may be more than 1 target. - /// Is raised when this class is Disposed and this property is accessed. - public RTP_Address[] Targets - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pTargets.ToArray(); - } - } - - /// - /// Gets maximum transfet unit size in bytes. - /// - /// Is raised when this class is Disposed and this property is accessed. - public int MTU - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_MTU; - } - } - - /// - /// Gets or sets sending payload. - /// - /// Is raised when this class is Disposed and this property is accessed. - public int Payload - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_Payload; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - m_Payload = value; - } - } - - /// - /// Gets or sets session bandwidth in bits per second. - /// - /// Is raised when this class is Disposed and this property is accessed. - /// Is raised when invalid value is passed. - public int Bandwidth - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_Bandwidth; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (value < 8) - { - throw new ArgumentException("Property 'Bandwidth' value must be >= 8."); - } - - m_Bandwidth = value; - } - } - - /// - /// Gets session members. Session member is local/remote source what sends RTCP,RTP or RTCP-RTP data. - /// - /// Is raised when this class is Disposed and this property is accessed. - public RTP_Source[] Members - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - lock (m_pMembers) - { - RTP_Source[] sources = new RTP_Source[m_pMembers.Count]; - m_pMembers.Values.CopyTo(sources, 0); - - return sources; - } - } - } - - /// - /// Gets session senders. Sender is local/remote source what sends RTP data. - /// - /// Is raised when this class is Disposed and this property is accessed. - public RTP_Source[] Senders - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - lock (m_pSenders) - { - RTP_Source[] sources = new RTP_Source[m_pSenders.Count]; - m_pSenders.Values.CopyTo(sources, 0); - - return sources; - } - } - } - - /// - /// Gets the RTP streams what we send. - /// - /// Is raised when this class is Disposed and this property is accessed. - public RTP_SendStream[] SendStreams - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - lock (m_pLocalSources) - { - List retVal = new List(); - foreach (RTP_Source_Local source in m_pLocalSources) - { - if (source.Stream != null) - { - retVal.Add(source.Stream); - } - } - - return retVal.ToArray(); - } - } - } - - /// - /// Gets the RTP streams what we receive. - /// - /// Is raised when this class is Disposed and this property is accessed. - public RTP_ReceiveStream[] ReceiveStreams - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - lock (m_pSenders) - { - List retVal = new List(); - foreach (RTP_Source source in m_pSenders.Values) - { - if (!source.IsLocal) - { - retVal.Add(((RTP_Source_Remote) source).Stream); - } - } - - return retVal.ToArray(); - } - } - } - - /// - /// Gets total of RTP packets sent by this session. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long RtpPacketsSent - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_RtpPacketsSent; - } - } - - /// - /// Gets total of RTP bytes(RTP headers included) sent by this session. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long RtpBytesSent - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_RtpBytesSent; - } - } - - /// - /// Gets total of RTP packets received by this session. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long RtpPacketsReceived - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_RtpPacketsReceived; - } - } - - /// - /// Gets total of RTP bytes(RTP headers included) received by this session. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long RtpBytesReceived - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_RtpBytesReceived; - } - } - - /// - /// Gets number of times RTP packet sending has failed. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long RtpFailedTransmissions - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_RtpFailedTransmissions; - } - } - - /// - /// Gets total of RTCP packets sent by this session. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long RtcpPacketsSent - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_RtcpPacketsSent; - } - } - - /// - /// Gets total of RTCP bytes(RTCP headers included) sent by this session. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long RtcpBytesSent - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_RtcpBytesSent; - } - } - - /// - /// Gets total of RTCP packets received by this session. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long RtcpPacketsReceived - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_RtcpPacketsReceived; - } - } - - /// - /// Gets total of RTCP bytes(RTCP headers included) received by this session. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long RtcpBytesReceived - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_RtcpBytesReceived; - } - } - - /// - /// Gets number of times RTCP packet sending has failed. - /// - /// Is raised when this class is Disposed and this property is accessed. - public long RtcpFailedTransmissions - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_RtcpFailedTransmissions; - } - } - - /// - /// Current RTCP reporting interval in seconds. - /// - /// Is raised when this class is Disposed and this property is accessed. - public int RtcpInterval - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return (int) (m_pRtcpTimer.Interval/1000); - } - } - - /// - /// Gets time when last RTCP report was sent. - /// - /// Is raised when this class is Disposed and this property is accessed. - public DateTime RtcpLastTransmission - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_RtcpLastTransmission; - } - } - - /// - /// Gets number of times local SSRC collision dedected. - /// - public long LocalCollisions - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_LocalCollisions; - } - } - - /// - /// Gets number of times remote SSRC collision dedected. - /// - public long RemoteCollisions - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_RemoteCollisions; - } - } - - /// - /// Gets number of times local packets loop dedected. - /// - public long LocalPacketsLooped - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_LocalPacketsLooped; - } - } - - /// - /// Gets number of times remote packets loop dedected. - /// - public long RemotePacketsLooped - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_RemotePacketsLooped; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner RTP multimedia session. - /// Local RTP end point. - /// RTP media clock. - /// Is raised when localEP, localEP or clock is null reference. - internal RTP_Session(RTP_MultimediaSession session, RTP_Address localEP, RTP_Clock clock) - { - if (session == null) - { - throw new ArgumentNullException("session"); - } - if (localEP == null) - { - throw new ArgumentNullException("localEP"); - } - if (clock == null) - { - throw new ArgumentNullException("clock"); - } - - m_pSession = session; - m_pLocalEP = localEP; - m_pRtpClock = clock; - - m_pRtpReceiveBuffer = new byte[Workaround.Definitions.MaxStreamLineLength]; - m_pRtcpReceiveBuffer = new byte[Workaround.Definitions.MaxStreamLineLength]; - - m_pLocalSources = new List(); - m_pTargets = new List(); - m_pMembers = new Dictionary(); - m_pSenders = new Dictionary(); - m_pConflictingEPs = new Dictionary(); - - m_pRtpSocket = new Socket(localEP.IP.AddressFamily, SocketType.Dgram, ProtocolType.Udp); - m_pRtpSocket.Bind(localEP.RtpEP); - m_pRtcpSocket = new Socket(localEP.IP.AddressFamily, SocketType.Dgram, ProtocolType.Udp); - m_pRtcpSocket.Bind(localEP.RtcpEP); - - m_pRtcpTimer = new TimerEx(); - m_pRtcpTimer.Elapsed += delegate { SendRtcp(); }; - m_pRtcpTimer.AutoReset = false; - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public void Dispose() - { - if (m_IsDisposed) - { - return; - } - m_IsDisposed = true; - - if (m_pRtcpTimer != null) - { - m_pRtcpTimer.Dispose(); - m_pRtcpTimer = null; - } - m_pSession = null; - m_pLocalEP = null; - m_pTargets = null; - foreach (RTP_Source_Local source in m_pLocalSources.ToArray()) - { - source.Dispose(); - } - m_pLocalSources = null; - m_pRtcpSource = null; - foreach (RTP_Source source in m_pMembers.Values) - { - source.Dispose(); - } - m_pMembers = null; - m_pSenders = null; - m_pConflictingEPs = null; - m_pRtpReceiveBuffer = null; - m_pRtcpReceiveBuffer = null; - m_pRtpSocket.Close(); - m_pRtpSocket = null; - m_pRtcpSocket.Close(); - m_pRtcpSocket = null; - - OnDisposed(); - - Disposed = null; - Closed = null; - NewSendStream = null; - NewReceiveStream = null; - } - - /// - /// Closes RTP session, sends BYE with optional reason text to remote targets. - /// - /// Close reason. Value null means not specified. - /// Is raised when this class is Disposed and this method is accessed. - public void Close(string closeReason) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - // Generate BYE packet(s). - RTCP_CompoundPacket compundPacket = new RTCP_CompoundPacket(); - RTCP_Packet_RR rr = new RTCP_Packet_RR(); - rr.SSRC = m_pRtcpSource.SSRC; - compundPacket.Packets.Add(rr); - int sourcesProcessed = 0; - while (sourcesProcessed < m_pLocalSources.Count) - { - uint[] sources = new uint[Math.Min(m_pLocalSources.Count - sourcesProcessed, 31)]; - for (int i = 0; i < sources.Length; i++) - { - sources[i] = m_pLocalSources[sourcesProcessed].SSRC; - sourcesProcessed++; - } - - RTCP_Packet_BYE bye = new RTCP_Packet_BYE(); - bye.Sources = sources; - compundPacket.Packets.Add(bye); - } - - // Send BYE. - SendRtcpPacket(compundPacket); - - OnClosed(); - Dispose(); - } - - /// - /// Starts RTP session. - /// - /// Is raised when this class is Disposed and this method is accessed. - public void Start() - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - /* RFC 3550 6.3.2 Initialization - Upon joining the session, the participant initializes tp to 0, tc to - 0, senders to 0, pmembers to 1, members to 1, we_sent to false, - rtcp_bw to the specified fraction of the session bandwidth, initial - to true, and avg_rtcp_size to the probable size of the first RTCP - packet that the application will later construct. The calculated - interval T is then computed, and the first packet is scheduled for - time tn = T. This means that a transmission timer is set which - expires at time T. Note that an application MAY use any desired - approach for implementing this timer. - - The participant adds its own SSRC to the member table. - */ - - m_PMembersCount = 1; - m_RtcpAvgPacketSize = 100; - - // Add ourself to members list. - m_pRtcpSource = CreateLocalSource(); - m_pMembers.Add(m_pRtcpSource.SSRC, m_pRtcpSource); - - #region IO completion ports - - if (Net_Utils.IsIoCompletionPortsSupported()) - { - // Start receiving RTP packet. - SocketAsyncEventArgs rtpArgs = new SocketAsyncEventArgs(); - rtpArgs.Completed += delegate(object s, SocketAsyncEventArgs e) - { - if (m_IsDisposed) - { - return; - } - - if (e.SocketError == SocketError.Success) - { - ProcessRtp(m_pRtpReceiveBuffer, - e.BytesTransferred, - (IPEndPoint) rtpArgs.RemoteEndPoint); - } - - // Start receiving next packet. - RtpIOCompletionReceive(e); - }; - // Move processing off the active thread, because ReceiveFromAsync can complete synchronously. - ThreadPool.QueueUserWorkItem(delegate { RtpIOCompletionReceive(rtpArgs); }); - - // Start receiving RTCP packet. - SocketAsyncEventArgs rtcpArgs = new SocketAsyncEventArgs(); - rtcpArgs.SetBuffer(m_pRtcpReceiveBuffer, 0, m_pRtcpReceiveBuffer.Length); - rtcpArgs.Completed += delegate(object s, SocketAsyncEventArgs e) - { - if (m_IsDisposed) - { - return; - } - - if (e.SocketError == SocketError.Success) - { - ProcessRtcp(m_pRtcpReceiveBuffer, - e.BytesTransferred, - (IPEndPoint) e.RemoteEndPoint); - } - - // Start receiving next packet. - RtcpIOCompletionReceive(e); - }; - // Move processing off the active thread, because ReceiveFromAsync can complete synchronously. - ThreadPool.QueueUserWorkItem(delegate { RtcpIOCompletionReceive(rtcpArgs); }); - } - - #endregion - - #region Async sockets - - else - { - // Start receiving RTP packet. - EndPoint rtpRemoteEP = - new IPEndPoint( - m_pRtpSocket.AddressFamily == AddressFamily.InterNetwork - ? IPAddress.Any - : IPAddress.IPv6Any, - 0); - m_pRtpSocket.BeginReceiveFrom(m_pRtpReceiveBuffer, - 0, - m_pRtpReceiveBuffer.Length, - SocketFlags.None, - ref rtpRemoteEP, - RtpAsyncSocketReceiveCompleted, - null); - - // Start receiving RTCP packet. - EndPoint rtcpRemoteEP = - new IPEndPoint( - m_pRtcpSocket.AddressFamily == AddressFamily.InterNetwork - ? IPAddress.Any - : IPAddress.IPv6Any, - 0); - m_pRtcpSocket.BeginReceiveFrom(m_pRtcpReceiveBuffer, - 0, - m_pRtcpReceiveBuffer.Length, - SocketFlags.None, - ref rtcpRemoteEP, - RtcpAsyncSocketReceiveCompleted, - null); - } - - #endregion - - // Start RTCP reporting. - Schedule(ComputeRtcpTransmissionInterval(m_pMembers.Count, - m_pSenders.Count, - m_Bandwidth*0.25, - false, - m_RtcpAvgPacketSize, - true)); - } - - /// - /// Stops RTP session. - /// - /// Is raised when this class is Disposed and this method is accessed. - public void Stop() - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - // TODO: - - throw new NotImplementedException(); - } - - /// - /// Creates new send stream. - /// - /// Returns new created send stream. - /// Is raised when this class is Disposed and this method is accessed. - public RTP_SendStream CreateSendStream() - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - RTP_Source_Local source = CreateLocalSource(); - source.CreateStream(); - - OnNewSendStream(source.Stream); - - return source.Stream; - } - - /// - /// Opens RTP session to the specified remote target. - /// - /// Once RTP session opened, RTCP reports sent to that target and also each local sending stream data. - /// Session remote target. - /// Is raised when target is null reference. - /// Is raised when any of the arguments has invalid values. - public void AddTarget(RTP_Address target) - { - if (target == null) - { - throw new ArgumentNullException("target"); - } - if (m_pLocalEP.Equals(target)) - { - throw new ArgumentException("Argument 'target' value collapses with property 'LocalEP'.", - "target"); - } - - foreach (RTP_Address t in Targets) - { - if (t.Equals(target)) - { - throw new ArgumentException("Specified target already exists.", "target"); - } - } - - m_pTargets.Add(target); - } - - /// - /// Removes specified target. - /// - /// Session remote target. - /// Is raised when target is null reference. - public void RemoveTarget(RTP_Address target) - { - if (target == null) - { - throw new ArgumentNullException("target"); - } - - m_pTargets.Remove(target); - } - - #endregion - - #region Utility methods - - /// - /// Processes specified RTCP data. - /// - /// Data buffer. - /// Number of bytes in data buffer. - /// IP end point what sent RTCP packet. - /// Is raised when buffer or remoteEP is null reference. - private void ProcessRtcp(byte[] buffer, int count, IPEndPoint remoteEP) - { - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (remoteEP == null) - { - throw new ArgumentNullException("remoteEP"); - } - - /* RFC 3550 6.3.3 Receiving an RTP or Non-BYE RTCP Packet - When an RTP or RTCP packet is received from a participant whose SSRC - is not in the member table, the SSRC is added to the table, and the - value for members is updated once the participant has been validated - as described in Section 6.2.1. The same processing occurs for each - CSRC in a validated RTP packet. - - For each compound RTCP packet received, the value of avg_rtcp_size is - updated: - avg_rtcp_size = (1/16) * packet_size + (15/16) * avg_rtcp_size - - where packet_size is the size of the RTCP packet just received. - - 6.3.4 Receiving an RTCP BYE Packet - Except as described in Section 6.3.7 for the case when an RTCP BYE is - to be transmitted, if the received packet is an RTCP BYE packet, the - SSRC is checked against the member table. If present, the entry is - removed from the table, and the value for members is updated. The - SSRC is then checked against the sender table. If present, the entry - is removed from the table, and the value for senders is updated. - - Furthermore, to make the transmission rate of RTCP packets more - adaptive to changes in group membership, the following "reverse - reconsideration" algorithm SHOULD be executed when a BYE packet is - received. - */ - - m_RtcpPacketsReceived++; - m_RtcpBytesReceived += count; - // RFC requires IP header counted too, we just don't do it. - m_RtcpAvgPacketSize = (1/16)*count + (15/16)*m_RtcpAvgPacketSize; - - try - { - RTCP_CompoundPacket compoundPacket = RTCP_CompoundPacket.Parse(buffer, count); - // Process each RTCP packet. - foreach (RTCP_Packet packet in compoundPacket.Packets) - { - #region APP - - if (packet.Type == RTCP_PacketType.APP) - { - RTCP_Packet_APP app = ((RTCP_Packet_APP) packet); - - RTP_Source_Remote source = GetOrCreateSource(true, app.Source, null, remoteEP); - if (source != null) - { - source.SetLastRtcpPacket(DateTime.Now); - source.OnAppPacket(app); - } - } - - #endregion - - #region BYE - - else if (packet.Type == RTCP_PacketType.BYE) - { - RTCP_Packet_BYE bye = ((RTCP_Packet_BYE) packet); - - bool membersChanges = false; - foreach (uint src in bye.Sources) - { - RTP_Source source = GetOrCreateSource(true, src, null, remoteEP); - if (source != null) - { - membersChanges = true; - m_pMembers.Remove(src); - source.Close(bye.LeavingReason); - // Closing source will take care of closing it's underlaying stream, if source is "active". - } - - m_pSenders.Remove(src); - } - if (membersChanges) - { - DoReverseReconsideration(); - } - } - - #endregion - - #region RR - - else if (packet.Type == RTCP_PacketType.RR) - { - RTCP_Packet_RR rr = ((RTCP_Packet_RR) packet); - - RTP_Source source = GetOrCreateSource(true, rr.SSRC, null, remoteEP); - if (source != null) - { - source.SetLastRtcpPacket(DateTime.Now); - - foreach (RTCP_Packet_ReportBlock reportBlock in rr.ReportBlocks) - { - source = GetOrCreateSource(true, rr.SSRC, null, remoteEP); - if (source != null) - { - source.SetLastRtcpPacket(DateTime.Now); - source.SetRR(reportBlock); - } - } - } - } - - #endregion - - #region SDES - - else if (packet.Type == RTCP_PacketType.SDES) - { - foreach (RTCP_Packet_SDES_Chunk sdes in ((RTCP_Packet_SDES) packet).Chunks) - { - RTP_Source source = GetOrCreateSource(true, sdes.Source, sdes.CName, remoteEP); - if (source != null) - { - source.SetLastRtcpPacket(DateTime.Now); - - RTP_Participant_Remote participant = - m_pSession.GetOrCreateParticipant(sdes.CName); - - // Map participant to source. - ((RTP_Source_Remote) source).SetParticipant(participant); - - // Map source to participant. - participant.EnsureSource(source); - - // Update participant SDES items. - participant.Update(sdes); - } - } - } - - #endregion - - #region SR - - else if (packet.Type == RTCP_PacketType.SR) - { - RTCP_Packet_SR sr = ((RTCP_Packet_SR) packet); - - RTP_Source_Remote source = GetOrCreateSource(true, sr.SSRC, null, remoteEP); - if (source != null) - { - source.SetLastRtcpPacket(DateTime.Now); - source.OnSenderReport(new RTCP_Report_Sender(sr)); - - foreach (RTCP_Packet_ReportBlock reportBlock in sr.ReportBlocks) - { - source = GetOrCreateSource(true, sr.SSRC, null, remoteEP); - if (source != null) - { - source.SetLastRtcpPacket(DateTime.Now); - source.SetRR(reportBlock); - } - } - } - } - - #endregion - - // Unknown packet. - else - { - m_RtcpUnknownPacketsReceived++; - } - } - } - catch (Exception x) - { - m_pSession.OnError(x); - } - } - - /// - /// Processes specified RTP data. - /// - /// Data buffer. - /// Number of bytes in data buffer. - /// IP end point what sent RTCP packet. - /// Is raised when buffer or remoteEP is null reference. - private void ProcessRtp(byte[] buffer, int count, IPEndPoint remoteEP) - { - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - if (remoteEP == null) - { - throw new ArgumentNullException("remoteEP"); - } - - /* RFC 3550 6.3.3 Receiving an RTP or Non-BYE RTCP Packet - When an RTP or RTCP packet is received from a participant whose SSRC - is not in the member table, the SSRC is added to the table, and the - value for members is updated once the participant has been validated - as described in Section 6.2.1. The same processing occurs for each - CSRC in a validated RTP packet. - - When an RTP packet is received from a participant whose SSRC is not - in the sender table, the SSRC is added to the table, and the value - for senders is updated. - */ - - m_RtpPacketsReceived++; - m_RtpBytesReceived += count; - - try - { - RTP_Packet packet = RTP_Packet.Parse(buffer, count); - - RTP_Source source = GetOrCreateSource(false, packet.SSRC, null, remoteEP); - if (source != null) - { - // Process CSRC. - foreach (uint csrc in packet.CSRC) - { - RTP_Source dummy = GetOrCreateSource(false, packet.SSRC, null, remoteEP); - } - - lock (m_pSenders) - { - if (!m_pSenders.ContainsKey(source.SSRC)) - { - m_pSenders.Add(source.SSRC, source); - } - } - - // Let source to process RTP packet. - ((RTP_Source_Remote) source).OnRtpPacketReceived(packet, count); - } - } - catch (Exception x) - { - m_pSession.OnError(x); - } - } - - /// - /// Gets or creates source. This method also does RFC 3550 8.2 "Collision Resolution and Loop Detection". - /// - /// If true src is RTCP identifier, otherwise RTP identifier. - /// Source SSRC or CSRC identifier. - /// RTCP SDES chunk CNAME. Must be passed only if src if from RTCP SDES chunk. - /// Packet sender end point. - /// Returns specified source. Returns null if source has "collision or loop". - /// Is raised when packetEP is null reference. - private RTP_Source_Remote GetOrCreateSource(bool rtcp_rtp, uint src, string cname, IPEndPoint packetEP) - { - if (packetEP == null) - { - throw new ArgumentNullException("packetEP"); - } - - /* RFC 3550 8.2. - if(SSRC or CSRC identifier is not found in the source identifier table){ - create a new entry storing the data or control source transport address, the SSRC or CSRC and other state; - } - else if(table entry was created on receipt of a control packet and this is the first data packet or vice versa){ - store the source transport address from this packet; - } - else if(source transport address from the packet does not match the one saved in the table entry for this identifier){ - // An identifier collision or a loop is indicated - if(source identifier is not the participant's own){ - // OPTIONAL error counter step - if(source identifier is from an RTCP SDES chunk containing a CNAME item that differs from the CNAME in the table entry){ - count a third-party collision; - } - else{ - count a third-party loop; - } - - abort processing of data packet or control element; - // MAY choose a different policy to keep new source - } - // A collision or loop of the participant's own packets - else if(source transport address is found in the list of conflicting data or control source transport addresses){ - // OPTIONAL error counter step - if(source identifier is not from an RTCP SDES chunk containing a CNAME item or CNAME is the participant's own){ - count occurrence of own traffic looped; - } - - mark current time in conflicting address list entry; - abort processing of data packet or control element; - } - // New collision, change SSRC identifier - else{ - log occurrence of a collision; - create a new entry in the conflicting data or control source transport address list and mark current time; - send an RTCP BYE packet with the old SSRC identifier; - choose a new SSRC identifier; - create a new entry in the source identifier table with the old SSRC plus the source transport address from - the data or control packet being processed; - } - } - */ - - RTP_Source source = null; - - lock (m_pMembers) - { - m_pMembers.TryGetValue(src, out source); - - // SSRC or CSRC identifier is not found in the source identifier table. - if (source == null) - { - source = new RTP_Source_Remote(this, src); - if (rtcp_rtp) - { - source.SetRtcpEP(packetEP); - } - else - { - source.SetRtpEP(packetEP); - } - m_pMembers.Add(src, source); - } - // Table entry was created on receipt of a control packet and this is the first data packet or vice versa. - else if ((rtcp_rtp ? source.RtcpEP : source.RtpEP) == null) - { - if (rtcp_rtp) - { - source.SetRtcpEP(packetEP); - } - else - { - source.SetRtpEP(packetEP); - } - } - // Source transport address from the packet does not match the one saved in the table entry for this identifier. - else if (!packetEP.Equals((rtcp_rtp ? source.RtcpEP : source.RtpEP))) - { - // Source identifier is not the participant's own. - if (!source.IsLocal) - { - if (cname != null && cname != source.CName) - { - m_RemoteCollisions++; - } - else - { - m_RemotePacketsLooped++; - } - - return null; - } - // A collision or loop of the participant's own packets. - else if (m_pConflictingEPs.ContainsKey(packetEP.ToString())) - { - if (cname == null || cname == source.CName) - { - m_LocalPacketsLooped++; - } - - m_pConflictingEPs[packetEP.ToString()] = DateTime.Now; - - return null; - } - // New collision, change SSRC identifier. - else - { - m_LocalCollisions++; - m_pConflictingEPs.Add(packetEP.ToString(), DateTime.Now); - - // Remove SSRC from members,senders. Choose new SSRC, CNAME new and BYE old. - m_pMembers.Remove(source.SSRC); - m_pSenders.Remove(source.SSRC); - uint oldSSRC = source.SSRC; - source.GenerateNewSSRC(); - // Ensure that new SSRC is not in use, if so repaeat while not conflicting SSRC. - while (m_pMembers.ContainsKey(source.SSRC)) - { - source.GenerateNewSSRC(); - } - m_pMembers.Add(source.SSRC, source); - - RTCP_CompoundPacket compoundPacket = new RTCP_CompoundPacket(); - RTCP_Packet_RR rr = new RTCP_Packet_RR(); - rr.SSRC = m_pRtcpSource.SSRC; - compoundPacket.Packets.Add(rr); - RTCP_Packet_SDES sdes = new RTCP_Packet_SDES(); - RTCP_Packet_SDES_Chunk sdes_chunk = new RTCP_Packet_SDES_Chunk(source.SSRC, - m_pSession. - LocalParticipant. - CNAME); - sdes.Chunks.Add(sdes_chunk); - compoundPacket.Packets.Add(sdes); - RTCP_Packet_BYE bye = new RTCP_Packet_BYE(); - bye.Sources = new[] {oldSSRC}; - bye.LeavingReason = "Collision, changing SSRC."; - compoundPacket.Packets.Add(bye); - - SendRtcpPacket(compoundPacket); - //---------------------------------------------------------------------- - - // Add new source to members, it's not conflicting any more, we changed SSRC. - source = new RTP_Source_Remote(this, src); - if (rtcp_rtp) - { - source.SetRtcpEP(packetEP); - } - else - { - source.SetRtpEP(packetEP); - } - m_pMembers.Add(src, source); - } - } - } - - return (RTP_Source_Remote) source; - } - - /// - /// Schedules RTCP transmission. - /// - /// After number of seconds to transmit next RTCP. - private void Schedule(int seconds) - { - m_pRtcpTimer.Stop(); - m_pRtcpTimer.Interval = seconds*1000; - m_pRtcpTimer.Enabled = true; - } - - /// - /// Computes RTCP transmission interval. Defined in RFC 3550 6.3.1. - /// - /// Current mebers count. - /// Current sender count. - /// RTCP bandwidth. - /// Specifies if we have sent data after last 2 RTCP interval. - /// Average RTCP raw packet size, IP headers included. - /// Specifies if we ever have sent data to target. - /// Returns transmission interval in seconds. - private int ComputeRtcpTransmissionInterval(int members, - int senders, - double rtcp_bw, - bool we_sent, - double avg_rtcp_size, - bool initial) - { - // RFC 3550 A.7. - - /* - Minimum average time between RTCP packets from this site (in - seconds). This time prevents the reports from `clumping' when - sessions are small and the law of large numbers isn't helping - to smooth out the traffic. It also keeps the report interval - from becoming ridiculously small during transient outages like - a network partition. - */ - double RTCP_MIN_TIME = 5; - /* - Fraction of the RTCP bandwidth to be shared among active - senders. (This fraction was chosen so that in a typical - session with one or two active senders, the computed report - time would be roughly equal to the minimum report time so that - we don't unnecessarily slow down receiver reports.) The - receiver fraction must be 1 - the sender fraction. - */ - double RTCP_SENDER_BW_FRACTION = 0.25; - double RTCP_RCVR_BW_FRACTION = (1 - RTCP_SENDER_BW_FRACTION); - /* - To compensate for "timer reconsideration" converging to a - value below the intended average. - */ - double COMPENSATION = 2.71828 - 1.5; - - double t; /* interval */ - double rtcp_min_time = RTCP_MIN_TIME; - int n; /* no. of members for computation */ - - /* - Very first call at application start-up uses half the min - delay for quicker notification while still allowing some time - before reporting for randomization and to learn about other - sources so the report interval will converge to the correct - interval more quickly. - */ - if (initial) - { - rtcp_min_time /= 2; - } - /* - Dedicate a fraction of the RTCP bandwidth to senders unless - the number of senders is large enough that their share is - more than that fraction. - */ - n = members; - if (senders <= (members*RTCP_SENDER_BW_FRACTION)) - { - if (we_sent) - { - rtcp_bw = (rtcp_bw*RTCP_SENDER_BW_FRACTION); - n = senders; - } - else - { - rtcp_bw = (rtcp_bw*RTCP_SENDER_BW_FRACTION); - n -= senders; - } - } - - /* - The effective number of sites times the average packet size is - the total number of octets sent when each site sends a report. - Dividing this by the effective bandwidth gives the time - interval over which those packets must be sent in order to - meet the bandwidth target, with a minimum enforced. In that - time interval we send one report so this time is also our - average time between reports. - */ - t = avg_rtcp_size*n/rtcp_bw; - if (t < rtcp_min_time) - { - t = rtcp_min_time; - } - - /* - To avoid traffic bursts from unintended synchronization with - other sites, we then pick our actual next report interval as a - random number uniformly distributed between 0.5*t and 1.5*t. - */ - t = t*(new Random().Next(5, 15)/10.0); - t = t/COMPENSATION; - - return (int) Math.Max(t, 2.0); - } - - /// - /// Does "reverse reconsideration" algorithm. Defined in RFC 3550 6.3.4. - /// - private void DoReverseReconsideration() - { - /* RFC 3550 6.3.4. "reverse reconsideration" - o The value for tn is updated according to the following formula: - tn = tc + (members/pmembers) * (tn - tc) - - o The value for tp is updated according the following formula: - tp = tc - (members/pmembers) * (tc - tp). - - o The next RTCP packet is rescheduled for transmission at time tn, - which is now earlier. - - o The value of pmembers is set equal to members. - - This algorithm does not prevent the group size estimate from - incorrectly dropping to zero for a short time due to premature - timeouts when most participants of a large session leave at once but - some remain. The algorithm does make the estimate return to the - correct value more rapidly. This situation is unusual enough and the - consequences are sufficiently harmless that this problem is deemed - only a secondary concern. - */ - - DateTime timeNext = m_RtcpLastTransmission == DateTime.MinValue - ? DateTime.Now - : m_RtcpLastTransmission.AddMilliseconds(m_pRtcpTimer.Interval); - - Schedule( - (int) Math.Max((m_pMembers.Count/m_PMembersCount)*((timeNext - DateTime.Now)).TotalSeconds, 2)); - - m_PMembersCount = m_pMembers.Count; - } - - /// - /// Does RFC 3550 6.3.5 Timing Out an SSRC. - /// - private void TimeOutSsrc() - { - /* RFC 3550 6.3.5 Timing Out an SSRC. - At occasional intervals, the participant MUST check to see if any of - the other participants time out. To do this, the participant - computes the deterministic (without the randomization factor) - calculated interval Td for a receiver, that is, with we_sent false. - Any other session member who has not sent an RTP or RTCP packet since - time tc - MTd (M is the timeout multiplier, and defaults to 5) is - timed out. This means that its SSRC is removed from the member list, - and members is updated. A similar check is performed on the sender - list. Any member on the sender list who has not sent an RTP packet - since time tc - 2T (within the last two RTCP report intervals) is - removed from the sender list, and senders is updated. - - If any members time out, the reverse reconsideration algorithm - described in Section 6.3.4 SHOULD be performed. - - The participant MUST perform this check at least once per RTCP - transmission interval. - */ - - bool membersUpdated = false; - - // Senders check. - RTP_Source[] senders = new RTP_Source[m_pSenders.Count]; - m_pSenders.Values.CopyTo(senders, 0); - foreach (RTP_Source sender in senders) - { - // Sender has not sent RTP data since last two RTCP intervals. - if (sender.LastRtpPacket.AddMilliseconds(2*m_pRtcpTimer.Interval) < DateTime.Now) - { - m_pSenders.Remove(sender.SSRC); - - // Mark source "passive". - sender.SetActivePassive(false); - } - } - - int Td = ComputeRtcpTransmissionInterval(m_pMembers.Count, - m_pSenders.Count, - m_Bandwidth*0.25, - false, - m_RtcpAvgPacketSize, - false); - - // Members check. - foreach (RTP_Source member in Members) - { - // Source timed out. - if (member.LastActivity.AddSeconds(5*Td) < DateTime.Now) - { - m_pMembers.Remove(member.SSRC); - // Don't dispose local source, just remove only from members. - if (!member.IsLocal) - { - member.Dispose(); - } - membersUpdated = true; - } - } - - if (membersUpdated) - { - DoReverseReconsideration(); - } - } - - /// - /// Sends RTCP report. - /// - private void SendRtcp() - { - /* RFC 3550 6.4 Sender and Receiver Reports - RTP receivers provide reception quality feedback using RTCP report - packets which may take one of two forms depending upon whether or not - the receiver is also a sender. The only difference between the - sender report (SR) and receiver report (RR) forms, besides the packet - type code, is that the sender report includes a 20-byte sender - information section for use by active senders. The SR is issued if a - site has sent any data packets during the interval since issuing the - last report or the previous one, otherwise the RR is issued. - - Both the SR and RR forms include zero or more reception report - blocks, one for each of the synchronization sources from which this - receiver has received RTP data packets since the last report. - Reports are not issued for contributing sources listed in the CSRC - list. Each reception report block provides statistics about the data - received from the particular source indicated in that block. Since a - maximum of 31 reception report blocks will fit in an SR or RR packet, - additional RR packets SHOULD be stacked after the initial SR or RR - packet as needed to contain the reception reports for all sources - heard during the interval since the last report. If there are too - many sources to fit all the necessary RR packets into one compound - RTCP packet without exceeding the MTU of the network path, then only - the subset that will fit into one MTU SHOULD be included in each - interval. The subsets SHOULD be selected round-robin across multiple - intervals so that all sources are reported. - */ - - bool we_sent = false; - - try - { - m_pRtcpSource.SetLastRtcpPacket(DateTime.Now); - - RTCP_CompoundPacket compundPacket = new RTCP_CompoundPacket(); - - RTCP_Packet_RR rr = null; - - // Find active send streams. - List activeSendStreams = new List(); - foreach (RTP_SendStream stream in SendStreams) - { - if (stream.RtcpCyclesSinceWeSent < 2) - { - activeSendStreams.Add(stream); - we_sent = true; - } - // Notify stream about RTCP cycle. - stream.RtcpCycle(); - } - - #region SR(s) / RR - - // We are sender. - if (we_sent) - { - // Create SR for each active send stream. - for (int i = 0; i < activeSendStreams.Count; i++) - { - RTP_SendStream sendStream = activeSendStreams[i]; - - RTCP_Packet_SR sr = new RTCP_Packet_SR(sendStream.Source.SSRC); - sr.NtpTimestamp = RTP_Utils.DateTimeToNTP64(DateTime.Now); - sr.RtpTimestamp = m_pRtpClock.RtpTimestamp; - sr.SenderPacketCount = (uint) sendStream.RtpPacketsSent; - sr.SenderOctetCount = (uint) sendStream.RtpBytesSent; - - compundPacket.Packets.Add(sr); - } - } - // We are receiver. - else - { - rr = new RTCP_Packet_RR(); - rr.SSRC = m_pRtcpSource.SSRC; - compundPacket.Packets.Add(rr); - - // Report blocks added later. - } - - #endregion - - #region SDES - - RTCP_Packet_SDES sdes = new RTCP_Packet_SDES(); - // Add default SSRC. - RTCP_Packet_SDES_Chunk sdesChunk = new RTCP_Packet_SDES_Chunk(m_pRtcpSource.SSRC, - m_pSession.LocalParticipant. - CNAME); - // Add next optional SDES item, if any. (We round-robin optional items) - m_pSession.LocalParticipant.AddNextOptionalSdesItem(sdesChunk); - sdes.Chunks.Add(sdesChunk); - // Add all active send streams SSRC -> CNAME. This enusres that all send streams will be mapped to participant. - foreach (RTP_SendStream stream in activeSendStreams) - { - sdes.Chunks.Add(new RTCP_Packet_SDES_Chunk(stream.Source.SSRC, - m_pSession.LocalParticipant.CNAME)); - } - compundPacket.Packets.Add(sdes); - - #endregion - - #region RR filling - - /* RR reporting: - Report up to 31 active senders, if more senders, reoprt next with next interval. - Report oldest not reported first,then ventually all sources will be reported with this algorythm. - */ - RTP_Source[] senders = Senders; - DateTime[] acitveSourceRRTimes = new DateTime[senders.Length]; - RTP_ReceiveStream[] activeSenders = new RTP_ReceiveStream[senders.Length]; - int activeSenderCount = 0; - foreach (RTP_Source sender in senders) - { - // Remote sender sent RTP data during last RTCP interval. - if (!sender.IsLocal && sender.LastRtpPacket > m_RtcpLastTransmission) - { - acitveSourceRRTimes[activeSenderCount] = sender.LastRRTime; - activeSenders[activeSenderCount] = ((RTP_Source_Remote) sender).Stream; - activeSenderCount++; - } - } - // Create RR is SR report and no RR created yet. - if (rr == null) - { - rr = new RTCP_Packet_RR(); - rr.SSRC = m_pRtcpSource.SSRC; - compundPacket.Packets.Add(rr); - } - // Sort ASC. - Array.Sort(acitveSourceRRTimes, activeSenders, 0, activeSenderCount); - // Add up to 31 oldest not reported sources to report. - for (int i = 1; i < 31; i++) - { - if ((activeSenderCount - i) < 0) - { - break; - } - rr.ReportBlocks.Add(activeSenders[activeSenderCount - i].CreateReceiverReport()); - } - - #endregion - - // Send RTPC packet. - SendRtcpPacket(compundPacket); - - // Timeout conflicting transport addresses, if not conflicting any more. - lock (m_pConflictingEPs) - { - string[] keys = new string[m_pConflictingEPs.Count]; - m_pConflictingEPs.Keys.CopyTo(keys, 0); - foreach (string key in keys) - { - if (m_pConflictingEPs[key].AddMinutes(3) < DateTime.Now) - { - m_pConflictingEPs.Remove(key); - } - } - } - - // Since we must check timing out sources at least once per RTCP interval, so we - // check this before sending RTCP. - TimeOutSsrc(); - } - catch (Exception x) - { - m_pSession.OnError(x); - } - - m_RtcpLastTransmission = DateTime.Now; - - // Schedule next RTCP sending. - Schedule(ComputeRtcpTransmissionInterval(m_pMembers.Count, - m_pSenders.Count, - m_Bandwidth*0.25, - we_sent, - m_RtcpAvgPacketSize, - false)); - } - - /// - /// Is called when RTP socket has received data. - /// - /// The result of the asynchronous operation. - private void RtpAsyncSocketReceiveCompleted(IAsyncResult ar) - { - try - { - EndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0); - int count = m_pRtpSocket.EndReceiveFrom(ar, ref remoteEP); - - ProcessRtp(m_pRtpReceiveBuffer, count, (IPEndPoint) remoteEP); - } - catch - { - // Skip receiving socket errors. - } - - // Start receiving next RTP packet. - EndPoint remEP = new IPEndPoint(IPAddress.Any, 0); - m_pRtpSocket.BeginReceiveFrom(m_pRtpReceiveBuffer, - 0, - m_pRtpReceiveBuffer.Length, - SocketFlags.None, - ref remEP, - RtpAsyncSocketReceiveCompleted, - null); - - // TODO: we may get 10054 error here if IP conflict - } - - /// - /// Is called when RTCP socket has received data. - /// - /// The result of the asynchronous operation. - private void RtcpAsyncSocketReceiveCompleted(IAsyncResult ar) - { - try - { - EndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0); - int count = m_pRtcpSocket.EndReceiveFrom(ar, ref remoteEP); - - ProcessRtcp(m_pRtcpReceiveBuffer, count, (IPEndPoint) remoteEP); - } - catch - { - // Skip receiving socket errors. - } - - // Start receiving next RTCP packet. - EndPoint remEP = new IPEndPoint(IPAddress.Any, 0); - m_pRtcpSocket.BeginReceiveFrom(m_pRtcpReceiveBuffer, - 0, - m_pRtcpReceiveBuffer.Length, - SocketFlags.None, - ref remEP, - RtcpAsyncSocketReceiveCompleted, - null); - - // TODO: we may get 10054 error here if IP conflict - } - - /// - /// Accepts or starts accepting incoming RTP data. - /// - /// ReceiveFromAsync method data. - /// Is raised when socketArgs is null reference. - private void RtpIOCompletionReceive(SocketAsyncEventArgs socketArgs) - { - if (socketArgs == null) - { - throw new ArgumentNullException("socketArgs"); - } - - try - { - // Reset state for reuse. - socketArgs.SetBuffer(m_pRtpReceiveBuffer, 0, m_pRtpReceiveBuffer.Length); - socketArgs.RemoteEndPoint = - new IPEndPoint( - m_pRtcpSocket.AddressFamily == AddressFamily.InterNetwork - ? IPAddress.Any - : IPAddress.IPv6Any, - 0); - - // Use active worker thread as long as ReceiveFromAsync completes synchronously. - // (With this approeach we don't have thread context switches while ReceiveFromAsync completes synchronously) - while (!m_pRtpSocket.ReceiveFromAsync(socketArgs)) - { - if (socketArgs.SocketError == SocketError.Success) - { - ProcessRtp(m_pRtpReceiveBuffer, - socketArgs.BytesTransferred, - (IPEndPoint) socketArgs.RemoteEndPoint); - } - } - } - catch (Exception x) - { - m_pSession.OnError(x); - } - } - - /// - /// Accepts or starts accepting incoming RTCP data. - /// - /// ReceiveFromAsync method data. - /// Is raised when socketArgs is null reference. - private void RtcpIOCompletionReceive(SocketAsyncEventArgs socketArgs) - { - if (socketArgs == null) - { - throw new ArgumentNullException("socketArgs"); - } - - // Reset state for reuse. - socketArgs.SetBuffer(m_pRtcpReceiveBuffer, 0, m_pRtcpReceiveBuffer.Length); - socketArgs.RemoteEndPoint = - new IPEndPoint( - m_pRtcpSocket.AddressFamily == AddressFamily.InterNetwork - ? IPAddress.Any - : IPAddress.IPv6Any, - 0); - - // Use active worker thread as long as ReceiveFromAsync completes synchronously. - // (With this approeach we don't have thread context switches while ReceiveFromAsync completes synchronously) - while (!m_pRtcpSocket.ReceiveFromAsync(socketArgs)) - { - if (socketArgs.SocketError == SocketError.Success) - { - ProcessRtcp(m_pRtcpReceiveBuffer, - socketArgs.BytesTransferred, - (IPEndPoint) socketArgs.RemoteEndPoint); - } - } - } - - /// - /// Raises Disposed event. - /// - private void OnDisposed() - { - if (Disposed != null) - { - Disposed(this, new EventArgs()); - } - } - - /// - /// Raises Closed event. - /// - private void OnClosed() - { - if (Closed != null) - { - Closed(this, new EventArgs()); - } - } - - /// - /// Raises NewSendStream event. - /// - /// New send stream. - private void OnNewSendStream(RTP_SendStream stream) - { - if (NewSendStream != null) - { - NewSendStream(this, new RTP_SendStreamEventArgs(stream)); - } - } - - #endregion - - #region Internal methods - - /// - /// Sends specified RTCP packet to the session remote party. - /// - /// RTCP compound packet. - /// Returns packet size in bytes. - /// Is raised when this class is Disposed and this method is accessed. - /// Is raised when packet is null reference. - internal int SendRtcpPacket(RTCP_CompoundPacket packet) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (packet == null) - { - throw new ArgumentNullException("packet"); - } - - byte[] packetBytes = packet.ToByte(); - - // Send packet to each remote target. - foreach (RTP_Address target in Targets) - { - try - { - m_pRtcpSocket.SendTo(packetBytes, packetBytes.Length, SocketFlags.None, target.RtcpEP); - - m_RtcpPacketsSent++; - m_RtcpBytesSent += packetBytes.Length; - // RFC requires IP header counted too, we just don't do it. - m_RtcpAvgPacketSize = (1/16)*packetBytes.Length + (15/16)*m_RtcpAvgPacketSize; - } - catch - { - m_RtcpFailedTransmissions++; - } - } - - return packetBytes.Length; - } - - /// - /// Sends specified RTP packet to the session remote party. - /// - /// RTP packet sending stream. - /// RTP packet. - /// Returns packet size in bytes. - /// Is raised when this class is Disposed and this method is accessed. - /// Is raised when stream or packet is null reference. - internal int SendRtpPacket(RTP_SendStream stream, RTP_Packet packet) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - if (packet == null) - { - throw new ArgumentNullException("packet"); - } - - // Check that we are in members table (because SSRC has timed out), add itself to senders table. - lock (m_pMembers) - { - if (!m_pMembers.ContainsKey(stream.Source.SSRC)) - { - m_pMembers.Add(stream.Source.SSRC, stream.Source); - } - } - - // If we are not in sender table (because SSRC has timed out), add itself to senders table. - lock (m_pSenders) - { - if (!m_pSenders.ContainsKey(stream.Source.SSRC)) - { - m_pSenders.Add(stream.Source.SSRC, stream.Source); - } - } - - byte[] packetBytes = new byte[m_MTU]; - int count = 0; - packet.ToByte(packetBytes, ref count); - - // Send packet to each remote target. - foreach (RTP_Address target in Targets) - { - try - { - m_pRtpSocket.SendTo(packetBytes, count, SocketFlags.None, target.RtpEP); - - m_RtpPacketsSent++; - m_RtpBytesSent += packetBytes.Length; - } - catch - { - m_RtpFailedTransmissions++; - } - } - - return count; - } - - /// - /// Creates local source. - /// - /// Returns new local source. - internal RTP_Source_Local CreateLocalSource() - { - uint ssrc = RTP_Utils.GenerateSSRC(); - // Ensure that any member don't have such SSRC. - while (m_pMembers.ContainsKey(ssrc)) - { - ssrc = RTP_Utils.GenerateSSRC(); - } - - RTP_Source_Local source = new RTP_Source_Local(this, ssrc, m_pLocalEP.RtcpEP, m_pLocalEP.RtpEP); - source.Disposing += delegate - { - m_pSenders.Remove(source.SSRC); - m_pMembers.Remove(source.SSRC); - m_pLocalSources.Remove(source); - }; - m_pLocalSources.Add(source); - m_pSession.LocalParticipant.EnsureSource(source); - - return source; - } - - /// - /// Raises NewReceiveStream event. - /// - /// New receive stream. - internal void OnNewReceiveStream(RTP_ReceiveStream stream) - { - if (NewReceiveStream != null) - { - NewReceiveStream(this, new RTP_ReceiveStreamEventArgs(stream)); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Source.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Source.cs deleted file mode 100644 index 5a84c001d..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Source.cs +++ /dev/null @@ -1,440 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - using System.Net; - - #endregion - - /// - /// This class represents RTP source. - /// - /// Source indicates an entity sending packets, either RTP and/or RTCP. - /// Sources what send RTP packets are called "active", only RTCP sending ones are "passive". - /// Source can be local(we send RTP and/or RTCP remote party) or remote(remote party sends RTP and/or RTCP to us). - /// - public abstract class RTP_Source - { - #region Events - - /// - /// Is raised when source is closed (by BYE). - /// - public event EventHandler Closed = null; - - /// - /// Is raised when source is disposing. - /// - public event EventHandler Disposing = null; - - /// - /// Is raised when source state has changed. - /// - public event EventHandler StateChanged = null; - - #endregion - - #region Members - - private readonly DateTime m_LastRRTime = DateTime.MinValue; - private string m_CloseReason; - private DateTime m_LastActivity = DateTime.Now; - private DateTime m_LastRtcpPacket = DateTime.MinValue; - private DateTime m_LastRtpPacket = DateTime.MinValue; - private IPEndPoint m_pRtcpEP; - private IPEndPoint m_pRtpEP; - private RTP_Session m_pSession; - private uint m_SSRC; - private RTP_SourceState m_State = RTP_SourceState.Passive; - - #endregion - - #region Properties - - /// - /// Gets source state. - /// - public RTP_SourceState State - { - get { return m_State; } - } - - /// - /// Gets owner RTP session. - /// - /// Is raised when this class is Disposed and this property is accessed. - public RTP_Session Session - { - get - { - if (m_State == RTP_SourceState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pSession; - } - } - - /// - /// Gets synchronization source ID. - /// - /// Is raised when this class is Disposed and this property is accessed. - public uint SSRC - { - get - { - if (m_State == RTP_SourceState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_SSRC; - } - } - - /// - /// Gets source RTCP end point. Value null means source haven't sent any RTCP packet. - /// - /// Is raised when this class is Disposed and this property is accessed. - public IPEndPoint RtcpEP - { - get - { - if (m_State == RTP_SourceState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pRtcpEP; - } - } - - /// - /// Gets source RTP end point. Value null means source haven't sent any RTCP packet. - /// - /// Is raised when this class is Disposed and this property is accessed. - public IPEndPoint RtpEP - { - get - { - if (m_State == RTP_SourceState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pRtpEP; - } - } - - /// - /// Gets if source is local or remote source. - /// - /// Is raised when this class is Disposed and this property is accessed. - public abstract bool IsLocal { get; } - - /// - /// Gets last time when source sent RTP or RCTP packet. - /// - /// Is raised when this class is Disposed and this property is accessed. - public DateTime LastActivity - { - get - { - if (m_State == RTP_SourceState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_LastActivity; - } - } - - /// - /// Gets last time when source sent RTCP packet. - /// - /// Is raised when this class is Disposed and this property is accessed. - public DateTime LastRtcpPacket - { - get - { - if (m_State == RTP_SourceState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_LastRtcpPacket; - } - } - - /// - /// Gets last time when source sent RTP packet. - /// - /// Is raised when this class is Disposed and this property is accessed. - public DateTime LastRtpPacket - { - get - { - if (m_State == RTP_SourceState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_LastRtpPacket; - } - } - - /// - /// Gets last time when source sent RTCP RR report. - /// - /// Is raised when this class is Disposed and this property is accessed. - public DateTime LastRRTime - { - get - { - if (m_State == RTP_SourceState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_LastRRTime; - } - } - - /// - /// Gets source closing reason. Value null means not specified. - /// - /// Is raised when this class is Disposed and this property is accessed. - public string CloseReason - { - get - { - if (m_State == RTP_SourceState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_CloseReason; - } - } - - /// - /// Gets or sets user data. - /// - public object Tag { get; set; } - - /// - /// Gets source CNAME. Value null means that source not binded to participant. - /// - internal abstract string CName { get; } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner RTP session. - /// Synchronization source ID. - /// Is raised when session is null reference. - internal RTP_Source(RTP_Session session, uint ssrc) - { - if (session == null) - { - throw new ArgumentNullException("session"); - } - - m_pSession = session; - m_SSRC = ssrc; - } - - #endregion - - #region Virtual methods - - /// - /// Cleans up any resources being used. - /// - internal virtual void Dispose() - { - if (m_State == RTP_SourceState.Disposed) - { - return; - } - OnDisposing(); - SetState(RTP_SourceState.Disposed); - - m_pSession = null; - m_pRtcpEP = null; - m_pRtpEP = null; - - Closed = null; - Disposing = null; - StateChanged = null; - } - - /// - /// Closes specified source. - /// - /// Closing reason. Value null means not specified. - internal virtual void Close(string closeReason) - { - m_CloseReason = closeReason; - - OnClosed(); - Dispose(); - } - - #endregion - - #region Utility methods - - /// - /// Raises Closed event. - /// - private void OnClosed() - { - if (Closed != null) - { - Closed(this, new EventArgs()); - } - } - - /// - /// Raises Disposing event. - /// - private void OnDisposing() - { - if (Disposing != null) - { - Disposing(this, new EventArgs()); - } - } - - /// - /// Raises StateChanged event. - /// - private void OnStateChaged() - { - if (StateChanged != null) - { - StateChanged(this, new EventArgs()); - } - } - - #endregion - - #region Internal methods - - /// - /// Sets property RtcpEP value. - /// - /// IP end point. - internal void SetRtcpEP(IPEndPoint ep) - { - m_pRtcpEP = ep; - } - - /// - /// Sets property RtpEP value. - /// - /// IP end point. - internal void SetRtpEP(IPEndPoint ep) - { - m_pRtpEP = ep; - } - - /// - /// Sets source active/passive state. - /// - /// If true, source switches to active, otherwise to passive. - internal void SetActivePassive(bool active) - { - if (active) {} - else {} - - // TODO: - } - - /// - /// Sets LastRtcpPacket property value. - /// - /// Time. - internal void SetLastRtcpPacket(DateTime time) - { - m_LastRtcpPacket = time; - m_LastActivity = time; - } - - /// - /// Sets LastRtpPacket property value. - /// - /// Time. - internal void SetLastRtpPacket(DateTime time) - { - m_LastRtpPacket = time; - m_LastActivity = time; - } - - /// - /// Sets property LastRR value. - /// - /// RTCP RR report. - /// Is raised when rr is null reference. - internal void SetRR(RTCP_Packet_ReportBlock rr) - { - if (rr == null) - { - throw new ArgumentNullException("rr"); - } - } - - /// - /// Generates new SSRC value. This must be called only if SSRC collision of local source. - /// - internal void GenerateNewSSRC() - { - m_SSRC = RTP_Utils.GenerateSSRC(); - } - - #endregion - - /// - /// Sets source state. - /// - /// New source state. - protected void SetState(RTP_SourceState state) - { - if (m_State == RTP_SourceState.Disposed) - { - return; - } - - if (m_State != state) - { - m_State = state; - - OnStateChaged(); - } - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_SourceEventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_SourceEventArgs.cs deleted file mode 100644 index 7c24a5444..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_SourceEventArgs.cs +++ /dev/null @@ -1,68 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - - #endregion - - /// - /// This class provides data for RTP source related evetns. - /// - public class RTP_SourceEventArgs : EventArgs - { - #region Members - - private readonly RTP_Source m_pSource; - - #endregion - - #region Properties - - /// - /// Gets RTP source. - /// - public RTP_Source Source - { - get { return m_pSource; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// RTP source. - /// Is raised when source is null reference. - public RTP_SourceEventArgs(RTP_Source source) - { - if (source == null) - { - throw new ArgumentNullException("source"); - } - - m_pSource = source; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_SourceState.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_SourceState.cs deleted file mode 100644 index 1a5064ff2..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_SourceState.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - /// - /// This enum specifies RTP_SyncSource state. - /// - public enum RTP_SourceState - { - /// - /// Source is passive, sending only RTCP packets. - /// - Passive = 1, - - /// - /// Source is active, sending RTP packets. - /// - Active = 2, - - /// - /// Source has disposed. - /// - Disposed = 3, - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Source_Local.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Source_Local.cs deleted file mode 100644 index 0592c714a..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Source_Local.cs +++ /dev/null @@ -1,239 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - using System.Net; - - #endregion - - /// - /// This class represents local source what we send. - /// - /// Source indicates an entity sending packets, either RTP and/or RTCP. - /// Sources what send RTP packets are called "active", only RTCP sending ones are "passive". - /// - public class RTP_Source_Local : RTP_Source - { - #region Members - - private RTP_SendStream m_pStream; - - #endregion - - #region Properties - - /// - /// Returns true. - /// - /// Is raised when this class is Disposed and this property is accessed. - public override bool IsLocal - { - get - { - if (State == RTP_SourceState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return true; - } - } - - /// - /// Gets local participant. - /// - public RTP_Participant_Local Participant - { - get { return Session.Session.LocalParticipant; } - } - - /// - /// Gets the stream we send. Value null means that source is passive and doesn't send any RTP data. - /// - public RTP_SendStream Stream - { - get { return m_pStream; } - } - - /// - /// Gets source CNAME. Value null means that source not binded to participant. - /// - internal override string CName - { - get - { - if (Participant != null) - { - return null; - } - else - { - return Participant.CNAME; - } - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner RTP session. - /// Synchronization source ID. - /// RTCP end point. - /// RTP end point. - /// Is raised when session,rtcpEP or rtpEP is null reference. - internal RTP_Source_Local(RTP_Session session, uint ssrc, IPEndPoint rtcpEP, IPEndPoint rtpEP) - : base(session, ssrc) - { - if (rtcpEP == null) - { - throw new ArgumentNullException("rtcpEP"); - } - if (rtpEP == null) - { - throw new ArgumentNullException("rtpEP"); - } - - SetRtcpEP(rtcpEP); - SetRtpEP(rtpEP); - } - - #endregion - - #region Methods - - /// - /// Sends specified application packet to the RTP session target(s). - /// - /// Is raised when this class is Disposed and this method is accessed. - /// Is raised when packet is null reference. - public void SendApplicationPacket(RTCP_Packet_APP packet) - { - if (State == RTP_SourceState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (packet == null) - { - throw new ArgumentNullException("packet"); - } - - packet.Source = SSRC; - - RTCP_CompoundPacket p = new RTCP_CompoundPacket(); - RTCP_Packet_RR rr = new RTCP_Packet_RR(); - rr.SSRC = SSRC; - p.Packets.Add(packet); - - // Send APP packet. - Session.SendRtcpPacket(p); - } - - #endregion - - #region Overrides - - /// - /// Closes this source, sends BYE to remote party. - /// - /// Stream closing reason text what is reported to the remote party. Value null means not specified. - /// Is raised when this class is Disposed and this method is accessed. - internal override void Close(string closeReason) - { - if (State == RTP_SourceState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - RTCP_CompoundPacket packet = new RTCP_CompoundPacket(); - RTCP_Packet_RR rr = new RTCP_Packet_RR(); - rr.SSRC = SSRC; - packet.Packets.Add(rr); - RTCP_Packet_BYE bye = new RTCP_Packet_BYE(); - bye.Sources = new[] {SSRC}; - if (!string.IsNullOrEmpty(closeReason)) - { - bye.LeavingReason = closeReason; - } - packet.Packets.Add(bye); - - // Send packet. - Session.SendRtcpPacket(packet); - - base.Close(closeReason); - } - - #endregion - - #region Internal methods - - /// - /// Creates RTP send stream for this source. - /// - /// Is raised when this method is called more than 1 times(source already created). - internal void CreateStream() - { - if (m_pStream != null) - { - throw new InvalidOperationException("Stream is already created."); - } - - m_pStream = new RTP_SendStream(this); - m_pStream.Disposed += delegate - { - m_pStream = null; - Dispose(); - }; - - SetState(RTP_SourceState.Active); - } - - /// - /// Sends specified RTP packet to the session remote party. - /// - /// RTP packet. - /// Returns packet size in bytes. - /// Is raised when this class is Disposed and this method is accessed. - /// Is raised when packet is null reference. - /// Is raised when CreateStream method has been not called. - internal int SendRtpPacket(RTP_Packet packet) - { - if (packet == null) - { - throw new ArgumentNullException("packet"); - } - if (m_pStream == null) - { - throw new InvalidOperationException("RTP stream is not created by CreateStream method."); - } - - SetLastRtpPacket(DateTime.Now); - SetState(RTP_SourceState.Active); - - return Session.SendRtpPacket(m_pStream, packet); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Source_Remote.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Source_Remote.cs deleted file mode 100644 index b007ebb2a..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Source_Remote.cs +++ /dev/null @@ -1,254 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - - #endregion - - /// - /// This class represents RTP remote source what we receive. - /// - /// Source indicates an entity sending packets, either RTP and/or RTCP. - /// Sources what send RTP packets are called "active", only RTCP sending ones are "passive". - /// - public class RTP_Source_Remote : RTP_Source - { - #region Events - - /// - /// Is raised when source sends RTCP APP packet. - /// - public event EventHandler> ApplicationPacket = null; - - #endregion - - #region Members - - private RTP_Participant_Remote m_pParticipant; - private RTP_ReceiveStream m_pStream; - - #endregion - - #region Properties - - /// - /// Returns false. - /// - /// Is raised when this class is Disposed and this property is accessed. - public override bool IsLocal - { - get - { - if (State == RTP_SourceState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return false; - } - } - - /// - /// Gets remote participant. - /// - /// Is raised when this class is Disposed and this property is accessed. - public RTP_Participant_Remote Participant - { - get - { - if (State == RTP_SourceState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pParticipant; - } - } - - /// - /// Gets the stream we receive. Value null means that source is passive and doesn't send any RTP data. - /// - /// Is raised when this class is Disposed and this property is accessed. - public RTP_ReceiveStream Stream - { - get - { - if (State == RTP_SourceState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pStream; - } - } - - /// - /// Gets source CNAME. Value null means that source not binded to participant. - /// - internal override string CName - { - get - { - if (Participant != null) - { - return null; - } - else - { - return Participant.CNAME; - } - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner RTP session. - /// Synchronization source ID. - /// Is raised when session is null reference. - internal RTP_Source_Remote(RTP_Session session, uint ssrc) : base(session, ssrc) {} - - #endregion - - #region Overrides - - /// - /// Cleans up any resources being used. - /// - internal override void Dispose() - { - m_pParticipant = null; - if (m_pStream != null) - { - m_pStream.Dispose(); - } - - ApplicationPacket = null; - - base.Dispose(); - } - - #endregion - - #region Utility methods - - /// - /// Raises ApplicationPacket event. - /// - /// Is raised when packet is null reference. - private void OnApplicationPacket(RTCP_Packet_APP packet) - { - if (packet == null) - { - throw new ArgumentNullException("packet"); - } - - if (ApplicationPacket != null) - { - ApplicationPacket(this, new EventArgs(packet)); - } - } - - #endregion - - #region Internal methods - - /// - /// Sets source owner participant. - /// - /// RTP participant. - /// Is raised when participant is null reference. - internal void SetParticipant(RTP_Participant_Remote participant) - { - if (participant == null) - { - throw new ArgumentNullException("participant"); - } - - m_pParticipant = participant; - } - - /// - /// Is called when RTP session receives new RTP packet. - /// - /// RTP packet. - /// Packet size in bytes. - /// Is raised when packet is null reference. - internal void OnRtpPacketReceived(RTP_Packet packet, int size) - { - if (packet == null) - { - throw new ArgumentNullException("packet"); - } - - SetLastRtpPacket(DateTime.Now); - - // Passive source and first RTP packet. - if (m_pStream == null) - { - m_pStream = new RTP_ReceiveStream(Session, this, packet.SeqNo); - - SetState(RTP_SourceState.Active); - } - - m_pStream.Process(packet, size); - } - - /// - /// This method is called when this source got sender report. - /// - /// Sender report. - /// Is raised when report is null reference. - internal void OnSenderReport(RTCP_Report_Sender report) - { - if (report == null) - { - throw new ArgumentNullException("report"); - } - - if (m_pStream != null) - { - m_pStream.SetSR(report); - } - } - - /// - /// This method is called when this source got RTCP APP apcket. - /// - /// RTCP APP packet. - /// Is raised when packet is null reference. - internal void OnAppPacket(RTCP_Packet_APP packet) - { - if (packet == null) - { - throw new ArgumentNullException("packet"); - } - - OnApplicationPacket(packet); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Utils.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Utils.cs deleted file mode 100644 index 0463ef4b7..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/RTP/RTP_Utils.cs +++ /dev/null @@ -1,100 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.RTP -{ - #region usings - - using System; - using System.Net; - - #endregion - - /// - /// This class provides utility methods for RTP. - /// - public class RTP_Utils - { - #region Methods - - /// - /// Generates random SSRC value. - /// - /// Returns random SSRC value. - public static uint GenerateSSRC() - { - return (uint) new Random().Next(100000, int.MaxValue); - } - - /// - /// Generates random CNAME value. - /// - /// - public static string GenerateCNAME() - { - // user@host.randomTag - - return Environment.UserName + "@" + Dns.GetHostName() + "." + - Guid.NewGuid().ToString().Substring(0, 8); - } - - /// - /// Converts specified DateTime value to short NTP time. Note: NTP time is in UTC. - /// - /// DateTime value to convert. This value must be in local time. - /// Returns NTP value. - public static uint DateTimeToNTP32(DateTime value) - { - /* - In some fields where a more compact representation is - appropriate, only the middle 32 bits are used; that is, the low 16 - bits of the integer part and the high 16 bits of the fractional part. - The high 16 bits of the integer part must be determined - independently. - */ - - return (uint) ((DateTimeToNTP64(value) >> 16) & 0xFFFFFFFF); - } - - /// - /// Converts specified DateTime value to long NTP time. Note: NTP time is in UTC. - /// - /// DateTime value to convert. This value must be in local time. - /// Returns NTP value. - public static ulong DateTimeToNTP64(DateTime value) - { - /* - Wallclock time (absolute date and time) is represented using the - timestamp format of the Network Time Protocol (NTP), which is in - seconds relative to 0h UTC on 1 January 1900 [4]. The full - resolution NTP timestamp is a 64-bit unsigned fixed-point number with - the integer part in the first 32 bits and the fractional part in the - last 32 bits. In some fields where a more compact representation is - appropriate, only the middle 32 bits are used; that is, the low 16 - bits of the integer part and the high 16 bits of the fractional part. - The high 16 bits of the integer part must be determined - independently. - */ - - TimeSpan ts = ((value.ToUniversalTime() - new DateTime(1900, 1, 1, 0, 0, 0))); - - return ((ulong) (ts.TotalMilliseconds%1000) << 32) | (uint) (ts.Milliseconds << 22); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SDP/SDP_Attribute.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SDP/SDP_Attribute.cs deleted file mode 100644 index 3634cd81c..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SDP/SDP_Attribute.cs +++ /dev/null @@ -1,134 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SDP -{ - #region usings - - using System; - - #endregion - - /// - /// Implements SDP attribute. - /// - public class SDP_Attribute - { - #region Members - - private readonly string m_Name = ""; - private string m_Value = ""; - - #endregion - - #region Properties - - /// - /// Gets attribute name. - /// - public string Name - { - get { return m_Name; } - } - - /// - /// Gets or sets attribute value. - /// - public string Value - { - get { return m_Value; } - - set { m_Value = value; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Attribute name. - /// Attribute value. - public SDP_Attribute(string name, string value) - { - m_Name = name; - Value = value; - } - - #endregion - - #region Methods - - /// - /// Parses media from "a" SDP message field. - /// - /// "a" SDP message field. - /// - public static SDP_Attribute Parse(string aValue) - { - // a= - // a=: - - // Remove a= - StringReader r = new StringReader(aValue); - r.QuotedReadToDelimiter('='); - - //--- ------------------------------------------------------------ - string name = ""; - string word = r.QuotedReadToDelimiter(':'); - if (word == null) - { - throw new Exception("SDP message \"a\" field name is missing !"); - } - name = word; - - //--- ---------------------------------------------------------------- - string value = ""; - word = r.ReadToEnd(); - if (word != null) - { - value = word; - } - - return new SDP_Attribute(name, value); - } - - /// - /// Converts this to valid "a" string. - /// - /// - public string ToValue() - { - // a= - // a=: - - // a= - if (string.IsNullOrEmpty(m_Value)) - { - return "a=" + m_Name + "\r\n"; - } - // a=: - else - { - return "a=" + m_Name + ":" + m_Value + "\r\n"; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SDP/SDP_ConnectionData.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SDP/SDP_ConnectionData.cs deleted file mode 100644 index 961b9e89f..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SDP/SDP_ConnectionData.cs +++ /dev/null @@ -1,144 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SDP -{ - #region usings - - using System; - - #endregion - - /// - /// A SDP_ConnectionData represents an c= SDP message field. Defined in RFC 4566 5.7. Connection Data. - /// - public class SDP_ConnectionData - { - #region Members - - private string m_Address = ""; - private string m_AddressType = ""; - private string m_NetType = "IN"; - - #endregion - - #region Properties - - /// - /// Gets net type. Currently it's always IN(Internet). - /// - public string NetType - { - get { return m_NetType; } - } - - /// - /// Gets or sets address type. Currently defined values IP4 or IP6. - /// - public string AddressType - { - get { return m_AddressType; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Property AddressType can't be null or empty !"); - } - - m_AddressType = value; - } - } - - /// - /// Gets or sets connection address. - /// - public string Address - { - get { return m_Address; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Property Address can't be null or empty !"); - } - - m_Address = value; - } - } - - #endregion - - #region Methods - - /// - /// Parses media from "c" SDP message field. - /// - /// "m" SDP message field. - /// - public static SDP_ConnectionData Parse(string cValue) - { - // c= - - SDP_ConnectionData connectionInfo = new SDP_ConnectionData(); - - // Remove c= - StringReader r = new StringReader(cValue); - r.QuotedReadToDelimiter('='); - - //--- ------------------------------------------------------------ - string word = r.ReadWord(); - if (word == null) - { - throw new Exception("SDP message \"c\" field value is missing !"); - } - connectionInfo.m_NetType = word; - - //--- ----------------------------------------------------------- - word = r.ReadWord(); - if (word == null) - { - throw new Exception("SDP message \"c\" field value is missing !"); - } - connectionInfo.m_AddressType = word; - - //--- ------------------------------------------------- - word = r.ReadWord(); - if (word == null) - { - throw new Exception("SDP message \"c\" field value is missing !"); - } - connectionInfo.m_Address = word; - - return connectionInfo; - } - - /// - /// Converts this to valid connection data stirng. - /// - /// - public string ToValue() - { - // c= - - return "c=" + NetType + " " + AddressType + " " + Address + "\r\n"; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SDP/SDP_Media.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SDP/SDP_Media.cs deleted file mode 100644 index ac42b54ae..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SDP/SDP_Media.cs +++ /dev/null @@ -1,128 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SDP -{ - #region usings - - using System.Collections.Generic; - using System.Text; - - #endregion - - /// - /// SDP media. - /// - public class SDP_Media - { - #region Members - - private readonly List m_pAttributes; - - #endregion - - #region Properties - - /// - /// Gets or sets media description. - /// - public SDP_MediaDescription MediaDescription { get; set; } - - /// - /// Gets or sets media title. - /// - public string Title { get; set; } - - /// - /// Gets or sets connection data. This is optional value if SDP message specifies this value, - /// null means not specified. - /// - public SDP_ConnectionData ConnectionData { get; set; } - - /// - /// Gets or sets media encryption key info. - /// - public string EncryptionKey { get; set; } - - /// - /// Gets media attributes collection. This is optional value, Count == 0 means not specified. - /// - public List Attributes - { - get { return m_pAttributes; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public SDP_Media() - { - m_pAttributes = new List(); - } - - #endregion - - #region Methods - - /// - /// Converts media entity to corresponding media lines. Attributes included. - /// - /// - public string ToValue() - { - /* - m= (media name and transport address) - i=* (media title) - c=* (connection information -- optional if included at session level) - b=* (zero or more bandwidth information lines) - k=* (encryption key) - a=* (zero or more media attribute lines) - */ - - StringBuilder retVal = new StringBuilder(); - - // m Media description - if (MediaDescription != null) - { - retVal.Append(MediaDescription.ToValue()); - } - // i media title - if (!string.IsNullOrEmpty(Title)) - { - retVal.AppendLine("i=" + Title); - } - // c Connection Data - if (ConnectionData != null) - { - retVal.Append(ConnectionData.ToValue()); - } - // a Attributes - foreach (SDP_Attribute attribute in Attributes) - { - retVal.Append(attribute.ToValue()); - } - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SDP/SDP_MediaDescription.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SDP/SDP_MediaDescription.cs deleted file mode 100644 index 76cb8a247..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SDP/SDP_MediaDescription.cs +++ /dev/null @@ -1,206 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SDP -{ - #region usings - - using System; - - #endregion - - /// - /// A SDP_MediaDescription represents an m= SDP message field. Defined in RFC 4566 5.14. Media Descriptions. - /// - public class SDP_MediaDescription - { - #region Members - - private string m_MediaFormat = ""; - private string m_MediaType = ""; - private int m_NumberOfPorts = 1; - private int m_Port; - private string m_Protocol = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets meadia type. Currently defined media are "audio", "video", "text", - /// "application", and "message", although this list may be extended in the future. - /// - public string MediaType - { - get { return m_MediaType; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Property Protocol can't be null or empty !"); - } - - m_MediaType = value; - } - } - - /// - /// Gets or sets the transport port to which the media stream is sent. - /// - public int Port - { - get { return m_Port; } - - set { m_Port = value; } - } - - /// - /// Gets or sets number of continuos media ports. - /// - public int NumberOfPorts - { - get { return m_NumberOfPorts; } - - set - { - if (value < 1) - { - throw new ArgumentException("Property NumberOfPorts must be >= 1 !"); - } - - m_NumberOfPorts = value; - } - } - - /// - /// Gets or sets transport protocol. Currently known protocols: UDP;RTP/AVP;RTP/SAVP. - /// - public string Protocol - { - get { return m_Protocol; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Property Protocol cant be null or empty !"); - } - - m_Protocol = value; - } - } - - /// - /// Gets or sets media format description. The interpretation of the media - /// format depends on the value of the "proto" sub-field. - /// - public string MediaFormatDescription - { - get { return m_MediaFormat; } - - set - { - if (value == null) - { - throw new ArgumentException("Property Protocol cant be null !"); - } - - m_MediaFormat = value; - } - } - - #endregion - - #region Methods - - /// - /// Parses media from "m" SDP message field. - /// - /// "m" SDP message field. - /// - public static SDP_MediaDescription Parse(string mValue) - { - SDP_MediaDescription media = new SDP_MediaDescription(); - - // m= / ... - StringReader r = new StringReader(mValue); - r.QuotedReadToDelimiter('='); - - //--- ------------------------------------------------------------ - string word = r.ReadWord(); - if (word == null) - { - throw new Exception("SDP message \"m\" field value is missing !"); - } - media.m_MediaType = word; - - //--- / ------------------------------------------- - word = r.ReadWord(); - if (word == null) - { - throw new Exception("SDP message \"m\" field value is missing !"); - } - if (word.IndexOf('/') > -1) - { - string[] port_nPorts = word.Split('/'); - media.m_Port = Convert.ToInt32(port_nPorts[0]); - media.m_NumberOfPorts = Convert.ToInt32(port_nPorts[1]); - } - else - { - media.m_Port = Convert.ToInt32(word); - media.m_NumberOfPorts = 1; - } - - //--- -------------------------------------------------------------- - word = r.ReadWord(); - if (word == null) - { - throw new Exception("SDP message \"m\" field value is missing !"); - } - media.m_Protocol = word; - - //--- ---------------------------------------------------------------- - word = r.ReadWord(); - if (word == null) - { - media.m_MediaFormat = ""; - } - else - { - media.m_MediaFormat = word; - } - - return media; - } - - /// - /// Converts this to valid media string. - /// - /// - public string ToValue() - { - // m= / ... - - return "m=" + MediaType + " " + Port + "/" + NumberOfPorts + " " + Protocol + " " + - MediaFormatDescription + "\r\n"; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SDP/SDP_Message.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SDP/SDP_Message.cs deleted file mode 100644 index 02db8f08a..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SDP/SDP_Message.cs +++ /dev/null @@ -1,357 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SDP -{ - #region usings - - using System; - using System.Collections.Generic; - using System.IO; - using System.Text; - - #endregion - - /// - /// Session Description Protocol. Defined in RFC 4566. - /// - public class SDP_Message - { - #region Members - - private readonly List m_pAttributes; - private readonly List m_pMedias; - private readonly List m_pTimes; - private string m_Origin = ""; - private string m_RepeatTimes = ""; - private string m_SessionDescription = ""; - private string m_SessionName = ""; - private string m_Uri = ""; - private string m_Version = "0"; - - #endregion - - #region Properties - - /// - /// Gets or sets version of the Session Description Protocol. - /// - public string Version - { - get { return m_Version; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Property Version can't be null or empty !"); - } - - m_Version = value; - } - } - - /// - /// Gets originator and session identifier. - /// - public string Originator - { - get { return m_Origin; } - - set { m_Origin = value; } - } - - /// - /// Gets or sets textual session name. - /// - public string SessionName - { - get { return m_SessionName; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Property SessionName can't be null or empty !"); - } - - m_SessionName = value; - } - } - - /// - /// Gets or sets textual information about the session. This is optional value, null means not specified. - /// - public string SessionDescription - { - get { return m_SessionDescription; } - - set { m_SessionDescription = value; } - } - - /// - /// Gets or sets Uniform Resource Identifier. The URI should be a pointer to additional information - /// about the session. This is optional value, null means not specified. - /// - public string Uri - { - get { return m_Uri; } - - set { m_Uri = value; } - } - - /// - /// Gets or sets connection data. This is optional value if each media part specifies this value, - /// null means not specified. - /// - public SDP_ConnectionData ConnectionData { get; set; } - - /// - /// Gets start and stop times for a session. If Count = 0, t field not written dot SDP data. - /// - public List Times - { - get { return m_pTimes; } - } - - /// - /// Gets or sets repeat times for a session. This is optional value, null means not specified. - /// - public string RepeatTimes - { - get { return m_RepeatTimes; } - - set { m_RepeatTimes = value; } - } - - /// - /// Gets attributes collection. This is optional value, Count == 0 means not specified. - /// - public List Attributes - { - get { return m_pAttributes; } - } - - /// - /// Gets media parts collection. - /// - public List Media - { - get { return m_pMedias; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public SDP_Message() - { - m_pTimes = new List(); - m_pAttributes = new List(); - m_pMedias = new List(); - } - - #endregion - - #region Methods - - /// - /// Parses SDP from raw data. - /// - /// Raw SDP data. - /// Is raised when data is null reference. - public static SDP_Message Parse(string data) - { - if (data == null) - { - throw new ArgumentNullException("data"); - } - - SDP_Message sdp = new SDP_Message(); - - StringReader r = new StringReader(data); - - string line = r.ReadLine(); - - //--- Read global fields --------------------------------------------- - while (line != null) - { - line = line.Trim(); - - // We reached to media descriptions - if (line.ToLower().StartsWith("m")) - { - /* - m= (media name and transport address) - i=* (media title) - c=* (connection information -- optional if included at session level) - b=* (zero or more bandwidth information lines) - k=* (encryption key) - a=* (zero or more media attribute lines) - */ - - SDP_Media media = new SDP_Media(); - media.MediaDescription = SDP_MediaDescription.Parse(line); - sdp.Media.Add(media); - line = r.ReadLine(); - // Pasrse media fields and attributes - while (line != null) - { - line = line.Trim(); - - // Next media descrition, just stop active media description parsing, - // fall through main while, allow next while loop to process it. - if (line.ToLower().StartsWith("m")) - { - break; - } - // i media title - else if (line.ToLower().StartsWith("i")) - { - media.Title = line.Split(new[] {'='}, 2)[1].Trim(); - } - // c connection information - else if (line.ToLower().StartsWith("c")) - { - media.ConnectionData = SDP_ConnectionData.Parse(line); - } - // a Attributes - else if (line.ToLower().StartsWith("a")) - { - media.Attributes.Add(SDP_Attribute.Parse(line)); - } - - line = r.ReadLine(); - } - break; - } - // v Protocol Version - else if (line.ToLower().StartsWith("v")) - { - sdp.Version = line.Split(new[] {'='}, 2)[1].Trim(); - } - // o Origin - else if (line.ToLower().StartsWith("o")) - { - sdp.Originator = line.Split(new[] {'='}, 2)[1].Trim(); - } - // s Session Name - else if (line.ToLower().StartsWith("s")) - { - sdp.SessionName = line.Split(new[] {'='}, 2)[1].Trim(); - } - // i Session Information - else if (line.ToLower().StartsWith("i")) - { - sdp.SessionDescription = line.Split(new[] {'='}, 2)[1].Trim(); - } - // u URI - else if (line.ToLower().StartsWith("u")) - { - sdp.Uri = line.Split(new[] {'='}, 2)[1].Trim(); - } - // c Connection Data - else if (line.ToLower().StartsWith("c")) - { - sdp.ConnectionData = SDP_ConnectionData.Parse(line); - } - // t Timing - else if (line.ToLower().StartsWith("t")) - { - sdp.Times.Add(SDP_Time.Parse(line)); - } - // a Attributes - else if (line.ToLower().StartsWith("a")) - { - sdp.Attributes.Add(SDP_Attribute.Parse(line)); - } - - line = r.ReadLine().Trim(); - } - - return sdp; - } - - /// - /// Stores SDP data to specified file. Note: official suggested file extention is .sdp. - /// - /// File name with path where to store SDP data. - public void ToFile(string fileName) - { - File.WriteAllText(fileName, ToStringData()); - } - - /// - /// Returns SDP as string data. - /// - /// - public string ToStringData() - { - StringBuilder retVal = new StringBuilder(); - - // v Protocol Version - retVal.AppendLine("v=" + Version); - // o Origin - if (!string.IsNullOrEmpty(Originator)) - { - retVal.AppendLine("o=" + Originator); - } - // s Session Name - if (!string.IsNullOrEmpty(SessionName)) - { - retVal.AppendLine("s=" + SessionName); - } - // i Session Information - if (!string.IsNullOrEmpty(SessionDescription)) - { - retVal.AppendLine("i=" + SessionDescription); - } - // u URI - if (!string.IsNullOrEmpty(Uri)) - { - retVal.AppendLine("u=" + Uri); - } - // c Connection Data - if (ConnectionData != null) - { - retVal.Append(ConnectionData.ToValue()); - } - // t Timing - foreach (SDP_Time time in Times) - { - retVal.Append(time.ToValue()); - } - // a Attributes - foreach (SDP_Attribute attribute in Attributes) - { - retVal.Append(attribute.ToValue()); - } - // m media description(s) - foreach (SDP_Media media in Media) - { - retVal.Append(media.ToValue()); - } - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SDP/SDP_Time.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SDP/SDP_Time.cs deleted file mode 100644 index b08bbaf2e..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SDP/SDP_Time.cs +++ /dev/null @@ -1,129 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SDP -{ - #region usings - - using System; - - #endregion - - /// - /// A SDP_Time represents an t= SDP message field. Defined in RFC 4566 5.9. Timing. - /// - public class SDP_Time - { - #region Members - - private long m_StartTime; - private long m_StopTime; - - #endregion - - #region Properties - - /// - /// Gets or sets start time when session must start. Network Time Protocol (NTP) time values in - /// seconds since 1900. 0 value means not specified, if StopTime is also 0, then means infinite session. - /// - public long StartTime - { - get { return m_StartTime; } - - set - { - if (value < 0) - { - throw new ArgumentException("Property StartTime value must be >= 0 !"); - } - - m_StopTime = value; - } - } - - /// - /// Gets or sets stop time when session must end. Network Time Protocol (NTP) time values in - /// seconds since 1900. 0 value means not specified, if StopTime is also 0, then means infinite session. - /// - public long StopTime - { - get { return m_StopTime; } - - set - { - if (value < 0) - { - throw new ArgumentException("Property StopTime value must be >= 0 !"); - } - - m_StopTime = value; - } - } - - #endregion - - #region Methods - - /// - /// Parses media from "t" SDP message field. - /// - /// "t" SDP message field. - /// - public static SDP_Time Parse(string tValue) - { - // t= - - SDP_Time time = new SDP_Time(); - - // Remove t= - StringReader r = new StringReader(tValue); - r.QuotedReadToDelimiter('='); - - //--- ------------------------------------------------------------ - string word = r.ReadWord(); - if (word == null) - { - throw new Exception("SDP message \"t\" field value is missing !"); - } - time.m_StartTime = Convert.ToInt64(word); - - //--- ------------------------------------------------------------- - word = r.ReadWord(); - if (word == null) - { - throw new Exception("SDP message \"t\" field value is missing !"); - } - time.m_StopTime = Convert.ToInt64(word); - - return time; - } - - /// - /// Converts this to valid "t" string. - /// - /// - public string ToValue() - { - // t= - - return "t=" + StartTime + " " + StopTime + "\r\n"; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_HeaderField.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_HeaderField.cs deleted file mode 100644 index 8cc6842bd..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_HeaderField.cs +++ /dev/null @@ -1,91 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - /// - /// Represents SIP message header field. - /// - public class SIP_HeaderField - { - #region Members - - private readonly string m_Name = ""; - private bool m_IsMultiValue; - private string m_Value = ""; - - #endregion - - #region Properties - - /// - /// Gets header field name. - /// - public string Name - { - get { return m_Name; } - } - - /// - /// Gets or sets header field value. - /// - public virtual string Value - { - get { return m_Value; } - - set { m_Value = value; } - } - - /// - /// Gets if header field is multi value header field. - /// - public bool IsMultiValue - { - get { return m_IsMultiValue; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Header field name. - /// Header field value. - internal SIP_HeaderField(string name, string value) - { - m_Name = name; - m_Value = value; - } - - #endregion - - #region Internal methods - - /// - /// Sets property IsMultiValue value. - /// - /// Value to set. - internal void SetMultiValue(bool value) - { - m_IsMultiValue = value; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_HeaderFieldCollection.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_HeaderFieldCollection.cs deleted file mode 100644 index 4d7ba94af..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_HeaderFieldCollection.cs +++ /dev/null @@ -1,679 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System.Collections; - using System.Collections.Generic; - using System.IO; - using System.Text; - - #endregion - - /// - /// SIP header fields collection. - /// - public class SIP_HeaderFieldCollection : IEnumerable - { - #region Members - - private readonly List m_pHeaderFields; - - #endregion - - #region Properties - - /// - /// Gets header field from specified index. - /// - public SIP_HeaderField this[int index] - { - get { return m_pHeaderFields[index]; } - } - - /// - /// Gets header fields count in the collection. - /// - public int Count - { - get { return m_pHeaderFields.Count; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public SIP_HeaderFieldCollection() - { - m_pHeaderFields = new List(); - } - - #endregion - - #region Methods - - /// - /// Adds a new header field with specified name and value to the end of the collection. - /// - /// Header field name. - /// Header field value. - public void Add(string fieldName, string value) - { - Add(GetheaderField(fieldName, value)); - } - - /// - /// Adds specified header field to the end of the collection. - /// - /// Header field. - public void Add(SIP_HeaderField headerField) - { - m_pHeaderFields.Add(headerField); - } - - /// - /// Inserts a new header field into the collection at the specified location. - /// - /// The location in the collection where you want to add the header field. - /// Header field name. - /// Header field value. - public void Insert(int index, string fieldName, string value) - { - m_pHeaderFields.Insert(index, GetheaderField(fieldName, value)); - } - - /// - /// Sets specified header field value. If header field existst, first found value is set. - /// If field doesn't exist, it will be added. - /// - /// Header field name. - /// Header field value. - public void Set(string fieldName, string value) - { - SIP_HeaderField h = GetFirst(fieldName); - if (h != null) - { - h.Value = value; - } - else - { - Add(fieldName, value); - } - } - - /// - /// Removes header field at the specified index from the collection. - /// - /// The index of the header field to remove. - public void Remove(int index) - { - m_pHeaderFields.RemoveAt(index); - } - - /// - /// Removes specified header field from the collection. - /// - /// Header field to remove. - public void Remove(SIP_HeaderField field) - { - m_pHeaderFields.Remove(field); - } - - /// - /// Removes first header field with specified name. - /// - /// Header fields name. - public void RemoveFirst(string name) - { - foreach (SIP_HeaderField h in m_pHeaderFields) - { - if (h.Name.ToLower() == name.ToLower()) - { - m_pHeaderFields.Remove(h); - break; - } - } - } - - /// - /// Removes all header fields with specified name from the collection. - /// - /// Header field name. - public void RemoveAll(string fieldName) - { - for (int i = 0; i < m_pHeaderFields.Count; i++) - { - SIP_HeaderField h = m_pHeaderFields[i]; - if (h.Name.ToLower() == fieldName.ToLower()) - { - m_pHeaderFields.Remove(h); - i--; - } - } - } - - /// - /// Clears the collection of all header fields. - /// - public void Clear() - { - m_pHeaderFields.Clear(); - } - - /// - /// Gets if collection contains specified header field. - /// - /// Header field name. - /// - public bool Contains(string fieldName) - { - foreach (SIP_HeaderField h in m_pHeaderFields) - { - if (h.Name.ToLower() == fieldName.ToLower()) - { - return true; - } - } - - return false; - } - - /// - /// Gets if collection contains specified header field. - /// - /// Header field. - /// - public bool Contains(SIP_HeaderField headerField) - { - return m_pHeaderFields.Contains(headerField); - } - - /// - /// Gets first header field with specified name, returns null if specified field doesn't exist. - /// - /// Header field name. - /// - public SIP_HeaderField GetFirst(string fieldName) - { - foreach (SIP_HeaderField h in m_pHeaderFields) - { - if (h.Name.ToLower() == fieldName.ToLower()) - { - return h; - } - } - - return null; - } - - /// - /// Gets header fields with specified name. - /// - /// Header field name. - /// - public SIP_HeaderField[] Get(string fieldName) - { - List fields = new List(); - foreach (SIP_HeaderField h in m_pHeaderFields) - { - if (h.Name.ToLower() == fieldName.ToLower()) - { - fields.Add(h); - } - } - - return fields.ToArray(); - } - - /// - /// Parses header fields from string. - /// - /// Header string. - public void Parse(string headerString) - { - Parse(new MemoryStream(Encoding.Default.GetBytes(headerString))); - } - - /// - /// Parses header fields from stream. Stream position stays where header reading ends. - /// - /// Stream from where to parse. - public void Parse(Stream stream) - { - /* Rfc 2822 2.2 Header Fields - Header fields are lines composed of a field name, followed by a colon - (":"), followed by a field body, and terminated by CRLF. A field - name MUST be composed of printable US-ASCII characters (i.e., - characters that have values between 33 and 126, inclusive), except - colon. A field body may be composed of any US-ASCII characters, - except for CR and LF. However, a field body may contain CRLF when - used in header "folding" and "unfolding" as described in section - 2.2.3. All field bodies MUST conform to the syntax described in - sections 3 and 4 of this standard. - - Rfc 2822 2.2.3 Long Header Fields - The process of moving from this folded multiple-line representation - of a header field to its single line representation is called - "unfolding". Unfolding is accomplished by simply removing any CRLF - that is immediately followed by WSP. Each header field should be - treated in its unfolded form for further syntactic and semantic - evaluation. - - Example: - Subject: aaaaa - aaaaa - */ - - m_pHeaderFields.Clear(); - - StreamLineReader r = new StreamLineReader(stream); - r.CRLF_LinesOnly = false; - string line = r.ReadLineString(); - while (line != null) - { - // End of header reached - if (line == "") - { - break; - } - - // Store current header line and read next. We need to read 1 header line to ahead, - // because of multiline header fields. - string headerField = line; - line = r.ReadLineString(); - - // See if header field is multiline. See comment above. - while (line != null && (line.StartsWith("\t") || line.StartsWith(" "))) - { - headerField += line; - line = r.ReadLineString(); - } - - string[] name_value = headerField.Split(new[] {':'}, 2); - // There must be header field name and value, otherwise invalid header field - if (name_value.Length == 2) - { - Add(name_value[0] + ":", name_value[1].Trim()); - } - } - } - - /// - /// Converts header fields to SIP message header string. - /// - /// Returns SIP message header as string. - public string ToHeaderString() - { - StringBuilder headerString = new StringBuilder(); - foreach (SIP_HeaderField f in this) - { - headerString.Append(f.Name + " " + f.Value + "\r\n"); - } - headerString.Append("\r\n"); - - return headerString.ToString(); - } - - /// - /// Gets enumerator. - /// - /// - public IEnumerator GetEnumerator() - { - return m_pHeaderFields.GetEnumerator(); - } - - #endregion - - #region Utility methods - - /// - /// Gets right type header field. - /// - /// Header field name. - /// Header field name. - /// Returns right type header field. - private SIP_HeaderField GetheaderField(string name, string value) - { - string nameLower = name.Replace(":", "").ToLower().Trim(); - - //--- Replace short names to long -------// - if (nameLower == "i") - { - nameLower = "call-id"; - } - else if (nameLower == "m") - { - nameLower = "contact"; - } - else if (nameLower == "e") - { - nameLower = "content-encoding"; - } - else if (nameLower == "l") - { - nameLower = "content-length"; - } - else if (nameLower == "c") - { - nameLower = "content-yype"; - } - else if (nameLower == "f") - { - nameLower = "from"; - } - else if (nameLower == "s") - { - nameLower = "subject"; - } - else if (nameLower == "k") - { - nameLower = "supported"; - } - else if (nameLower == "t") - { - nameLower = "to"; - } - else if (nameLower == "v") - { - nameLower = "via"; - } - else if (nameLower == "u") - { - nameLower = "allow-events"; - } - else if (nameLower == "r") - { - nameLower = "refer-to"; - } - else if (nameLower == "d") - { - nameLower = "request-disposition"; - } - else if (nameLower == "x") - { - nameLower = "session-expires"; - } - else if (nameLower == "o") - { - nameLower = "event"; - } - else if (nameLower == "b") - { - nameLower = "referred-by"; - } - else if (nameLower == "a") - { - nameLower = "accept-contact"; - } - else if (nameLower == "y") - { - nameLower = "identity"; - } - else if (nameLower == "n") - { - nameLower = "identity-info"; - } - else if (nameLower == "j") - { - nameLower = "reject-contact"; - } - //--------------------------------------// - - if (nameLower == "accept") - { - return new SIP_MultiValueHF("Accept:", value); - } - else if (nameLower == "accept-contact") - { - return new SIP_MultiValueHF("Accept-Contact:", value); - } - else if (nameLower == "accept-encoding") - { - return new SIP_MultiValueHF("Accept-Encoding:", value); - } - else if (nameLower == "accept-language") - { - return new SIP_MultiValueHF("Accept-Language:", value); - } - else if (nameLower == "accept-resource-priority") - { - return new SIP_MultiValueHF("Accept-Resource-Priority:", value); - } - else if (nameLower == "alert-info") - { - return new SIP_MultiValueHF("Alert-Info:", value); - } - else if (nameLower == "allow") - { - return new SIP_MultiValueHF("Allow:", value); - } - else if (nameLower == "allow-events") - { - return new SIP_MultiValueHF("Allow-Events:", value); - } - else if (nameLower == "authentication-info") - { - return new SIP_SingleValueHF("Authentication-Info:", - new SIP_t_AuthenticationInfo(value)); - } - else if (nameLower == "authorization") - { - return new SIP_SingleValueHF("Authorization:", new SIP_t_Credentials(value)); - } - else if (nameLower == "contact") - { - return new SIP_MultiValueHF("Contact:", value); - } - else if (nameLower == "Content-Disposition") - { - return new SIP_SingleValueHF("Content-Disposition:", - new SIP_t_ContentDisposition(value)); - } - else if (nameLower == "cseq") - { - return new SIP_SingleValueHF("CSeq:", new SIP_t_CSeq(value)); - } - else if (nameLower == "content-encoding") - { - return new SIP_MultiValueHF("Content-Encoding:", value); - } - else if (nameLower == "content-language") - { - return new SIP_MultiValueHF("Content-Language:", value); - } - else if (nameLower == "error-info") - { - return new SIP_MultiValueHF("Error-Info:", value); - } - else if (nameLower == "event") - { - return new SIP_SingleValueHF("Event:", new SIP_t_Event(value)); - } - else if (nameLower == "from") - { - return new SIP_SingleValueHF("From:", new SIP_t_From(value)); - } - else if (nameLower == "history-info") - { - return new SIP_MultiValueHF("History-Info:", value); - } - else if (nameLower == "identity-info") - { - return new SIP_SingleValueHF("Identity-Info:", - new SIP_t_IdentityInfo(value)); - } - else if (nameLower == "in-replay-to") - { - return new SIP_MultiValueHF("In-Reply-To:", value); - } - else if (nameLower == "join") - { - return new SIP_SingleValueHF("Join:", new SIP_t_Join(value)); - } - else if (nameLower == "min-se") - { - return new SIP_SingleValueHF("Min-SE:", new SIP_t_MinSE(value)); - } - else if (nameLower == "path") - { - return new SIP_MultiValueHF("Path:", value); - } - else if (nameLower == "proxy-authenticate") - { - return new SIP_SingleValueHF("Proxy-Authenticate:", - new SIP_t_Challenge(value)); - } - else if (nameLower == "proxy-authorization") - { - return new SIP_SingleValueHF("Proxy-Authorization:", - new SIP_t_Credentials(value)); - } - else if (nameLower == "proxy-require") - { - return new SIP_MultiValueHF("Proxy-Require:", value); - } - else if (nameLower == "rack") - { - return new SIP_SingleValueHF("RAck:", new SIP_t_RAck(value)); - } - else if (nameLower == "reason") - { - return new SIP_MultiValueHF("Reason:", value); - } - else if (nameLower == "record-route") - { - return new SIP_MultiValueHF("Record-Route:", value); - } - else if (nameLower == "refer-sub") - { - return new SIP_SingleValueHF("Refer-Sub:", new SIP_t_ReferSub(value)); - } - else if (nameLower == "refer-to") - { - return new SIP_SingleValueHF("Refer-To:", new SIP_t_AddressParam(value)); - } - else if (nameLower == "referred-by") - { - return new SIP_SingleValueHF("Referred-By:", new SIP_t_ReferredBy(value)); - } - else if (nameLower == "reject-contact") - { - return new SIP_MultiValueHF("Reject-Contact:", value); - } - else if (nameLower == "replaces") - { - return new SIP_SingleValueHF("Replaces:", - new SIP_t_SessionExpires(value)); - } - else if (nameLower == "reply-to") - { - return new SIP_MultiValueHF("Reply-To:", value); - } - else if (nameLower == "request-disposition") - { - return new SIP_MultiValueHF("Request-Disposition:", value); - } - else if (nameLower == "require") - { - return new SIP_MultiValueHF("Require:", value); - } - else if (nameLower == "resource-priority") - { - return new SIP_MultiValueHF("Resource-Priority:", value); - } - else if (nameLower == "retry-after") - { - return new SIP_SingleValueHF("Retry-After:", new SIP_t_RetryAfter(value)); - } - else if (nameLower == "route") - { - return new SIP_MultiValueHF("Route:", value); - } - else if (nameLower == "security-client") - { - return new SIP_MultiValueHF("Security-Client:", value); - } - else if (nameLower == "security-server") - { - return new SIP_MultiValueHF("Security-Server:", value); - } - else if (nameLower == "security-verify") - { - return new SIP_MultiValueHF("Security-Verify:", value); - } - else if (nameLower == "service-route") - { - return new SIP_MultiValueHF("Service-Route:", value); - } - else if (nameLower == "session-expires") - { - return new SIP_SingleValueHF("Session-Expires:", - new SIP_t_SessionExpires(value)); - } - else if (nameLower == "subscription-state") - { - return new SIP_SingleValueHF("Subscription-State:", - new SIP_t_SubscriptionState(value)); - } - else if (nameLower == "supported") - { - return new SIP_MultiValueHF("Supported:", value); - } - else if (nameLower == "target-dialog") - { - return new SIP_SingleValueHF("Target-Dialog:", - new SIP_t_TargetDialog(value)); - } - else if (nameLower == "timestamp") - { - return new SIP_SingleValueHF("Timestamp:", new SIP_t_Timestamp(value)); - } - else if (nameLower == "to") - { - return new SIP_SingleValueHF("To:", new SIP_t_To(value)); - } - else if (nameLower == "unsupported") - { - return new SIP_MultiValueHF("Unsupported:", value); - } - else if (nameLower == "via") - { - return new SIP_MultiValueHF("Via:", value); - } - else if (nameLower == "warning") - { - return new SIP_MultiValueHF("Warning:", value); - } - else if (nameLower == "www-authenticate") - { - return new SIP_SingleValueHF("WWW-Authenticate:", new SIP_t_Challenge(value)); - } - else - { - return new SIP_HeaderField(name, value); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_MVGroupHFCollection.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_MVGroupHFCollection.cs deleted file mode 100644 index 8a521134c..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_MVGroupHFCollection.cs +++ /dev/null @@ -1,209 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System.Collections.Generic; - - #endregion - - /// - /// Implements same multi value header fields group. Group can contain one type header fields only. - /// This is class is used by Via:,Route:, ... . - /// - public class SIP_MVGroupHFCollection where T : SIP_t_Value, new() - { - #region Members - - private readonly string m_FieldName = ""; - private readonly List> m_pFields; - private readonly SIP_Message m_pMessage; - - #endregion - - #region Properties - - /// - /// Gets header field name what this group holds. - /// - public string FieldName - { - get { return m_FieldName; } - } - - /// - /// Gets number of header fields in this group. - /// - public int Count - { - get { return m_pFields.Count; } - } - - /// - /// Gets header fields what are in this group. - /// - public SIP_MultiValueHF[] HeaderFields - { - get { return m_pFields.ToArray(); } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner message that owns this group. - /// Header field name what group holds. - public SIP_MVGroupHFCollection(SIP_Message owner, string fieldName) - { - m_pMessage = owner; - m_FieldName = fieldName; - - m_pFields = new List>(); - - Refresh(); - } - - #endregion - - #region Methods - - /// - /// Add new header field on the top of the whole header. - /// - /// Header field value. - public void AddToTop(string value) - { - m_pMessage.Header.Insert(0, m_FieldName, value); - Refresh(); - } - - /// - /// Add new header field on the bottom of the whole header. - /// - /// Header field value. - public void Add(string value) - { - m_pMessage.Header.Add(m_FieldName, value); - Refresh(); - } - - /// - /// Removes all specified header fields with their values. - /// - public void RemoveAll() - { - m_pMessage.Header.RemoveAll(m_FieldName); - m_pFields.Clear(); - } - - /// - /// Gets top most header field first value. - /// - public T GetTopMostValue() - { - if (m_pFields.Count > 0) - { - return m_pFields[0].Values[0]; - } - - return null; - } - - /// - /// Removes top most header field first value. If value is the last value, - /// the whole header field will be removed. - /// - public void RemoveTopMostValue() - { - if (m_pFields.Count > 0) - { - SIP_MultiValueHF h = m_pFields[0]; - if (h.Count > 1) - { - h.Remove(0); - } - else - { - m_pMessage.Header.Remove(m_pFields[0]); - m_pFields.Remove(m_pFields[0]); - } - } - } - - /// - /// Removes last value. If value is the last value n header field, the whole header field will be removed. - /// - public void RemoveLastValue() - { - SIP_MultiValueHF h = m_pFields[m_pFields.Count - 1]; - if (h.Count > 1) - { - h.Remove(h.Count - 1); - } - else - { - m_pMessage.Header.Remove(m_pFields[0]); - m_pFields.Remove(h); - } - } - - /// - /// Gets all header field values. - /// - /// - public T[] GetAllValues() - { - List retVal = new List(); - foreach (SIP_MultiValueHF h in m_pFields) - { - foreach (SIP_t_Value v in h.Values) - { - retVal.Add((T) v); - } - } - - return retVal.ToArray(); - } - - #endregion - - #region Utility methods - - /// - /// Refreshes header fields in group from actual header. - /// - private void Refresh() - { - m_pFields.Clear(); - - foreach (SIP_HeaderField h in m_pMessage.Header) - { - if (h.Name.ToLower() == m_FieldName.ToLower()) - { - m_pFields.Add((SIP_MultiValueHF) h); - } - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_Message.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_Message.cs deleted file mode 100644 index 4f439a643..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_Message.cs +++ /dev/null @@ -1,1566 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Globalization; - using System.IO; - using System.Text; - - #endregion - - /// - /// Implements SIP message. This is base class for SIP_Request and SIP_Response. Defined in RFC 3261. - /// - public abstract class SIP_Message - { - #region Members - - private readonly SIP_HeaderFieldCollection m_pHeader; - private byte[] m_Data; - - #endregion - - #region Properties - - /// - /// Gets direct access to header. - /// - public SIP_HeaderFieldCollection Header - { - get { return m_pHeader; } - } - - /// - /// Gets or sets what features end point supports. - /// - public SIP_MVGroupHFCollection Accept - { - get { return new SIP_MVGroupHFCollection(this, "Accept:"); } - } - - /// - /// Gets or sets Accept-Contact header value. Defined in RFC 3841. - /// - public SIP_MVGroupHFCollection AcceptContact - { - get { return new SIP_MVGroupHFCollection(this, "Accept-Contact:"); } - } - - /// - /// Gets encodings what end point supports. Example: Accept-Encoding: gzip. - /// - public SIP_MVGroupHFCollection AcceptEncoding - { - get { return new SIP_MVGroupHFCollection(this, "Accept-Encoding:"); } - } - - /// - /// Gets preferred languages for reason phrases, session descriptions, or - /// status responses carried as message bodies in the response. If no Accept-Language - /// header field is present, the server SHOULD assume all languages are acceptable to the client. - /// - public SIP_MVGroupHFCollection AcceptLanguage - { - get { return new SIP_MVGroupHFCollection(this, "Accept-Language:"); } - } - - /// - /// Gets Accept-Resource-Priority headers. Defined in RFC 4412. - /// - public SIP_MVGroupHFCollection AcceptResourcePriority - { - get { return new SIP_MVGroupHFCollection(this, "Accept-Resource-Priority:"); } - } - - /// - /// Gets AlertInfo values collection. When present in an INVITE request, the Alert-Info header - /// field specifies an alternative ring tone to the UAS. When present in a 180 (Ringing) response, - /// the Alert-Info header field specifies an alternative ringback tone to the UAC. - /// - public SIP_MVGroupHFCollection AlertInfo - { - get { return new SIP_MVGroupHFCollection(this, "Alert-Info:"); } - } - - /// - /// Gets methods collection which is supported by the UA which generated the message. - /// - public SIP_MVGroupHFCollection Allow - { - get { return new SIP_MVGroupHFCollection(this, "Allow:"); } - } - - /// - /// Gets Allow-Events header which indicates the event packages supported by the client. Defined in rfc 3265. - /// - public SIP_MVGroupHFCollection AllowEvents - { - get { return new SIP_MVGroupHFCollection(this, "Allow-Events:"); } - } - - /// - /// Gets the Authentication-Info header fields which provides for mutual authentication - /// with HTTP Digest. - /// - public SIP_SVGroupHFCollection AuthenticationInfo - { - get { return new SIP_SVGroupHFCollection(this, "Authentication-Info:"); } - } - - /// - /// Gets the Authorization header fields which contains authentication credentials of a UA. - /// - public SIP_SVGroupHFCollection Authorization - { - get { return new SIP_SVGroupHFCollection(this, "Authorization:"); } - } - - /// - /// Gets or sets the Call-ID header field which uniquely identifies a particular invitation or all - /// registrations of a particular client. - /// Value null means not specified. - /// - public string CallID - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("Call-ID:"); - if (h != null) - { - return h.Value; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("Call-ID:"); - } - else - { - m_pHeader.Set("Call-ID:", value); - } - } - } - - /// - /// Gets the Call-Info header field which provides additional information about the - /// caller or callee, depending on whether it is found in a request or response. - /// - public SIP_MVGroupHFCollection CallInfo - { - get { return new SIP_MVGroupHFCollection(this, "Call-Info:"); } - } - - /// - /// Gets contact header fields. The Contact header field provides a SIP or SIPS URI that can be used - /// to contact that specific instance of the UA for subsequent requests. - /// - public SIP_MVGroupHFCollection Contact - { - get { return new SIP_MVGroupHFCollection(this, "Contact:"); } - } - - /// - /// Gets or sets the Content-Disposition header field which describes how the message body - /// or, for multipart messages, a message body part is to be interpreted by the UAC or UAS. - /// Value null means not specified. - /// - public SIP_t_ContentDisposition ContentDisposition - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("Content-Disposition:"); - if (h != null) - { - return ((SIP_SingleValueHF) h).ValueX; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("Content-Disposition:"); - } - else - { - m_pHeader.Set("Content-Disposition:", value.ToStringValue()); - } - } - } - - /// - /// Gets the Content-Encodings which is used as a modifier to the "media-type". When present, - /// its value indicates what additional content codings have been applied to the entity-body, - /// and thus what decoding mechanisms MUST be applied in order to obtain the media-type referenced - /// by the Content-Type header field. - /// - public SIP_MVGroupHFCollection ContentEncoding - { - get { return new SIP_MVGroupHFCollection(this, "Content-Encoding:"); } - } - - /// - /// Gets content languages. - /// - public SIP_MVGroupHFCollection ContentLanguage - { - get { return new SIP_MVGroupHFCollection(this, "Content-Language:"); } - } - - /// - /// Gets SIP request content data size in bytes. - /// - public int ContentLength - { - get - { - if (m_Data == null) - { - return 0; - } - else - { - return m_Data.Length; - } - } - } - - /// - /// Gets or sets the Content-Type header field which indicates the media type of the - /// message-body sent to the recipient. - /// Value null means not specified. - /// - public string ContentType - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("Content-Type:"); - if (h != null) - { - return h.Value; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("Content-Type:"); - } - else - { - m_pHeader.Set("Content-Type:", value); - } - } - } - - /// - /// Gets or sets command sequence number and the request method. - /// Value null means not specified. - /// - public SIP_t_CSeq CSeq - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("CSeq:"); - if (h != null) - { - return ((SIP_SingleValueHF) h).ValueX; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("CSeq:"); - } - else - { - m_pHeader.Set("CSeq:", value.ToStringValue()); - } - } - } - - /// - /// Gets or sets date and time. Value DateTime.MinValue means that value not specified. - /// - public DateTime Date - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("Date:"); - if (h != null) - { - return DateTime.ParseExact(h.Value, "r", DateTimeFormatInfo.InvariantInfo); - } - else - { - return DateTime.MinValue; - } - } - - set - { - if (value == DateTime.MinValue) - { - m_pHeader.RemoveFirst("Date:"); - } - else - { - m_pHeader.Set("Date:", value.ToString("r")); - } - } - } - - /// - /// Gets the Error-Info header field which provides a pointer to additional - /// information about the error status response. - /// - public SIP_MVGroupHFCollection ErrorInfo - { - get { return new SIP_MVGroupHFCollection(this, "Error-Info:"); } - } - - /// - /// Gets or sets Event header. Defined in RFC 3265. - /// - public SIP_t_Event Event - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("Event:"); - if (h != null) - { - return ((SIP_SingleValueHF) h).ValueX; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("Event:"); - } - else - { - m_pHeader.Set("Event:", value.ToStringValue()); - } - } - } - - /// - /// Gets or sets relative time after which the message (or content) expires. - /// Value -1 means that value not specified. - /// - public int Expires - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("Expires:"); - if (h != null) - { - return Convert.ToInt32(h.Value); - } - else - { - return -1; - } - } - - set - { - if (value < 0) - { - m_pHeader.RemoveFirst("Expires:"); - } - else - { - m_pHeader.Set("Expires:", value.ToString()); - } - } - } - - /// - /// Gets or sets initiator of the request. - /// Value null means not specified. - /// - public SIP_t_From From - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("From:"); - if (h != null) - { - return ((SIP_SingleValueHF) h).ValueX; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("From:"); - } - else - { - m_pHeader.Add(new SIP_SingleValueHF("From:", value)); - } - } - } - - /// - /// Gets History-Info headers. Defined in RFC 4244. - /// - public SIP_MVGroupHFCollection HistoryInfo - { - get { return new SIP_MVGroupHFCollection(this, "History-Info:"); } - } - - /// - /// Identity header value. Value null means not specified. Defined in RFC 4474. - /// - public string Identity - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("Identity:"); - if (h != null) - { - return h.Value; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("Identity:"); - } - else - { - m_pHeader.Set("Identity:", value); - } - } - } - - /// - /// Gets or sets Identity-Info header value. Value null means not specified. - /// Defined in RFC 4474. - /// - public SIP_t_IdentityInfo IdentityInfo - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("Identity-Info:"); - if (h != null) - { - return ((SIP_SingleValueHF) h).ValueX; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("Identity-Info:"); - } - else - { - m_pHeader.Add(new SIP_SingleValueHF("Identity-Info:", value)); - } - } - } - - /// - /// Gets the In-Reply-To header fields which enumerates the Call-IDs that this call - /// references or returns. - /// - public SIP_MVGroupHFCollection InReplyTo - { - get { return new SIP_MVGroupHFCollection(this, "In-Reply-To:"); } - } - - /// - /// Gets or sets Join header which indicates that a new dialog (created by the INVITE in which - /// the Join header field in contained) should be joined with a dialog identified by the header - /// field, and any associated dialogs or conferences. Defined in 3911. Value null means not specified. - /// - public SIP_t_Join Join - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("Join:"); - if (h != null) - { - return ((SIP_SingleValueHF) h).ValueX; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("Join:"); - } - else - { - m_pHeader.Add(new SIP_SingleValueHF("Join:", value)); - } - } - } - - /// - /// Gets or sets limit the number of proxies or gateways that can forward the request - /// to the next downstream server. - /// Value -1 means that value not specified. - /// - public int MaxForwards - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("Max-Forwards:"); - if (h != null) - { - return Convert.ToInt32(h.Value); - } - else - { - return -1; - } - } - - set - { - if (value < 0) - { - m_pHeader.RemoveFirst("Max-Forwards:"); - } - else - { - m_pHeader.Set("Max-Forwards:", value.ToString()); - } - } - } - - /// - /// Gets or sets mime version. Currently 1.0 is only defined value. - /// Value null means not specified. - /// - public string MimeVersion - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("Mime-Version:"); - if (h != null) - { - return h.Value; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("Mime-Version:"); - } - else - { - m_pHeader.Set("Mime-Version:", value); - } - } - } - - /// - /// Gets or sets minimum refresh interval supported for soft-state elements managed by that server. - /// Value -1 means that value not specified. - /// - public int MinExpires - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("Min-Expires:"); - if (h != null) - { - return Convert.ToInt32(h.Value); - } - else - { - return -1; - } - } - - set - { - if (value < 0) - { - m_pHeader.RemoveFirst("Min-Expires:"); - } - else - { - m_pHeader.Set("Min-Expires:", value.ToString()); - } - } - } - - /// - /// Gets or sets Min-SE header which indicates the minimum value for the session interval, - /// in units of delta-seconds. Defined in 4028. Value null means not specified. - /// - public SIP_t_MinSE MinSE - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("Min-SE:"); - if (h != null) - { - return ((SIP_SingleValueHF) h).ValueX; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("Min-SE:"); - } - else - { - m_pHeader.Set("Min-SE:", value.ToStringValue()); - } - } - } - - /// - /// Gets or sets organization name which the SIP element issuing the request or response belongs. - /// Value null means not specified. - /// - public string Organization - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("Organization:"); - if (h != null) - { - return h.Value; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("Organization:"); - } - else - { - m_pHeader.Set("Organization:", value); - } - } - } - - /// - /// Gets an Path header. It is used in conjunction with SIP REGISTER requests and with 200 - /// class messages in response to REGISTER (REGISTER responses). Defined in rfc 3327. - /// - public SIP_SVGroupHFCollection Path - { - get { return new SIP_SVGroupHFCollection(this, "Path:"); } - } - - /// - /// Gest or sets priority that the SIP request should have to the receiving human or its agent. - /// Value null means not specified. - /// - public string Priority - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("Priority:"); - if (h != null) - { - return h.Value; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("Priority:"); - } - else - { - m_pHeader.Set("Priority:", value); - } - } - } - - // Privacy [RFC3323] - - /// - /// Gets an proxy authentication challenge. - /// - public SIP_SVGroupHFCollection ProxyAuthenticate - { - get { return new SIP_SVGroupHFCollection(this, "Proxy-Authenticate:"); } - } - - /// - /// Gest credentials containing the authentication information of the user agent - /// for the proxy and/or realm of the resource being requested. - /// - public SIP_SVGroupHFCollection ProxyAuthorization - { - get { return new SIP_SVGroupHFCollection(this, "Proxy-Authorization:"); } - } - - /// - /// Gets proxy-sensitive features that must be supported by the proxy. - /// - public SIP_MVGroupHFCollection ProxyRequire - { - get { return new SIP_MVGroupHFCollection(this, "Proxy-Require:"); } - } - - /// - /// Gets or sets RAck header. Defined in 3262. Value null means not specified. - /// - public SIP_t_RAck RAck - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("RAck:"); - if (h != null) - { - return ((SIP_SingleValueHF) h).ValueX; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("RAck:"); - } - else - { - m_pHeader.Set("RAck:", value.ToStringValue()); - } - } - } - - /// - /// Gets the Reason header. Defined in rfc 3326. - /// - public SIP_MVGroupHFCollection Reason - { - get { return new SIP_MVGroupHFCollection(this, "Reason:"); } - } - - /// - /// Gets the Record-Route header fields what is inserted by proxies in a request to - /// force future requests in the dialog to be routed through the proxy. - /// - public SIP_MVGroupHFCollection RecordRoute - { - get { return new SIP_MVGroupHFCollection(this, "Record-Route:"); } - } - - /// - /// Gets or sets Refer-Sub header. Defined in rfc 4488. Value null means not specified. - /// - public SIP_t_ReferSub ReferSub - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("Refer-Sub:"); - if (h != null) - { - return ((SIP_SingleValueHF) h).ValueX; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("Refer-Sub:"); - } - else - { - m_pHeader.Add(new SIP_SingleValueHF("Refer-Sub:", value)); - } - } - } - - /// - /// Gets or sets Refer-To header. Defined in rfc 3515. Value null means not specified. - /// - public SIP_t_AddressParam ReferTo - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("Refer-To:"); - if (h != null) - { - return ((SIP_SingleValueHF) h).ValueX; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("Refer-To:"); - } - else - { - m_pHeader.Add(new SIP_SingleValueHF("Refer-To:", value)); - } - } - } - - /// - /// Gets or sets Referred-By header. Defined in rfc 3892. Value null means not specified. - /// - public SIP_t_ReferredBy ReferredBy - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("Referred-By:"); - if (h != null) - { - return ((SIP_SingleValueHF) h).ValueX; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("Referred-By:"); - } - else - { - m_pHeader.Add(new SIP_SingleValueHF("Referred-By:", value)); - } - } - } - - /// - /// Gets Reject-Contact headers. Defined in RFC 3841. - /// - public SIP_MVGroupHFCollection RejectContact - { - get { return new SIP_MVGroupHFCollection(this, "Reject-Contact:"); } - } - - /// - /// Gets or sets Replaces header. Defined in rfc 3891. Value null means not specified. - /// - public SIP_t_Replaces Replaces - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("Replaces:"); - if (h != null) - { - return ((SIP_SingleValueHF) h).ValueX; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("Replaces:"); - } - else - { - m_pHeader.Add(new SIP_SingleValueHF("Replaces:", value)); - } - } - } - - /// - /// Gets logical return URI that may be different from the From header field. - /// - public SIP_MVGroupHFCollection ReplyTo - { - get { return new SIP_MVGroupHFCollection(this, "Reply-To:"); } - } - - /// - /// Gets or sets Request-Disposition header. The Request-Disposition header field specifies caller preferences for - /// how a server should process a request. Defined in rfc 3841. - /// - public SIP_MVGroupHFCollection RequestDisposition - { - get { return new SIP_MVGroupHFCollection(this, "Request-Disposition:"); } - } - - /// - /// Gets options that the UAC expects the UAS to support in order to process the request. - /// - public SIP_MVGroupHFCollection Require - { - get { return new SIP_MVGroupHFCollection(this, "Require:"); } - } - - /// - /// Gets Resource-Priority headers. Defined in RFC 4412. - /// - public SIP_MVGroupHFCollection ResourcePriority - { - get { return new SIP_MVGroupHFCollection(this, "Resource-Priority:"); } - } - - /// - /// Gets or sets how many seconds the service is expected to be unavailable to the requesting client. - /// Value null means that value not specified. - /// - public SIP_t_RetryAfter RetryAfter - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("Retry-After:"); - if (h != null) - { - return ((SIP_SingleValueHF) h).ValueX; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("Retry-After:"); - } - else - { - m_pHeader.Add(new SIP_SingleValueHF("Retry-After:", value)); - } - } - } - - /// - /// Gets force routing for a request through the listed set of proxies. - /// - public SIP_MVGroupHFCollection Route - { - get { return new SIP_MVGroupHFCollection(this, "Route:"); } - } - - /// - /// Gets or sets RSeq header. Value -1 means that value not specified. Defined in rfc 3262. - /// - public int RSeq - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("RSeq:"); - if (h != null) - { - return Convert.ToInt32(h.Value); - } - else - { - return -1; - } - } - - set - { - if (value < 0) - { - m_pHeader.RemoveFirst("RSeq:"); - } - else - { - m_pHeader.Set("RSeq:", value.ToString()); - } - } - } - - /// - /// Gets Security-Client headers. Defined in RFC 3329. - /// - public SIP_MVGroupHFCollection SecurityClient - { - get { return new SIP_MVGroupHFCollection(this, "Security-Client:"); } - } - - /// - /// Gets Security-Server headers. Defined in RFC 3329. - /// - public SIP_MVGroupHFCollection SecurityServer - { - get { return new SIP_MVGroupHFCollection(this, "Security-Server:"); } - } - - /// - /// Gets Security-Verify headers. Defined in RFC 3329. - /// - public SIP_MVGroupHFCollection SecurityVerify - { - get { return new SIP_MVGroupHFCollection(this, "Security-Verify:"); } - } - - /// - /// Gets or sets the software used by the UAS to handle the request. - /// Value null means not specified. - /// - public string Server - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("Server:"); - if (h != null) - { - return h.Value; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("Server:"); - } - else - { - m_pHeader.Set("Server:", value); - } - } - } - - /// - /// Gets the Service-Route header. Defined in rfc 3608. - /// - public SIP_MVGroupHFCollection ServiceRoute - { - get { return new SIP_MVGroupHFCollection(this, "Service-Route:"); } - } - - /// - /// Gets or sets Session-Expires expires header. Value null means that value not specified. - /// Defined in rfc 4028. - /// - public SIP_t_SessionExpires SessionExpires - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("Session-Expires:"); - if (h != null) - { - return ((SIP_SingleValueHF) h).ValueX; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("Session-Expires:"); - } - else - { - m_pHeader.Set("Session-Expires:", value.ToStringValue()); - } - } - } - - /// - /// Gets or sets SIP-ETag header value. Value null means not specified. Defined in RFC 3903. - /// - public string SIPETag - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("SIP-ETag:"); - if (h != null) - { - return h.Value; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("SIP-ETag:"); - } - else - { - m_pHeader.Set("SIP-ETag:", value); - } - } - } - - /// - /// Gets or sets SIP-ETag header value. Value null means not specified. Defined in RFC 3903. - /// - public string SIPIfMatch - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("SIP-If-Match:"); - if (h != null) - { - return h.Value; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("SIP-If-Match:"); - } - else - { - m_pHeader.Set("SIP-If-Match:", value); - } - } - } - - /// - /// Gets or sets call subject text. - /// Value null means not specified. - /// - public string Subject - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("Subject:"); - if (h != null) - { - return h.Value; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("Subject:"); - } - else - { - m_pHeader.Set("Subject:", value); - } - } - } - - /// - /// Gets or sets Subscription-State header value. Value null means that value not specified. - /// Defined in RFC 3265. - /// - public SIP_t_SubscriptionState SubscriptionState - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("Subscription-State:"); - if (h != null) - { - return ((SIP_SingleValueHF) h).ValueX; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("Subscription-State:"); - } - else - { - m_pHeader.Add(new SIP_SingleValueHF("Subscription-State:", value)); - } - } - } - - /// - /// Gets extensions supported by the UAC or UAS. Known values are defined in SIP_OptionTags class. - /// - public SIP_MVGroupHFCollection Supported - { - get { return new SIP_MVGroupHFCollection(this, "Supported:"); } - } - - /// - /// Gets or sets Target-Dialog header value. Value null means that value not specified. - /// Defined in RFC 4538. - /// - public SIP_t_TargetDialog TargetDialog - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("Target-Dialog:"); - if (h != null) - { - return ((SIP_SingleValueHF) h).ValueX; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("Target-Dialog:"); - } - else - { - m_pHeader.Add(new SIP_SingleValueHF("Target-Dialog:", value)); - } - } - } - - /// - /// Gets or sets when the UAC sent the request to the UAS. - /// Value null means that value not specified. - /// - public SIP_t_Timestamp Timestamp - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("Timestamp:"); - if (h != null) - { - return ((SIP_SingleValueHF) h).ValueX; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("Timestamp:"); - } - else - { - m_pHeader.Add(new SIP_SingleValueHF("Timestamp:", value)); - } - } - } - - /// - /// Gets or sets logical recipient of the request. - /// Value null means not specified. - /// - public SIP_t_To To - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("To:"); - if (h != null) - { - return ((SIP_SingleValueHF) h).ValueX; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("To:"); - } - else - { - m_pHeader.Add(new SIP_SingleValueHF("To:", value)); - } - } - } - - /// - /// Gets features not supported by the UAS. - /// - public SIP_MVGroupHFCollection Unsupported - { - get { return new SIP_MVGroupHFCollection(this, "Unsupported:"); } - } - - /// - /// Gets or sets information about the UAC originating the request. - /// Value null means not specified. - /// - public string UserAgent - { - get - { - SIP_HeaderField h = m_pHeader.GetFirst("User-Agent:"); - if (h != null) - { - return h.Value; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - m_pHeader.RemoveFirst("User-Agent:"); - } - else - { - m_pHeader.Set("User-Agent:", value); - } - } - } - - /// - /// Gets Via header fields.The Via header field indicates the transport used for the transaction - /// and identifies the location where the response is to be sent. - /// - public SIP_MVGroupHFCollection Via - { - get { return new SIP_MVGroupHFCollection(this, "Via:"); } - } - - /// - /// Gets additional information about the status of a response. - /// - public SIP_MVGroupHFCollection Warning - { - get { return new SIP_MVGroupHFCollection(this, "Warning:"); } - } - - /// - /// Gets or authentication challenge. - /// - public SIP_SVGroupHFCollection WWWAuthenticate - { - get { return new SIP_SVGroupHFCollection(this, "WWW-Authenticate:"); } - } - - /// - /// Gets or sets content data. - /// - public byte[] Data - { - get { return m_Data; } - - set { m_Data = value; } - } - - #endregion - - #region Constructor - - /// - /// Default constuctor. - /// - public SIP_Message() - { - m_pHeader = new SIP_HeaderFieldCollection(); - } - - #endregion - - /// - /// Parses SIP message from specified byte array. - /// - /// SIP message data. - protected void InternalParse(byte[] data) - { - InternalParse(new MemoryStream(data)); - } - - /// - /// Parses SIP message from specified stream. - /// - /// SIP message stream. - protected void InternalParse(Stream stream) - { - /* SIP message syntax: - header-line - .... - - data size of Content-Length header field. - */ - - // Parse header - Header.Parse(stream); - - // Parse data - int contentLength = 0; - try - { - contentLength = Convert.ToInt32(m_pHeader.GetFirst("Content-Length:").Value); - } - catch {} - if (contentLength > 0) - { - byte[] data = new byte[contentLength]; - stream.Read(data, 0, data.Length); - Data = data; - } - } - - /// - /// Stores SIP_Message to specified stream. - /// - /// Stream where to store SIP_Message. - protected void InternalToStream(Stream stream) - { - // Ensure that we add right Contnet-Length. - m_pHeader.RemoveAll("Content-Length:"); - if (m_Data != null) - { - m_pHeader.Add("Content-Length:", Convert.ToString(m_Data.Length)); - } - else - { - m_pHeader.Add("Content-Length:", Convert.ToString(0)); - } - - // Store header - byte[] header = Encoding.UTF8.GetBytes(m_pHeader.ToHeaderString()); - stream.Write(header, 0, header.Length); - - // Store data - if (m_Data != null && m_Data.Length > 0) - { - stream.Write(m_Data, 0, m_Data.Length); - } - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_MultiValueHF.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_MultiValueHF.cs deleted file mode 100644 index 122fecd45..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_MultiValueHF.cs +++ /dev/null @@ -1,174 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Collections.Generic; - using System.Text; - - #endregion - - /// - /// Implements generic multi value SIP header field. - /// This is used by header fields like Via,Contact, ... . - /// - public class SIP_MultiValueHF : SIP_HeaderField where T : SIP_t_Value, new() - { - #region Members - - private readonly List m_pValues; - - #endregion - - #region Properties - - /// - /// Gets or sets header field value. - /// - public override string Value - { - get { return ToStringValue(); } - - set - { - if (value != null) - { - throw new ArgumentNullException("Property Value value may not be null !"); - } - - Parse(value); - - base.Value = value; - } - } - - /// - /// Gets header field values. - /// - public List Values - { - get { return m_pValues; } - } - - /// - /// Gets values count. - /// - public int Count - { - get { return m_pValues.Count; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Header field name. - /// Header field value. - public SIP_MultiValueHF(string name, string value) : base(name, value) - { - m_pValues = new List(); - - SetMultiValue(true); - - Parse(value); - } - - #endregion - - #region Methods - - /// - /// Gets header field values. - /// - /// - public object[] GetValues() - { - return m_pValues.ToArray(); - } - - /// - /// Removes value from specified index. - /// - /// Index of value to remove. - public void Remove(int index) - { - if (index > -1 && index < m_pValues.Count) - { - m_pValues.RemoveAt(index); - } - } - - #endregion - - #region Utility methods - - /// - /// Parses multi value header field values. - /// - /// Header field value. - private void Parse(string value) - { - m_pValues.Clear(); - - StringReader r = new StringReader(value); - while (r.Available > 0) - { - r.ReadToFirstChar(); - // If we have COMMA, just consume it, it last value end. - if (r.StartsWith(",")) - { - r.ReadSpecifiedLength(1); - } - - // Allow xxx-param to pasre 1 value from reader. - T param = new T(); - param.Parse(r); - m_pValues.Add(param); - } - } - - /// - /// Converts to valid mutli value header field value. - /// - /// - private string ToStringValue() - { - StringBuilder retVal = new StringBuilder(); - // Syntax: xxx-parm *(COMMA xxx-parm) - for (int i = 0; i < m_pValues.Count; i++) - { - retVal.Append(m_pValues[i].ToStringValue()); - - // Don't add comma for last item. - if (i < m_pValues.Count - 1) - { - retVal.Append(','); - } - } - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_OptionTags.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_OptionTags.cs deleted file mode 100644 index 8fc675da2..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_OptionTags.cs +++ /dev/null @@ -1,153 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - /// - /// SIP Option Tags. Defined in RFC 3261 27.1, defined values are in: http://www.iana.org/assignments/sip-parameters. - /// - /// - /// Option tags are used in header fields such as Require, Supported, Proxy-Require, and - /// Unsupported in support of SIP compatibility mechanisms for extensions (Section 19.2). - /// The option tag itself is a string that is associated with a particular SIP option (that is, an extension). - /// - public class SIP_OptionTags - { - #region Members - - /// - /// A UA adding the early-session option tag to a message indicates that it understands the - /// early-session content disposition. Defined in rfc 3959. - /// - public const string early_session = "early-session"; - - /// - /// Extension to allow subscriptions to lists of resources. Defined in rfc 4662. - /// - public const string eventlist = "eventlist"; - - /// - /// When used with the Supported header, this option tag indicates support for the - /// History Information to be captured for requests and returned in subsequent responses. - /// This tag is not used in a Proxy-Require or Require header field since support of - /// History-Info is optional. Defined in rfc 4244. - /// - public const string histinfo = "histinfo"; - - /// - /// Support for the SIP Join Header. Defined in rfc 3911. - /// - public const string join = "join"; - - /// - /// This option tag specifies a User Agent ability of accepting a REFER request without - /// establishing an implicit subscription (compared to the default case defined in RFC3515). - /// Defined in rfc 3911. - /// - public const string norefersub = "norefersub"; - - /// - /// A SIP UA that supports the Path extension header field includes this option tag as a - /// header field value in a Supported header field in all requests generated by that UA. - /// Intermediate proxies may use the presence of this option tag in a REGISTER request to - /// determine whether to offer Path service for for that request. If an intermediate proxy - /// requires that the registrar support Path for a request, then it includes this option tag - /// as a header field value in a Requires header field in that request. Defined in rfc 3327. - /// - public const string path = "path"; - - /// - /// An offerer MUST include this tag in the Require header field if the offer contains - /// one or more "mandatory" strength-tags. If all the strength-tags in the description are - /// "optional" or "none" the offerer MUST include this tag either in a Supported header field or - /// in a Require header field. Defined in rfc 3312. - /// - public const string precondition = "precondition"; - - /// - /// This option tag is used to ensure that a server understands the callee capabilities - /// parameters used in the request. Defined in rfc 3840. - /// - public const string pref = "pref"; - - /// - /// This option tag indicates support for the Privacy mechanism. When used in the - /// Proxy-Require header, it indicates that proxy servers do not forward the request unless they - /// can provide the requested privacy service. This tag is not used in the Require or - /// Supported headers. Proxies remove this option tag before forwarding the request if the desired - /// privacy function has been performed. Defined in rfc 3323. - /// - public const string privacy = "privacy"; - - /// - /// This option tag indicates support for the SIP Replaces header. Defined in rfc 3891. - /// - public const string replaces = "replaces"; - - /// - /// Indicates or requests support for the resource priority mechanism. Defined in rfc 4412. - /// - public const string resource_priority = "resource-priority"; - - /// - /// The option-tag sdp-anat is defined for use in the Require and Supported SIP [RFC3261] - /// header fields. SIP user agents that place this option-tag in a Supported header field understand - /// the ANAT semantics as defined in [RFC4091]. Defined in rfc 4092. - /// - public const string sdp_anat = "sdp-anat"; - - /// - /// This option tag indicates support for the Security Agreement mechanism. When used in the - /// Require, or Proxy-Require headers, it indicates that proxy servers are required to use the Security - /// Agreement mechanism. When used in the Supported header, it indicates that the User Agent Client - /// supports the Security Agreement mechanism. When used in the Require header in the 494 (Security Agreement - /// Required) or 421 (Extension Required) responses, it indicates that the User Agent Client must use the - /// Security Agreement Mechanism. Defined in rfc 3329. - /// - public const string sec_agree = "sec-agree"; - - /// - /// This option tag is used to identify the target dialog header field extension. When used in a - /// Require header field, it implies that the recipient needs to support the Target-Dialog header field. - /// When used in a Supported header field, it implies that the sender of the message supports it. - /// Defined in rfc 4538. - /// - public const string tdialog = "tdialog"; - - /// - /// This option tag is for support of the session timer extension. Inclusion in a Supported - /// header field in a request or response indicates that the UA is capable of performing - /// refreshes according to that specification. Inclusion in a Require header in a request - /// means that the UAS must understand the session timer extension to process the request. - /// Inclusion in a Require header field in a response indicates that the UAC must look for the - /// Session-Expires header field in the response, and process accordingly. Defined in rfc 4028. - /// - public const string timer = "timer"; - - /// - /// This option tag is for reliability of provisional responses. When present in a - /// Supported header, it indicates that the UA can send or receive reliable provisional - /// responses. When present in a Require header in a request it indicates that the UAS MUST - /// send all provisional responses reliably. When present in a Require header in a - /// reliable provisional response, it indicates that the response is to be sent reliably. - /// Defined in rfc 3262. - /// - public const string x100rel = "100rel"; - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_Parameter.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_Parameter.cs deleted file mode 100644 index 53d415957..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_Parameter.cs +++ /dev/null @@ -1,90 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - - #endregion - - /// - /// This class represents SIP value parameter. - /// - public class SIP_Parameter - { - #region Members - - private readonly string m_Name = ""; - private string m_Value = ""; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Parameter name. - public SIP_Parameter(string name) : this(name, "") {} - - /// - /// Default constructor. - /// - /// Parameter name. - /// Parameter value. - public SIP_Parameter(string name, string value) - { - if (name == null) - { - throw new ArgumentNullException("name"); - } - if (name == "") - { - throw new ArgumentException("Parameter 'name' value may no be empty string !"); - } - - m_Name = name; - m_Value = value; - } - - #endregion - - #region Properties - - /// - /// Gets parameter name. - /// - public string Name - { - get { return m_Name; } - } - - /// - /// Gets or sets parameter name. Value null means value less tag prameter. - /// - public string Value - { - get { return m_Value; } - - set { m_Value = value; } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_ParameterCollection.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_ParameterCollection.cs deleted file mode 100644 index c22a056b2..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_ParameterCollection.cs +++ /dev/null @@ -1,176 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Collections; - using System.Collections.Generic; - - #endregion - - /// - /// This class represents SIP value parameters collection. - /// - public class SIP_ParameterCollection : IEnumerable - { - #region Members - - private readonly List m_pCollection; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public SIP_ParameterCollection() - { - m_pCollection = new List(); - } - - #endregion - - #region Properties - - /// - /// Gets parameters count in the collection. - /// - public int Count - { - get { return m_pCollection.Count; } - } - - /// - /// Gets specified parameter from collection. Returns null if parameter with specified name doesn't exist. - /// - /// Parameter name. - /// Returns parameter with specified name or null if not found. - public SIP_Parameter this[string name] - { - get - { - foreach (SIP_Parameter parameter in m_pCollection) - { - if (parameter.Name.ToLower() == name.ToLower()) - { - return parameter; - } - } - - return null; - } - } - - #endregion - - #region Methods - - /// - /// Adds new parameter to the collection. - /// - /// Parameter name. - /// Parameter value. - /// Is raised when name is null. - /// Is raised when 'name' is '' or parameter with specified name - /// already exists in the collection. - public void Add(string name, string value) - { - if (name == null) - { - throw new ArgumentNullException("name"); - } - if (Contains(name)) - { - throw new ArgumentException( - "Prameter '' with specified name already exists in the collection !"); - } - - m_pCollection.Add(new SIP_Parameter(name, value)); - } - - /// - /// Adds or updates specified parameter value. - /// - /// Parameter name. - /// Parameter value. - public void Set(string name, string value) - { - if (Contains(name)) - { - this[name].Value = value; - } - else - { - Add(name, value); - } - } - - /// - /// Removes all parameters from the collection. - /// - public void Clear() - { - m_pCollection.Clear(); - } - - /// - /// Removes specified parameter from the collection. - /// - /// Parameter name. - public void Remove(string name) - { - SIP_Parameter parameter = this[name]; - if (parameter != null) - { - m_pCollection.Remove(parameter); - } - } - - /// - /// Checks if the collection contains parameter with the specified name. - /// - /// Parameter name. - /// Returns true if collection contains specified parameter. - public bool Contains(string name) - { - SIP_Parameter parameter = this[name]; - if (parameter != null) - { - return true; - } - else - { - return false; - } - } - - /// - /// Gets enumerator. - /// - /// - public IEnumerator GetEnumerator() - { - return m_pCollection.GetEnumerator(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_ParseException.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_ParseException.cs deleted file mode 100644 index 309e3ac41..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_ParseException.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - - #endregion - - /// - /// The exception that is thrown when a SIP message parsing fails. - /// - public class SIP_ParseException : Exception - { - #region Constructor - - /// - /// Default constructor. - /// - /// The message what describes the error. - public SIP_ParseException(string message) : base(message) {} - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_SVGroupHFCollection.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_SVGroupHFCollection.cs deleted file mode 100644 index 567e5af8f..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_SVGroupHFCollection.cs +++ /dev/null @@ -1,184 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System.Collections.Generic; - - #endregion - - /// - /// Implements same single value header fields group. Group can contain one type header fields only. - /// This is class is used by Authorization:,Proxy-Authorization:, ... . - /// - public class SIP_SVGroupHFCollection where T : SIP_t_Value - { - #region Members - - private readonly string m_FieldName = ""; - private readonly List> m_pFields; - private readonly SIP_Message m_pMessage; - - #endregion - - #region Properties - - /// - /// Gets header field name what this group holds. - /// - public string FieldName - { - get { return m_FieldName; } - } - - /// - /// Gets number of header fields in this group. - /// - public int Count - { - get { return m_pFields.Count; } - } - - /// - /// Gets header fields what are in this group. - /// - public SIP_SingleValueHF[] HeaderFields - { - get { return m_pFields.ToArray(); } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner message that owns this group. - /// Header field name what group holds. - public SIP_SVGroupHFCollection(SIP_Message owner, string fieldName) - { - m_pMessage = owner; - m_FieldName = fieldName; - - m_pFields = new List>(); - - Refresh(); - } - - #endregion - - #region Methods - - /// - /// Adds specified header field value to the end of header. - /// - /// Header field value. - public void Add(string value) - { - m_pMessage.Header.Add(m_FieldName, value); - Refresh(); - } - - /// - /// Removes header field from specified index. - /// - /// Index of the header field what to remove. Index is relative ths group. - public void Remove(int index) - { - m_pMessage.Header.Remove(m_pFields[index]); - m_pFields.RemoveAt(index); - } - - /// - /// Removes specified header field from header. - /// - /// Header field to remove. - public void Remove(SIP_SingleValueHF field) - { - m_pMessage.Header.Remove(field); - m_pFields.Remove(field); - } - - /// - /// Removes all this gorup header fields from header. - /// - public void RemoveAll() - { - foreach (SIP_SingleValueHF h in m_pFields) - { - m_pMessage.Header.Remove(h); - } - m_pFields.Clear(); - } - - /// - /// Gets the first(Top-Most) header field. Returns null if no header fields. - /// - /// Returns first header field or null if no header fields. - public SIP_SingleValueHF GetFirst() - { - if (m_pFields.Count > 0) - { - return m_pFields[0]; - } - else - { - return null; - } - } - - /// - /// Gets all header field values. - /// - /// - public T[] GetAllValues() - { - List retVal = new List(); - foreach (SIP_SingleValueHF hf in m_pFields) - { - retVal.Add(hf.ValueX); - } - - return retVal.ToArray(); - } - - #endregion - - #region Utility methods - - /// - /// Refreshes header fields in group from actual header. - /// - private void Refresh() - { - m_pFields.Clear(); - - foreach (SIP_HeaderField h in m_pMessage.Header) - { - if (h.Name.ToLower() == m_FieldName.ToLower()) - { - m_pFields.Add((SIP_SingleValueHF) h); - } - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_SingleValueHF.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_SingleValueHF.cs deleted file mode 100644 index e50d854ba..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_SingleValueHF.cs +++ /dev/null @@ -1,108 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - - #endregion - - /// - /// Implements single value header field. - /// Used by header fields like To:,From:,CSeq:, ... . - /// - public class SIP_SingleValueHF : SIP_HeaderField where T : SIP_t_Value - { - #region Members - - private T m_pValue; - - #endregion - - #region Properties - - /// - /// Gets or sets header field value. - /// - public override string Value - { - get { return ToStringValue(); } - - set - { - if (value == null) - { - throw new ArgumentNullException("Property Value value may not be null !"); - } - - Parse(new StringReader(value)); - } - } - - /// - /// Gets or sets header field value. - /// - public T ValueX - { - get { return m_pValue; } - - set { m_pValue = value; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Header field name. - /// Header field value. - public SIP_SingleValueHF(string name, T value) : base(name, "") - { - m_pValue = value; - } - - #endregion - - #region Methods - - /// - /// Parses single value from specified reader. - /// - /// Reader what contains - public void Parse(StringReader reader) - { - m_pValue.Parse(reader); - } - - /// - /// Convert this to string value. - /// - /// Returns this as string value. - public string ToStringValue() - { - return m_pValue.ToStringValue(); - } - - #endregion - - // FIX ME: Change base class Value property or this to new name - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_WarningCodes.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_WarningCodes.cs deleted file mode 100644 index eb54baa39..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_WarningCodes.cs +++ /dev/null @@ -1,92 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - /// - /// SIP Warning Codes. Defined in RFC 3261 27.2. - /// - public class SIP_WarningCodes - { - #region Members - - /// - /// One or more network protocols contained in the session description are not available. - /// - public const int x300_Incompatible_network_protocol = 300; - - /// - /// One or more network address formats contained in the session description are not available. - /// - public const int x301_Incompatible_network_address_formats = 301; - - /// - /// One or more transport protocols described in the session description are not available. - /// - public const int x302_Incompatible_network_address_formats = 302; - - /// - /// One or more bandwidth measurement units contained in the session description were not understood. - /// - public const int x303_Incompatible_bandwidth_units = 303; - - /// - /// One or more media types contained in the session description are not available. - /// - public const int x304_Media_type_not_available = 304; - - /// - /// One or more media formats contained in the session description are not available. - /// - public const int x305_Incompatible_media_format = 305; - - /// - /// One or more of the media attributes in the session description are not supported. - /// - public const int x306_Attribute_not_understood = 306; - - /// - /// A parameter other than those listed above was not understood. - /// - public const int x307_Session_description_parameter_not_understood = 307; - - /// - /// The site where the user is located does not support multicast. - /// - public const int x330_Multicast_not_available = 330; - - /// - /// The site where the user is located does not support unicast communication - /// (usually due to the presence of a firewall). - /// - public const int x331_Unicast_not_available = 331; - - /// - /// The bandwidth specified in the session description or defined by the media - /// exceeds that known to be available. - /// - public const int x370_Insufficient_bandwidth = 370; - - /// - /// The warning text can include arbitrary information to be presented to a human user or logged. - /// A system receiving this warning MUST NOT take any automated action. - /// - public const int x399_Miscellaneous_warning = 399; - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ACValue.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ACValue.cs deleted file mode 100644 index 29c671561..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ACValue.cs +++ /dev/null @@ -1,205 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements SIP "ac-value" value. Defined in RFC 3841. - /// - /// - /// - /// RFC 3841 Syntax: - /// ac-value = "*" *(SEMI ac-params) - /// ac-params = feature-param / req-param / explicit-param / generic-param - /// ;;feature param from RFC 3840 - /// ;;generic-param from RFC 3261 - /// req-param = "require" - /// explicit-param = "explicit" - /// - /// - public class SIP_t_ACValue : SIP_t_ValueWithParams - { - #region Properties - - /// - /// Gets or sets 'require' parameter value. - /// - public bool Require - { - get - { - SIP_Parameter parameter = Parameters["require"]; - if (parameter != null) - { - return true; - } - else - { - return false; - } - } - - set - { - if (!value) - { - Parameters.Remove("require"); - } - else - { - Parameters.Set("require", null); - } - } - } - - /// - /// Gets or sets 'explicit' parameter value. - /// - public bool Explicit - { - get - { - SIP_Parameter parameter = Parameters["explicit"]; - if (parameter != null) - { - return true; - } - else - { - return false; - } - } - - set - { - if (!value) - { - Parameters.Remove("explicit"); - } - else - { - Parameters.Set("explicit", null); - } - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public SIP_t_ACValue() {} - - /// - /// Default constructor. - /// - /// SIP 'ac-value' value. - public SIP_t_ACValue(string value) - { - Parse(value); - } - - #endregion - - #region Methods - - /// - /// Parses "ac-value" from specified value. - /// - /// SIP "ac-value" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "ac-value" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - ac-value = "*" *(SEMI ac-params) - ac-params = feature-param / req-param / explicit-param / generic-param - ;;feature param from RFC 3840 - ;;generic-param from RFC 3261 - req-param = "require" - explicit-param = "explicit" - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("Invalid 'ac-value', '*' is missing !"); - } - - // Parse parameters - ParseParameters(reader); - } - - /// - /// Converts this to valid "ac-value" value. - /// - /// Returns "ac-value" value. - public override string ToStringValue() - { - /* - ac-value = "*" *(SEMI ac-params) - ac-params = feature-param / req-param / explicit-param / generic-param - ;;feature param from RFC 3840 - ;;generic-param from RFC 3261 - req-param = "require" - explicit-param = "explicit" - */ - - StringBuilder retVal = new StringBuilder(); - - // * - retVal.Append("*"); - - // Add parameters - retVal.Append(ParametersToString()); - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_AcceptRange.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_AcceptRange.cs deleted file mode 100644 index 0a4e3ad6b..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_AcceptRange.cs +++ /dev/null @@ -1,283 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements SIP "accept-range" value. Defined in RFC 3261. - /// - /// - /// - /// RFC 3261 Syntax: - /// accept-range = media-range [ accept-params ] - /// media-range = ("*//*" / (m-type SLASH "*") / (m-type SLASH m-subtype)) *(SEMI m-parameter) - /// accept-params = SEMI "q" EQUAL qvalue *(SEMI generic-param) - /// - /// - public class SIP_t_AcceptRange : SIP_t_Value - { - #region Members - - private readonly SIP_ParameterCollection m_pMediaParameters; - private readonly SIP_ParameterCollection m_pParameters; - private string m_MediaType = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets media type. Value *(STAR) means all values. Syntax: mediaType / mediaSubType. - /// Examples: */*,video/*,text/html. - /// - public string MediaType - { - get { return m_MediaType; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Property MediaType value can't be null or empty !"); - } - if (value.IndexOf('/') == -1) - { - throw new ArgumentException( - "Invalid roperty MediaType value, syntax: mediaType / mediaSubType !"); - } - - m_MediaType = value; - } - } - - /// - /// Gets media parameters collection. - /// - /// - public SIP_ParameterCollection MediaParameters - { - get { return m_pMediaParameters; } - } - - /// - /// Gets accept value parameters. - /// - public SIP_ParameterCollection Parameters - { - get { return m_pParameters; } - } - - /// - /// Gets or sets qvalue parameter. Targets are processed from highest qvalue to lowest. - /// This value must be between 0.0 and 1.0. Value -1 means that value not specified. - /// - public double QValue - { - get - { - SIP_Parameter parameter = Parameters["qvalue"]; - if (parameter != null) - { - return Convert.ToDouble(parameter.Value); - } - else - { - return -1; - } - } - - set - { - if (value < 0 || value > 1) - { - throw new ArgumentException("Property QValue value must be between 0.0 and 1.0 !"); - } - - if (value < 0) - { - Parameters.Remove("qvalue"); - } - else - { - Parameters.Set("qvalue", value.ToString()); - } - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public SIP_t_AcceptRange() - { - m_pMediaParameters = new SIP_ParameterCollection(); - m_pParameters = new SIP_ParameterCollection(); - } - - #endregion - - #region Methods - - /// - /// Parses "accept-range" from specified value. - /// - /// SIP "accept-range" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "accept-range" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - accept-range = media-range [ accept-params ] - media-range = ("*/ - /*" / (m-type SLASH "*") / (m-type SLASH m-subtype)) *(SEMI m-parameter) - accept-params = SEMI "q" EQUAL qvalue *(SEMI generic-param) - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // Parse m-type - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("Invalid 'accept-range' value, m-type is missing !"); - } - MediaType = word; - - // Parse media and accept parameters !!! thats confusing part, RFC invalid. - bool media_accept = true; - while (reader.Available > 0) - { - reader.ReadToFirstChar(); - - // We have 'next' value, so we are done here. - if (reader.SourceString.StartsWith(",")) - { - break; - } - // We have parameter - else if (reader.SourceString.StartsWith(";")) - { - reader.ReadSpecifiedLength(1); - string paramString = reader.QuotedReadToDelimiter(new[] {';', ','}, false); - if (paramString != "") - { - string[] name_value = paramString.Split(new[] {'='}, 2); - string name = name_value[0].Trim(); - string value = ""; - if (name_value.Length == 2) - { - value = name_value[1]; - } - - // If q, then accept parameters begin - if (name.ToLower() == "q") - { - media_accept = false; - } - - if (media_accept) - { - MediaParameters.Add(name, value); - } - else - { - Parameters.Add(name, value); - } - } - } - // Unknown data - else - { - throw new SIP_ParseException("SIP_t_AcceptRange unexpected prarameter value !"); - } - } - } - - /// - /// Converts this to valid "accept-range" value. - /// - /// Returns "accept-range" value. - public override string ToStringValue() - { - /* - Accept = "Accept" HCOLON [ accept-range *(COMMA accept-range) ] - accept-range = media-range [ accept-params ] - media-range = ("*/ - /*" / (m-type SLASH "*") / (m-type SLASH m-subtype)) *(SEMI m-parameter) - accept-params = SEMI "q" EQUAL qvalue *(SEMI generic-param) - */ - - StringBuilder retVal = new StringBuilder(); - retVal.Append(m_MediaType); - foreach (SIP_Parameter parameter in m_pMediaParameters) - { - if (parameter.Value != null) - { - retVal.Append(";" + parameter.Name + "=" + parameter.Value); - } - else - { - retVal.Append(";" + parameter.Name); - } - } - foreach (SIP_Parameter parameter in m_pParameters) - { - if (parameter.Value != null) - { - retVal.Append(";" + parameter.Name + "=" + parameter.Value); - } - else - { - retVal.Append(";" + parameter.Name); - } - } - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_AddressParam.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_AddressParam.cs deleted file mode 100644 index 9a37967a0..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_AddressParam.cs +++ /dev/null @@ -1,137 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements SIP_t_NameAddress + parameters value. - /// - public class SIP_t_AddressParam : SIP_t_ValueWithParams - { - #region Members - - private SIP_t_NameAddress m_pAddress; - - #endregion - - #region Properties - - /// - /// Gets address. - /// - public SIP_t_NameAddress Address - { - get { return m_pAddress; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public SIP_t_AddressParam() {} - - /// - /// Default constructor. - /// - /// SIP_t_NameAddress + parameters value. - public SIP_t_AddressParam(string value) - { - Parse(value); - } - - #endregion - - #region Methods - - /// - /// Parses this from specified value. - /// - /// Address + params value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses this from address param string. - /// - /// Reader what contains address param string. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // Parse address - SIP_t_NameAddress address = new SIP_t_NameAddress(); - address.Parse(reader); - m_pAddress = address; - - // Parse parameters. - ParseParameters(reader); - } - - /// - /// Converts this to valid value string. - /// - /// - public override string ToStringValue() - { - StringBuilder retVal = new StringBuilder(); - - // Add address - retVal.Append(m_pAddress.ToStringValue()); - - // Add parameters - foreach (SIP_Parameter parameter in Parameters) - { - if (parameter.Value != null) - { - retVal.Append(";" + parameter.Name + "=" + parameter.Value); - } - else - { - retVal.Append(";" + parameter.Name); - } - } - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_AlertParam.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_AlertParam.cs deleted file mode 100644 index ecd804078..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_AlertParam.cs +++ /dev/null @@ -1,129 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements SIP "alert-param" value. Defined in RFC 3261. - /// - /// - /// - /// RFC 3261 Syntax: - /// alert-param = LAQUOT absoluteURI RAQUOT *( SEMI generic-param ) - /// - /// - public class SIP_t_AlertParam : SIP_t_ValueWithParams - { - #region Members - - private string m_Uri = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets uri value. - /// - public string Uri - { - get { return m_Uri; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Property Uri value can't be null or empty !"); - } - - m_Uri = value; - } - } - - #endregion - - #region Methods - - /// - /// Parses "alert-param" from specified value. - /// - /// SIP "alert-param" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "alert-param" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - alert-param = LAQUOT absoluteURI RAQUOT *( SEMI generic-param ) - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // Parse uri - // Read to LAQUOT - reader.QuotedReadToDelimiter('<'); - if (!reader.StartsWith("<")) - { - throw new SIP_ParseException("Invalid Alert-Info value, Uri not between <> !"); - } - m_Uri = reader.ReadParenthesized(); - - // Parse parameters - ParseParameters(reader); - } - - /// - /// Converts this to valid "alert-param" value. - /// - /// Returns "alert-param" value. - public override string ToStringValue() - { - StringBuilder retVal = new StringBuilder(); - retVal.Append("<" + m_Uri + ">"); - retVal.Append(ParametersToString()); - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_AuthenticationInfo.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_AuthenticationInfo.cs deleted file mode 100644 index 094ac868a..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_AuthenticationInfo.cs +++ /dev/null @@ -1,267 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements SIP "Authentication-Info" value. Defined in RFC 3261. - /// According RFC 3261 authentication info can contain Digest authentication info only. - /// - public class SIP_t_AuthenticationInfo : SIP_t_Value - { - #region Members - - private string m_CNonce; - private string m_NextNonce; - private int m_NonceCount = -1; - private string m_Qop; - private string m_ResponseAuth; - - #endregion - - #region Properties - - /// - /// Gets or sets server next predicted nonce value. Value null means that value not specified. - /// - public string NextNonce - { - get { return m_NextNonce; } - - set { m_NextNonce = value; } - } - - /// - /// Gets or sets QOP value. Value null means that value not specified. - /// - public string Qop - { - get { return m_Qop; } - - set { m_Qop = value; } - } - - /// - /// Gets or sets rspauth value. Value null means that value not specified. - /// This can be only HEX value. - /// - public string ResponseAuth - { - get { return m_ResponseAuth; } - - set - { - // TODO: Check that value is hex value - - m_ResponseAuth = value; - } - } - - /// - /// Gets or sets cnonce value. Value null means that value not specified. - /// - public string CNonce - { - get { return m_CNonce; } - - set { m_CNonce = value; } - } - - /// - /// Gets or sets nonce count. Value -1 means that value not specified. - /// - public int NonceCount - { - get { return m_NonceCount; } - - set - { - if (value < 0) - { - m_NonceCount = -1; - } - else - { - m_NonceCount = value; - } - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Authentication-Info valu value. - public SIP_t_AuthenticationInfo(string value) - { - Parse(new StringReader(value)); - } - - #endregion - - #region Methods - - /// - /// Parses "Authentication-Info" from specified value. - /// - /// SIP "Authentication-Info" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "Authentication-Info" from specified reader. - /// - /// Reader what contains Authentication-Info value. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - Authentication-Info = "Authentication-Info" HCOLON ainfo *(COMMA ainfo) - ainfo = nextnonce / message-qop / response-auth / cnonce / nonce-count - nextnonce = "nextnonce" EQUAL nonce-value - response-auth = "rspauth" EQUAL response-digest - response-digest = LDQUOT *LHEX RDQUOT - nc-value = 8LHEX - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - while (reader.Available > 0) - { - string word = reader.QuotedReadToDelimiter(','); - if (word != null && word.Length > 0) - { - string[] name_value = word.Split(new[] {'='}, 2); - if (name_value[0].ToLower() == "nextnonce") - { - NextNonce = name_value[1]; - } - else if (name_value[0].ToLower() == "qop") - { - Qop = name_value[1]; - } - else if (name_value[0].ToLower() == "rspauth") - { - ResponseAuth = name_value[1]; - } - else if (name_value[0].ToLower() == "cnonce") - { - CNonce = name_value[1]; - } - else if (name_value[0].ToLower() == "nc") - { - NonceCount = Convert.ToInt32(name_value[1]); - } - else - { - throw new SIP_ParseException("Invalid Authentication-Info value !"); - } - } - } - } - - /// - /// Converts SIP_t_AuthenticationInfo to valid Authentication-Info value. - /// - /// - public override string ToStringValue() - { - /* - Authentication-Info = "Authentication-Info" HCOLON ainfo *(COMMA ainfo) - ainfo = nextnonce / message-qop / response-auth / cnonce / nonce-count - nextnonce = "nextnonce" EQUAL nonce-value - response-auth = "rspauth" EQUAL response-digest - response-digest = LDQUOT *LHEX RDQUOT - nc-value = 8LHEX - */ - - StringBuilder retVal = new StringBuilder(); - - if (m_NextNonce != null) - { - retVal.Append("nextnonce=" + m_NextNonce); - } - - if (m_Qop != null) - { - if (retVal.Length > 0) - { - retVal.Append(','); - } - - retVal.Append("qop=" + m_Qop); - } - - if (m_ResponseAuth != null) - { - if (retVal.Length > 0) - { - retVal.Append(','); - } - - retVal.Append("rspauth=" + TextUtils.QuoteString(m_ResponseAuth)); - } - - if (m_CNonce != null) - { - if (retVal.Length > 0) - { - retVal.Append(','); - } - - retVal.Append("cnonce=" + m_CNonce); - } - - if (m_NonceCount != -1) - { - if (retVal.Length > 0) - { - retVal.Append(','); - } - - retVal.Append("nc=" + m_NonceCount.ToString("X8")); - } - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_CSeq.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_CSeq.cs deleted file mode 100644 index fe6c445bd..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_CSeq.cs +++ /dev/null @@ -1,180 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - - #endregion - - /// - /// Implements SIP "Cseq" value. Defined in RFC 3261. - /// A CSeq in a request contains a single decimal sequence number and - /// the request method. The method part of CSeq is case-sensitive. The CSeq header - /// field serves to order transactions within a dialog, to provide a means to uniquely - /// identify transactions, and to differentiate between new requests and request retransmissions. - /// - /// - /// - /// RFC 3261 Syntax: - /// CSeq = 1*DIGIT LWS Method - /// - /// - public class SIP_t_CSeq : SIP_t_Value - { - #region Members - - private string m_RequestMethod = ""; - private int m_SequenceNumber = 1; - - #endregion - - #region Properties - - /// - /// Gets or sets sequence number. - /// - public int SequenceNumber - { - get { return m_SequenceNumber; } - - set - { - if (value < 1) - { - throw new ArgumentException("Property SequenceNumber value must be >= 1 !"); - } - - m_SequenceNumber = value; - } - } - - /// - /// Gets or sets request method. Note: this value is case-sensitive ! - /// - public string RequestMethod - { - get { return m_RequestMethod; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Property RequestMethod value can't be null or empty !"); - } - - m_RequestMethod = value; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// CSeq: header field value. - public SIP_t_CSeq(string value) - { - Parse(new StringReader(value)); - } - - /// - /// Default constructor. - /// - /// Command sequence number. - /// Request method. - public SIP_t_CSeq(int sequenceNumber, string requestMethod) - { - m_SequenceNumber = sequenceNumber; - m_RequestMethod = requestMethod; - } - - #endregion - - #region Methods - - /// - /// Parses "CSeq" from specified value. - /// - /// SIP "CSeq" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "CSeq" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - // CSeq = 1*DIGIT LWS Method - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // Get sequence number - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("Invalid 'CSeq' value, sequence number is missing !"); - } - try - { - m_SequenceNumber = Convert.ToInt32(word); - } - catch - { - throw new SIP_ParseException("Invalid CSeq 'sequence number' value !"); - } - - // Get request method - word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("Invalid 'CSeq' value, request method is missing !"); - } - m_RequestMethod = word; - } - - /// - /// Converts this to valid "CSeq" value. - /// - /// Returns "CSeq" value. - public override string ToStringValue() - { - return m_SequenceNumber + " " + m_RequestMethod; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_CallID.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_CallID.cs deleted file mode 100644 index 221d4a568..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_CallID.cs +++ /dev/null @@ -1,130 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - - #endregion - - /// - /// Implements SIP "callid" value. Defined in RFC 3261. - /// - /// - /// - /// RFC 3261 Syntax: - /// callid = word [ "@" word ] - /// - /// - public class SIP_t_CallID : SIP_t_Value - { - #region Members - - private string m_CallID = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets call ID. - /// - public string CallID - { - get { return m_CallID; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Property CallID value may not be null or empty !"); - } - - m_CallID = value; - } - } - - #endregion - - #region Methods - - /// - /// Creates new call ID value. - /// - /// Returns call ID value. - public static SIP_t_CallID CreateCallID() - { - SIP_t_CallID callID = new SIP_t_CallID(); - callID.CallID = Guid.NewGuid().ToString().Replace("-", ""); - - return callID; - } - - /// - /// Parses "callid" from specified value. - /// - /// SIP "callid" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "callid" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - // callid = word [ "@" word ] - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // Get Method - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("Invalid 'callid' value, callid is missing !"); - } - m_CallID = word; - } - - /// - /// Converts this to valid "callid" value. - /// - /// Returns "callid" value. - public override string ToStringValue() - { - return m_CallID; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Challenge.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Challenge.cs deleted file mode 100644 index e38925252..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Challenge.cs +++ /dev/null @@ -1,160 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - - #endregion - - /// - /// Implements SIP "challenge" value. Defined in RFC 3261. - /// - /// - /// - /// RFC 3261 Syntax: - /// challenge = ("Digest" LWS digest-cln *(COMMA digest-cln)) / other-challenge - /// - /// - public class SIP_t_Challenge : SIP_t_Value - { - #region Members - - private string m_AuthData = ""; - private string m_Method = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets authentication method. Normally this value is always 'Digest'. - /// - public string Method - { - get { return m_Method; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Property Method value cant be null or mepty !"); - } - - m_Method = value; - } - } - - /// - /// Gets or sets authentication data. That value depends on authentication type. - /// - public string AuthData - { - get { return m_AuthData; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Property AuthData value cant be null or mepty !"); - } - - m_AuthData = value; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// SIP challenge value. - public SIP_t_Challenge(string value) - { - Parse(new StringReader(value)); - } - - #endregion - - #region Methods - - /// - /// Parses "challenge" from specified value. - /// - /// SIP "challenge" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "challenge" from specified reader. - /// - /// Reader what contains challenge value. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - // challenge = ("Digest" LWS digest-cln *(COMMA digest-cln)) / other-challenge - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // Get authentication method - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException( - "Invalid WWW-Authenticate: value, authentication method is missing !"); - } - m_Method = word; - - // Get authentication data - word = reader.ReadToEnd(); - if (word == null) - { - throw new SIP_ParseException( - "Invalid WWW-Authenticate: value, authentication parameters are missing !"); - } - m_AuthData = word.Trim(); - } - - /// - /// Converts this to valid "challenge" value. - /// - /// Returns "challenge" value. - public override string ToStringValue() - { - return m_Method + " " + m_AuthData; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ContactParam.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ContactParam.cs deleted file mode 100644 index aaa9845a9..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ContactParam.cs +++ /dev/null @@ -1,256 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Globalization; - using System.Text; - - #endregion - - /// - /// Implements SIP "contact-param" value. Defined in RFC 3261. - /// - /// - /// - /// RFC 3261 Syntax: - /// contact-param = (name-addr / addr-spec) *(SEMI contact-params) - /// contact-params = c-p-q / c-p-expires / contact-extension - /// c-p-q = "q" EQUAL qvalue - /// c-p-expires = "expires" EQUAL delta-seconds - /// contact-extension = generic-param - /// delta-seconds = 1*DIGIT - /// - /// - public class SIP_t_ContactParam : SIP_t_ValueWithParams - { - #region Members - - private SIP_t_NameAddress m_pAddress; - - #endregion - - #region Properties - - /// - /// Gets is this SIP contact is special STAR contact. - /// - public bool IsStarContact - { - get - { - if (m_pAddress.Uri.Value.StartsWith("*")) - { - return true; - } - else - { - return false; - } - } - } - - /// - /// Gets contact address. - /// - public SIP_t_NameAddress Address - { - get { return m_pAddress; } - } - - /// - /// Gets or sets qvalue parameter. Targets are processed from highest qvalue to lowest. - /// This value must be between 0.0 and 1.0. Value -1 means that value not specified. - /// - public double QValue - { - get - { - if (!Parameters.Contains("qvalue")) - { - return -1; - } - else - { - return double.Parse(Parameters["qvalue"].Value, NumberStyles.Any); - } - } - - set - { - if (value < 0 || value > 1) - { - throw new ArgumentException("Property QValue value must be between 0.0 and 1.0 !"); - } - - if (value < 0) - { - Parameters.Remove("qvalue"); - } - else - { - Parameters.Set("qvalue", value.ToString()); - } - } - } - - /// - /// Gets or sets expire parameter (time in seconds when contact expires). Value -1 means not specified. - /// - public int Expires - { - get - { - SIP_Parameter parameter = Parameters["expires"]; - if (parameter != null) - { - return Convert.ToInt32(parameter.Value); - } - else - { - return -1; - } - } - - set - { - if (value < 0) - { - Parameters.Remove("expires"); - } - else - { - Parameters.Set("expires", value.ToString()); - } - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public SIP_t_ContactParam() - { - m_pAddress = new SIP_t_NameAddress(); - } - - #endregion - - #region Methods - - /// - /// Parses "contact-param" from specified value. - /// - /// SIP "contact-param" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "contact-param" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - Contact = ("Contact" / "m" ) HCOLON - ( STAR / (contact-param *(COMMA contact-param))) - contact-param = (name-addr / addr-spec) *(SEMI contact-params) - name-addr = [ display-name ] LAQUOT addr-spec RAQUOT - addr-spec = SIP-URI / SIPS-URI / absoluteURI - display-name = *(token LWS)/ quoted-string - - contact-params = c-p-q / c-p-expires / contact-extension - c-p-q = "q" EQUAL qvalue - c-p-expires = "expires" EQUAL delta-seconds - contact-extension = generic-param - delta-seconds = 1*DIGIT - - When the header field value contains a display name, the URI including all URI - parameters is enclosed in "<" and ">". If no "<" and ">" are present, all - parameters after the URI are header parameters, not URI parameters. - - Even if the "display-name" is empty, the "name-addr" form MUST be - used if the "addr-spec" contains a comma, semicolon, or question - mark. There may or may not be LWS between the display-name and the "<". - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // Parse address - SIP_t_NameAddress address = new SIP_t_NameAddress(); - address.Parse(reader); - m_pAddress = address; - - // Parse parameters - ParseParameters(reader); - } - - /// - /// Converts this to valid "contact-param" value. - /// - /// Returns "contact-param" value. - public override string ToStringValue() - { - /* - Contact = ("Contact" / "m" ) HCOLON - ( STAR / (contact-param *(COMMA contact-param))) - contact-param = (name-addr / addr-spec) *(SEMI contact-params) - name-addr = [ display-name ] LAQUOT addr-spec RAQUOT - addr-spec = SIP-URI / SIPS-URI / absoluteURI - display-name = *(token LWS)/ quoted-string - - contact-params = c-p-q / c-p-expires / contact-extension - c-p-q = "q" EQUAL qvalue - c-p-expires = "expires" EQUAL delta-seconds - contact-extension = generic-param - delta-seconds = 1*DIGIT - */ - - StringBuilder retVal = new StringBuilder(); - - // Add address - retVal.Append(m_pAddress.ToStringValue()); - - // Add parameters - retVal.Append(ParametersToString()); - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ContentCoding.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ContentCoding.cs deleted file mode 100644 index 03506741c..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ContentCoding.cs +++ /dev/null @@ -1,124 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - - #endregion - - /// - /// Implements SIP "content-coding" value. Defined in RFC 3261. - /// - /// - /// - /// RFC 3261 Syntax: - /// content-coding = token - /// - /// - public class SIP_t_ContentCoding : SIP_t_Value - { - #region Members - - private string m_Encoding = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets content encoding. - /// - public string Encoding - { - get { return m_Encoding; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Property Encoding value may not be null or empty !"); - } - if (!TextUtils.IsToken(value)) - { - throw new ArgumentException("Encoding value may be 'token' only !"); - } - - m_Encoding = value; - } - } - - #endregion - - #region Methods - - /// - /// Parses "content-coding" from specified value. - /// - /// SIP "content-coding" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "content-coding" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - content-coding = token - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // Get Method - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("Invalid 'content-coding' value, value is missing !"); - } - m_Encoding = word; - } - - /// - /// Converts this to valid "content-coding" value. - /// - /// Returns "content-coding" value. - public override string ToStringValue() - { - return m_Encoding; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ContentDisposition.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ContentDisposition.cs deleted file mode 100644 index 8b3e0f6ec..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ContentDisposition.cs +++ /dev/null @@ -1,187 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements SIP "Content-Disposition" value. Defined in RFC 3261. - /// - /// - /// - /// RFC 3261 Syntax: - /// Content-Disposition = disp-type *( SEMI disp-param ) - /// disp-type = "render" / "session" / "icon" / "alert" / disp-extension-token - /// disp-param = handling-param / generic-param - /// handling-param = "handling" EQUAL ( "optional" / "required" / other-handling ) - /// other-handling = token - /// disp-extension-token = token - /// - /// - public class SIP_t_ContentDisposition : SIP_t_ValueWithParams - { - #region Members - - private string m_DispositionType = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets disposition type. Known values: "render","session","icon","alert". - /// - public string DispositionType - { - get { return m_DispositionType; } - - set - { - if (value == null) - { - throw new ArgumentNullException("DispositionType"); - } - if (!TextUtils.IsToken(value)) - { - throw new ArgumentException("Invalid DispositionType value, value must be 'token' !"); - } - - m_DispositionType = value; - } - } - - /// - /// Gets or sets 'handling' parameter value. Value null means not specified. - /// Known value: "optional","required". - /// - public string Handling - { - get - { - SIP_Parameter parameter = Parameters["handling"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (string.IsNullOrEmpty(value)) - { - Parameters.Remove("handling"); - } - else - { - Parameters.Set("handling", value); - } - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// SIP SIP_t_ContentDisposition value. - public SIP_t_ContentDisposition(string value) - { - Parse(value); - } - - #endregion - - #region Methods - - /// - /// Parses "Content-Disposition" from specified value. - /// - /// SIP "Content-Disposition" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "Content-Disposition" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - Content-Disposition = disp-type *( SEMI disp-param ) - disp-type = "render" / "session" / "icon" / "alert" / disp-extension-token - disp-param = handling-param / generic-param - handling-param = "handling" EQUAL ( "optional" / "required" / other-handling ) - other-handling = token - disp-extension-token = token - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // disp-type - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("SIP Content-Disposition 'disp-type' value is missing !"); - } - m_DispositionType = word; - - // Parse parameters - ParseParameters(reader); - } - - /// - /// Converts this to valid "Content-Disposition" value. - /// - /// Returns "Content-Disposition" value. - public override string ToStringValue() - { - StringBuilder retVal = new StringBuilder(); - retVal.Append(m_DispositionType); - retVal.Append(ParametersToString()); - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Credentials.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Credentials.cs deleted file mode 100644 index 4b670b296..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Credentials.cs +++ /dev/null @@ -1,163 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - - #endregion - - /// - /// Implements SIP "credentials" value. Defined in RFC 3261. - /// - /// - /// - /// RFC 3261 Syntax: - /// credentials = ("Digest" LWS digest-response) / other-response - /// other-response = auth-scheme LWS auth-param *(COMMA auth-param) - /// auth-scheme = token - /// - /// - public class SIP_t_Credentials : SIP_t_Value - { - #region Members - - private string m_AuthData = ""; - private string m_Method = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets authentication method. Normally this value is always 'Digest'. - /// - public string Method - { - get { return m_Method; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Property Method value cant be null or mepty !"); - } - - m_Method = value; - } - } - - /// - /// Gets or sets authentication data. That value depends on authentication type. - /// - public string AuthData - { - get { return m_AuthData; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Property AuthData value cant be null or mepty !"); - } - - m_AuthData = value; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// SIP credentials value. - public SIP_t_Credentials(string value) - { - Parse(new StringReader(value)); - } - - #endregion - - #region Methods - - /// - /// Parses "credentials" from specified value. - /// - /// SIP "credentials" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "credentials" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - credentials = ("Digest" LWS digest-response) / other-response - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // Get authentication method - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("Invalid 'credentials' value, authentication method is missing !"); - } - m_Method = word; - - // Get authentication data - word = reader.ReadToEnd(); - if (word == null) - { - throw new SIP_ParseException( - "Invalid 'credentials' value, authentication parameters are missing !"); - } - m_AuthData = word.Trim(); - } - - /// - /// Converts this to valid "credentials" value. - /// - /// Returns "credentials" value. - public override string ToStringValue() - { - return m_Method + " " + m_AuthData; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Directive.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Directive.cs deleted file mode 100644 index c704e2a61..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Directive.cs +++ /dev/null @@ -1,319 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - - #endregion - - /// - /// Implements SIP "directive" value. Defined in RFC 3841. - /// - /// - /// - /// RFC 3841 Syntax: - /// directive = proxy-directive / cancel-directive / fork-directive / recurse-directive / - /// parallel-directive / queue-directive - /// proxy-directive = "proxy" / "redirect" - /// cancel-directive = "cancel" / "no-cancel" - /// fork-directive = "fork" / "no-fork" - /// recurse-directive = "recurse" / "no-recurse" - /// parallel-directive = "parallel" / "sequential" - /// queue-directive = "queue" / "no-queue" - /// - /// - public class SIP_t_Directive : SIP_t_Value - { - #region DirectiveType enum - - /// - /// Proccess directives. Defined in rfc 3841 9.1. - /// - public enum DirectiveType - { - /// - /// This directive indicates whether the caller would like each server to proxy request. - /// - Proxy, - - /// - /// This directive indicates whether the caller would like each server to redirect request. - /// - Redirect, - - /// - /// This directive indicates whether the caller would like each proxy server to send a CANCEL - /// request to forked branches. - /// - Cancel, - - /// - /// This directive indicates whether the caller would NOT want each proxy server to send a CANCEL - /// request to forked branches. - /// - NoCancel, - - /// - /// This type of directive indicates whether a proxy should fork a request. - /// - Fork, - - /// - /// This type of directive indicates whether a proxy should proxy to only a single address. - /// The server SHOULD proxy the request to the "best" address (generally the one with the highest q-value). - /// - NoFork, - - /// - /// This directive indicates whether a proxy server receiving a 3xx response should send - /// requests to the addresses listed in the response. - /// - Recurse, - - /// - /// This directive indicates whether a proxy server receiving a 3xx response should forward - /// the list of addresses upstream towards the caller. - /// - NoRecurse, - - /// - /// This directive indicates whether the caller would like the proxy server to proxy - /// the request to all known addresses at once. - /// - Parallel, - - /// - /// This directive indicates whether the caller would like the proxy server to go through - /// all known addresses sequentially, contacting the next address only after it has received - /// a non-2xx or non-6xx final response for the previous one. - /// - Sequential, - - /// - /// This directive indicates whether if the called party is temporarily unreachable, caller - /// wants to have its call queued. - /// - Queue, - - /// - /// This directive indicates whether if the called party is temporarily unreachable, caller - /// don't want its call to be queued. - /// - NoQueue - } - - #endregion - - #region Members - - private DirectiveType m_Directive = DirectiveType.Fork; - - #endregion - - #region Properties - - /// - /// Gets or sets directive. - /// - public DirectiveType Directive - { - get { return m_Directive; } - - set { m_Directive = value; } - } - - #endregion - - #region Methods - - /// - /// Parses "directive" from specified value. - /// - /// SIP "directive" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "directive" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - directive = proxy-directive / cancel-directive / fork-directive / recurse-directive / - parallel-directive / queue-directive - proxy-directive = "proxy" / "redirect" - cancel-directive = "cancel" / "no-cancel" - fork-directive = "fork" / "no-fork" - recurse-directive = "recurse" / "no-recurse" - parallel-directive = "parallel" / "sequential" - queue-directive = "queue" / "no-queue" - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // Get Method - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("'directive' value is missing !"); - } - if (word.ToLower() == "proxy") - { - m_Directive = DirectiveType.Proxy; - } - else if (word.ToLower() == "redirect") - { - m_Directive = DirectiveType.Redirect; - } - else if (word.ToLower() == "cancel") - { - m_Directive = DirectiveType.Cancel; - } - else if (word.ToLower() == "no-cancel") - { - m_Directive = DirectiveType.NoCancel; - } - else if (word.ToLower() == "fork") - { - m_Directive = DirectiveType.Fork; - } - else if (word.ToLower() == "no-fork") - { - m_Directive = DirectiveType.NoFork; - } - else if (word.ToLower() == "recurse") - { - m_Directive = DirectiveType.Recurse; - } - else if (word.ToLower() == "no-recurse") - { - m_Directive = DirectiveType.NoRecurse; - } - else if (word.ToLower() == "parallel") - { - m_Directive = DirectiveType.Parallel; - } - else if (word.ToLower() == "sequential") - { - m_Directive = DirectiveType.Sequential; - } - else if (word.ToLower() == "queue") - { - m_Directive = DirectiveType.Queue; - } - else if (word.ToLower() == "no-queue") - { - m_Directive = DirectiveType.NoQueue; - } - else - { - throw new SIP_ParseException("Invalid 'directive' value !"); - } - } - - /// - /// Converts this to valid "directive" value. - /// - /// Returns "directive" value. - public override string ToStringValue() - { - /* - directive = proxy-directive / cancel-directive / fork-directive / recurse-directive / - parallel-directive / queue-directive - proxy-directive = "proxy" / "redirect" - cancel-directive = "cancel" / "no-cancel" - fork-directive = "fork" / "no-fork" - recurse-directive = "recurse" / "no-recurse" - parallel-directive = "parallel" / "sequential" - queue-directive = "queue" / "no-queue" - */ - - if (m_Directive == DirectiveType.Proxy) - { - return "proxy"; - } - else if (m_Directive == DirectiveType.Redirect) - { - return "redirect"; - } - else if (m_Directive == DirectiveType.Cancel) - { - return "cancel"; - } - else if (m_Directive == DirectiveType.NoCancel) - { - return "no-cancel"; - } - else if (m_Directive == DirectiveType.Fork) - { - return "fork"; - } - else if (m_Directive == DirectiveType.NoFork) - { - return "no-fork"; - } - else if (m_Directive == DirectiveType.Recurse) - { - return "recurse"; - } - else if (m_Directive == DirectiveType.NoRecurse) - { - return "no-recurse"; - } - else if (m_Directive == DirectiveType.Parallel) - { - return "parallel"; - } - else if (m_Directive == DirectiveType.Sequential) - { - return "sequential"; - } - else if (m_Directive == DirectiveType.Queue) - { - return "queue"; - } - else if (m_Directive == DirectiveType.NoQueue) - { - return "no-queue"; - } - else - { - throw new ArgumentException("Invalid property Directive value, this should never happen !"); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Encoding.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Encoding.cs deleted file mode 100644 index f60024fa8..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Encoding.cs +++ /dev/null @@ -1,181 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements "encoding" value. Defined in RFC 3261. - /// - /// - /// - /// RFC 3261 Syntax: - /// encoding = codings *(SEMI accept-param) - /// codings = content-coding / "*" - /// content-coding = token - /// accept-param = ("q" EQUAL qvalue) / generic-param - /// - /// - public class SIP_t_Encoding : SIP_t_ValueWithParams - { - #region Members - - private string m_ContentEncoding = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets content encoding. Value *(STAR) means all content encodings. - /// - public string ContentEncoding - { - get { return m_ContentEncoding; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Property ContentEncoding value can't be null or empty !"); - } - if (!TextUtils.IsToken(value)) - { - throw new ArgumentException("Property ContentEncoding value may be 'token' only !"); - } - - m_ContentEncoding = value; - } - } - - /// - /// Gets or sets qvalue parameter. Targets are processed from highest qvalue to lowest. - /// This value must be between 0.0 and 1.0. Value -1 means that value not specified. - /// - public double QValue - { - get - { - SIP_Parameter parameter = Parameters["qvalue"]; - if (parameter != null) - { - return Convert.ToDouble(parameter.Value); - } - else - { - return -1; - } - } - - set - { - if (value < 0 || value > 1) - { - throw new ArgumentException("Property QValue value must be between 0.0 and 1.0 !"); - } - - if (value < 0) - { - Parameters.Remove("qvalue"); - } - else - { - Parameters.Set("qvalue", value.ToString()); - } - } - } - - #endregion - - #region Methods - - /// - /// Parses "encoding" from specified value. - /// - /// Accept-Encoding value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "encoding" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - encoding = codings *(SEMI accept-param) - codings = content-coding / "*" - content-coding = token - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // Parse content-coding - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("Invalid 'encoding' value is missing !"); - } - m_ContentEncoding = word; - - // Parse parameters - ParseParameters(reader); - } - - /// - /// Converts this to valid "encoding" value. - /// - /// Returns "encoding" value. - public override string ToStringValue() - { - /* - Accept-Encoding = "Accept-Encoding" HCOLON [ encoding *(COMMA encoding) ] - encoding = codings *(SEMI accept-param) - codings = content-coding / "*" - content-coding = token - */ - - StringBuilder retVal = new StringBuilder(); - retVal.Append(m_ContentEncoding); - retVal.Append(ParametersToString()); - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ErrorUri.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ErrorUri.cs deleted file mode 100644 index 45f910935..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ErrorUri.cs +++ /dev/null @@ -1,129 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements SIP "error-uri" value. Defined in RFC 3261. - /// - /// - /// - /// RFC 3261 Syntax: - /// error-uri = LAQUOT absoluteURI RAQUOT *( SEMI generic-param ) - /// - /// - public class SIP_t_ErrorUri : SIP_t_ValueWithParams - { - #region Members - - private string m_Uri = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets uri value. - /// - public string Uri - { - get { return m_Uri; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Property Uri value can't be null or empty !"); - } - - m_Uri = value; - } - } - - #endregion - - #region Methods - - /// - /// Parses "error-uri" from specified value. - /// - /// SIP "error-uri" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "error-uri" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - Error-Info = "Error-Info" HCOLON error-uri *(COMMA error-uri) - error-uri = LAQUOT absoluteURI RAQUOT *( SEMI generic-param ) - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // Parse uri - // Read to LAQUOT - reader.QuotedReadToDelimiter('<'); - if (!reader.StartsWith("<")) - { - throw new SIP_ParseException("Invalid 'error-uri' value, Uri not between <> !"); - } - - // Parse parameters - ParseParameters(reader); - } - - /// - /// Converts this to valid "error-uri" value. - /// - /// Returns "error-uri" value. - public override string ToStringValue() - { - StringBuilder retVal = new StringBuilder(); - retVal.Append("<" + m_Uri + ">"); - retVal.Append(ParametersToString()); - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Event.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Event.cs deleted file mode 100644 index adb1f2d3a..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Event.cs +++ /dev/null @@ -1,189 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements SIP "Event" value. Defined in RFC 3265. - /// - /// - /// - /// RFC 3265 Syntax: - /// Event = event-type *( SEMI event-param ) - /// event-param = generic-param / ( "id" EQUAL token ) - /// - /// - public class SIP_t_Event : SIP_t_ValueWithParams - { - #region Members - - private string m_EventType = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets event type. - /// - /// Is raised when null vallue is passed. - /// Is raised when emptu string passed. - public string EventType - { - get { return m_EventType; } - - set - { - if (value == null) - { - throw new ArgumentNullException("EventType"); - } - if (value == "") - { - throw new ArgumentException("Property EventType value can't be '' !"); - } - - m_EventType = value; - } - } - - /// - /// Gets or sets 'id' parameter value. Value null means not specified. - /// - public string ID - { - get - { - SIP_Parameter parameter = Parameters["id"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (string.IsNullOrEmpty(value)) - { - Parameters.Remove("id"); - } - else - { - Parameters.Set("id", value); - } - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// SIP 'Event' value. - public SIP_t_Event(string value) - { - Parse(value); - } - - #endregion - - #region Methods - - /// - /// Parses "Event" from specified value. - /// - /// SIP "Event" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "Event" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - Event = event-type *( SEMI event-param ) - event-param = generic-param / ( "id" EQUAL token ) - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // event-type - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("SIP Event 'event-type' value is missing !"); - } - m_EventType = word; - - // Parse parameters - ParseParameters(reader); - } - - /// - /// Converts this to valid "Event" value. - /// - /// Returns "Event" value. - public override string ToStringValue() - { - /* - Event = event-type *( SEMI event-param ) - event-param = generic-param / ( "id" EQUAL token ) - */ - - StringBuilder retVal = new StringBuilder(); - - // event-type - retVal.Append(m_EventType); - - // Add parameters - retVal.Append(ParametersToString()); - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_EventType.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_EventType.cs deleted file mode 100644 index b82b01782..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_EventType.cs +++ /dev/null @@ -1,111 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - - #endregion - - /// - /// Implements SIP "event-type" value. Defined in RFC 3265. - /// - public class SIP_t_EventType : SIP_t_Value - { - #region Members - - private string m_EventType = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets event type. - /// - /// Is raised when null value passed as value. - public string EventType - { - get { return m_EventType; } - - set - { - if (value == null) - { - throw new ArgumentNullException("EventType"); - } - - m_EventType = value; - } - } - - #endregion - - #region Methods - - /// - /// Parses "event-type" from specified value. - /// - /// SIP "event-type" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "event-type" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // Get Method - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("Invalid 'event-type' value, event-type is missing !"); - } - m_EventType = word; - } - - /// - /// Converts this to valid "event-type" value. - /// - /// Returns "event-type" value. - public override string ToStringValue() - { - return m_EventType; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_From.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_From.cs deleted file mode 100644 index db6d4ee29..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_From.cs +++ /dev/null @@ -1,173 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements SIP "From" value. Defined in RFC 3261. - /// The From header field indicates the initiator of the request. - /// - /// - /// - /// RFC 3261 Syntax: - /// From = ( name-addr / addr-spec ) *( SEMI from-param ) - /// from-param = tag-param / generic-param - /// tag-param = "tag" EQUAL token - /// - /// - public class SIP_t_From : SIP_t_ValueWithParams - { - #region Members - - private readonly SIP_t_NameAddress m_pAddress; - - #endregion - - #region Properties - - /// - /// Gets address. - /// - public SIP_t_NameAddress Address - { - get { return m_pAddress; } - } - - /// - /// Gets or sets tag parameter value. - /// The "tag" parameter serves as a general mechanism for dialog identification. - /// Value null means that tag paramter doesn't exist. - /// - public string Tag - { - get - { - SIP_Parameter parameter = Parameters["tag"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (string.IsNullOrEmpty(value)) - { - Parameters.Remove("tag"); - } - else - { - Parameters.Set("tag", value); - } - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// From: header field value. - public SIP_t_From(string value) - { - m_pAddress = new SIP_t_NameAddress(); - - Parse(new StringReader(value)); - } - - /// - /// Default constructor. - /// - /// From address. - public SIP_t_From(SIP_t_NameAddress address) - { - m_pAddress = address; - } - - #endregion - - #region Methods - - /// - /// Parses "From" from specified value. - /// - /// SIP "accept-range" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "From" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* From = ( name-addr / addr-spec ) *( SEMI from-param ) - from-param = tag-param / generic-param - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // Parse address - m_pAddress.Parse(reader); - - // Parse parameters - ParseParameters(reader); - } - - /// - /// Converts this to valid "From" value. - /// - /// Returns "From" value. - public override string ToStringValue() - { - StringBuilder retVal = new StringBuilder(); - retVal.Append(m_pAddress.ToStringValue()); - retVal.Append(ParametersToString()); - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_HiEntry.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_HiEntry.cs deleted file mode 100644 index 97e1bf7b7..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_HiEntry.cs +++ /dev/null @@ -1,176 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements SIP "hi-entry" value. Defined in RFC 4244. - /// - /// - /// - /// RFC 4244 Syntax: - /// hi-entry = hi-targeted-to-uri *( SEMI hi-param ) - /// hi-targeted-to-uri= name-addr - /// hi-param = hi-index / hi-extension - /// hi-index = "index" EQUAL 1*DIGIT *(DOT 1*DIGIT) - /// hi-extension = generic-param - /// - /// - public class SIP_t_HiEntry : SIP_t_ValueWithParams - { - #region Members - - private SIP_t_NameAddress m_pAddress; - - #endregion - - #region Properties - - /// - /// Gets or sets address. - /// - /// Is raised when null value is passed. - public SIP_t_NameAddress Address - { - get { return m_pAddress; } - - set - { - if (m_pAddress == null) - { - throw new ArgumentNullException("m_pAddress"); - } - - m_pAddress = value; - } - } - - /// - /// Gets or sets 'index' parameter value. Value -1 means not specified. - /// - public double Index - { - get - { - SIP_Parameter parameter = Parameters["index"]; - if (parameter != null) - { - return Convert.ToInt32(parameter.Value); - } - else - { - return -1; - } - } - - set - { - if (value == -1) - { - Parameters.Remove("index"); - } - else - { - Parameters.Set("index", value.ToString()); - } - } - } - - #endregion - - #region Methods - - /// - /// Parses "hi-entry" from specified value. - /// - /// SIP "hi-entry" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "hi-entry" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - hi-entry = hi-targeted-to-uri *( SEMI hi-param ) - hi-targeted-to-uri= name-addr - hi-param = hi-index / hi-extension - hi-index = "index" EQUAL 1*DIGIT *(DOT 1*DIGIT) - hi-extension = generic-param - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // name-addr - m_pAddress = new SIP_t_NameAddress(); - m_pAddress.Parse(reader); - - // Parse parameters - ParseParameters(reader); - } - - /// - /// Converts this to valid "hi-entry" value. - /// - /// Returns "hi-entry" value. - public override string ToStringValue() - { - /* - hi-entry = hi-targeted-to-uri *( SEMI hi-param ) - hi-targeted-to-uri= name-addr - hi-param = hi-index / hi-extension - hi-index = "index" EQUAL 1*DIGIT *(DOT 1*DIGIT) - hi-extension = generic-param - */ - - StringBuilder retVal = new StringBuilder(); - - // name-addr - retVal.Append(m_pAddress.ToStringValue()); - - // Add parameters - retVal.Append(ParametersToString()); - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_IdentityInfo.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_IdentityInfo.cs deleted file mode 100644 index 4a53534ea..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_IdentityInfo.cs +++ /dev/null @@ -1,205 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements SIP "Identity-Info" value. Defined in RFC 4474. - /// - /// - /// - /// RFC 4474 Syntax: - /// Identity-Info = ident-info *( SEMI ident-info-params ) - /// ident-info = LAQUOT absoluteURI RAQUOT - /// ident-info-params = ident-info-alg / ident-info-extension - /// ident-info-alg = "alg" EQUAL token - /// ident-info-extension = generic-param - /// - /// - public class SIP_t_IdentityInfo : SIP_t_ValueWithParams - { - #region Members - - private string m_Uri = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets URI value. - /// - /// Is raised when null value is passed. - /// Is raised when invalid 'absoluteURI' value is passed. - public string Uri - { - get { return m_Uri; } - - set - { - if (value == null) - { - throw new ArgumentNullException("Uri"); - } - if (value == "") - { - throw new ArgumentException("Invalid Identity-Info 'absoluteURI' value !"); - } - - m_Uri = value; - } - } - - /// - /// Gets or sets 'alg' parameter value. Value null means not specified. - /// - public string Alg - { - get - { - SIP_Parameter parameter = Parameters["alg"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (string.IsNullOrEmpty(value)) - { - Parameters.Remove("alg"); - } - else - { - Parameters.Set("alg", value); - } - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// SIP 'Identity-Info' value. - public SIP_t_IdentityInfo(string value) - { - Parse(value); - } - - #endregion - - #region Methods - - /// - /// Parses "Identity-Info" from specified value. - /// - /// SIP "Identity-Info" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "Identity-Info" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - Identity-Info = ident-info *( SEMI ident-info-params ) - ident-info = LAQUOT absoluteURI RAQUOT - ident-info-params = ident-info-alg / ident-info-extension - ident-info-alg = "alg" EQUAL token - ident-info-extension = generic-param - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // absoluteURI - try - { - string word = reader.ReadParenthesized(); - if (word == null) - { - throw new SIP_ParseException("Invalid Identity-Info 'absoluteURI' value !"); - } - m_Uri = word; - } - catch - { - throw new SIP_ParseException("Invalid Identity-Info 'absoluteURI' value !"); - } - - // Parse parameters - ParseParameters(reader); - } - - /// - /// Converts this to valid "Identity-Info" value. - /// - /// Returns "Identity-Info" value. - public override string ToStringValue() - { - /* - Identity-Info = ident-info *( SEMI ident-info-params ) - ident-info = LAQUOT absoluteURI RAQUOT - ident-info-params = ident-info-alg / ident-info-extension - ident-info-alg = "alg" EQUAL token - ident-info-extension = generic-param - */ - - StringBuilder retVal = new StringBuilder(); - - // absoluteURI - retVal.Append("<" + m_Uri + ">"); - - // Add parameters - retVal.Append(ParametersToString()); - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Info.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Info.cs deleted file mode 100644 index e8efeeb1e..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Info.cs +++ /dev/null @@ -1,145 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements SIP "info" value. Defined in RFC 3261. - /// - /// - /// - /// RFC 3261 Syntax: - /// info = LAQUOT absoluteURI RAQUOT *( SEMI info-param) - /// info-param = ( "purpose" EQUAL ( "icon" / "info" / "card" / token ) ) / generic-param - /// - /// - public class SIP_t_Info : SIP_t_ValueWithParams - { - #region Members - - private string m_Uri = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets 'purpose' parameter value. Value null means not specified. - /// Known values: "icon","info","card". - /// - public string Purpose - { - get - { - SIP_Parameter parameter = Parameters["purpose"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (string.IsNullOrEmpty(value)) - { - Parameters.Remove("purpose"); - } - else - { - Parameters.Set("purpose", value); - } - } - } - - #endregion - - #region Methods - - /// - /// Parses "info" from specified value. - /// - /// SIP "info" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "info" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - Call-Info = "Call-Info" HCOLON info *(COMMA info) - info = LAQUOT absoluteURI RAQUOT *( SEMI info-param) - info-param = ( "purpose" EQUAL ( "icon" / "info" / "card" / token ) ) / generic-param - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // Parse uri - // Read to LAQUOT - reader.QuotedReadToDelimiter('<'); - if (!reader.StartsWith("<")) - { - throw new SIP_ParseException("Invalid Alert-Info value, Uri not between <> !"); - } - - // Parse parameters - ParseParameters(reader); - } - - /// - /// Converts this to valid "info" value. - /// - /// Returns "info" value. - public override string ToStringValue() - { - StringBuilder retVal = new StringBuilder(); - retVal.Append("<" + m_Uri + ">"); - retVal.Append(ParametersToString()); - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Join.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Join.cs deleted file mode 100644 index 06d680b25..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Join.cs +++ /dev/null @@ -1,233 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements SIP "Join" value. Defined in RFC 3911. - /// - /// - /// - /// RFC 3911 Syntax: - /// Join = callid *(SEMI join-param) - /// join-param = to-tag / from-tag / generic-param - /// to-tag = "to-tag" EQUAL token - /// from-tag = "from-tag" EQUAL token - /// - /// - public class SIP_t_Join : SIP_t_ValueWithParams - { - #region Members - - private SIP_t_CallID m_pCallID; - - #endregion - - #region Properties - - /// - /// Gets or sets call ID value. - /// - /// Is raised �when null value passed. - public SIP_t_CallID CallID - { - get { return m_pCallID; } - - set - { - if (value == null) - { - throw new ArgumentNullException("CallID"); - } - - m_pCallID = value; - } - } - - /// - /// Gets or sets to-tag parameter value. This value is mandatory. - /// - /// Is raised when invalid ToTag value is passed. - public string ToTag - { - get - { - SIP_Parameter parameter = Parameters["to-tag"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("ToTag is mandatory and cant be null or empty !"); - } - else - { - Parameters.Set("to-tag", value); - } - } - } - - /// - /// Gets or sets from-tag parameter value. This value is mandatory. - /// - /// Is raised when invalid FromTag value is passed. - public string FromTag - { - get - { - SIP_Parameter parameter = Parameters["from-tag"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("FromTag is mandatory and cant be null or empty !"); - } - else - { - Parameters.Set("from-tag", value); - } - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Join value. - public SIP_t_Join(string value) - { - Parse(value); - } - - #endregion - - #region Methods - - /// - /// Parses "Join" from specified value. - /// - /// SIP "Join" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "Join" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - Join = callid *(SEMI join-param) - join-param = to-tag / from-tag / generic-param - to-tag = "to-tag" EQUAL token - from-tag = "from-tag" EQUAL token - - A Join header MUST contain exactly one to-tag and exactly one from- - tag, as they are required for unique dialog matching. - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // Parse address - SIP_t_CallID callID = new SIP_t_CallID(); - callID.Parse(reader); - m_pCallID = callID; - - // Parse parameters - ParseParameters(reader); - - // Check that to and from tags exist. - if (Parameters["to-tag"] == null) - { - throw new SIP_ParseException("Join value mandatory to-tag value is missing !"); - } - if (Parameters["from-tag"] == null) - { - throw new SIP_ParseException("Join value mandatory from-tag value is missing !"); - } - } - - /// - /// Converts this to valid "Join" value. - /// - /// Returns "Join" value. - public override string ToStringValue() - { - /* - Join = callid *(SEMI join-param) - join-param = to-tag / from-tag / generic-param - to-tag = "to-tag" EQUAL token - from-tag = "from-tag" EQUAL token - */ - - StringBuilder retVal = new StringBuilder(); - - // Add address - retVal.Append(m_pCallID.ToStringValue()); - - // Add parameters - retVal.Append(ParametersToString()); - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Language.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Language.cs deleted file mode 100644 index dd7256e46..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Language.cs +++ /dev/null @@ -1,175 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements SIP "language" value. Defined in RFC 3261. - /// - /// - /// - /// RFC 3261 Syntax: - /// language = language-range *(SEMI accept-param) - /// language-range = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) / "*" ) - /// accept-param = ("q" EQUAL qvalue) / generic-param - /// - /// - public class SIP_t_Language : SIP_t_ValueWithParams - { - #region Members - - private string m_LanguageRange = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets language range. Value *(STAR) means all languages. - /// - public string LanguageRange - { - get { return m_LanguageRange; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Property LanguageRange value can't be null or empty !"); - } - - m_LanguageRange = value; - } - } - - /// - /// Gets or sets qvalue parameter. Targets are processed from highest qvalue to lowest. - /// This value must be between 0.0 and 1.0. Value -1 means that value not specified. - /// - public double QValue - { - get - { - SIP_Parameter parameter = Parameters["qvalue"]; - if (parameter != null) - { - return Convert.ToDouble(parameter.Value); - } - else - { - return -1; - } - } - - set - { - if (value < 0 || value > 1) - { - throw new ArgumentException("Property QValue value must be between 0.0 and 1.0 !"); - } - - if (value < 0) - { - Parameters.Remove("qvalue"); - } - else - { - Parameters.Set("qvalue", value.ToString()); - } - } - } - - #endregion - - #region Methods - - /// - /// Parses "language" from specified value. - /// - /// SIP "language" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "language" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - language = language-range *(SEMI accept-param) - language-range = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) / "*" ) - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // Parse content-coding - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException( - "Invalid Accept-Language value, language-range value is missing !"); - } - m_LanguageRange = word; - - // Parse parameters - ParseParameters(reader); - } - - /// - /// Converts this to valid "language" value. - /// - /// Restuns "language" value. - public override string ToStringValue() - { - /* - Accept-Language = "Accept-Language" HCOLON [ language *(COMMA language) ] - language = language-range *(SEMI accept-param) - language-range = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) / "*" ) - */ - - StringBuilder retVal = new StringBuilder(); - retVal.Append(m_LanguageRange); - retVal.Append(ParametersToString()); - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_LanguageTag.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_LanguageTag.cs deleted file mode 100644 index 77bbdca60..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_LanguageTag.cs +++ /dev/null @@ -1,140 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements SIP "language-tag" value. Defined in RFC 3261. - /// - /// - /// - /// RFC 3261 Syntax: - /// language-tag = primary-tag *( "-" subtag ) - /// primary-tag = 1*8ALPHA - /// subtag = 1*8ALPHA - /// - /// - public class SIP_t_LanguageTag : SIP_t_ValueWithParams - { - #region Members - - private string m_LanguageTag = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets language tag. - /// - public string LanguageTag - { - get { return m_LanguageTag; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Property LanguageTag value can't be null or empty !"); - } - - m_LanguageTag = value; - } - } - - #endregion - - #region Methods - - /// - /// Parses "language-tag" from specified value. - /// - /// SIP "language-tag" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "language-tag" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - Content-Language = "Content-Language" HCOLON language-tag *(COMMA language-tag) - language-tag = primary-tag *( "-" subtag ) - primary-tag = 1*8ALPHA - subtag = 1*8ALPHA - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // Parse content-coding - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("Invalid Content-Language value, language-tag value is missing !"); - } - m_LanguageTag = word; - - // Parse parameters - ParseParameters(reader); - } - - /// - /// Converts this to valid "language-tag" value. - /// - /// Returns "language-tag" value. - public override string ToStringValue() - { - /* - Content-Language = "Content-Language" HCOLON language-tag *(COMMA language-tag) - language-tag = primary-tag *( "-" subtag ) - primary-tag = 1*8ALPHA - subtag = 1*8ALPHA - */ - - StringBuilder retVal = new StringBuilder(); - retVal.Append(m_LanguageTag); - retVal.Append(ParametersToString()); - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Method.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Method.cs deleted file mode 100644 index 343dfd604..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Method.cs +++ /dev/null @@ -1,126 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - - #endregion - - /// - /// Implements SIP "Method" value. Defined in RFC 3261. - /// - /// - /// - /// RFC 3261 Syntax: - /// Method = INVITEm / ACKm / OPTIONSm / BYEm / CANCELm / REGISTERm / extension-method - /// extension-method = token - /// - /// - public class SIP_t_Method : SIP_t_Value - { - #region Members - - private string m_Method = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets SIP method what is allowed. - /// - public string Method - { - get { return m_Method; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Property Method value can't be null or empty !"); - } - if (TextUtils.IsToken(value)) - { - throw new ArgumentException("Property Method value must be 'token' !"); - } - - m_Method = value; - } - } - - #endregion - - #region Methods - - /// - /// Parses "Method" from specified value. - /// - /// SIP "Method" value. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("reader"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "Method" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* Allow = "Allow" HCOLON [Method *(COMMA Method)] - Method = INVITEm / ACKm / OPTIONSm / BYEm / CANCELm / REGISTERm / extension-method - extension-method = token - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // Get Method - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("Invalid 'Method' value, value is missing !"); - } - m_Method = word; - } - - /// - /// Converts this to valid "Method" value. - /// - /// - public override string ToStringValue() - { - return m_Method; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_MinSE.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_MinSE.cs deleted file mode 100644 index e785946fc..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_MinSE.cs +++ /dev/null @@ -1,166 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements SIP "Min-SE" value. Defined in RFC 4028. - /// - /// - /// - /// RFC 4028 Syntax: - /// Min-SE = delta-seconds *(SEMI generic-param) - /// - /// - public class SIP_t_MinSE : SIP_t_ValueWithParams - { - #region Members - - private int m_Time = 90; - - #endregion - - #region Properties - - /// - /// Gets or sets time in seconds when session expires. - /// - /// Is raised when value is less than 1. - public int Time - { - get { return m_Time; } - - set - { - if (m_Time < 1) - { - throw new ArgumentException("Time value must be > 0 !"); - } - - m_Time = value; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Min-SE value. - public SIP_t_MinSE(string value) - { - Parse(value); - } - - /// - /// Default constructor. - /// - /// Minimum session expries value in seconds. - public SIP_t_MinSE(int minExpires) - { - m_Time = minExpires; - } - - #endregion - - #region Methods - - /// - /// Parses "Min-SE" from specified value. - /// - /// SIP "Min-SE" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "Min-SE" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - Min-SE = delta-seconds *(SEMI generic-param) - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // Parse address - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("Min-SE delta-seconds value is missing !"); - } - try - { - m_Time = Convert.ToInt32(word); - } - catch - { - throw new SIP_ParseException("Invalid Min-SE delta-seconds value !"); - } - - // Parse parameters - ParseParameters(reader); - } - - /// - /// Converts this to valid "Min-SE" value. - /// - /// Returns "Min-SE" value. - public override string ToStringValue() - { - /* - Min-SE = delta-seconds *(SEMI generic-param) - */ - - StringBuilder retVal = new StringBuilder(); - - // Add address - retVal.Append(m_Time.ToString()); - - // Add parameters - retVal.Append(ParametersToString()); - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_NameAddress.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_NameAddress.cs deleted file mode 100644 index cc56988cd..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_NameAddress.cs +++ /dev/null @@ -1,269 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - - #endregion - - /// - /// Implements SIP "name-addr" value. Defined in RFC 3261. - /// - /// - /// - /// RFC 3261 Syntax: - /// name-addr = [ display-name ] LAQUOT addr-spec RAQUOT - /// addr-spec = SIP-URI / SIPS-URI / absoluteURI - /// - /// - public class SIP_t_NameAddress - { - #region Members - - private string m_DisplayName = ""; - private AbsoluteUri m_pUri; - - #endregion - - #region Properties - - /// - /// Gets or sets display name. - /// - public string DisplayName - { - get { return m_DisplayName; } - - set - { - if (value == null) - { - value = ""; - } - - m_DisplayName = value; - } - } - - /// - /// Gets or sets URI. This can be SIP-URI / SIPS-URI / absoluteURI. - /// Examples: sip:ivar@lumisoft.ee,sips:ivar@lumisoft.ee,mailto:ivar@lumisoft.ee, .... . - /// - /// Is raised when null reference passed. - public AbsoluteUri Uri - { - get { return m_pUri; } - - set - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - m_pUri = value; - } - } - - /// - /// Gets if current URI is sip or sips URI. - /// - public bool IsSipOrSipsUri - { - get { return IsSipUri || IsSecureSipUri; } - } - - /// - /// Gets if current URI is SIP uri. - /// - public bool IsSipUri - { - get - { - if (m_pUri.Scheme == UriSchemes.sip) - { - return true; - } - return false; - } - } - - /// - /// Gets if current URI is SIPS uri. - /// - public bool IsSecureSipUri - { - get - { - if (m_pUri.Scheme == UriSchemes.sips) - { - return true; - } - return false; - } - } - - /// - /// Gets if current URI is MAILTO uri. - /// - public bool IsMailToUri - { - get - { - if (m_pUri.Scheme == UriSchemes.mailto) - { - return true; - } - return false; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public SIP_t_NameAddress() {} - - /// - /// Default constructor. - /// - /// SIP name-addr value. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public SIP_t_NameAddress(string value) - { - Parse(value); - } - - /// - /// Default constructor. - /// - /// Display name. - /// Uri. - /// Is raised when uri is null reference. - public SIP_t_NameAddress(string displayName, AbsoluteUri uri) - { - if (uri == null) - { - throw new ArgumentNullException("uri"); - } - - DisplayName = displayName; - Uri = uri; - } - - #endregion - - #region Methods - - /// - /// Parses "name-addr" or "addr-spec" from specified value. - /// - /// SIP "name-addr" or "addr-spec" value. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("reader"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "name-addr" or "addr-spec" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public void Parse(StringReader reader) - { - /* RFC 3261. - name-addr = [ display-name ] LAQUOT addr-spec RAQUOT - addr-spec = SIP-URI / SIPS-URI / absoluteURI - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - reader.ReadToFirstChar(); - - // LAQUOT addr-spec RAQUOT - if (reader.StartsWith("<")) - { - m_pUri = AbsoluteUri.Parse(reader.ReadParenthesized()); - } - else - { - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("Invalid 'name-addr' or 'addr-spec' value !"); - } - - reader.ReadToFirstChar(); - - // name-addr - if (reader.StartsWith("<")) - { - m_DisplayName = word; - m_pUri = AbsoluteUri.Parse(reader.ReadParenthesized()); - } - // addr-spec - else - { - m_pUri = AbsoluteUri.Parse(word); - } - } - } - - /// - /// Converts this to valid name-addr or addr-spec string as needed. - /// - /// Returns name-addr or addr-spec string. - public string ToStringValue() - { - /* RFC 3261. - name-addr = [ display-name ] LAQUOT addr-spec RAQUOT - addr-spec = SIP-URI / SIPS-URI / absoluteURI - */ - - // addr-spec - if (string.IsNullOrEmpty(m_DisplayName)) - { - return "<" + m_pUri + ">"; - } - // name-addr - else - { - return TextUtils.QuoteString(m_DisplayName) + " <" + m_pUri + ">"; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_OptionTag.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_OptionTag.cs deleted file mode 100644 index 9fadbb899..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_OptionTag.cs +++ /dev/null @@ -1,118 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - - #endregion - - /// - /// Implements SIP "option-tag" value. Defined in RFC 3261. - /// - /// - /// - /// RFC 3261 Syntax: - /// option-tag = token - /// - /// - public class SIP_t_OptionTag : SIP_t_Value - { - #region Members - - private string m_OptionTag = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets option tag. - /// - public string OptionTag - { - get { return m_OptionTag; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("property OptionTag value cant be null or empty !"); - } - - m_OptionTag = value; - } - } - - #endregion - - #region Methods - - /// - /// Parses "option-tag" from specified value. - /// - /// SIP "option-tag" value. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("reader"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "option-tag" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - // option-tag = token - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // Get Method - string word = reader.ReadWord(); - if (word == null) - { - throw new ArgumentException("Invalid 'option-tag' value, value is missing !"); - } - m_OptionTag = word; - } - - /// - /// Converts this to valid "option-tag" value. - /// - /// Returns "option-tag" value. - public override string ToStringValue() - { - return m_OptionTag; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_RAck.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_RAck.cs deleted file mode 100644 index 4c920aaae..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_RAck.cs +++ /dev/null @@ -1,224 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - - #endregion - - /// - /// Implements SIP "RAck" value. Defined in RFC 3262. - /// - /// - /// - /// RFC 3262 Syntax: - /// RAck = response-num LWS CSeq-num LWS Method - /// response-num = 1*DIGIT - /// CSeq-num = 1*DIGIT - /// - /// - public class SIP_t_RAck : SIP_t_Value - { - #region Members - - private int m_CSeqNumber = 1; - private string m_Method = ""; - private int m_ResponseNumber = 1; - - #endregion - - #region Properties - - /// - /// Gets or sets response number. - /// - public int ResponseNumber - { - get { return m_ResponseNumber; } - - set - { - if (value < 1) - { - throw new ArgumentException("ResponseNumber value must be >= 1 !"); - } - - m_ResponseNumber = value; - } - } - - /// - /// Gets or sets CSeq number. - /// - public int CSeqNumber - { - get { return m_CSeqNumber; } - - set - { - if (value < 1) - { - throw new ArgumentException("CSeqNumber value must be >= 1 !"); - } - - m_CSeqNumber = value; - } - } - - /// - /// Gets or sets method. - /// - public string Method - { - get { return m_Method; } - - set - { - if (value == null) - { - throw new ArgumentNullException("Method"); - } - - m_Method = value; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// RAck value. - public SIP_t_RAck(string value) - { - Parse(value); - } - - /// - /// Default constructor. - /// - /// Response number. - /// CSeq number. - /// Request method. - public SIP_t_RAck(int responseNo, int cseqNo, string method) - { - ResponseNumber = responseNo; - CSeqNumber = cseqNo; - Method = method; - } - - #endregion - - #region Methods - - /// - /// Parses "RAck" from specified value. - /// - /// SIP "RAck" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "RAck" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - RAck = response-num LWS CSeq-num LWS Method - response-num = 1*DIGIT - CSeq-num = 1*DIGIT - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // response-num - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("RAck response-num value is missing !"); - } - try - { - m_ResponseNumber = Convert.ToInt32(word); - } - catch - { - throw new SIP_ParseException("Invalid RAck response-num value !"); - } - - // CSeq-num - word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("RAck CSeq-num value is missing !"); - } - try - { - m_CSeqNumber = Convert.ToInt32(word); - } - catch - { - throw new SIP_ParseException("Invalid RAck CSeq-num value !"); - } - - // Get request method - word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("RAck Method value is missing !"); - } - m_Method = word; - } - - /// - /// Converts this to valid "RAck" value. - /// - /// Returns "RAck" value. - public override string ToStringValue() - { - /* - RAck = response-num LWS CSeq-num LWS Method - response-num = 1*DIGIT - CSeq-num = 1*DIGIT - */ - - return m_ResponseNumber + " " + m_CSeqNumber + " " + m_Method; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_RCValue.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_RCValue.cs deleted file mode 100644 index 3152492a1..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_RCValue.cs +++ /dev/null @@ -1,109 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements SIP "rc-value" value. Defined in RFC 3841. - /// - /// - /// - /// RFC 3841 Syntax: - /// rc-value = "*" *(SEMI rc-params) - /// rc-params = feature-param / generic-param - /// - /// - public class SIP_t_RCValue : SIP_t_ValueWithParams - { - #region Methods - - /// - /// Parses "rc-value" from specified value. - /// - /// SIP "rc-value" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "rc-value" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - rc-value = "*" *(SEMI rc-params) - rc-params = feature-param / generic-param - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("Invalid 'rc-value', '*' is missing !"); - } - - // Parse parameters - ParseParameters(reader); - } - - /// - /// Converts this to valid "rc-value" value. - /// - /// Returns "rc-value" value. - public override string ToStringValue() - { - /* - rc-value = "*" *(SEMI rc-params) - rc-params = feature-param / generic-param - */ - - StringBuilder retVal = new StringBuilder(); - - // * - retVal.Append("*"); - - // Add parameters - retVal.Append(ParametersToString()); - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_RValue.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_RValue.cs deleted file mode 100644 index 558ae685c..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_RValue.cs +++ /dev/null @@ -1,176 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - - #endregion - - /// - /// Implements SIP "r-value" value. Defined in RFC 4412. - /// - /// - /// - /// RFC 4412 Syntax: - /// r-value = namespace "." r-priority - /// namespace = token-nodot - /// r-priority = token-nodot - /// - /// - public class SIP_t_RValue : SIP_t_Value - { - #region Members - - private string m_Namespace = ""; - private string m_Priority = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets Namespace. - /// - /// Is raised when null value passed. - /// Is raised when invalid Namespace value passed. - public string Namespace - { - get { return m_Namespace; } - - set - { - if (value == null) - { - throw new ArgumentNullException("Namespace"); - } - if (value == "") - { - throw new ArgumentException("Property Namespace value may not be '' !"); - } - if (!TextUtils.IsToken(value)) - { - throw new ArgumentException("Property Namespace value must be 'token' !"); - } - - m_Namespace = value; - } - } - - /// - /// Gets or sets priority. - /// - /// Is raised when null value passed. - /// Is raised when invalid Priority value passed. - public string Priority - { - get { return m_Priority; } - - set - { - if (value == null) - { - throw new ArgumentNullException("Priority"); - } - if (value == "") - { - throw new ArgumentException("Property Priority value may not be '' !"); - } - if (!TextUtils.IsToken(value)) - { - throw new ArgumentException("Property Priority value must be 'token' !"); - } - - m_Priority = value; - } - } - - #endregion - - #region Methods - - /// - /// Parses "r-value" from specified value. - /// - /// SIP "r-value" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "r-value" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - r-value = namespace "." r-priority - namespace = token-nodot - r-priority = token-nodot - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // namespace "." r-priority - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException( - "Invalid 'r-value' value, 'namespace \".\" r-priority' is missing !"); - } - string[] namespace_priority = word.Split('.'); - if (namespace_priority.Length != 2) - { - throw new SIP_ParseException("Invalid r-value !"); - } - m_Namespace = namespace_priority[0]; - m_Priority = namespace_priority[1]; - } - - /// - /// Converts this to valid "r-value" value. - /// - /// Returns "r-value" value. - public override string ToStringValue() - { - /* - r-value = namespace "." r-priority - namespace = token-nodot - r-priority = token-nodot - */ - - return m_Namespace + "." + m_Priority; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ReasonValue.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ReasonValue.cs deleted file mode 100644 index ea1bbee24..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ReasonValue.cs +++ /dev/null @@ -1,235 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements SIP "reason-value" value. Defined in rfc 3326. - /// - /// - /// - /// RFC 3326 Syntax: - /// reason-value = protocol *(SEMI reason-params) - /// protocol = "SIP" / "Q.850" / token - /// reason-params = protocol-cause / reason-text / reason-extension - /// protocol-cause = "cause" EQUAL cause - /// cause = 1*DIGIT - /// reason-text = "text" EQUAL quoted-string - /// reason-extension = generic-param - /// - /// - public class SIP_t_ReasonValue : SIP_t_ValueWithParams - { - #region Members - - private string m_Protocol = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets protocol. - /// - /// Is raised when null value is passed. - public string Protocol - { - get { return m_Protocol; } - - set - { - if (value == null) - { - throw new ArgumentNullException("Protocol"); - } - - m_Protocol = value; - } - } - - /// - /// Gets or sets 'cause' parameter value. The cause parameter contains a SIP status code. - /// Value -1 means not specified. - /// - public int Cause - { - get - { - if (Parameters["cause"] == null) - { - return -1; - } - else - { - return Convert.ToInt32(Parameters["cause"].Value); - } - } - - set - { - if (value < 0) - { - Parameters.Remove("cause"); - } - else - { - Parameters.Set("cause", value.ToString()); - } - } - } - - /// - /// Gets or sets 'text' parameter value. Value null means not specified. - /// - public string Text - { - get - { - SIP_Parameter parameter = Parameters["text"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - Parameters.Remove("text"); - } - else - { - Parameters.Set("text", value); - } - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public SIP_t_ReasonValue() {} - - /// - /// Default constructor. - /// - /// SIP reason-value value. - public SIP_t_ReasonValue(string value) - { - Parse(value); - } - - #endregion - - #region Methods - - /// - /// Parses "reason-value" from specified value. - /// - /// SIP "reason-value" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "reason-value" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - reason-value = protocol *(SEMI reason-params) - protocol = "SIP" / "Q.850" / token - reason-params = protocol-cause / reason-text / reason-extension - protocol-cause = "cause" EQUAL cause - cause = 1*DIGIT - reason-text = "text" EQUAL quoted-string - reason-extension = generic-param - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // protocol - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("SIP reason-value 'protocol' value is missing !"); - } - m_Protocol = word; - - // Parse parameters - ParseParameters(reader); - } - - /// - /// Converts this to valid "reason-value" value. - /// - /// Returns "reason-value" value. - public override string ToStringValue() - { - /* - reason-value = protocol *(SEMI reason-params) - protocol = "SIP" / "Q.850" / token - reason-params = protocol-cause / reason-text / reason-extension - protocol-cause = "cause" EQUAL cause - cause = 1*DIGIT - reason-text = "text" EQUAL quoted-string - reason-extension = generic-param - */ - - StringBuilder retVal = new StringBuilder(); - - // Add protocol - retVal.Append(m_Protocol); - - // Add parameters - retVal.Append(ParametersToString()); - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ReferSub.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ReferSub.cs deleted file mode 100644 index f29a2feb9..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ReferSub.cs +++ /dev/null @@ -1,159 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements SIP "refer-sub" value. Defined in RFC 4488. - /// - /// - /// - /// RFC 4488 Syntax: - /// Refer-Sub = refer-sub-value *(SEMI exten) - /// refer-sub-value = "true" / "false" - /// exten = generic-param - /// - /// - public class SIP_t_ReferSub : SIP_t_ValueWithParams - { - #region Members - - private bool m_Value; - - #endregion - - #region Properties - - /// - /// Gets or sets refer-sub-value value. - /// - public bool Value - { - get { return m_Value; } - - set { m_Value = value; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public SIP_t_ReferSub() {} - - /// - /// Default constructor. - /// - /// Refer-Sub value. - public SIP_t_ReferSub(string value) - { - Parse(value); - } - - #endregion - - #region Methods - - /// - /// Parses "Refer-Sub" from specified value. - /// - /// SIP "Refer-Sub" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "Refer-Sub" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - Refer-Sub = refer-sub-value *(SEMI exten) - refer-sub-value = "true" / "false" - exten = generic-param - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // refer-sub-value - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("Refer-Sub refer-sub-value value is missing !"); - } - try - { - m_Value = Convert.ToBoolean(word); - } - catch - { - throw new SIP_ParseException("Invalid Refer-Sub refer-sub-value value !"); - } - - // Parse parameters - ParseParameters(reader); - } - - /// - /// Converts this to valid "contact-param" value. - /// - /// Returns "contact-param" value. - public override string ToStringValue() - { - /* - Refer-Sub = refer-sub-value *(SEMI exten) - refer-sub-value = "true" / "false" - exten = generic-param - */ - - StringBuilder retVal = new StringBuilder(); - - // refer-sub-value - retVal.Append(m_Value.ToString()); - - // Add parameters - retVal.Append(ParametersToString()); - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ReferredBy.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ReferredBy.cs deleted file mode 100644 index 28e184c51..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ReferredBy.cs +++ /dev/null @@ -1,187 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements SIP "Referred-By" value. Defined in RFC 3892. - /// - /// - /// - /// RFC 3892 Syntax: - /// Referred-By = referrer-uri *( SEMI (referredby-id-param / generic-param) ) - /// referrer-uri = ( name-addr / addr-spec ) - /// referredby-id-param = "cid" EQUAL sip-clean-msg-id - /// sip-clean-msg-id = LDQUOT dot-atom "@" (dot-atom / host) RDQUOT - /// - /// - public class SIP_t_ReferredBy : SIP_t_ValueWithParams - { - #region Members - - private SIP_t_NameAddress m_pAddress; - - #endregion - - #region Properties - - /// - /// Gets or sets address. - /// - /// Is raised when null value is passed. - public SIP_t_NameAddress Address - { - get { return m_pAddress; } - - set - { - if (value == null) - { - throw new ArgumentNullException("Address"); - } - - m_pAddress = value; - } - } - - /// - /// Gets or sets 'cid' parameter value. Value null means not specified. - /// - public string CID - { - get - { - SIP_Parameter parameter = Parameters["cid"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (string.IsNullOrEmpty(value)) - { - Parameters.Remove("cid"); - } - else - { - Parameters.Set("cid", value); - } - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// SIP 'Referred-By' value. - public SIP_t_ReferredBy(string value) - { - m_pAddress = new SIP_t_NameAddress(); - - Parse(value); - } - - #endregion - - #region Methods - - /// - /// Parses "Referred-By" from specified value. - /// - /// SIP "Referred-By" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "Referred-By" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - Referred-By = referrer-uri *( SEMI (referredby-id-param / generic-param) ) - referrer-uri = ( name-addr / addr-spec ) - referredby-id-param = "cid" EQUAL sip-clean-msg-id - sip-clean-msg-id = LDQUOT dot-atom "@" (dot-atom / host) RDQUOT - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // referrer-uri - m_pAddress.Parse(reader); - - // Parse parameters - ParseParameters(reader); - } - - /// - /// Converts this to valid "Referred-By" value. - /// - /// Returns "Referred-By" value. - public override string ToStringValue() - { - /* - Referred-By = referrer-uri *( SEMI (referredby-id-param / generic-param) ) - referrer-uri = ( name-addr / addr-spec ) - referredby-id-param = "cid" EQUAL sip-clean-msg-id - sip-clean-msg-id = LDQUOT dot-atom "@" (dot-atom / host) RDQUOT - */ - - StringBuilder retVal = new StringBuilder(); - - // referrer-uri - retVal.Append(m_pAddress.ToStringValue()); - - // Add parameters - retVal.Append(ParametersToString()); - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Replaces.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Replaces.cs deleted file mode 100644 index 9794d94bb..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Replaces.cs +++ /dev/null @@ -1,241 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements SIP "Replaces" value. Defined in RFC 3891. - /// - /// - /// - /// RFC 3891 Syntax: - /// Replaces = callid *(SEMI replaces-param) - /// replaces-param = to-tag / from-tag / early-flag / generic-param - /// to-tag = "to-tag" EQUAL token - /// from-tag = "from-tag" EQUAL token - /// early-flag = "early-only" - /// - /// - public class SIP_t_Replaces : SIP_t_ValueWithParams - { - #region Members - - private string m_CallID = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets call id. - /// - /// Is raised when null value is passed. - public string CallID - { - get { return m_CallID; } - - set - { - if (value == null) - { - throw new ArgumentNullException("CallID"); - } - - m_CallID = value; - } - } - - /// - /// Gets or sets Replaces 'to-tag' parameter. Value null means not specified. - /// - public string ToTag - { - get - { - SIP_Parameter parameter = Parameters["to-tag"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - Parameters.Remove("to-tag"); - } - else - { - Parameters.Set("to-tag", value); - } - } - } - - /// - /// Gets or sets Replaces 'from-tag' parameter. Value null means not specified. - /// - public string FromTag - { - get - { - SIP_Parameter parameter = Parameters["from-tag"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - Parameters.Remove("from-tag"); - } - else - { - Parameters.Set("from-tag", value); - } - } - } - - /// - /// Gets or sets Replaces 'early-flag' parameter. - /// - public bool EarlyFlag - { - get - { - if (Parameters.Contains("early-only")) - { - return true; - } - else - { - return false; - } - } - - set - { - if (!value) - { - Parameters.Remove("early-only"); - } - else - { - Parameters.Set("early-only", null); - } - } - } - - #endregion - - #region Methods - - /// - /// Parses "Replaces" from specified value. - /// - /// SIP "Replaces" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "Replaces" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - Replaces = callid *(SEMI replaces-param) - replaces-param = to-tag / from-tag / early-flag / generic-param - to-tag = "to-tag" EQUAL token - from-tag = "from-tag" EQUAL token - early-flag = "early-only" - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // callid - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("Replaces 'callid' value is missing !"); - } - m_CallID = word; - - // Parse parameters - ParseParameters(reader); - } - - /// - /// Converts this to valid "Replaces" value. - /// - /// Returns "Replaces" value. - public override string ToStringValue() - { - /* - Replaces = callid *(SEMI replaces-param) - replaces-param = to-tag / from-tag / early-flag / generic-param - to-tag = "to-tag" EQUAL token - from-tag = "from-tag" EQUAL token - early-flag = "early-only" - */ - - StringBuilder retVal = new StringBuilder(); - - // delta-seconds - retVal.Append(m_CallID); - - // Add parameters - retVal.Append(ParametersToString()); - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_RetryAfter.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_RetryAfter.cs deleted file mode 100644 index 3b5b2c0cd..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_RetryAfter.cs +++ /dev/null @@ -1,200 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements SIP "Retry-After" value. Defined in rfc 3261. - /// Retry after specifies how many seconds the service is expected to be unavailable to the requesting client. - /// - /// - /// - /// RFC 3261 Syntax: - /// Retry-After = delta-seconds [ comment ] *( SEMI retry-param ) - /// retry-param = ("duration" EQUAL delta-seconds) / generic-param - /// - /// - public class SIP_t_RetryAfter : SIP_t_ValueWithParams - { - #region Members - - private int m_Time; - - #endregion - - #region Properties - - /// - /// Gets or sets how many seconds the service is expected to be unavailable to the requesting client. - /// - /// Is raised when when value less than 1 is passed. - public int Time - { - get { return m_Time; } - - set - { - if (value < 1) - { - throw new ArgumentException("Property Time value must be >= 1 !"); - } - - m_Time = value; - } - } - - /// - /// Gets or sets 'duration' parameter value. The 'duration' parameter indicates how long the - /// called party will be reachable starting at the initial time of availability. If no duration - /// parameter is given, the service is assumed to be available indefinitely. Value -1 means not specified. - /// - /// Is raised when when value less than 1 is passed. - public int Duration - { - get - { - SIP_Parameter parameter = Parameters["duration"]; - if (parameter != null) - { - return Convert.ToInt32(parameter.Value); - } - else - { - return -1; - } - } - - set - { - if (value == -1) - { - Parameters.Remove("duration"); - } - else - { - if (value < 1) - { - throw new ArgumentException("Property Duration value must be >= 1 !"); - } - - Parameters.Set("duration", value.ToString()); - } - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// SIP Retry-After value. - public SIP_t_RetryAfter(string value) - { - Parse(value); - } - - #endregion - - #region Methods - - /// - /// Parses "Retry-After" from specified value. - /// - /// SIP "Retry-After" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "Retry-After" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - Retry-After = delta-seconds [ comment ] *( SEMI retry-param ) - retry-param = ("duration" EQUAL delta-seconds) / generic-param - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // delta-seconds - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("SIP Retry-After 'delta-seconds' value is missing !"); - } - try - { - m_Time = Convert.ToInt32(word); - } - catch - { - throw new SIP_ParseException("Invalid SIP Retry-After 'delta-seconds' value !"); - } - - // Parse parameters - ParseParameters(reader); - } - - /// - /// Converts this to valid "Retry-After" value. - /// - /// Returns "Retry-After" value. - public override string ToStringValue() - { - /* - Retry-After = delta-seconds [ comment ] *( SEMI retry-param ) - retry-param = ("duration" EQUAL delta-seconds) / generic-param - */ - - StringBuilder retVal = new StringBuilder(); - - // delta-seconds - retVal.Append(m_Time); - - // Add parameters - retVal.Append(ParametersToString()); - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_SecMechanism.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_SecMechanism.cs deleted file mode 100644 index 342dee189..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_SecMechanism.cs +++ /dev/null @@ -1,298 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Globalization; - using System.Text; - - #endregion - - /// - /// Implements SIP "sec-mechanism" value. Defined in RFC 3329. - /// - /// - /// - /// RFC 3329 Syntax: - /// sec-mechanism = mechanism-name *(SEMI mech-parameters) - /// mechanism-name = ( "digest" / "tls" / "ipsec-ike" / "ipsec-man" / token ) - /// mech-parameters = ( preference / digest-algorithm / digest-qop / digest-verify / extension ) - /// preference = "q" EQUAL qvalue - /// qvalue = ( "0" [ "." 0*3DIGIT ] ) / ( "1" [ "." 0*3("0") ] ) - /// digest-algorithm = "d-alg" EQUAL token - /// digest-qop = "d-qop" EQUAL token - /// digest-verify = "d-ver" EQUAL LDQUOT 32LHEX RDQUOT - /// extension = generic-param - /// - /// - public class SIP_t_SecMechanism : SIP_t_ValueWithParams - { - #region Members - - private string m_Mechanism = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets security mechanism name. Defined values: "digest","tls","ipsec-ike","ipsec-man". - /// - /// Is raised when null value is passed. - /// Is raised when invalid Mechanism value is passed. - public string Mechanism - { - get { return m_Mechanism; } - - set - { - if (value == null) - { - throw new ArgumentNullException("Mechanism"); - } - if (value == "") - { - throw new ArgumentException("Property Mechanism value may not be '' !"); - } - if (!TextUtils.IsToken(value)) - { - throw new ArgumentException("Property Mechanism value must be 'token' !"); - } - - m_Mechanism = value; - } - } - - /// - /// Gets or sets 'q' parameter value. Value -1 means not specified. - /// - public double Q - { - get - { - if (!Parameters.Contains("qvalue")) - { - return -1; - } - else - { - return double.Parse(Parameters["qvalue"].Value, NumberStyles.Any); - } - } - - set - { - if (value < 0 || value > 2) - { - throw new ArgumentException("Property QValue value must be between 0.0 and 2.0 !"); - } - - if (value < 0) - { - Parameters.Remove("qvalue"); - } - else - { - Parameters.Set("qvalue", value.ToString()); - } - } - } - - /// - /// Gets or sets 'd-alg' parameter value. Value null means not specified. - /// - public string D_Alg - { - get - { - SIP_Parameter parameter = Parameters["d-alg"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (string.IsNullOrEmpty(value)) - { - Parameters.Remove("d-alg"); - } - else - { - Parameters.Set("d-alg", value); - } - } - } - - /// - /// Gets or sets 'd-qop' parameter value. Value null means not specified. - /// - public string D_Qop - { - get - { - SIP_Parameter parameter = Parameters["d-qop"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (string.IsNullOrEmpty(value)) - { - Parameters.Remove("d-qop"); - } - else - { - Parameters.Set("d-qop", value); - } - } - } - - /// - /// Gets or sets 'd-ver' parameter value. Value null means not specified. - /// - public string D_Ver - { - get - { - SIP_Parameter parameter = Parameters["d-ver"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (string.IsNullOrEmpty(value)) - { - Parameters.Remove("d-ver"); - } - else - { - Parameters.Set("d-ver", value); - } - } - } - - #endregion - - #region Methods - - /// - /// Parses "sec-mechanism" from specified value. - /// - /// SIP "sec-mechanism" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "sec-mechanism" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - sec-mechanism = mechanism-name *(SEMI mech-parameters) - mechanism-name = ( "digest" / "tls" / "ipsec-ike" / "ipsec-man" / token ) - mech-parameters = ( preference / digest-algorithm / digest-qop / digest-verify / extension ) - preference = "q" EQUAL qvalue - qvalue = ( "0" [ "." 0*3DIGIT ] ) / ( "1" [ "." 0*3("0") ] ) - digest-algorithm = "d-alg" EQUAL token - digest-qop = "d-qop" EQUAL token - digest-verify = "d-ver" EQUAL LDQUOT 32LHEX RDQUOT - extension = generic-param - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // mechanism-name - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("Invalid 'sec-mechanism', 'mechanism-name' is missing !"); - } - - // Parse parameters - ParseParameters(reader); - } - - /// - /// Converts this to valid "sec-mechanism" value. - /// - /// Returns "sec-mechanism" value. - public override string ToStringValue() - { - /* - sec-mechanism = mechanism-name *(SEMI mech-parameters) - mechanism-name = ( "digest" / "tls" / "ipsec-ike" / "ipsec-man" / token ) - mech-parameters = ( preference / digest-algorithm / digest-qop / digest-verify / extension ) - preference = "q" EQUAL qvalue - qvalue = ( "0" [ "." 0*3DIGIT ] ) / ( "1" [ "." 0*3("0") ] ) - digest-algorithm = "d-alg" EQUAL token - digest-qop = "d-qop" EQUAL token - digest-verify = "d-ver" EQUAL LDQUOT 32LHEX RDQUOT - extension = generic-param - */ - - StringBuilder retVal = new StringBuilder(); - - // mechanism-name - retVal.Append(m_Mechanism); - - // Add parameters - retVal.Append(ParametersToString()); - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_SessionExpires.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_SessionExpires.cs deleted file mode 100644 index 76d0c5ad0..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_SessionExpires.cs +++ /dev/null @@ -1,212 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements SIP "Session-Expires" value. Defined in RFC 4028. - /// - /// - /// - /// RFC 4028 Syntax: - /// Session-Expires = delta-seconds *(SEMI se-params) - /// se-params = refresher-param / generic-param - /// refresher-param = "refresher" EQUAL ("uas" / "uac") - /// - /// - public class SIP_t_SessionExpires : SIP_t_ValueWithParams - { - #region Members - - private int m_Expires = 90; - - #endregion - - #region Properties - - /// - /// Gets or sets after how many seconds session expires. - /// - /// Is raised when value less than 90 is passed. - public int Expires - { - get { return m_Expires; } - - set - { - if (m_Expires < 90) - { - throw new ArgumentException("Property Expires value must be >= 90 !"); - } - - m_Expires = value; - } - } - - /// - /// Gets or sets Session-Expires 'refresher' parameter. Normally this value is 'ua' or 'uas'. - /// Value null means not specified. - /// - public string Refresher - { - get - { - SIP_Parameter parameter = Parameters["refresher"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - Parameters.Remove("refresher"); - } - else - { - Parameters.Set("refresher", value); - } - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Session-Expires value. - public SIP_t_SessionExpires(string value) - { - Parse(value); - } - - /// - /// Default constructor. - /// - /// Specifies after many seconds session expires. - /// Specifies session refresher(uac/uas/null). Value null means not specified. - /// Is raised when any of the arguments has invalid value. - public SIP_t_SessionExpires(int expires, string refresher) - { - if (m_Expires < 90) - { - throw new ArgumentException("Argument 'expires' value must be >= 90 !"); - } - - m_Expires = expires; - Refresher = refresher; - } - - #endregion - - #region Methods - - /// - /// Parses "Session-Expires" from specified value. - /// - /// SIP "Session-Expires" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "Session-Expires" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - Session-Expires = delta-seconds *(SEMI se-params) - se-params = refresher-param / generic-param - refresher-param = "refresher" EQUAL ("uas" / "uac") - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // delta-seconds - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("Session-Expires delta-seconds value is missing !"); - } - try - { - m_Expires = Convert.ToInt32(word); - } - catch - { - throw new SIP_ParseException("Invalid Session-Expires delta-seconds value !"); - } - - // Parse parameters - ParseParameters(reader); - } - - /// - /// Converts this to valid "Session-Expires" value. - /// - /// Returns "Session-Expires" value. - public override string ToStringValue() - { - /* - Session-Expires = delta-seconds *(SEMI se-params) - se-params = refresher-param / generic-param - refresher-param = "refresher" EQUAL ("uas" / "uac") - */ - - StringBuilder retVal = new StringBuilder(); - - // delta-seconds - retVal.Append(m_Expires.ToString()); - - // Add parameters - retVal.Append(ParametersToString()); - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_SubscriptionState.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_SubscriptionState.cs deleted file mode 100644 index b075b2bf9..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_SubscriptionState.cs +++ /dev/null @@ -1,378 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements SIP "Subscription-State" value. Defined in RFC 3265. - /// - /// - /// - /// RFC 3265 Syntax: - /// Subscription-State = substate-value *( SEMI subexp-params ) - /// substate-value = "active" / "pending" / "terminated" / extension-substate - /// extension-substate = token - /// subexp-params = ("reason" EQUAL event-reason-value) - /// / ("expires" EQUAL delta-seconds) - /// / ("retry-after" EQUAL delta-seconds) - /// / generic-param - /// event-reason-value = "deactivated" / "probation" / "rejected" / "timeout" / "giveup" - /// / "noresource" / event-reason-extension - /// event-reason-extension = token - /// - /// - public class SIP_t_SubscriptionState : SIP_t_ValueWithParams - { - #region Nested type: EventReason - - /// - /// This class holds 'event-reason-value' values. - /// - public class EventReason - { - #region Members - - /// - /// The subscription has been terminated, but the subscriber SHOULD retry immediately - /// with a new subscription. One primary use of such a status code is to allow migration of - /// subscriptions between nodes. The "retry-after" parameter has no semantics for "deactivated". - /// - public const string deactivated = "deactivated"; - - /// - /// The subscription has been terminated because the notifier could not obtain authorization in a - /// timely fashion. If a "retry-after" parameter is also present, the client SHOULD wait at least - /// the number of seconds specified by that parameter before attempting to re-subscribe; otherwise, - /// the client MAY retry immediately, but will likely get put back into pending state. - /// - public const string giveup = "giveup"; - - /// - /// The subscription has been terminated because the resource state which was being monitored - /// no longer exists. Clients SHOULD NOT attempt to re-subscribe. The "retry-after" parameter - /// has no semantics for "noresource". - /// - public const string noresource = "noresource"; - - /// - /// The subscription has been terminated, but the client SHOULD retry at some later time. - /// If a "retry-after" parameter is also present, the client SHOULD wait at least the number of - /// seconds specified by that parameter before attempting to re-subscribe. - /// - public const string probation = "probation"; - - /// - /// The subscription has been terminated due to change in authorization policy. - /// Clients SHOULD NOT attempt to re-subscribe. The "retry-after" parameter has no - /// semantics for "rejected". - /// - public const string rejected = "rejected"; - - /// - /// The subscription has been terminated because it was not refreshed before it expired. - /// Clients MAY re-subscribe immediately. The "retry-after" parameter has no semantics for "timeout". - /// - public const string timeout = "timeout"; - - #endregion - } - - #endregion - - #region Nested type: SubscriptionState - - /// - /// This class holds 'substate-value' values. - /// - public class SubscriptionState - { - #region Members - - /// - /// The subscription has been accepted and (in general) has been authorized. - /// - public const string active = "active"; - - /// - /// The subscription has been received by the notifier, but there is insufficient policy - /// information to grant or deny the subscription yet. - /// - public const string pending = "pending"; - - /// - /// The subscriber should consider the subscription terminated. - /// - public const string terminated = "terminated"; - - #endregion - } - - #endregion - - #region Members - - private string m_Value = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets subscription state value. Known values are defined in SubscriptionState class. - /// - /// Is raised when null value passed. - /// Is raised when empty string is passed or value is not token. - public string Value - { - get { return m_Value; } - - set - { - if (value == null) - { - throw new ArgumentNullException("Value"); - } - if (value == "") - { - throw new ArgumentException("Property 'Value' value may not be '' !"); - } - if (!TextUtils.IsToken(value)) - { - throw new ArgumentException("Property 'Value' value must be 'token' !"); - } - - m_Value = value; - } - } - - /// - /// Gets or sets 'reason' parameter value. Known reason values are defined in EventReason class. - /// Value null means not specified. - /// - public string Reason - { - get - { - SIP_Parameter parameter = Parameters["reason"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (string.IsNullOrEmpty(value)) - { - Parameters.Remove("reason"); - } - else - { - Parameters.Set("reason", value); - } - } - } - - /// - /// Gets or sets 'expires' parameter value. Value -1 means not specified. - /// - /// Is raised when negative value(except -1) is passed. - public int Expires - { - get - { - SIP_Parameter parameter = Parameters["expires"]; - if (parameter != null) - { - return Convert.ToInt32(parameter.Value); - } - else - { - return -1; - } - } - - set - { - if (value == -1) - { - Parameters.Remove("expires"); - } - else - { - if (value < 0) - { - throw new ArgumentException("Property 'Expires' value must >= 0 !"); - } - - Parameters.Set("expires", value.ToString()); - } - } - } - - /// - /// Gets or sets 'expires' parameter value. Value -1 means not specified. - /// - /// Is raised when negative value(except -1) is passed. - public int RetryAfter - { - get - { - SIP_Parameter parameter = Parameters["retry-after"]; - if (parameter != null) - { - return Convert.ToInt32(parameter.Value); - } - else - { - return -1; - } - } - - set - { - if (value == -1) - { - Parameters.Remove("retry-after"); - } - else - { - if (value < 0) - { - throw new ArgumentException("Property 'RetryAfter' value must >= 0 !"); - } - - Parameters.Set("retry-after", value.ToString()); - } - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// - public SIP_t_SubscriptionState(string value) - { - Parse(value); - } - - #endregion - - #region Methods - - /// - /// Parses "Subscription-State" from specified value. - /// - /// SIP "Subscription-State" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "Subscription-State" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - Subscription-State = substate-value *( SEMI subexp-params ) - substate-value = "active" / "pending" / "terminated" / extension-substate - extension-substate = token - subexp-params = ("reason" EQUAL event-reason-value) - / ("expires" EQUAL delta-seconds) - / ("retry-after" EQUAL delta-seconds) - / generic-param - event-reason-value = "deactivated" / "probation" / "rejected" / "timeout" / "giveup" - / "noresource" / event-reason-extension - event-reason-extension = token - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // substate-value - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("SIP Event 'substate-value' value is missing !"); - } - m_Value = word; - - // Parse parameters - ParseParameters(reader); - } - - /// - /// Converts this to valid "Subscription-State" value. - /// - /// Returns "Subscription-State" value. - public override string ToStringValue() - { - /* - Subscription-State = substate-value *( SEMI subexp-params ) - substate-value = "active" / "pending" / "terminated" / extension-substate - extension-substate = token - subexp-params = ("reason" EQUAL event-reason-value) - / ("expires" EQUAL delta-seconds) - / ("retry-after" EQUAL delta-seconds) - / generic-param - event-reason-value = "deactivated" / "probation" / "rejected" / "timeout" / "giveup" - / "noresource" / event-reason-extension - event-reason-extension = token - */ - - StringBuilder retVal = new StringBuilder(); - - // substate-value - retVal.Append(m_Value); - - // Add parameters - retVal.Append(ParametersToString()); - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_TargetDialog.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_TargetDialog.cs deleted file mode 100644 index c63b24f04..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_TargetDialog.cs +++ /dev/null @@ -1,226 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements SIP "Target-Dialog" value. Defined in RFC 4538. - /// - /// - /// - /// RFC 4538 Syntax: - /// Target-Dialog = callid *(SEMI td-param) ;callid from RFC 3261 - /// td-param = remote-param / local-param / generic-param - /// remote-param = "remote-tag" EQUAL token - /// local-param = "local-tag" EQUAL token - /// - /// - public class SIP_t_TargetDialog : SIP_t_ValueWithParams - { - #region Members - - private string m_CallID = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets call ID. - /// - /// Is raised when null value is passed. - /// Is raised when invalid CallID value is passed. - public string CallID - { - get { return m_CallID; } - - set - { - if (m_CallID == null) - { - throw new ArgumentNullException("CallID"); - } - if (m_CallID == "") - { - throw new ArgumentException("Property 'CallID' may not be '' !"); - } - - m_CallID = value; - } - } - - /// - /// Gets or sets 'remote-tag' parameter value. Value null means not specified. - /// - public string RemoteTag - { - get - { - SIP_Parameter parameter = Parameters["remote-tag"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (string.IsNullOrEmpty(value)) - { - Parameters.Remove("remote-tag"); - } - else - { - Parameters.Set("remote-tag", value); - } - } - } - - /// - /// Gets or sets 'local-tag' parameter value. Value null means not specified. - /// - public string LocalTag - { - get - { - SIP_Parameter parameter = Parameters["local-tag"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (string.IsNullOrEmpty(value)) - { - Parameters.Remove("local-tag"); - } - else - { - Parameters.Set("local-tag", value); - } - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// SIP Target-Dialog value. - public SIP_t_TargetDialog(string value) - { - Parse(value); - } - - #endregion - - #region Methods - - /// - /// Parses "Target-Dialog" from specified value. - /// - /// SIP "Target-Dialog" value. - /// Raised when value is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "Target-Dialog" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - Target-Dialog = callid *(SEMI td-param) ;callid from RFC 3261 - td-param = remote-param / local-param / generic-param - remote-param = "remote-tag" EQUAL token - local-param = "local-tag" EQUAL token - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // callid - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("SIP Target-Dialog 'callid' value is missing !"); - } - m_CallID = word; - - // Parse parameters - ParseParameters(reader); - } - - /// - /// Converts this to valid "Target-Dialog" value. - /// - /// Returns "Target-Dialog" value. - public override string ToStringValue() - { - /* - Target-Dialog = callid *(SEMI td-param) ;callid from RFC 3261 - td-param = remote-param / local-param / generic-param - remote-param = "remote-tag" EQUAL token - local-param = "local-tag" EQUAL token - */ - - StringBuilder retVal = new StringBuilder(); - - // callid - retVal.Append(m_CallID); - - // Add parameters - retVal.Append(ParametersToString()); - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Timestamp.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Timestamp.cs deleted file mode 100644 index c85db4cd4..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Timestamp.cs +++ /dev/null @@ -1,173 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - - #endregion - - /// - /// Implements SIP "Timestamp" value. Defined in RFC 3261. - /// - /// - /// - /// RFC 3261 Syntax: - /// Timestamp = 1*(DIGIT) [ "." *(DIGIT) ] [ LWS delay ] - /// delay = *(DIGIT) [ "." *(DIGIT) ] - /// - /// - public class SIP_t_Timestamp : SIP_t_Value - { - #region Members - - private decimal m_Delay; - private decimal m_Time; - - #endregion - - #region Properties - - /// - /// Gets or sets time in seconds when request was sent. - /// - public decimal Time - { - get { return m_Time; } - - set { m_Time = value; } - } - - /// - /// Gets or sets delay time in seconds. Delay specifies the time between the UAS received - /// the request and generated response. - /// - public decimal Delay - { - get { return m_Delay; } - - set { m_Delay = value; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Timestamp: header field value. - public SIP_t_Timestamp(string value) - { - Parse(new StringReader(value)); - } - - /// - /// Default constructor. - /// - /// Time in seconds when request was sent. - /// Delay time in seconds. - public SIP_t_Timestamp(decimal time, decimal delay) - { - m_Time = time; - m_Delay = delay; - } - - #endregion - - #region Methods - - /// - /// Parses "Timestamp" from specified value. - /// - /// SIP "Timestamp" value. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("reader"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "Timestamp" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* RFC 3261. - Timestamp = "Timestamp" HCOLON 1*(DIGIT) [ "." *(DIGIT) ] [ LWS delay ] - delay = *(DIGIT) [ "." *(DIGIT) ] - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // Get time - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("Invalid 'Timestamp' value, time is missing !"); - } - m_Time = Convert.ToDecimal(word); - - // Get optional delay - word = reader.ReadWord(); - if (word != null) - { - m_Delay = Convert.ToDecimal(word); - } - else - { - m_Delay = 0; - } - } - - /// - /// Converts this to valid "Timestamp" value. - /// - /// Returns "accept-range" value. - public override string ToStringValue() - { - /* RFC 3261. - Timestamp = "Timestamp" HCOLON 1*(DIGIT) [ "." *(DIGIT) ] [ LWS delay ] - delay = *(DIGIT) [ "." *(DIGIT) ] - */ - - if (m_Delay > 0) - { - return m_Time + " " + m_Delay; - } - else - { - return m_Time.ToString(); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_To.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_To.cs deleted file mode 100644 index aa9d9215d..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_To.cs +++ /dev/null @@ -1,173 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements SIP "To" value. Defined in RFC 3261. - /// The To header field specifies the logical recipient of the request. - /// - /// - /// - /// RFC 3261 Syntax: - /// To = ( name-addr / addr-spec ) *( SEMI to-param ) - /// to-param = tag-param / generic-param - /// tag-param = "tag" EQUAL token - /// - /// - public class SIP_t_To : SIP_t_ValueWithParams - { - #region Members - - private readonly SIP_t_NameAddress m_pAddress; - - #endregion - - #region Properties - - /// - /// Gets to address. - /// - public SIP_t_NameAddress Address - { - get { return m_pAddress; } - } - - /// - /// Gets or sets tag parameter value. - /// The "tag" parameter serves as a general mechanism for dialog identification. - /// Value null means that 'tag' paramter doesn't exist. - /// - public string Tag - { - get - { - SIP_Parameter parameter = Parameters["tag"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (string.IsNullOrEmpty(value)) - { - Parameters.Remove("tag"); - } - else - { - Parameters.Set("tag", value); - } - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// To: header field value. - public SIP_t_To(string value) - { - m_pAddress = new SIP_t_NameAddress(); - - Parse(new StringReader(value)); - } - - /// - /// Default constructor. - /// - /// To address. - public SIP_t_To(SIP_t_NameAddress address) - { - m_pAddress = address; - } - - #endregion - - #region Methods - - /// - /// Parses "To" from specified value. - /// - /// SIP "To" value. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("reader"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "To" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* To = ( name-addr / addr-spec ) *( SEMI to-param ) - to-param = tag-param / generic-param - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // Parse address - m_pAddress.Parse(reader); - - // Parse parameters - ParseParameters(reader); - } - - /// - /// Converts this to valid "To" value. - /// - /// Returns "To" value. - public override string ToStringValue() - { - StringBuilder retVal = new StringBuilder(); - retVal.Append(m_pAddress.ToStringValue()); - retVal.Append(ParametersToString()); - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Value.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Value.cs deleted file mode 100644 index 12bf98b59..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_Value.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - /// - /// This base class for all SIP data types. - /// - public abstract class SIP_t_Value - { - #region Methods - - /// - /// Parses single value from specified reader. - /// - /// Reader what contains - public abstract void Parse(StringReader reader); - - /// - /// Convert this to string value. - /// - /// Returns this as string value. - public abstract string ToStringValue(); - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ValueWithParams.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ValueWithParams.cs deleted file mode 100644 index 9dbbb7a73..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ValueWithParams.cs +++ /dev/null @@ -1,129 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System.Text; - - #endregion - - /// - /// This base class for SIP data types what has parameters support. - /// - public abstract class SIP_t_ValueWithParams : SIP_t_Value - { - #region Members - - private readonly SIP_ParameterCollection m_pParameters; - - #endregion - - #region Properties - - /// - /// Gets via parameters. - /// - public SIP_ParameterCollection Parameters - { - get { return m_pParameters; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public SIP_t_ValueWithParams() - { - m_pParameters = new SIP_ParameterCollection(); - } - - #endregion - - /// - /// Parses parameters from specified reader. Reader position must be where parameters begin. - /// - /// Reader from where to read parameters. - /// Raised when invalid SIP message. - protected void ParseParameters(StringReader reader) - { - // Remove all old parameters. - m_pParameters.Clear(); - - // Parse parameters - while (reader.Available > 0) - { - reader.ReadToFirstChar(); - - // We have parameter - if (reader.SourceString.StartsWith(";")) - { - reader.ReadSpecifiedLength(1); - string paramString = reader.QuotedReadToDelimiter(new[] {';', ','}, false); - if (paramString != "") - { - string[] name_value = paramString.Split(new[] {'='}, 2); - if (name_value.Length == 2) - { - Parameters.Add(name_value[0], name_value[1]); - } - else - { - Parameters.Add(name_value[0], null); - } - } - } - // Next value - else if (reader.SourceString.StartsWith(",")) - { - break; - } - // Unknown data - else - { - throw new SIP_ParseException("Unexpected value '" + reader.SourceString + "' !"); - } - } - } - - /// - /// Convert parameters to valid parameters string. - /// - /// Returns parameters string. - protected string ParametersToString() - { - StringBuilder retVal = new StringBuilder(); - foreach (SIP_Parameter parameter in m_pParameters) - { - if (!string.IsNullOrEmpty(parameter.Value)) - { - retVal.Append(";" + parameter.Name + "=" + parameter.Value); - } - else - { - retVal.Append(";" + parameter.Name); - } - } - - return retVal.ToString(); - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ViaParm.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ViaParm.cs deleted file mode 100644 index a82a5fb24..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_ViaParm.cs +++ /dev/null @@ -1,537 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - using System.Net; - using System.Text; - using Stack; - - #endregion - - /// - /// Implements SIP "via-parm" value. Defined in RFC 3261. - /// - /// - /// - /// RFC 3261 Syntax: - /// via-parm = sent-protocol LWS sent-by *( SEMI via-params ) - /// via-params = via-ttl / via-maddr / via-received / via-branch / via-extension - /// via-ttl = "ttl" EQUAL ttl - /// via-maddr = "maddr" EQUAL host - /// via-received = "received" EQUAL (IPv4address / IPv6address) - /// via-branch = "branch" EQUAL token - /// via-extension = generic-param - /// sent-protocol = protocol-name SLASH protocol-version SLASH transport - /// protocol-name = "SIP" / token - /// protocol-version = token - /// transport = "UDP" / "TCP" / "TLS" / "SCTP" / other-transport - /// sent-by = host [ COLON port ] - /// ttl = 1*3DIGIT ; 0 to 255 - /// - /// Via extentions: - /// // RFC 3486 - /// via-compression = "comp" EQUAL ("sigcomp" / other-compression) - /// // RFC 3581 - /// response-port = "rport" [EQUAL 1*DIGIT] - /// - /// - public class SIP_t_ViaParm : SIP_t_ValueWithParams - { - #region Members - - private string m_ProtocolName = ""; - private string m_ProtocolTransport = ""; - private string m_ProtocolVersion = ""; - private HostEndPoint m_pSentBy; - - #endregion - - #region Properties - - /// - /// Gets sent protocol name. Normally this is always SIP. - /// - public string ProtocolName - { - get { return m_ProtocolName; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Property ProtocolName can't be null or empty !"); - } - - m_ProtocolName = value; - } - } - - /// - /// Gets sent protocol version. - /// - public string ProtocolVersion - { - get { return m_ProtocolVersion; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Property ProtocolVersion can't be null or empty !"); - } - - m_ProtocolVersion = value; - } - } - - /// - /// Gets sent protocol transport. Currently known values are: UDP,TCP,TLS,SCTP. This value is always in upper-case. - /// - public string ProtocolTransport - { - get { return m_ProtocolTransport.ToUpper(); } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Property ProtocolTransport can't be null or empty !"); - } - - m_ProtocolTransport = value; - } - } - - /// - /// Gets host name or IP with optional port. Examples: lumiosft.ee,10.0.0.1:80. - /// - /// Is raised when null reference passed. - public HostEndPoint SentBy - { - get { return m_pSentBy; } - - set - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - m_pSentBy = value; - } - } - - /// - /// Gets sent-by port, if no port explicity set, transport default is returned. - /// - public int SentByPortWithDefault - { - get - { - if (m_pSentBy.Port != -1) - { - return m_pSentBy.Port; - } - else - { - if (ProtocolTransport == SIP_Transport.TLS) - { - return 5061; - } - else - { - return 5060; - } - } - } - } - - /// - /// Gets or sets 'branch' parameter value. The branch parameter in the Via header field values - /// serves as a transaction identifier. The value of the branch parameter MUST start - /// with the magic cookie "z9hG4bK". Value null means that branch paramter doesn't exist. - /// - public string Branch - { - get - { - SIP_Parameter parameter = Parameters["branch"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (string.IsNullOrEmpty(value)) - { - Parameters.Remove("branch"); - } - else - { - if (!value.StartsWith("z9hG4bK")) - { - throw new ArgumentException( - "Property Branch value must start with magic cookie 'z9hG4bK' !"); - } - - Parameters.Set("branch", value); - } - } - } - - /// - /// Gets or sets 'comp' parameter value. Value null means not specified. Defined in RFC 3486. - /// - public string Comp - { - get - { - SIP_Parameter parameter = Parameters["comp"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (string.IsNullOrEmpty(value)) - { - Parameters.Remove("comp"); - } - else - { - Parameters.Set("comp", value); - } - } - } - - /// - /// Gets or sets 'maddr' parameter value. Value null means not specified. - /// - public string Maddr - { - get - { - SIP_Parameter parameter = Parameters["maddr"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (string.IsNullOrEmpty(value)) - { - Parameters.Remove("maddr"); - } - else - { - Parameters.Set("maddr", value); - } - } - } - - /// - /// Gets or sets 'received' parameter value. Value null means not specified. - /// - public IPAddress Received - { - get - { - SIP_Parameter parameter = Parameters["received"]; - if (parameter != null) - { - return IPAddress.Parse(parameter.Value); - } - else - { - return null; - } - } - - set - { - if (value == null) - { - Parameters.Remove("received"); - } - else - { - Parameters.Set("received", value.ToString()); - } - } - } - - /// - /// Gets or sets 'rport' parameter value. Value -1 means not specified and value 0 means empty "" rport. - /// - public int RPort - { - get - { - SIP_Parameter parameter = Parameters["rport"]; - if (parameter != null) - { - if (parameter.Value == "") - { - return 0; - } - else - { - return Convert.ToInt32(parameter.Value); - } - } - else - { - return -1; - } - } - - set - { - if (value < 0) - { - Parameters.Remove("rport"); - } - else if (value == 0) - { - Parameters.Set("rport", ""); - } - else - { - Parameters.Set("rport", value.ToString()); - } - } - } - - /// - /// Gets or sets 'ttl' parameter value. Value -1 means not specified. - /// - public int Ttl - { - get - { - SIP_Parameter parameter = Parameters["ttl"]; - if (parameter != null) - { - return Convert.ToInt32(parameter.Value); - } - else - { - return -1; - } - } - - set - { - if (value < 0) - { - Parameters.Remove("ttl"); - } - else - { - Parameters.Set("ttl", value.ToString()); - } - } - } - - #endregion - - #region Constructor - - /// - /// Defualt constructor. - /// - public SIP_t_ViaParm() - { - m_ProtocolName = "SIP"; - m_ProtocolVersion = "2.0"; - m_ProtocolTransport = "UDP"; - m_pSentBy = new HostEndPoint("localhost", -1); - } - - #endregion - - #region Methods - - /// - /// Creates new branch paramter value. - /// - /// - public static string CreateBranch() - { - // The value of the branch parameter MUST start with the magic cookie "z9hG4bK". - - return "z9hG4bK-" + Guid.NewGuid().ToString().Replace("-", ""); - } - - /// - /// Parses "via-parm" from specified value. - /// - /// SIP "via-parm" value. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("reader"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "via-parm" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - via-parm = sent-protocol LWS sent-by *( SEMI via-params ) - via-params = via-ttl / via-maddr / via-received / via-branch / via-extension - via-ttl = "ttl" EQUAL ttl - via-maddr = "maddr" EQUAL host - via-received = "received" EQUAL (IPv4address / IPv6address) - via-branch = "branch" EQUAL token - via-extension = generic-param - sent-protocol = protocol-name SLASH protocol-version - SLASH transport - protocol-name = "SIP" / token - protocol-version = token - transport = "UDP" / "TCP" / "TLS" / "SCTP" / other-transport - sent-by = host [ COLON port ] - ttl = 1*3DIGIT ; 0 to 255 - - Via extentions: - // RFC 3486 - via-compression = "comp" EQUAL ("sigcomp" / other-compression) - // RFC 3581 - response-port = "rport" [EQUAL 1*DIGIT] - - Examples: - Via: SIP/2.0/UDP 127.0.0.1:58716;branch=z9hG4bK-d87543-4d7e71216b27df6e-1--d87543- - // Specifically, LWS on either side of the ":" or "/" is allowed. - Via: SIP / 2.0 / UDP 127.0.0.1:58716;branch=z9hG4bK-d87543-4d7e71216b27df6e-1--d87543- - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - // protocol-name - string word = reader.QuotedReadToDelimiter('/'); - if (word == null) - { - throw new SIP_ParseException("Via header field protocol-name is missing !"); - } - ProtocolName = word.Trim(); - // protocol-version - word = reader.QuotedReadToDelimiter('/'); - if (word == null) - { - throw new SIP_ParseException("Via header field protocol-version is missing !"); - } - ProtocolVersion = word.Trim(); - // transport - word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("Via header field transport is missing !"); - } - ProtocolTransport = word.Trim(); - // sent-by - word = reader.QuotedReadToDelimiter(new[] {';', ','}, false); - if (word == null) - { - throw new SIP_ParseException("Via header field sent-by is missing !"); - } - SentBy = HostEndPoint.Parse(word.Trim()); - - // Parse parameters - ParseParameters(reader); - } - - /// - /// Converts this to valid "via-parm" value. - /// - /// Returns "via-parm" value. - public override string ToStringValue() - { - /* - Via = ( "Via" / "v" ) HCOLON via-parm *(COMMA via-parm) - via-parm = sent-protocol LWS sent-by *( SEMI via-params ) - via-params = via-ttl / via-maddr / via-received / via-branch / via-extension - via-ttl = "ttl" EQUAL ttl - via-maddr = "maddr" EQUAL host - via-received = "received" EQUAL (IPv4address / IPv6address) - via-branch = "branch" EQUAL token - via-extension = generic-param - sent-protocol = protocol-name SLASH protocol-version - SLASH transport - protocol-name = "SIP" / token - protocol-version = token - transport = "UDP" / "TCP" / "TLS" / "SCTP" / other-transport - sent-by = host [ COLON port ] - ttl = 1*3DIGIT ; 0 to 255 - - Via extentions: - // RFC 3486 - via-compression = "comp" EQUAL ("sigcomp" / other-compression) - // RFC 3581 - response-port = "rport" [EQUAL 1*DIGIT] - - Examples: - Via: SIP/2.0/UDP 127.0.0.1:58716;branch=z9hG4bK-d87543-4d7e71216b27df6e-1--d87543- - // Specifically, LWS on either side of the ":" or "/" is allowed. - Via: SIP / 2.0 / UDP 127.0.0.1:58716;branch=z9hG4bK-d87543-4d7e71216b27df6e-1--d87543- - */ - - StringBuilder retVal = new StringBuilder(); - retVal.Append(ProtocolName + "/" + ProtocolVersion + "/" + ProtocolTransport + " "); - retVal.Append(SentBy.ToString()); - retVal.Append(ParametersToString()); - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_WarningValue.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_WarningValue.cs deleted file mode 100644 index f77d6dc67..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Message/SIP_t_WarningValue.cs +++ /dev/null @@ -1,190 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Message -{ - #region usings - - using System; - - #endregion - - /// - /// Implements SIP "warning-value" value. Defined in RFC 3261. - /// - /// - /// - /// RFC 3261 Syntax: - /// warning-value = warn-code SP warn-agent SP warn-text - /// warn-code = 3DIGIT - /// warn-agent = hostport / pseudonym - /// ; the name or pseudonym of the server adding - /// ; the Warning header, for use in debugging - /// warn-text = quoted-string - /// pseudonym = token - /// - /// - public class SIP_t_WarningValue : SIP_t_Value - { - #region Members - - private string m_Agent = ""; - private int m_Code; - private string m_Text = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets warning code. - /// - public int Code - { - get { return m_Code; } - - set - { - if (value < 100 || value > 999) - { - throw new ArgumentException("Property Code value must be 3 digit !"); - } - - m_Code = value; - } - } - - /// - /// Gets or sets name or pseudonym of the server. - /// - public string Agent - { - get { return m_Agent; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Property Agent value may not be null or empty !"); - } - - m_Agent = value; - } - } - - /// - /// Gets or sets warning text. - /// - public string Text - { - get { return m_Text; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Property Text value may not be null or empty !"); - } - - m_Text = value; - } - } - - #endregion - - #region Methods - - /// - /// Parses "warning-value" from specified value. - /// - /// SIP "warning-value" value. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public void Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("reader"); - } - - Parse(new StringReader(value)); - } - - /// - /// Parses "warning-value" from specified reader. - /// - /// Reader from where to parse. - /// Raised when reader is null. - /// Raised when invalid SIP message. - public override void Parse(StringReader reader) - { - /* - warning-value = warn-code SP warn-agent SP warn-text - warn-code = 3DIGIT - warn-agent = hostport / pseudonym - ; the name or pseudonym of the server adding - ; the Warning header, for use in debugging - warn-text = quoted-string - pseudonym = token - */ - - if (reader == null) - { - throw new ArgumentNullException("reader"); - } - - string word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("Invalid 'warning-value' value, warn-code is missing !"); - } - try - { - Code = Convert.ToInt32(word); - } - catch - { - throw new SIP_ParseException("Invalid 'warning-value' warn-code value, warn-code is missing !"); - } - - word = reader.ReadWord(); - if (word == null) - { - throw new SIP_ParseException("Invalid 'warning-value' value, warn-agent is missing !"); - } - Agent = word; - - word = reader.ReadToEnd(); - if (word == null) - { - throw new SIP_ParseException("Invalid 'warning-value' value, warn-text is missing !"); - } - Agent = TextUtils.UnQuoteString(word); - } - - /// - /// Converts this to valid "warning-value" value. - /// - /// Returns "warning-value" value. - public override string ToStringValue() - { - return m_Code + " " + m_Agent + " " + TextUtils.QuoteString(m_Text); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_AuthenticateEventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_AuthenticateEventArgs.cs deleted file mode 100644 index 646fa6932..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_AuthenticateEventArgs.cs +++ /dev/null @@ -1,67 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Proxy -{ - #region usings - - using AUTH; - - #endregion - - /// - /// This class provides data for SIP_ProxyCore.Authenticate event. - /// - public class SIP_AuthenticateEventArgs - { - #region Members - - private readonly Auth_HttpDigest m_pAuth; - - #endregion - - #region Properties - - /// - /// Gets authentication context. - /// - public Auth_HttpDigest AuthContext - { - get { return m_pAuth; } - } - - /// - /// Gets or sets if specified request is authenticated. - /// - public bool Authenticated { get; set; } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Authentication context. - public SIP_AuthenticateEventArgs(Auth_HttpDigest auth) - { - m_pAuth = auth; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_B2BUA.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_B2BUA.cs deleted file mode 100644 index f65e35076..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_B2BUA.cs +++ /dev/null @@ -1,349 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Proxy -{ - #region usings - - using System; - using System.Collections.Generic; - using AUTH; - using Message; - using Stack; - - #endregion - - /// - /// This class implements SIP b2bua(back to back user agent). Defined in RFC 3261. - /// - public class SIP_B2BUA : IDisposable - { - #region Events - - /// - /// Is called when new call is created. - /// - public event EventHandler CallCreated = null; - - /// - /// Is called when call has terminated. - /// - public event EventHandler CallTerminated = null; - - #endregion - - #region Members - - private readonly List m_pCalls; - private readonly SIP_ProxyCore m_pProxy; - private bool m_IsDisposed; - - #endregion - - #region Properties - - /// - /// Gets B2BUA owner SIP stack. - /// - public SIP_Stack Stack - { - get { return m_pProxy.Stack; } - } - - /// - /// Gets active calls. - /// - public SIP_B2BUA_Call[] Calls - { - get { return m_pCalls.ToArray(); } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Onwer SIP proxy. - internal SIP_B2BUA(SIP_ProxyCore owner) - { - m_pProxy = owner; - m_pCalls = new List(); - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public void Dispose() - { - if (m_IsDisposed) - { - return; - } - m_IsDisposed = true; - - // Terminate all calls. - foreach (SIP_B2BUA_Call call in m_pCalls) - { - call.Terminate(); - } - } - - /// - /// Gets call by call ID. - /// - /// Call ID. - /// Returns call with specified ID or null if no call with specified ID. - public SIP_B2BUA_Call GetCallByID(string callID) - { - foreach (SIP_B2BUA_Call call in m_pCalls.ToArray()) - { - if (call.CallID == callID) - { - return call; - } - } - - return null; - } - - #endregion - - #region Internal methods - - /// - /// This method is called when new request is received. - /// - /// Request event arguments. - internal void OnRequestReceived(SIP_RequestReceivedEventArgs e) - { - SIP_Request request = e.Request; - - if (request.RequestLine.Method == SIP_Methods.CANCEL) - { - /* RFC 3261 9.2. - If the UAS did not find a matching transaction for the CANCEL - according to the procedure above, it SHOULD respond to the CANCEL - with a 481 (Call Leg/Transaction Does Not Exist). - - Regardless of the method of the original request, as long as the - CANCEL matched an existing transaction, the UAS answers the CANCEL - request itself with a 200 (OK) response. - */ - - SIP_ServerTransaction trToCancel = - m_pProxy.Stack.TransactionLayer.MatchCancelToTransaction(e.Request); - if (trToCancel != null) - { - trToCancel.Cancel(); - //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x200_Ok)); - } - else - { - //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist)); - } - } - // We never should ge BYE here, because transport layer must match it to dialog. - else if (request.RequestLine.Method == SIP_Methods.BYE) - { - /* RFC 3261 15.1.2. - If the BYE does not match an existing dialog, the UAS core SHOULD generate a 481 - (Call/Transaction Does Not Exist) response and pass that to the server transaction. - */ - //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist)); - } - // We never should ge ACK here, because transport layer must match it to dialog. - else if (request.RequestLine.Method == SIP_Methods.ACK) - { - // ACK is response less request, so we may not return error to it. - } - // B2BUA must respond to OPTIONS request, not to forward it. - else if (request.RequestLine.Method == SIP_Methods.OPTIONS) - { - /* - SIP_Response response = e.Request.CreateResponse(SIP_ResponseCodes.x200_Ok); - // Add Allow to non ACK response. - if(e.Request.RequestLine.Method != SIP_Methods.ACK){ - response.Allow.Add("INVITE,ACK,OPTIONS,CANCEL,BYE,PRACK,MESSAGE,UPDATE"); - } - // Add Supported to 2xx non ACK response. - if(response.StatusCodeType == SIP_StatusCodeType.Success && e.Request.RequestLine.Method != SIP_Methods.ACK){ - response.Supported.Add("100rel,timer"); - } - e.ServerTransaction.SendResponse(response);*/ - } - // We never should get PRACK here, because transport layer must match it to dialog. - else if (request.RequestLine.Method == SIP_Methods.PRACK) - { - //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist)); - } - // We never should get UPDATE here, because transport layer must match it to dialog. - else if (request.RequestLine.Method == SIP_Methods.UPDATE) - { - //e.ServerTransaction.SendResponse(request.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist)); - } - else - { - /* draft-marjou-sipping-b2bua-00 4.1.3. - When the UAS of the B2BUA receives an upstream SIP request, its - associated UAC generates a new downstream SIP request with its new - Via, Max-Forwards, Call-Id, CSeq, and Contact header fields. Route - header fields of the upstream request are copied in the downstream - request, except the first Route header if it is under the - responsibility of the B2BUA. Record-Route header fields of the - upstream request are not copied in the new downstream request, as - Record-Route is only meaningful for the upstream dialog. The UAC - SHOULD copy other header fields and body from the upstream request - into this downstream request before sending it. - */ - - SIP_Request b2buaRequest = e.Request.Copy(); - b2buaRequest.Via.RemoveAll(); - b2buaRequest.MaxForwards = 70; - b2buaRequest.CallID = SIP_t_CallID.CreateCallID().CallID; - b2buaRequest.CSeq.SequenceNumber = 1; - b2buaRequest.Contact.RemoveAll(); - // b2buaRequest.Contact.Add(m_pProxy.CreateContact(b2buaRequest.To.Address).ToStringValue()); - if (b2buaRequest.Route.Count > 0 && - m_pProxy.IsLocalRoute( - SIP_Uri.Parse(b2buaRequest.Route.GetTopMostValue().Address.Uri.ToString()))) - { - b2buaRequest.Route.RemoveTopMostValue(); - } - b2buaRequest.RecordRoute.RemoveAll(); - - // Remove our Authorization header if it's there. - foreach ( - SIP_SingleValueHF header in - b2buaRequest.ProxyAuthorization.HeaderFields) - { - try - { - Auth_HttpDigest digest = new Auth_HttpDigest(header.ValueX.AuthData, - b2buaRequest.RequestLine.Method); - if (m_pProxy.Stack.Realm == digest.Realm) - { - b2buaRequest.ProxyAuthorization.Remove(header); - } - } - catch - { - // We don't care errors here. This can happen if remote server xxx auth method here and - // we don't know how to parse it, so we leave it as is. - } - } - - //--- Add/replace default fields. ------------------------------------------ - b2buaRequest.Allow.RemoveAll(); - b2buaRequest.Supported.RemoveAll(); - // Accept to non ACK,BYE request. - if (request.RequestLine.Method != SIP_Methods.ACK && - request.RequestLine.Method != SIP_Methods.BYE) - { - b2buaRequest.Allow.Add("INVITE,ACK,OPTIONS,CANCEL,BYE,PRACK"); - } - // Supported to non ACK request. - if (request.RequestLine.Method != SIP_Methods.ACK) - { - b2buaRequest.Supported.Add("100rel,timer"); - } - // Remove Require: header. - b2buaRequest.Require.RemoveAll(); - - // RFC 4028 7.4. For re-INVITE and UPDATE we need to add Session-Expires and Min-SE: headers. - if (request.RequestLine.Method == SIP_Methods.INVITE || - request.RequestLine.Method == SIP_Methods.UPDATE) - { - b2buaRequest.SessionExpires = new SIP_t_SessionExpires(m_pProxy.Stack.SessionExpries, - "uac"); - b2buaRequest.MinSE = new SIP_t_MinSE(m_pProxy.Stack.MinimumSessionExpries); - } - - // Forward request. - m_pProxy.ForwardRequest(true, e, b2buaRequest, false); - } - } - - /// - /// This method is called when new response is received. - /// - /// Response event arguments. - internal void OnResponseReceived(SIP_ResponseReceivedEventArgs e) - { - // If we get response here, that means we have stray response, just do nothing. - // All reponses must match to transactions, so we never should reach here. - } - - /// - /// Adds specified call to calls list. - /// - /// Caller side dialog. - /// Calee side dialog. - internal void AddCall(SIP_Dialog caller, SIP_Dialog calee) - { - lock (m_pCalls) - { - SIP_B2BUA_Call call = new SIP_B2BUA_Call(this, caller, calee); - m_pCalls.Add(call); - - OnCallCreated(call); - } - } - - /// - /// Removes specified call from calls list. - /// - /// Call to remove. - internal void RemoveCall(SIP_B2BUA_Call call) - { - m_pCalls.Remove(call); - - OnCallTerminated(call); - } - - #endregion - - /// - /// Raises CallCreated event. - /// - /// Call created. - protected void OnCallCreated(SIP_B2BUA_Call call) - { - if (CallCreated != null) - { - CallCreated(call, new EventArgs()); - } - } - - /// - /// Raises CallTerminated event. - /// - /// Call terminated. - protected internal void OnCallTerminated(SIP_B2BUA_Call call) - { - if (CallTerminated != null) - { - CallTerminated(call, new EventArgs()); - } - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_B2BUA_Call.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_B2BUA_Call.cs deleted file mode 100644 index 30849aca8..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_B2BUA_Call.cs +++ /dev/null @@ -1,298 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Proxy -{ - #region usings - - using System; - using Message; - using Stack; - - #endregion - - /// - /// This class represents B2BUA call. - /// - public class SIP_B2BUA_Call - { - #region Members - - private readonly string m_CallID = ""; - private readonly SIP_B2BUA m_pOwner; - private readonly DateTime m_StartTime; - private bool m_IsTerminated; - private SIP_Dialog m_pCallee; - private SIP_Dialog m_pCaller; - - #endregion - - #region Properties - - /// - /// Gets call start time. - /// - public DateTime StartTime - { - get { return m_StartTime; } - } - - /// - /// Gets current call ID. - /// - public string CallID - { - get { return m_CallID; } - } - - /// - /// Gets caller SIP dialog. - /// - public SIP_Dialog CallerDialog - { - get { return m_pCaller; } - } - - /// - /// Gets callee SIP dialog. - /// - public SIP_Dialog CalleeDialog - { - get { return m_pCallee; } - } - - /// - /// Gets if call has timed out and needs to be terminated. - /// - public bool IsTimedOut - { - // TODO: - - get { return false; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner B2BUA server. - /// Caller side dialog. - /// Callee side dialog. - internal SIP_B2BUA_Call(SIP_B2BUA owner, SIP_Dialog caller, SIP_Dialog callee) - { - m_pOwner = owner; - m_pCaller = caller; - m_pCallee = callee; - m_StartTime = DateTime.Now; - m_CallID = Guid.NewGuid().ToString().Replace("-", ""); - - //m_pCaller.RequestReceived += new SIP_RequestReceivedEventHandler(m_pCaller_RequestReceived); - //m_pCaller.Terminated += new EventHandler(m_pCaller_Terminated); - - //m_pCallee.RequestReceived += new SIP_RequestReceivedEventHandler(m_pCallee_RequestReceived); - //m_pCallee.Terminated += new EventHandler(m_pCallee_Terminated); - } - - #endregion - - #region Methods - - /// - /// Terminates call. - /// - public void Terminate() - { - if (m_IsTerminated) - { - return; - } - m_IsTerminated = true; - - m_pOwner.RemoveCall(this); - - if (m_pCaller != null) - { - //m_pCaller.Terminate(); - m_pCaller.Dispose(); - m_pCaller = null; - } - if (m_pCallee != null) - { - //m_pCallee.Terminate(); - m_pCallee.Dispose(); - m_pCallee = null; - } - - m_pOwner.OnCallTerminated(this); - } - - #endregion - - #region Utility methods - - /// - /// Is called when caller sends new request. - /// - /// Event data. - private void m_pCaller_RequestReceived(SIP_RequestReceivedEventArgs e) - { - // TODO: If we get UPDATE, but callee won't support it ? generate INVITE instead ? - /* - SIP_Request request = m_pCallee.CreateRequest(e.Request.RequestLine.Method); - CopyMessage(e.Request,request,new string[]{"Via:","Call-Id:","To:","From:","CSeq:","Contact:","Route:","Record-Route:","Max-Forwards:","Allow:","Require:","Supported:"}); - // Remove our Authentication header if it's there. - foreach(SIP_SingleValueHF header in request.ProxyAuthorization.HeaderFields){ - try{ - Auth_HttpDigest digest = new Auth_HttpDigest(header.ValueX.AuthData,request.RequestLine.Method); - if(m_pOwner.Stack.Realm == digest.Realm){ - request.ProxyAuthorization.Remove(header); - } - } - catch{ - // We don't care errors here. This can happen if remote server xxx auth method here and - // we don't know how to parse it, so we leave it as is. - } - } - - SIP_ClientTransaction clientTransaction = m_pCallee.CreateTransaction(request); - clientTransaction.ResponseReceived += new EventHandler(m_pCallee_ResponseReceived); - clientTransaction.Tag = e.ServerTransaction; - clientTransaction.Start();*/ - } - - /// - /// This method is called when caller dialog has terminated, normally this happens - /// when dialog gets BYE request. - /// - /// Sender. - /// Event data. - private void m_pCaller_Terminated(object sender, EventArgs e) - { - Terminate(); - } - - /// - /// This method is called when callee dialog client transaction receives response. - /// - /// Sender. - /// Event data. - private void m_pCallee_ResponseReceived(object sender, SIP_ResponseReceivedEventArgs e) - { - SIP_ServerTransaction serverTransaction = (SIP_ServerTransaction) e.ClientTransaction.Tag; - //SIP_Response response = serverTransaction.Request.CreateResponse(e.Response.StatusCode_ReasonPhrase); - //CopyMessage(e.Response,response,new string[]{"Via:","Call-Id:","To:","From:","CSeq:","Contact:","Route:","Record-Route:","Allow:","Supported:"}); - //serverTransaction.SendResponse(response); - } - - /// - /// Is called when callee sends new request. - /// - /// Event data. - private void m_pCallee_RequestReceived(SIP_RequestReceivedEventArgs e) - { - /* - SIP_Request request = m_pCaller.CreateRequest(e.Request.RequestLine.Method); - CopyMessage(e.Request,request,new string[]{"Via:","Call-Id:","To:","From:","CSeq:","Contact:","Route:","Record-Route:","Max-Forwards:","Allow:","Require:","Supported:"}); - // Remove our Authentication header if it's there. - foreach(SIP_SingleValueHF header in request.ProxyAuthorization.HeaderFields){ - try{ - Auth_HttpDigest digest = new Auth_HttpDigest(header.ValueX.AuthData,request.RequestLine.Method); - if(m_pOwner.Stack.Realm == digest.Realm){ - request.ProxyAuthorization.Remove(header); - } - } - catch{ - // We don't care errors here. This can happen if remote server xxx auth method here and - // we don't know how to parse it, so we leave it as is. - } - } - - SIP_ClientTransaction clientTransaction = m_pCaller.CreateTransaction(request); - clientTransaction.ResponseReceived += new EventHandler(m_pCaller_ResponseReceived); - clientTransaction.Tag = e.ServerTransaction; - clientTransaction.Start();*/ - } - - /// - /// This method is called when callee dialog has terminated, normally this happens - /// when dialog gets BYE request. - /// - /// Sender. - /// Event data. - private void m_pCallee_Terminated(object sender, EventArgs e) - { - Terminate(); - } - - /// - /// This method is called when caller dialog client transaction receives response. - /// - /// Sender. - /// Event data. - private void m_pCaller_ResponseReceived(object sender, SIP_ResponseReceivedEventArgs e) - { - SIP_ServerTransaction serverTransaction = (SIP_ServerTransaction) e.ClientTransaction.Tag; - //SIP_Response response = serverTransaction.Request.CreateResponse(e.Response.StatusCode_ReasonPhrase); - //CopyMessage(e.Response,response,new string[]{"Via:","Call-Id:","To:","From:","CSeq:","Contact:","Route:","Record-Route:","Allow:","Supported:"}); - //serverTransaction.SendResponse(response); - } - - /* - /// - /// Transfers call to specified recipient. - /// - /// Address where to transfer call. - public void CallTransfer(string to) - { - throw new NotImplementedException(); - }*/ - - /// - /// Copies header fileds from 1 message to antother. - /// - /// Source message. - /// Destination message. - /// Header fields not to copy. - private void CopyMessage(SIP_Message source, SIP_Message destination, string[] exceptHeaders) - { - foreach (SIP_HeaderField headerField in source.Header) - { - bool copy = true; - foreach (string h in exceptHeaders) - { - if (h.ToLower() == headerField.Name.ToLower()) - { - copy = false; - break; - } - } - - if (copy) - { - destination.Header.Add(headerField.Name, headerField.Value); - } - } - - destination.Data = source.Data; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_ForkingMode.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_ForkingMode.cs deleted file mode 100644 index 489d86b80..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_ForkingMode.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Proxy -{ - /// - /// This enum specifies SIP proxy server 'forking' mode. - /// - public enum SIP_ForkingMode - { - /// - /// No forking. The contact with highest q value is used. - /// - None, - - /// - /// All contacts are processed parallel at same time. - /// - Parallel, - - /// - /// In a sequential search, a proxy server attempts each contact address in sequence, - /// proceeding to the next one only after the previous has generated a final response. - /// Contacts are processed from highest q value to lower. - /// - Sequential, - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_Gateway.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_Gateway.cs deleted file mode 100644 index 4de12e51f..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_Gateway.cs +++ /dev/null @@ -1,194 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Proxy -{ - #region usings - - using System; - using Stack; - - #endregion - - /// - /// This class represents SIP gateway to other system. - /// - public class SIP_Gateway - { - #region Members - - private string m_Host = ""; - private string m_Password = ""; - private int m_Port = 5060; - private string m_Realm = ""; - private string m_Transport = SIP_Transport.UDP; - private string m_UserName = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets transport. - /// - /// Is raised when invalid value passed. - public string Transport - { - get { return m_Transport; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Value cant be null or empty !"); - } - - m_Transport = value; - } - } - - /// - /// Gets or sets remote gateway host name or IP address. - /// - /// Is raised when invalid value passed. - public string Host - { - get { return m_Host; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Value cant be null or empty !"); - } - - m_Host = value; - } - } - - /// - /// Gets or sets remote gateway port. - /// - /// Is raised when invalid value passed. - public int Port - { - get { return m_Port; } - - set - { - if (value < 1) - { - throw new ArgumentException("Value must be >= 1 !"); - } - - m_Port = value; - } - } - - /// - /// Gets or sets remote gateway realm(domain). - /// - public string Realm - { - get { return m_Realm; } - - set - { - if (value == null) - { - m_Realm = ""; - } - - m_Realm = value; - } - } - - /// - /// Gets or sets remote gateway user name. - /// - public string UserName - { - get { return m_UserName; } - - set - { - if (value == null) - { - m_UserName = ""; - } - - m_UserName = value; - } - } - - /// - /// Gets or sets remote gateway password. - /// - public string Password - { - get { return m_Password; } - - set - { - if (value == null) - { - m_Password = ""; - } - - m_Password = value; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Transport to use. - /// Remote gateway host name or IP address. - /// Remote gateway port. - public SIP_Gateway(string transport, string host, int port) : this(transport, host, port, "", "", "") {} - - /// - /// Default constructor. - /// - /// Transport to use. - /// Remote gateway host name or IP address. - /// Remote gateway port. - /// Remote gateway realm. - /// Remote gateway user name. - /// Remote gateway password. - public SIP_Gateway(string transport, - string host, - int port, - string realm, - string userName, - string password) - { - Transport = transport; - Host = host; - Port = port; - Realm = realm; - UserName = userName; - Password = password; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_GatewayEventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_GatewayEventArgs.cs deleted file mode 100644 index 770f123a3..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_GatewayEventArgs.cs +++ /dev/null @@ -1,101 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Proxy -{ - #region usings - - using System; - using System.Collections.Generic; - - #endregion - - /// - /// This class provides data for SIP_ProxyCore.GetGateways event. - /// - public class SIP_GatewayEventArgs - { - #region Members - - private readonly List m_pGateways; - private readonly string m_UriScheme = ""; - private readonly string m_UserName = ""; - - #endregion - - #region Properties - - /// - /// Gets URI scheme which gateways to get. - /// - public string UriScheme - { - get { return m_UriScheme; } - } - - /// - /// Gets authenticated user name. - /// - public string UserName - { - get { return m_UserName; } - } - - /* - /// - /// Gets or sets if specified user has - /// - public bool IsForbidden - { - get{ return false; } - - set{ } - }*/ - - /// - /// Gets gateways collection. - /// - public List Gateways - { - get { return m_pGateways; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// URI scheme which gateways to get. - /// Authenticated user name. - /// If any argument has invalid value. - public SIP_GatewayEventArgs(string uriScheme, string userName) - { - if (string.IsNullOrEmpty(uriScheme)) - { - throw new ArgumentException("Argument 'uriScheme' value can't be null or empty !"); - } - - m_UriScheme = uriScheme; - m_UserName = userName; - m_pGateways = new List(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_Presence.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_Presence.cs deleted file mode 100644 index a2809f391..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_Presence.cs +++ /dev/null @@ -1,47 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Proxy -{ - #region usings - - using Stack; - - #endregion - - /// - /// This class implements SIP presence server. - /// - public class SIP_Presence - { - #region Internal methods - - /// - /// Handles SUBSCRIBE method. - /// - /// Request event arguments. - internal void Subscribe(SIP_RequestReceivedEventArgs e) {} - - /// - /// Handles NOTIFY method. - /// - /// Request event arguments. - internal void Notify(SIP_RequestReceivedEventArgs e) {} - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_ProxyContext.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_ProxyContext.cs deleted file mode 100644 index 8911f050a..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_ProxyContext.cs +++ /dev/null @@ -1,1875 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Proxy -{ - #region usings - - using System; - using System.Collections.Generic; - using System.Net; - using System.Timers; - using Message; - using Stack; - - #endregion - - /// - /// Implements SIP 'proxy context'. Defined in RFC 3261. - /// - /// Proxy context is bridge between caller and calee. - /// Proxy context job is to forward request to contact(s) and send received responses back to caller. - public class SIP_ProxyContext : IDisposable - { - #region Nested type: TargetHandler - - /// - /// This class is responsible for sending request to target(HOPs) and processing responses. - /// - private class TargetHandler : IDisposable - { - #region Members - - private readonly bool m_AddRecordRoute = true; - private readonly bool m_IsRecursed; - private readonly SIP_Flow m_pFlow; - private readonly object m_pLock = new object(); - private bool m_HasReceivedResponse; - private bool m_IsCompleted; - private bool m_IsDisposed; - private bool m_IsStarted; - private Queue m_pHops; - private SIP_ProxyContext m_pOwner; - private SIP_Request m_pRequest; - private SIP_Uri m_pTargetUri; - private TimerEx m_pTimerC; - private SIP_ClientTransaction m_pTransaction; - - #endregion - - #region Properties - - /// - /// Gets if this object is disposed. - /// - public bool IsDisposed - { - get { return m_IsDisposed; } - } - - /// - /// Gets if this target processing has been started. - /// - /// Is raised when this object is disposed and and this property is accessed. - public bool IsStarted - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_IsStarted; - } - } - - /// - /// Gets if request sender has completed. - /// - public bool IsCompleted - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_IsCompleted; - } - } - - /// - /// Gets SIP request what this Target is sending. - /// - /// Is raised when this object is disposed and and this property is accessed. - public SIP_Request Request - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pRequest; - } - } - - /// - /// Gets target URI where request is being sent. - /// - /// Is raised when this object is disposed and and this property is accessed. - public SIP_Uri TargetUri - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pTargetUri; - } - } - - /// - /// Gets if this target is recording routing(By adding Record-Route header field to forwarded requests). - /// - public bool IsRecordingRoute - { - get { return m_AddRecordRoute; } - } - - /// - /// Gets if this target is redirected by proxy context. - /// - public bool IsRecursed - { - get { return m_IsRecursed; } - } - - /// - /// Gets if this handler has received any response from target. - /// - /// Is raised when this object is disposed and and this property is accessed. - public bool HasReceivedResponse - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_HasReceivedResponse; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner proxy context. - /// Data flow to use for sending. Value null means system will choose it. - /// Target URI where to send request. - /// If true, handler will add Record-Route header to forwarded message. - /// If true then this target is redirected by proxy context. - /// Is raised when owner or targetURI is null reference. - public TargetHandler(SIP_ProxyContext owner, - SIP_Flow flow, - SIP_Uri targetUri, - bool addRecordRoute, - bool isRecursed) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - if (targetUri == null) - { - throw new ArgumentNullException("targetUri"); - } - - m_pOwner = owner; - m_pFlow = flow; - m_pTargetUri = targetUri; - m_AddRecordRoute = addRecordRoute; - m_IsRecursed = isRecursed; - - m_pHops = new Queue(); - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public void Dispose() - { - lock (m_pLock) - { - if (m_IsDisposed) - { - return; - } - m_IsDisposed = true; - - m_pOwner.TargetHandler_Disposed(this); - - m_pOwner = null; - m_pRequest = null; - m_pTargetUri = null; - m_pHops = null; - if (m_pTransaction != null) - { - m_pTransaction.Dispose(); - m_pTransaction = null; - } - if (m_pTimerC != null) - { - m_pTimerC.Dispose(); - m_pTimerC = null; - } - } - } - - /// - /// Starts target processing. - /// - /// Is raised when this object is disposed and and this method is accessed. - /// Is raised when Start method is already called and this method is called. - public void Start() - { - lock (m_pLock) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (m_IsStarted) - { - throw new InvalidOperationException("Start has already called."); - } - m_IsStarted = true; - - Init(); - - // No hops for target. Invalid target domain name,dns server down,target won't support needed target. - if (m_pHops.Count == 0) - { - m_pOwner.ProcessResponse(this, - m_pTransaction, - m_pOwner.Proxy.Stack.CreateResponse( - SIP_ResponseCodes.x503_Service_Unavailable + - ": No hop(s) for target.", - m_pTransaction.Request)); - Dispose(); - return; - } - else - { - if (m_pFlow != null) - { - SendToFlow(m_pFlow, m_pRequest.Copy()); - } - else - { - SendToNextHop(); - } - } - } - } - - /// - /// Cancels target processing. - /// - /// Is raised when this object is disposed and and this method is accessed. - public void Cancel() - { - lock (m_pLock) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (m_IsStarted) - { - m_pTransaction.Cancel(); - } - else - { - Dispose(); - } - } - } - - #endregion - - #region Event handlers - - /// - /// Is called when client transactions receives response. - /// - /// Sender. - /// Event data. - private void ClientTransaction_ResponseReceived(object sender, SIP_ResponseReceivedEventArgs e) - { - lock (m_pLock) - { - m_HasReceivedResponse = true; - - /* RFC 3261 16.7 Response Processing. - 1. Find the appropriate response context - 2. Update timer C for provisional responses - - Steps 3 - 10 done in ProxyContext.ProcessResponse method. - */ - - #region 1. Find Context - - // Done, m_pOwner is it. - - #endregion - - #region 2. Update timer C for provisional responses - - /* For an INVITE transaction, if the response is a provisional - response with status codes 101 to 199 inclusive (i.e., anything - but 100), the proxy MUST reset timer C for that client - transaction. The timer MAY be reset to a different value, but - this value MUST be greater than 3 minutes. - */ - if (m_pTimerC != null && e.Response.StatusCode >= 101 && e.Response.StatusCode <= 199) - { - m_pTimerC.Interval = 3*60*1000; - } - - #endregion - - /* - // If 401 or 407 (Authentication required), see i we have specified realm(s) credentials, - // if so try to authenticate. - if(e.Response.StatusCode == 401 || e.Response.StatusCode == 407){ - SIP_t_Challenge[] challanges = null; - if(e.Response.StatusCode == 401){ - challanges = e.Response.WWWAuthenticate.GetAllValues(); - } - else{ - challanges = e.Response.ProxyAuthenticate.GetAllValues(); - } - - // TODO: Porbably we need to auth only if we can provide authentication data to all realms ? - - SIP_Request request = m_pServerTransaction.Request.Copy(); - request.CSeq.SequenceNumber++; - bool hasAny = false; - foreach(SIP_t_Challenge challange in challanges){ - Auth_HttpDigest authDigest = new Auth_HttpDigest(challange.AuthData,m_pServerTransaction.Request.Method); - NetworkCredential credential = GetCredential(authDigest.Realm); - if(credential != null){ - // Don't authenticate again, if we tried already once and failed. - // FIX ME: if user passed authorization, then works wrong. - if(e.ClientTransaction.Request.Authorization.Count == 0 && e.ClientTransaction.Request.ProxyAuthorization.Count == 0){ - authDigest.RequestMethod = m_pServerTransaction.Request.Method; - authDigest.Uri = e.ClientTransaction.Request.Uri; - authDigest.Realm = credential.Domain; - authDigest.UserName = credential.UserName; - authDigest.Password = credential.Password; - authDigest.CNonce = Auth_HttpDigest.CreateNonce(); - authDigest.Qop = authDigest.Qop; - authDigest.Opaque = authDigest.Opaque; - authDigest.Algorithm = authDigest.Algorithm; - if(e.Response.StatusCode == 401){ - request.Authorization.Add(authDigest.ToAuthorization()); - } - else{ - request.ProxyAuthorization.Add(authDigest.ToAuthorization()); - } - hasAny = true; - } - } - } - if(hasAny){ - // CreateClientTransaction((SIP_Target)e.ClientTransaction.Tag,request); - return; - } - }*/ - - if (e.Response.StatusCodeType != SIP_StatusCodeType.Provisional) - { - m_IsCompleted = true; - } - - m_pOwner.ProcessResponse(this, m_pTransaction, e.Response); - } - } - - /// - /// Is called when client transaction has timed out. - /// - /// Sender. - /// Event data. - private void ClientTransaction_TimedOut(object sender, EventArgs e) - { - lock (m_pLock) - { - /* RFC 3263 4.3. - For SIP requests, failure occurs if the transaction layer reports a - 503 error response or a transport failure of some sort (generally, - due to fatal ICMP errors in UDP or connection failures in TCP). - Failure also occurs if the transaction layer times out without ever - having received any response, provisional or final (i.e., timer B or - timer F in RFC 3261 [1] fires). If a failure occurs, the client - SHOULD create a new request, which is identical to the previous, but - has a different value of the Via branch ID than the previous (and - therefore constitutes a new SIP transaction). That request is sent - to the next element in the list as specified by RFC 2782. - */ - if (m_pHops.Count > 0) - { - CleanUpActiveHop(); - SendToNextHop(); - } - /* RFC 3161 16.6.7. - If the ordered set is exhausted, the request cannot be forwarded to this element in - the target set. The proxy does not need to place anything in the response context, - but otherwise acts as if this element of the target set returned a - 408 (Request Timeout) final response. - */ - else - { - m_IsCompleted = true; - m_pOwner.ProcessResponse(this, - m_pTransaction, - m_pOwner.Proxy.Stack.CreateResponse( - SIP_ResponseCodes.x408_Request_Timeout, - m_pTransaction.Request)); - Dispose(); - } - } - } - - /// - /// Is called when client transaction encountered transport error. - /// - /// Sender. - /// Event data. - private void ClientTransaction_TransportError(object sender, ExceptionEventArgs e) - { - lock (m_pLock) - { - /* RFC 3263 4.3. - For SIP requests, failure occurs if the transaction layer reports a - 503 error response or a transport failure of some sort (generally, - due to fatal ICMP errors in UDP or connection failures in TCP). - Failure also occurs if the transaction layer times out without ever - having received any response, provisional or final (i.e., timer B or - timer F in RFC 3261 [1] fires). If a failure occurs, the client - SHOULD create a new request, which is identical to the previous, but - has a different value of the Via branch ID than the previous (and - therefore constitutes a new SIP transaction). That request is sent - to the next element in the list as specified by RFC 2782. - */ - if (m_pHops.Count > 0) - { - CleanUpActiveHop(); - SendToNextHop(); - } - /* RFC 3161 16.6.7. - If the ordered set is exhausted, the request cannot be forwarded to this element in - the target set. The proxy does not need to place anything in the response context, - but otherwise acts as if this element of the target set returned a - 408 (Request Timeout) final response. - */ - else - { - m_IsCompleted = true; - m_pOwner.ProcessResponse(this, - m_pTransaction, - m_pOwner.Proxy.Stack.CreateResponse( - SIP_ResponseCodes.x408_Request_Timeout, - m_pTransaction.Request)); - Dispose(); - } - } - } - - /// - /// This method is called when client transaction has disposed. - /// - /// Sender. - /// Event data. - private void m_pTransaction_Disposed(object sender, EventArgs e) - { - lock (m_pLock) - { - if (m_IsDisposed) - { - return; - } - - // If we have got any response from any client transaction, then all done. - if (HasReceivedResponse) - { - Dispose(); - } - } - } - - private void m_pTimerC_Elapsed(object sender, ElapsedEventArgs e) - { - lock (m_pLock) - { - /* RFC 3261 16.8 Processing Timer C. - If the client transaction has received a provisional response, the proxy - MUST generate a CANCEL request matching that transaction. If the client - transaction has not received a provisional response, the proxy MUST behave - as if the transaction received a 408 (Request Timeout) response. - */ - - if (m_pTransaction.HasProvisionalResponse) - { - m_pTransaction.Cancel(); - } - else - { - m_pOwner.ProcessResponse(this, - m_pTransaction, - m_pOwner.Proxy.Stack.CreateResponse( - SIP_ResponseCodes.x408_Request_Timeout, - m_pTransaction.Request)); - Dispose(); - } - } - } - - #endregion - - #region Utility methods - - /// - /// Initializes target. - /// - private void Init() - { - /* RFC 3261 16.6 Request Forwarding. - For each target, the proxy forwards the request following these steps: - 1. Make a copy of the received request - 2. Update the Request-URI - 3. Update the Max-Forwards header field - 4. Optionally add a Record-route header field value - 5. Optionally add additional header fields - 6. Postprocess routing information - 7. Determine the next-hop address, port, and transport - */ - - bool isStrictRoute = false; - - #region 1. Make a copy of the received request. - - // 1. Make a copy of the received request. - m_pRequest = m_pOwner.Request.Copy(); - - #endregion - - #region 2. Update the Request-URI. - - // 2. Update the Request-URI. - m_pRequest.RequestLine.Uri = m_pTargetUri; - - #endregion - - #region 3. Update the Max-Forwards header field. - - // 3. Update the Max-Forwards header field. - m_pRequest.MaxForwards--; - - #endregion - - #region 5. Optionally add additional header fields. - - // 5. Optionally add additional header fields. - // Skip. - - #endregion - - #region 6. Postprocess routing information. - - /* 6. Postprocess routing information. - - If the copy contains a Route header field, the proxy MUST inspect the URI in its first value. - If that URI does not contain an lr parameter, the proxy MUST modify the copy as follows: - - The proxy MUST place the Request-URI into the Route header - field as the last value. - - - The proxy MUST then place the first Route header field value - into the Request-URI and remove that value from the Route header field. - */ - if (m_pRequest.Route.GetAllValues().Length > 0 && - !m_pRequest.Route.GetTopMostValue().Parameters.Contains("lr")) - { - m_pRequest.Route.Add(m_pRequest.RequestLine.Uri.ToString()); - - m_pRequest.RequestLine.Uri = - SIP_Utils.UriToRequestUri(m_pRequest.Route.GetTopMostValue().Address.Uri); - m_pRequest.Route.RemoveTopMostValue(); - - isStrictRoute = true; - } - - #endregion - - #region 7. Determine the next-hop address, port, and transport. - - /* 7. Determine the next-hop address, port, and transport. - The proxy MAY have a local policy to send the request to a - specific IP address, port, and transport, independent of the - values of the Route and Request-URI. Such a policy MUST NOT be - used if the proxy is not certain that the IP address, port, and - transport correspond to a server that is a loose router. - However, this mechanism for sending the request through a - specific next hop is NOT RECOMMENDED; instead a Route header - field should be used for that purpose as described above. - - In the absence of such an overriding mechanism, the proxy - applies the procedures listed in [4] as follows to determine - where to send the request. If the proxy has reformatted the - request to send to a strict-routing element as described in - step 6 above, the proxy MUST apply those procedures to the - Request-URI of the request. Otherwise, the proxy MUST apply - the procedures to the first value in the Route header field, if - present, else the Request-URI. The procedures will produce an - ordered set of (address, port, transport) tuples. - Independently of which URI is being used as input to the - procedures of [4], if the Request-URI specifies a SIPS - resource, the proxy MUST follow the procedures of [4] as if the - input URI were a SIPS URI. - - As described in [4], the proxy MUST attempt to deliver the - message to the first tuple in that set, and proceed through the - set in order until the delivery attempt succeeds. - - For each tuple attempted, the proxy MUST format the message as - appropriate for the tuple and send the request using a new - client transaction as detailed in steps 8 through 10. - - Since each attempt uses a new client transaction, it represents - a new branch. Thus, the branch parameter provided with the Via - header field inserted in step 8 MUST be different for each - attempt. - - If the client transaction reports failure to send the request - or a timeout from its state machine, the proxy continues to the - next address in that ordered set. If the ordered set is - exhausted, the request cannot be forwarded to this element in - the target set. The proxy does not need to place anything in - the response context, but otherwise acts as if this element of - the target set returned a 408 (Request Timeout) final response. - */ - SIP_Uri uri = null; - if (isStrictRoute) - { - uri = (SIP_Uri) m_pRequest.RequestLine.Uri; - } - else if (m_pRequest.Route.GetTopMostValue() != null) - { - uri = (SIP_Uri) m_pRequest.Route.GetTopMostValue().Address.Uri; - } - else - { - uri = (SIP_Uri) m_pRequest.RequestLine.Uri; - } - - // Queue hops. - foreach ( - SIP_Hop hop in - m_pOwner.Proxy.Stack.GetHops(uri, - m_pRequest.ToByteData().Length, - ((SIP_Uri) m_pRequest.RequestLine.Uri).IsSecure)) - { - m_pHops.Enqueue(hop); - } - - #endregion - - #region 4. Optionally add a Record-route header field value. - - // We need to do step 4 after step 7, because then transport in known. - // Each transport can have own host-name, otherwise we don't know what to put into Record-Route. - - /* - If the Request-URI contains a SIPS URI, or the topmost Route header - field value (after the post processing of bullet 6) contains a SIPS URI, - the URI placed into the Record-Route header field MUST be a SIPS URI. - Furthermore, if the request was not received over TLS, the proxy MUST - insert a Record-Route header field. In a similar fashion, a proxy that - receives a request over TLS, but generates a request without a SIPS URI - in the Request-URI or topmost Route header field value (after the post - processing of bullet 6), MUST insert a Record-Route header field that - is not a SIPS URI. - */ - - // NOTE: ACK don't add Record-route header. - if (m_pHops.Count > 0 && m_AddRecordRoute && m_pRequest.RequestLine.Method != SIP_Methods.ACK) - { - string recordRoute = - m_pOwner.Proxy.Stack.TransportLayer.GetRecordRoute(m_pHops.Peek().Transport); - if (recordRoute != null) - { - m_pRequest.RecordRoute.Add(recordRoute); - } - } - - #endregion - } - - /// - /// Starts sending request to next hop in queue. - /// - /// Is raised when no next hop available(m_pHops.Count == 0) and this method is accessed. - private void SendToNextHop() - { - if (m_pHops.Count == 0) - { - throw new InvalidOperationException("No more hop(s)."); - } - - SIP_Hop hop = m_pHops.Dequeue(); - - SendToFlow( - m_pOwner.Proxy.Stack.TransportLayer.GetOrCreateFlow(hop.Transport, null, hop.EndPoint), - m_pRequest.Copy()); - } - - /// - /// Sends specified request to the specified data flow. - /// - /// SIP data flow. - /// SIP request to send. - /// Is raised when flow or request is null reference. - private void SendToFlow(SIP_Flow flow, SIP_Request request) - { - if (flow == null) - { - throw new ArgumentNullException("flow"); - } - if (request == null) - { - throw new ArgumentNullException("request"); - } - - /* NAT traversal. - When we do record routing, store request sender flow info and request target flow info. - Now the tricky part, how proxy later which flow is target (because both sides can send requests). - Sender-flow will store from-tag to flow and target-flow will store flowID only (Because we don't know to-tag). - Later if request to-tag matches(incoming request), use that flow, otherwise(outgoing request) other flow. - - flowInfo: sender-flow "/" target-flow - sender-flow = from-tag ":" flowID - target-flow = flowID - */ - if (m_AddRecordRoute && request.From.Tag != null) - { - string flowInfo = request.From.Tag + ":" + m_pOwner.ServerTransaction.Flow.ID + "/" + - flow.ID; - ((SIP_Uri) request.RecordRoute.GetTopMostValue().Address.Uri).Parameters.Add("flowInfo", - flowInfo); - } - - /* RFC 3261 16.6 Request Forwarding. - Common Steps 1 - 7 are done in target Init(). - - 8. Add a Via header field value - 9. Add a Content-Length header field if necessary - 10. Forward the new request - 11. Set timer C - */ - - #region 8. Add a Via header field value - - // Skip, Client transaction will add it. - - #endregion - - #region 9. Add a Content-Length header field if necessary - - // Skip, our SIP_Message class is smart and do it when ever it's needed. - - #endregion - - #region 10. Forward the new request - - m_pTransaction = m_pOwner.Proxy.Stack.TransactionLayer.CreateClientTransaction(flow, - request, - true); - m_pTransaction.ResponseReceived += ClientTransaction_ResponseReceived; - m_pTransaction.TimedOut += ClientTransaction_TimedOut; - m_pTransaction.TransportError += ClientTransaction_TransportError; - m_pTransaction.Disposed += m_pTransaction_Disposed; - - // Start transaction processing. - m_pTransaction.Start(); - - #endregion - - #region 11. Set timer C - - /* 11. Set timer C - In order to handle the case where an INVITE request never - generates a final response, the TU uses a timer which is called - timer C. Timer C MUST be set for each client transaction when - an INVITE request is proxied. The timer MUST be larger than 3 - minutes. Section 16.7 bullet 2 discusses how this timer is - updated with provisional responses, and Section 16.8 discusses - processing when it fires. - */ - if (request.RequestLine.Method == SIP_Methods.INVITE) - { - m_pTimerC = new TimerEx(); - m_pTimerC.AutoReset = false; - m_pTimerC.Interval = 3*60*1000; - m_pTimerC.Elapsed += m_pTimerC_Elapsed; - } - - #endregion - } - - /// - /// Cleans up acitve hop resources. - /// - private void CleanUpActiveHop() - { - if (m_pTimerC != null) - { - m_pTimerC.Dispose(); - m_pTimerC = null; - } - if (m_pTransaction != null) - { - m_pTransaction.Dispose(); - m_pTransaction = null; - } - } - - #endregion - } - - #endregion - - #region Members - - private readonly bool m_AddRecordRoute; - private readonly DateTime m_CreateTime; - private readonly SIP_ForkingMode m_ForkingMode = SIP_ForkingMode.Parallel; - private readonly string m_ID = ""; - private readonly bool m_IsB2BUA = true; - private readonly bool m_NoCancel; - private readonly bool m_NoRecurse = true; - private readonly List m_pCredentials; - private readonly object m_pLock = new object(); - private readonly SIP_Request m_pRequest; - private bool m_IsDisposed; - private bool m_IsFinalResponseSent; - private bool m_IsStarted; - private SIP_ProxyCore m_pProxy; - private List m_pResponses; - private SIP_ServerTransaction m_pServerTransaction; - private Queue m_pTargets; - private List m_pTargetsHandlers; - - #endregion - - #region Properties - - /// - /// Gets if this object is disposed. - /// - public bool IsDisposed - { - get { return m_IsDisposed; } - } - - /// - /// Gets owner SIP proxy server. - /// - /// Is raised when this object is disposed and and this property is accessed. - public SIP_ProxyCore Proxy - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pProxy; - } - } - - /// - /// Gets proxy context ID. - /// - /// Is raised when this object is disposed and and this property is accessed. - public string ID - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SIP_ProxyContext"); - } - - return m_ID; - } - } - - /// - /// Gets time when proxy context was created. - /// - /// Is raised when this object is disposed and and this property is accessed. - public DateTime CreateTime - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SIP_ProxyContext"); - } - - return m_CreateTime; - } - } - - /// - /// Gets forking mode used by this 'proxy context'. - /// - /// Is raised when this object is disposed and and this property is accessed. - public SIP_ForkingMode ForkingMode - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SIP_ProxyContext"); - } - - return m_ForkingMode; - } - } - - /// - /// Gets if proxy cancels forked requests what are not needed any more. If true, - /// requests not canceled, otherwise canceled. - /// - /// Is raised when this object is disposed and and this property is accessed. - public bool NoCancel - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SIP_ProxyContext"); - } - - return m_NoCancel; - } - } - - /// - /// Gets what proxy server does when it gets 3xx response. If true proxy will forward - /// request to new specified address if false, proxy will return 3xx response to caller. - /// - /// Is raised when this object is disposed and and this property is accessed. - public bool Recurse - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SIP_ProxyContext"); - } - - return !m_NoRecurse; - } - } - - /// - /// Gets server transaction what is responsible for sending responses to caller. - /// - /// Is raised when this class is Disposed and this property is accessed. - public SIP_ServerTransaction ServerTransaction - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SIP_ProxyContext"); - } - - return m_pServerTransaction; - } - } - - /// - /// Gets request what is forwarded by proxy context. - /// - /// Is raised when this object is disposed and and this property is accessed. - public SIP_Request Request - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SIP_ProxyContext"); - } - - return m_pRequest; - } - } - - /* - /// - /// Gets active client transactions that will handle forward request. - /// There may be more than 1 active client transaction if parallel forking. - /// - /// Is raised when this class is Disposed and this property is accessed. - public SIP_ClientTransaction[] ClientTransactions - { - get{ - if(m_IsDisposed){ - throw new ObjectDisposedException("SIP_ProxyContext"); - } - - return m_pClientTransactions.ToArray(); - } - } */ - - /// - /// Gets all responses what proxy context has received. - /// - /// Is raised when this class is Disposed and this property is accessed. - public SIP_Response[] Responses - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("SIP_ProxyContext"); - } - - return m_pResponses.ToArray(); - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner proxy. - /// Server transaction what is used to send SIP responses back to caller. - /// Request to forward. - /// If true, Record-Route header field will be added. - /// Specifies how proxy context must handle forking. - /// Specifies if proxy context is in B2BUA or just transaction satefull mode. - /// Specifies if proxy should not send Cancel to forked requests. - /// Specifies what proxy server does when it gets 3xx response. If true proxy will forward - /// request to new specified address if false, proxy will return 3xx response to caller. - /// Possible remote targets. NOTE: These values must be in priority order ! - /// Target set credentials. - /// Is raised when any of the reference type prameters is null. - /// Is raised when any of the arguments has invalid value. - internal SIP_ProxyContext(SIP_ProxyCore proxy, - SIP_ServerTransaction transaction, - SIP_Request request, - bool addRecordRoute, - SIP_ForkingMode forkingMode, - bool isB2BUA, - bool noCancel, - bool noRecurse, - SIP_ProxyTarget[] targets, - NetworkCredential[] credentials) - { - if (proxy == null) - { - throw new ArgumentNullException("proxy"); - } - if (transaction == null) - { - throw new ArgumentNullException("transaction"); - } - if (request == null) - { - throw new ArgumentNullException("request"); - } - if (targets == null) - { - throw new ArgumentNullException("targets"); - } - if (targets.Length == 0) - { - throw new ArgumentException("Argumnet 'targets' must contain at least 1 value."); - } - - m_pProxy = proxy; - - m_pServerTransaction = transaction; - m_pServerTransaction.Canceled += m_pServerTransaction_Canceled; - m_pServerTransaction.Disposed += m_pServerTransaction_Disposed; - - m_pRequest = request; - m_AddRecordRoute = addRecordRoute; - m_ForkingMode = forkingMode; - m_IsB2BUA = isB2BUA; - m_NoCancel = noCancel; - m_NoRecurse = noRecurse; - - m_pTargetsHandlers = new List(); - m_pResponses = new List(); - m_ID = Guid.NewGuid().ToString(); - m_CreateTime = DateTime.Now; - - // Queue targets up, higest to lowest. - m_pTargets = new Queue(); - foreach (SIP_ProxyTarget target in targets) - { - m_pTargets.Enqueue(new TargetHandler(this, - target.Flow, - target.TargetUri, - m_AddRecordRoute, - false)); - } - - m_pCredentials = new List(); - m_pCredentials.AddRange(credentials); - - /* RFC 3841 9.1. - The Request-Disposition header field specifies caller preferences for - how a server should process a request. - - Override SIP proxy default value. - */ - foreach (SIP_t_Directive directive in request.RequestDisposition.GetAllValues()) - { - if (directive.Directive == SIP_t_Directive.DirectiveType.NoFork) - { - m_ForkingMode = SIP_ForkingMode.None; - } - else if (directive.Directive == SIP_t_Directive.DirectiveType.Parallel) - { - m_ForkingMode = SIP_ForkingMode.Parallel; - } - else if (directive.Directive == SIP_t_Directive.DirectiveType.Sequential) - { - m_ForkingMode = SIP_ForkingMode.Sequential; - } - else if (directive.Directive == SIP_t_Directive.DirectiveType.NoCancel) - { - m_NoCancel = true; - } - else if (directive.Directive == SIP_t_Directive.DirectiveType.NoRecurse) - { - m_NoRecurse = true; - } - } - - m_pProxy.Stack.Logger.AddText("ProxyContext(id='" + m_ID + "') created."); - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public void Dispose() - { - lock (m_pLock) - { - if (m_IsDisposed) - { - return; - } - m_IsDisposed = true; - - m_pProxy.Stack.Logger.AddText("ProxyContext(id='" + m_ID + "') disposed."); - - m_pProxy.m_pProxyContexts.Remove(this); - - m_pProxy = null; - m_pServerTransaction = null; - m_pTargetsHandlers = null; - m_pResponses = null; - m_pTargets = null; - } - } - - /// - /// Starts processing. - /// - /// Is raised when this object is disposed and and this method is accessed. - /// Is raised when this method is called more than once. - public void Start() - { - lock (m_pLock) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (m_IsStarted) - { - throw new InvalidOperationException("Start has already called."); - } - m_IsStarted = true; - - // Only use destination with the highest q value. - // We already have ordered highest to lowest, so just get first destination. - if (m_ForkingMode == SIP_ForkingMode.None) - { - TargetHandler handler = m_pTargets.Dequeue(); - m_pTargetsHandlers.Add(handler); - handler.Start(); - } - // Use all destinations at same time. - else if (m_ForkingMode == SIP_ForkingMode.Parallel) - { - // NOTE: If target count == 1 and transport exception, target may Dispose proxy context., so we need to handle it. - while (!m_IsDisposed && m_pTargets.Count > 0) - { - TargetHandler handler = m_pTargets.Dequeue(); - m_pTargetsHandlers.Add(handler); - handler.Start(); - } - } - // Start processing destinations with highest q value to lowest. - else if (m_ForkingMode == SIP_ForkingMode.Sequential) - { - TargetHandler handler = m_pTargets.Dequeue(); - m_pTargetsHandlers.Add(handler); - handler.Start(); - } - } - } - - /// - /// Cancels proxy context processing. All client transactions and owner server transaction will be canceled, - /// proxy context will be disposed. - /// - /// Is raised when this object is disposed and and this method is accessed. - /// Is raised when Start method is not called and this method is accessed. - public void Cancel() - { - lock (m_pLock) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!m_IsStarted) - { - throw new InvalidOperationException("Start method is not called, nothing to cancel."); - } - - m_pServerTransaction.Cancel(); - - // Server transaction raised Cancled event, we dispose active targets there. - } - } - - #endregion - - #region Event handlers - - /// - /// Is called when server transaction has canceled. - /// - /// Sender. - /// Event data. - private void m_pServerTransaction_Canceled(object sender, EventArgs e) - { - lock (m_pLock) - { - CancelAllTargets(); - - // We dont need to Dispose proxy context, server transaction will call Terminated event - // after cancel, there we dispose it. - } - } - - /// - /// This method is called when server transaction has disposed. - /// - /// Sender. - /// Event data. - private void m_pServerTransaction_Disposed(object sender, EventArgs e) - { - // All done, just dispose proxy context. - Dispose(); - } - - #endregion - - #region Utility methods - - /// - /// This method is called when specified target handler has disposed. - /// - /// TargetHandler what disposed. - private void TargetHandler_Disposed(TargetHandler handler) - { - lock (m_pLock) - { - m_pTargetsHandlers.Remove(handler); - - // All targets are processed. - if (m_pTargets.Count == 0 && m_pTargetsHandlers.Count == 0) - { - Dispose(); - } - } - } - - /// - /// Processes received response. - /// - /// Target handler what received response. - /// Client transaction what response it is. - /// Response received. - /// Is raised when handler,transaction or response is null reference. - private void ProcessResponse(TargetHandler handler, - SIP_ClientTransaction transaction, - SIP_Response response) - { - if (handler == null) - { - throw new ArgumentNullException("handler"); - } - if (transaction == null) - { - throw new ArgumentNullException("transaction"); - } - if (response == null) - { - throw new ArgumentNullException("response"); - } - - /* RFC 3261 16.7 Response Processing. - Steps 1 - 2 handled in TargetHandler. - - 3. Remove the topmost Via - 4. Add the response to the response context - 5. Check to see if this response should be forwarded immediately - 6. When necessary, choose the best final response from the response context. - If no final response has been forwarded after every client - transaction associated with the response context has been terminated, - the proxy must choose and forward the "best" response from those it - has seen so far. - - The following processing MUST be performed on each response that is - forwarded. It is likely that more than one response to each request - will be forwarded: at least each provisional and one final response. - - 7. Aggregate authorization header field values if necessary - 8. Optionally rewrite Record-Route header field values - 9. Forward the response - 10. Generate any necessary CANCEL requests - */ - - bool forwardResponse = false; - - lock (m_pLock) - { - #region 3. Remove the topmost Via - - /* - The proxy removes the topmost Via header field value from the - response. - - If no Via header field values remain in the response, the - response was meant for this element and MUST NOT be forwarded. - The remainder of the processing described in this section is - not performed on this message, the UAC processing rules - described in Section 8.1.3 are followed instead (transport - layer processing has already occurred). - - This will happen, for instance, when the element generates - CANCEL requests as described in Section 10. - - NOTE: We MAY NOT do it for B2BUA, skip it for B2BUA - */ - if (!m_IsB2BUA) - { - response.Via.RemoveTopMostValue(); - if (response.Via.GetAllValues().Length == 0) - { - return; - } - } - - #endregion - - #region 4. Add the response to the response context - - /* - Final responses received are stored in the response context - until a final response is generated on the server transaction - associated with this context. The response may be a candidate - for the best final response to be returned on that server - transaction. Information from this response may be needed in - forming the best response, even if this response is not chosen. - - If the proxy chooses to recurse on any contacts in a 3xx - response by adding them to the target set, it MUST remove them - from the response before adding the response to the response - context. However, a proxy SHOULD NOT recurse to a non-SIPS URI - if the Request-URI of the original request was a SIPS URI. If - the proxy recurses on all of the contacts in a 3xx response, - the proxy SHOULD NOT add the resulting contactless response to - the response context. - - Removing the contact before adding the response to the response - context prevents the next element upstream from retrying a - location this proxy has already attempted. - - 3xx responses may contain a mixture of SIP, SIPS, and non-SIP - URIs. A proxy may choose to recurse on the SIP and SIPS URIs - and place the remainder into the response context to be - returned, potentially in the final response. - */ - - if (response.StatusCodeType == SIP_StatusCodeType.Redirection && !m_NoRecurse && - !handler.IsRecursed) - { - // Get SIP contacts and remove them from response. - SIP_t_ContactParam[] contacts = response.Contact.GetAllValues(); - // Remove all contacts from response, we add non-SIP URIs back. - response.Contact.RemoveAll(); - foreach (SIP_t_ContactParam contact in contacts) - { - // SIP URI add it to fork list. - if (contact.Address.IsSipOrSipsUri) - { - m_pTargets.Enqueue(new TargetHandler(this, - null, - (SIP_Uri) contact.Address.Uri, - m_AddRecordRoute, - true)); - } - // Add specified URI back to response. - else - { - response.Contact.Add(contact.ToStringValue()); - } - } - - // There are remaining non-SIP contacts, so we need to add the response to reponses collection. - if (response.Contact.GetAllValues().Length > 0) - { - m_pResponses.Add(response); - } - - // Handle forking - if (m_pTargets.Count > 0) - { - if (m_ForkingMode == SIP_ForkingMode.Parallel) - { - while (m_pTargets.Count > 0) - { - TargetHandler h = m_pTargets.Dequeue(); - m_pTargetsHandlers.Add(handler); - h.Start(); - } - } - // Just fork next. - else - { - TargetHandler h = m_pTargets.Dequeue(); - m_pTargetsHandlers.Add(handler); - h.Start(); - } - - // Because we forked request to new target(s), we don't need to do steps 5 - 10. - return; - } - } - // Not 3xx response or recursing disabled. - else - { - m_pResponses.Add(response); - } - - #endregion - - #region 5. Check to see if this response should be forwarded immediately - - /* - Until a final response has been sent on the server transaction, - the following responses MUST be forwarded immediately: - - - Any provisional response other than 100 (Trying) - - - Any 2xx response - - If a 6xx response is received, it is not immediately forwarded, - but the stateful proxy SHOULD cancel all client pending - transactions as described in Section 10, and it MUST NOT create - any new branches in this context. - - After a final response has been sent on the server transaction, - the following responses MUST be forwarded immediately: - - - Any 2xx response to an INVITE request - */ - - if (!m_IsFinalResponseSent) - { - if (response.StatusCodeType == SIP_StatusCodeType.Provisional && - response.StatusCode != 100) - { - forwardResponse = true; - } - else if (response.StatusCodeType == SIP_StatusCodeType.Success) - { - forwardResponse = true; - } - else if (response.StatusCodeType == SIP_StatusCodeType.GlobalFailure) - { - CancelAllTargets(); - } - } - else - { - if (response.StatusCodeType == SIP_StatusCodeType.Success && - m_pServerTransaction.Request.RequestLine.Method == SIP_Methods.INVITE) - { - forwardResponse = true; - } - } - - #endregion - - #region x. Handle sequential forking - - /* - Sequential Search: In a sequential search, a proxy server attempts - each contact address in sequence, proceeding to the next one - only after the previous has generated a final response. A 2xx - or 6xx class final response always terminates a sequential - search. - */ - if (m_ForkingMode == SIP_ForkingMode.Sequential && - response.StatusCodeType != SIP_StatusCodeType.Provisional) - { - if (response.StatusCodeType == SIP_StatusCodeType.Success) - { - // Do nothing, 2xx will be always forwarded and step 10. Cancels all targets. - } - else if (response.StatusCodeType == SIP_StatusCodeType.GlobalFailure) - { - // Do nothing, 6xx is already handled in setp 5. - } - else if (m_pTargets.Count > 0) - { - TargetHandler h = m_pTargets.Dequeue(); - m_pTargetsHandlers.Add(handler); - h.Start(); - - // Skip all next steps, we will get new responses from new target. - return; - } - } - - #endregion - - #region 6. When necessary, choose the best final response from the response context - - /* - A stateful proxy MUST send a final response to a response - context's server transaction if no final responses have been - immediately forwarded by the above rules and all client - transactions in this response context have been terminated. - - The stateful proxy MUST choose the "best" final response among - those received and stored in the response context. - - If there are no final responses in the context, the proxy MUST - send a 408 (Request Timeout) response to the server - transaction. - - */ - - if (!m_IsFinalResponseSent && !forwardResponse && m_pTargets.Count == 0) - { - bool mustChooseBestFinalResponse = true; - // Check if all transactions terminated. - foreach (TargetHandler h in m_pTargetsHandlers) - { - if (!h.IsCompleted) - { - mustChooseBestFinalResponse = false; - break; - } - } - - if (mustChooseBestFinalResponse) - { - response = GetBestFinalResponse(); - if (response == null) - { - response = Proxy.Stack.CreateResponse(SIP_ResponseCodes.x408_Request_Timeout, - m_pServerTransaction.Request); - } - - forwardResponse = true; - } - } - - #endregion - - if (forwardResponse) - { - #region 7. Aggregate authorization header field values if necessary - - /* - If the selected response is a 401 (Unauthorized) or 407 (Proxy Authentication Required), - the proxy MUST collect any WWW-Authenticate and Proxy-Authenticate header field values - from all other 401 (Unauthorized) and 407 (Proxy Authentication Required) responses - received so far in this response context and add them to this response without - modification before forwarding. The resulting 401 (Unauthorized) or 407 (Proxy - Authentication Required) response could have several WWW-Authenticate AND - Proxy-Authenticate header field values. - - This is necessary because any or all of the destinations the request was forwarded to - may have requested credentials. The client needs to receive all of those challenges and - supply credentials for each of them when it retries the request. - */ - if (response.StatusCode == 401 || response.StatusCode == 407) - { - foreach (SIP_Response resp in m_pResponses.ToArray()) - { - if (response != resp && (resp.StatusCode == 401 || resp.StatusCode == 407)) - { - // WWW-Authenticate - foreach (SIP_HeaderField hf in resp.WWWAuthenticate.HeaderFields) - { - resp.WWWAuthenticate.Add(hf.Value); - } - // Proxy-Authenticate - foreach (SIP_HeaderField hf in resp.ProxyAuthenticate.HeaderFields) - { - resp.ProxyAuthenticate.Add(hf.Value); - } - } - } - } - - #endregion - - #region 8. Optionally rewrite Record-Route header field values - - // This is optional so we currently won't do that. - - #endregion - - #region 9. Forward the response - - SendResponse(transaction, response); - if (response.StatusCodeType != SIP_StatusCodeType.Provisional) - { - m_IsFinalResponseSent = true; - } - - #endregion - - #region 10. Generate any necessary CANCEL requests - - /* - If the forwarded response was a final response, the proxy MUST - generate a CANCEL request for all pending client transactions - associated with this response context. - */ - if (response.StatusCodeType != SIP_StatusCodeType.Provisional) - { - CancelAllTargets(); - } - - #endregion - } - } - } - - /// - /// Sends SIP response to caller. If proxy context is in B2BUA mode, new response is generated - /// as needed. - /// - /// Client transaction what response it is. - /// Response to send. - private void SendResponse(SIP_ClientTransaction transaction, SIP_Response response) - { - if (m_IsB2BUA) - { - /* draft-marjou-sipping-b2bua-00 4.1.3. - When the UAC side of the B2BUA receives the downstream SIP response - of a forwarded request, its associated UAS creates an upstream - response (except for 100 responses). The creation of the Via, Max- - Forwards, Call-Id, CSeq, Record-Route and Contact header fields - follows the rules of [2]. The Record-Route header fields of the - downstream response are not copied in the new upstream response, as - Record-Route is meaningful for the downstream dialog. The UAS SHOULD - copy other header fields and body from the downstream response into - this upstream response before sending it. - */ - - SIP_Request originalRequest = m_pServerTransaction.Request; - - // We need to use caller original request to construct response from proxied response. - SIP_Response b2buaResponse = response.Copy(); - b2buaResponse.Via.RemoveAll(); - b2buaResponse.Via.AddToTop(originalRequest.Via.GetTopMostValue().ToStringValue()); - b2buaResponse.CallID = originalRequest.CallID; - b2buaResponse.CSeq = originalRequest.CSeq; - b2buaResponse.Contact.RemoveAll(); - //b2buaResponse.Contact.Add(m_pProxy.CreateContact(originalRequest.From.Address).ToStringValue()); - b2buaResponse.RecordRoute.RemoveAll(); - - b2buaResponse.Allow.RemoveAll(); - b2buaResponse.Supported.RemoveAll(); - // Accept to non ACK,BYE request. - if (originalRequest.RequestLine.Method != SIP_Methods.ACK && - originalRequest.RequestLine.Method != SIP_Methods.BYE) - { - b2buaResponse.Allow.Add("INVITE,ACK,OPTIONS,CANCEL,BYE,PRACK"); - } - // Supported to non ACK request. - if (originalRequest.RequestLine.Method != SIP_Methods.ACK) - { - b2buaResponse.Supported.Add("100rel,timer"); - } - // Remove Require: header. - b2buaResponse.Require.RemoveAll(); - - m_pServerTransaction.SendResponse(b2buaResponse); - - // If INVITE 2xx response do call here. - if (response.CSeq.RequestMethod.ToUpper() == SIP_Methods.INVITE && - response.StatusCodeType == SIP_StatusCodeType.Success) - { - m_pProxy.B2BUA.AddCall(m_pServerTransaction.Dialog, transaction.Dialog); - } - } - else - { - m_pServerTransaction.SendResponse(response); - } - } - - /// - /// Cancels all targets processing. - /// - private void CancelAllTargets() - { - if (!m_NoCancel) - { - m_pTargets.Clear(); - - foreach (TargetHandler target in m_pTargetsHandlers.ToArray()) - { - target.Cancel(); - } - } - } - - /// - /// Gets best final response. If no final response in responses collection, null is returned. - /// - /// Resturns best final response or null if no final response. - private SIP_Response GetBestFinalResponse() - { - // 6xx -> 2xx -> 3xx -> 4xx -> 5xx - - // 6xx - foreach (SIP_Response resp in m_pResponses.ToArray()) - { - if (resp.StatusCodeType == SIP_StatusCodeType.GlobalFailure) - { - return resp; - } - } - // 2xx - foreach (SIP_Response resp in m_pResponses.ToArray()) - { - if (resp.StatusCodeType == SIP_StatusCodeType.Success) - { - return resp; - } - } - // 3xx - foreach (SIP_Response resp in m_pResponses.ToArray()) - { - if (resp.StatusCodeType == SIP_StatusCodeType.Redirection) - { - return resp; - } - } - // 4xx - foreach (SIP_Response resp in m_pResponses.ToArray()) - { - if (resp.StatusCodeType == SIP_StatusCodeType.RequestFailure) - { - return resp; - } - } - // 5xx - foreach (SIP_Response resp in m_pResponses.ToArray()) - { - if (resp.StatusCodeType == SIP_StatusCodeType.ServerFailure) - { - return resp; - } - } - - return null; - } - - /// - /// Gets credentials for specified realm. Returns null if none such credentials. - /// - /// Realm which credentials to get. - /// Returns specified realm credentials or null in none. - /// Is raised when realm is null reference. - private NetworkCredential GetCredential(string realm) - { - if (realm == null) - { - throw new ArgumentNullException("realm"); - } - - foreach (NetworkCredential c in m_pCredentials) - { - if (c.Domain.ToLower() == realm.ToLower()) - { - return c; - } - } - return null; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_ProxyCore.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_ProxyCore.cs deleted file mode 100644 index 8152d490c..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_ProxyCore.cs +++ /dev/null @@ -1,1213 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Proxy -{ - #region usings - - using System; - using System.Collections.Generic; - using System.Net; - using AUTH; - using Message; - using Stack; - - #endregion - - #region Delegates - - /// - /// Represents the method that will handle the SIP_ProxyCore.IsLocalUri event. - /// - /// Request URI. - /// Returns true if server local URI, otherwise false. - public delegate bool SIP_IsLocalUriEventHandler(string uri); - - /// - /// Represents the method that will handle the SIP_ProxyCore.Authenticate event. - /// - public delegate void SIP_AuthenticateEventHandler(SIP_AuthenticateEventArgs e); - - /// - /// Represents the method that will handle the SIP_ProxyCore.AddressExists event. - /// - /// SIP address to check. - /// Returns true if specified address exists, otherwise false. - public delegate bool SIP_AddressExistsEventHandler(string address); - - /// - /// Represents the method that will handle the SIP_ProxyCore.GetGateways event. - /// - /// Event data. - public delegate void SIP_GetGatewaysEventHandler(SIP_GatewayEventArgs e); - - #endregion - - /// - /// Implements SIP registrar,statefull and stateless proxy. - /// - public class SIP_ProxyCore : IDisposable - { - #region Events - - /// - /// This event is raised when SIP proxy needs to know if specified local server address exists. - /// - public event SIP_AddressExistsEventHandler AddressExists = null; - - /// - /// This event is raised when SIP proxy or registrar server needs to authenticate user. - /// - public event SIP_AuthenticateEventHandler Authenticate = null; - - /// - /// This event is raised when SIP proxy needs to get gateways for non-SIP URI. - /// - public event SIP_GetGatewaysEventHandler GetGateways = null; - - /// - /// This event is raised when SIP proxy needs to know if specified request URI is local URI or remote URI. - /// - public event SIP_IsLocalUriEventHandler IsLocalUri = null; - - #endregion - - #region Members - - private readonly string m_Opaque = ""; - - private SIP_ForkingMode m_ForkingMode = SIP_ForkingMode.Parallel; - private bool m_IsDisposed; - private SIP_B2BUA m_pB2BUA; - internal List m_pProxyContexts; - private SIP_Registrar m_pRegistrar; - private SIP_ProxyMode m_ProxyMode = SIP_ProxyMode.Registrar | SIP_ProxyMode.Statefull; - private SIP_Stack m_pStack; - - #endregion - - #region Properties - - /// - /// Gets if this object is disposed. - /// - public bool IsDisposed - { - get { return m_IsDisposed; } - } - - /// - /// Gets owner SIP stack. - /// - /// Is raised when this object is disposed and and this property is accessed. - public SIP_Stack Stack - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pStack; - } - } - - /// - /// Gets or sets proxy mode. - /// - /// Is raised when this object is disposed and and this property is accessed. - /// Is raised when invalid combination modes passed. - public SIP_ProxyMode ProxyMode - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_ProxyMode; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - // Check for invalid mode () - if ((value & SIP_ProxyMode.Statefull) != 0 && (value & SIP_ProxyMode.Stateless) != 0) - { - throw new ArgumentException("Proxy can't be at Statefull and Stateless at same time !"); - } - - m_ProxyMode = value; - } - } - - /// - /// Gets or sets how proxy handle forking. This property applies for statefull proxy only. - /// - /// Is raised when this object is disposed and and this property is accessed. - public SIP_ForkingMode ForkingMode - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_ForkingMode; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - m_ForkingMode = value; - } - } - - /// - /// Gets SIP registrar server. - /// - /// Is raised when this object is disposed and and this property is accessed. - public SIP_Registrar Registrar - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pRegistrar; - } - } - - /// - /// Gets SIP B2BUA server. - /// - /// Is raised when this object is disposed and and this property is accessed. - public SIP_B2BUA B2BUA - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pB2BUA; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Reference to SIP stack. - /// Is raised when sipStack is null. - public SIP_ProxyCore(SIP_Stack stack) - { - if (stack == null) - { - throw new ArgumentNullException("stack"); - } - - m_pStack = stack; - m_pStack.RequestReceived += m_pStack_RequestReceived; - m_pStack.ResponseReceived += m_pStack_ResponseReceived; - - m_pRegistrar = new SIP_Registrar(this); - m_pB2BUA = new SIP_B2BUA(this); - m_Opaque = Auth_HttpDigest.CreateOpaque(); - m_pProxyContexts = new List(); - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public void Dispose() - { - if (m_IsDisposed) - { - return; - } - m_IsDisposed = true; - - if (m_pStack != null) - { - m_pStack.Dispose(); - m_pStack = null; - } - m_pRegistrar = null; - m_pB2BUA = null; - m_pProxyContexts = null; - } - - #endregion - - #region Event handlers - - /// - /// This method is called when SIP stack receives new request. - /// - /// Sender. - /// Event data. - private void m_pStack_RequestReceived(object sender, SIP_RequestReceivedEventArgs e) - { - OnRequestReceived(e); - } - - /// - /// This method is called when SIP stack receives new response. - /// - /// Sender. - /// Event data. - private void m_pStack_ResponseReceived(object sender, SIP_ResponseReceivedEventArgs e) - { - OnResponseReceived(e); - } - - #endregion - - #region Utility methods - - /// - /// This method is called when new request is received. - /// - /// Request event arguments. - private void OnRequestReceived(SIP_RequestReceivedEventArgs e) - { - /* RFC 3261 16.12. ????????? Forward does all thse steps. - 1. The proxy will inspect the Request-URI. If it indicates a - resource owned by this proxy, the proxy will replace it with - the results of running a location service. Otherwise, the - proxy will not change the Request-URI. - - 2. The proxy will inspect the URI in the topmost Route header - field value. If it indicates this proxy, the proxy removes it - from the Route header field (this route node has been reached). - - 3. The proxy will forward the request to the resource indicated - by the URI in the topmost Route header field value or in the - Request-URI if no Route header field is present. The proxy - determines the address, port and transport to use when - forwarding the request by applying the procedures in [4] to that URI. - */ - - SIP_Request request = e.Request; - try - { - #region Registrar - - // Registrar - if ((m_ProxyMode & SIP_ProxyMode.Registrar) != 0 && - request.RequestLine.Method == SIP_Methods.REGISTER) - { - m_pRegistrar.Register(e); - } - - #endregion - - #region Presence - /* - // Presence - else if((m_ProxyMode & SIP_ProxyMode.Presence) != 0 && (request.Method == "SUBSCRIBE" || request.Method == "NOTIFY")){ - - } -*/ - #endregion - - #region Statefull - - // Statefull - else if ((m_ProxyMode & SIP_ProxyMode.Statefull) != 0) - { - // Statefull proxy is transaction statefull proxy only, - // what don't create dialogs and keep dialog state. - - /* RFC 3261 16.10. - StateFull proxy: - If a matching response context is found, the element MUST - immediately return a 200 (OK) response to the CANCEL request. - - If a response context is not found, the element does not have any - knowledge of the request to apply the CANCEL to. It MUST statelessly - forward the CANCEL request (it may have statelessly forwarded the - associated request previously). - */ - if (e.Request.RequestLine.Method == SIP_Methods.CANCEL) - { - // Don't do server transaction before we get CANCEL matching transaction. - SIP_ServerTransaction trToCancel = - m_pStack.TransactionLayer.MatchCancelToTransaction(e.Request); - if (trToCancel != null) - { - trToCancel.Cancel(); - e.ServerTransaction.SendResponse(m_pStack.CreateResponse( - SIP_ResponseCodes.x200_Ok, request)); - } - else - { - ForwardRequest(false, e); - } - } - // ACK never creates transaction, it's always passed directly to transport layer. - else if (e.Request.RequestLine.Method == SIP_Methods.ACK) - { - ForwardRequest(false, e); - } - else - { - ForwardRequest(true, e); - } - } - - #endregion - - #region B2BUA - - // B2BUA - else if ((m_ProxyMode & SIP_ProxyMode.B2BUA) != 0) - { - m_pB2BUA.OnRequestReceived(e); - } - - #endregion - - #region Stateless - - // Stateless - else if ((m_ProxyMode & SIP_ProxyMode.Stateless) != 0) - { - // Stateless proxy don't do transaction, just forwards all. - ForwardRequest(false, e); - } - - #endregion - - #region Proxy won't accept command - - else - { - e.ServerTransaction.SendResponse( - m_pStack.CreateResponse(SIP_ResponseCodes.x501_Not_Implemented, request)); - } - - #endregion - } - catch (Exception x) - { - try - { - m_pStack.TransportLayer.SendResponse( - m_pStack.CreateResponse( - SIP_ResponseCodes.x500_Server_Internal_Error + ": " + x.Message, e.Request)); - } - catch - { - // Skip transport layer exception if send fails. - } - - // Don't raise OnError for transport errors. - if (!(x is SIP_TransportException)) - { - m_pStack.OnError(x); - } - } - } - - /// - /// This method is called when new response is received. - /// - /// Response event arguments. - private void OnResponseReceived(SIP_ResponseReceivedEventArgs e) - { - if ((m_ProxyMode & SIP_ProxyMode.B2BUA) != 0) - { - m_pB2BUA.OnResponseReceived(e); - } - else - { - /* This method is called when stateless proxy gets response or statefull proxy - has no matching server transaction. - */ - - /* RFC 3261 16.11. - When a response arrives at a stateless proxy, the proxy MUST inspect the sent-by - value in the first (topmost) Via header field value. If that address matches the proxy, - (it equals a value this proxy has inserted into previous requests) the proxy MUST - remove that header field value from the response and forward the result to the - location indicated in the next Via header field value. - */ - // Just remove topmost Via:, sent-by check is done in transport layer. - e.Response.Via.RemoveTopMostValue(); - - if ((m_ProxyMode & SIP_ProxyMode.Statefull) != 0) - { - // We should not reach here. This happens when no matching client transaction found. - // RFC 3161 18.1.2 orders to forward them statelessly. - m_pStack.TransportLayer.SendResponse(e.Response); - } - else if ((m_ProxyMode & SIP_ProxyMode.Stateless) != 0) - { - m_pStack.TransportLayer.SendResponse(e.Response); - } - } - } - - /// - /// Forwards specified request to destination recipient. - /// - /// Specifies if request is sent statefully or statelessly. - /// Request event arguments. - private void ForwardRequest(bool statefull, SIP_RequestReceivedEventArgs e) - { - ForwardRequest(statefull, e, e.Request, true); - } - - #endregion - - #region Internal methods - - /// - /// Forwards specified request to target recipient. - /// - /// Specifies if request is sent statefully or statelessly. - /// Request event arguments. - /// SIP request to forward. - /// Specifies if Record-Route header filed is added. - internal void ForwardRequest(bool statefull, - SIP_RequestReceivedEventArgs e, - SIP_Request request, - bool addRecordRoute) - { - List targetSet = new List(); - List credentials = new List(); - SIP_Uri route = null; - - /* RFC 3261 16. - 1. Validate the request (Section 16.3) - 1. Reasonable Syntax - 2. URI scheme - 3. Max-Forwards - 4. (Optional) Loop Detection - 5. Proxy-Require - 6. Proxy-Authorization - 2. Preprocess routing information (Section 16.4) - 3. Determine target(s) for the request (Section 16.5) - 4. Forward the request (Section 16.6) - */ - - #region 1. Validate the request (Section 16.3) - - // 1.1 Reasonable Syntax. - // SIP_Message will do it. - - // 1.2 URI scheme check. - if (!SIP_Utils.IsSipOrSipsUri(request.RequestLine.Uri.ToString())) - { - // TODO: - SIP_GatewayEventArgs eArgs = OnGetGateways("uriScheme", "userName"); - // No suitable gateway or authenticated user has no access. - if (eArgs.Gateways.Count == 0) - { - e.ServerTransaction.SendResponse( - m_pStack.CreateResponse(SIP_ResponseCodes.x416_Unsupported_URI_Scheme, e.Request)); - return; - } - } - - // 1.3 Max-Forwards. - if (request.MaxForwards <= 0) - { - e.ServerTransaction.SendResponse(m_pStack.CreateResponse( - SIP_ResponseCodes.x483_Too_Many_Hops, request)); - return; - } - - // 1.4 (Optional) Loop Detection. - // Skip. - - // 1.5 Proxy-Require. - // TODO: - - // 1.6 Proxy-Authorization. - // We need to auth all foreign calls. - if (!SIP_Utils.IsSipOrSipsUri(request.RequestLine.Uri.ToString()) || - !OnIsLocalUri(((SIP_Uri) request.RequestLine.Uri).Host)) - { - // We need to pass-through ACK. - if (request.RequestLine.Method == SIP_Methods.ACK) {} - else if (!AuthenticateRequest(e)) - { - return; - } - } - - #endregion - - #region 2. Preprocess routing information (Section 16.4). - - /* - The proxy MUST inspect the Request-URI of the request. If the - Request-URI of the request contains a value this proxy previously - placed into a Record-Route header field (see Section 16.6 item 4), - the proxy MUST replace the Request-URI in the request with the last - value from the Route header field, and remove that value from the - Route header field. The proxy MUST then proceed as if it received - this modified request. - - If the first value in the Route header field indicates this proxy, - the proxy MUST remove that value from the request. - */ - - // Strict route. - if (SIP_Utils.IsSipOrSipsUri(request.RequestLine.Uri.ToString()) && - IsLocalRoute(((SIP_Uri) request.RequestLine.Uri))) - { - request.RequestLine.Uri = - request.Route.GetAllValues()[request.Route.GetAllValues().Length - 1].Address.Uri; - SIP_t_AddressParam[] routes = request.Route.GetAllValues(); - route = (SIP_Uri) routes[routes.Length - 1].Address.Uri; - request.Route.RemoveLastValue(); - } - // Loose route. - else if (request.Route.GetAllValues().Length > 0 && - IsLocalRoute(SIP_Uri.Parse(request.Route.GetTopMostValue().Address.Uri.ToString()))) - { - route = (SIP_Uri) request.Route.GetTopMostValue().Address.Uri; - request.Route.RemoveTopMostValue(); - } - - #endregion - - #region 3. Determine target(s) for the request (Section 16.5) - - /* 3. Determine target(s) for the request (Section 16.5) - Next, the proxy calculates the target(s) of the request. The set of - targets will either be predetermined by the contents of the request - or will be obtained from an abstract location service. Each target - in the set is represented as a URI. - - If the domain of the Request-URI indicates a domain this element is - not responsible for, the Request-URI MUST be placed into the target - set as the only target, and the element MUST proceed to the task of - Request Forwarding (Section 16.6). - - If the target set for the request has not been predetermined as - described above, this implies that the element is responsible for the - domain in the Request-URI, and the element MAY use whatever mechanism - it desires to determine where to send the request. Any of these - mechanisms can be modeled as accessing an abstract Location Service. - This may consist of obtaining information from a location service - created by a SIP Registrar, reading a database, consulting a presence - server, utilizing other protocols, or simply performing an - algorithmic substitution on the Request-URI. When accessing the - location service constructed by a registrar, the Request-URI MUST - first be canonicalized as described in Section 10.3 before being used - as an index. The output of these mechanisms is used to construct the - target set. - */ - - // Non-SIP - // Foreign SIP - // Local SIP - - // FIX ME: we may have tel: here - SIP_Uri requestUri = (SIP_Uri) e.Request.RequestLine.Uri; - - // Proxy is not responsible for the domain in the Request-URI. - if (!OnIsLocalUri(requestUri.Host)) - { - /* NAT traversal. - When we do record routing, store request sender flow info and request target flow info. - Now the tricky part, how proxy later which flow is target (because both sides can send requests). - Sender-flow will store from-tag to flow and target-flow will store flowID only (Because we don't know to-tag). - Later if request to-tag matches(incoming request), use that flow, otherwise(outgoing request) other flow. - - flowInfo: sender-flow "/" target-flow - sender-flow = from-tag ":" flowID - target-flow = flowID - */ - - SIP_Flow targetFlow = null; - string flowInfo = (route != null && route.Parameters["flowInfo"] != null) - ? route.Parameters["flowInfo"].Value - : null; - if (flowInfo != null && request.To.Tag != null) - { - string flow1Tag = flowInfo.Substring(0, flowInfo.IndexOf(':')); - string flow1ID = flowInfo.Substring(flowInfo.IndexOf(':') + 1, - flowInfo.IndexOf('/') - flowInfo.IndexOf(':') - 1); - string flow2ID = flowInfo.Substring(flowInfo.IndexOf('/') + 1); - - if (flow1Tag == request.To.Tag) - { - targetFlow = m_pStack.TransportLayer.GetFlow(flow1ID); - } - else - { - ; - targetFlow = m_pStack.TransportLayer.GetFlow(flow2ID); - } - } - - targetSet.Add(new SIP_ProxyTarget(requestUri, targetFlow)); - } - // Proxy is responsible for the domain in the Request-URI. - else - { - // TODO: tel: - //SIP_Uri requestUri = SIP_Uri.Parse(e.Request.Uri); - - // Try to get AOR from registrar. - SIP_Registration registration = m_pRegistrar.GetRegistration(requestUri.Address); - - // We have AOR specified in request-URI in registrar server. - if (registration != null) - { - // Add all AOR SIP contacts to target set. - foreach (SIP_RegistrationBinding binding in registration.Bindings) - { - if (binding.ContactURI is SIP_Uri && binding.TTL > 0) - { - targetSet.Add(new SIP_ProxyTarget((SIP_Uri) binding.ContactURI, binding.Flow)); - } - } - } - // We don't have AOR specified in request-URI in registrar server. - else - { - // If the Request-URI indicates a resource at this proxy that does not - // exist, the proxy MUST return a 404 (Not Found) response. - if (!OnAddressExists(requestUri.Address)) - { - e.ServerTransaction.SendResponse( - m_pStack.CreateResponse(SIP_ResponseCodes.x404_Not_Found, e.Request)); - return; - } - } - } - - // If the target set remains empty after applying all of the above, the proxy MUST return an error response, - // which SHOULD be the 480 (Temporarily Unavailable) response. - if (targetSet.Count == 0) - { - e.ServerTransaction.SendResponse( - m_pStack.CreateResponse(SIP_ResponseCodes.x480_Temporarily_Unavailable, e.Request)); - return; - } - - #endregion - - #region 4. Forward the request (Section 16.6) - - #region Statefull - - if (statefull) - { - // Create proxy context that will be responsible for forwarding request. - SIP_ProxyContext proxyContext = new SIP_ProxyContext(this, - e.ServerTransaction, - request, - addRecordRoute, - m_ForkingMode, - (ProxyMode & SIP_ProxyMode.B2BUA) != 0, - false, - false, - targetSet.ToArray(), - credentials.ToArray()); - m_pProxyContexts.Add(proxyContext); - proxyContext.Start(); - } - - #endregion - - #region Stateless - - else - { - /* RFC 3261 16.6 Request Forwarding. - For each target, the proxy forwards the request following these steps: - 1. Make a copy of the received request - 2. Update the Request-URI - 3. Update the Max-Forwards header field - 4. Optionally add a Record-route header field value - 5. Optionally add additional header fields - 6. Postprocess routing information - 7. Determine the next-hop address, port, and transport - 8. Add a Via header field value - 9. Add a Content-Length header field if necessary - 10. Forward the new request - */ - - /* RFC 3261 16.11 Stateless Proxy. - o A stateless proxy MUST choose one and only one target from the target set. This choice - MUST only rely on fields in the message and time-invariant properties of the server. In - particular, a retransmitted request MUST be forwarded to the same destination each time - it is processed. Furthermore, CANCEL and non-Routed ACK requests MUST generate the same - choice as their associated INVITE. - - However, a stateless proxy cannot simply use a random number generator to compute - the first component of the branch ID, as described in Section 16.6 bullet 8. - This is because retransmissions of a request need to have the same value, and - a stateless proxy cannot tell a retransmission from the original request. - - We just use: "z9hG4bK-" + md5(topmost branch) - */ - - bool isStrictRoute = false; - SIP_Hop[] hops = null; - - #region 1. Make a copy of the received request - - SIP_Request forwardRequest = request.Copy(); - - #endregion - - #region 2. Update the Request-URI - - forwardRequest.RequestLine.Uri = targetSet[0].TargetUri; - - #endregion - - #region 3. Update the Max-Forwards header field - - forwardRequest.MaxForwards--; - - #endregion - - #region 4. Optionally add a Record-route header field value - - #endregion - - #region 5. Optionally add additional header fields - - #endregion - - #region 6. Postprocess routing information - - /* 6. Postprocess routing information. - - If the copy contains a Route header field, the proxy MUST inspect the URI in its first value. - If that URI does not contain an lr parameter, the proxy MUST modify the copy as follows: - - The proxy MUST place the Request-URI into the Route header - field as the last value. - - - The proxy MUST then place the first Route header field value - into the Request-URI and remove that value from the Route header field. - */ - if (forwardRequest.Route.GetAllValues().Length > 0 && - !forwardRequest.Route.GetTopMostValue().Parameters.Contains("lr")) - { - forwardRequest.Route.Add(forwardRequest.RequestLine.Uri.ToString()); - - forwardRequest.RequestLine.Uri = - SIP_Utils.UriToRequestUri(forwardRequest.Route.GetTopMostValue().Address.Uri); - forwardRequest.Route.RemoveTopMostValue(); - - isStrictRoute = true; - } - - #endregion - - #region 7. Determine the next-hop address, port, and transport - - /* 7. Determine the next-hop address, port, and transport. - The proxy MAY have a local policy to send the request to a - specific IP address, port, and transport, independent of the - values of the Route and Request-URI. Such a policy MUST NOT be - used if the proxy is not certain that the IP address, port, and - transport correspond to a server that is a loose router. - However, this mechanism for sending the request through a - specific next hop is NOT RECOMMENDED; instead a Route header - field should be used for that purpose as described above. - - In the absence of such an overriding mechanism, the proxy - applies the procedures listed in [4] as follows to determine - where to send the request. If the proxy has reformatted the - request to send to a strict-routing element as described in - step 6 above, the proxy MUST apply those procedures to the - Request-URI of the request. Otherwise, the proxy MUST apply - the procedures to the first value in the Route header field, if - present, else the Request-URI. The procedures will produce an - ordered set of (address, port, transport) tuples. - Independently of which URI is being used as input to the - procedures of [4], if the Request-URI specifies a SIPS - resource, the proxy MUST follow the procedures of [4] as if the - input URI were a SIPS URI. - - As described in [4], the proxy MUST attempt to deliver the - message to the first tuple in that set, and proceed through the - set in order until the delivery attempt succeeds. - - For each tuple attempted, the proxy MUST format the message as - appropriate for the tuple and send the request using a new - client transaction as detailed in steps 8 through 10. - - Since each attempt uses a new client transaction, it represents - a new branch. Thus, the branch parameter provided with the Via - header field inserted in step 8 MUST be different for each - attempt. - - If the client transaction reports failure to send the request - or a timeout from its state machine, the proxy continues to the - next address in that ordered set. If the ordered set is - exhausted, the request cannot be forwarded to this element in - the target set. The proxy does not need to place anything in - the response context, but otherwise acts as if this element of - the target set returned a 408 (Request Timeout) final response. - */ - SIP_Uri uri = null; - if (isStrictRoute) - { - uri = (SIP_Uri) forwardRequest.RequestLine.Uri; - } - else if (forwardRequest.Route.GetTopMostValue() != null) - { - uri = (SIP_Uri) forwardRequest.Route.GetTopMostValue().Address.Uri; - } - else - { - uri = (SIP_Uri) forwardRequest.RequestLine.Uri; - } - - hops = m_pStack.GetHops(uri, - forwardRequest.ToByteData().Length, - ((SIP_Uri) forwardRequest.RequestLine.Uri).IsSecure); - - if (hops.Length == 0) - { - if (forwardRequest.RequestLine.Method != SIP_Methods.ACK) - { - e.ServerTransaction.SendResponse( - m_pStack.CreateResponse( - SIP_ResponseCodes.x503_Service_Unavailable + ": No hop(s) for target.", - forwardRequest)); - } - - return; - } - - #endregion - - #region 8. Add a Via header field value - - forwardRequest.Via.AddToTop( - "SIP/2.0/transport-tl-addign sentBy-tl-assign-it;branch=z9hG4bK-" + - Core.ComputeMd5(request.Via.GetTopMostValue().Branch, true)); - - // Add 'flowID' what received request, you should use the same flow to send response back. - // For more info see RFC 3261 18.2.2. - forwardRequest.Via.GetTopMostValue().Parameters.Add("flowID", request.Flow.ID); - - #endregion - - #region 9. Add a Content-Length header field if necessary - - // Skip, our SIP_Message class is smart and do it when ever it's needed. - - #endregion - - #region 10. Forward the new request - - try - { - try - { - if (targetSet[0].Flow != null) - { - m_pStack.TransportLayer.SendRequest(targetSet[0].Flow, request); - - return; - } - } - catch - { - m_pStack.TransportLayer.SendRequest(request, null, hops[0]); - } - } - catch (SIP_TransportException x) - { - string dummy = x.Message; - - if (forwardRequest.RequestLine.Method != SIP_Methods.ACK) - { - /* RFC 3261 16.9 Handling Transport Errors - If the transport layer notifies a proxy of an error when it tries to - forward a request (see Section 18.4), the proxy MUST behave as if the - forwarded request received a 503 (Service Unavailable) response. - */ - e.ServerTransaction.SendResponse( - m_pStack.CreateResponse( - SIP_ResponseCodes.x503_Service_Unavailable + ": Transport error.", - forwardRequest)); - } - } - - #endregion - } - - #endregion - - #endregion - } - - /// - /// Authenticates SIP request. This method also sends all needed replys to request sender. - /// - /// Request event arguments. - /// Returns true if request was authenticated. - internal bool AuthenticateRequest(SIP_RequestReceivedEventArgs e) - { - string userName = null; - return AuthenticateRequest(e, out userName); - } - - /// - /// Authenticates SIP request. This method also sends all needed replys to request sender. - /// - /// Request event arguments. - /// If authentication sucessful, then authenticated user name is stored to this variable. - /// Returns true if request was authenticated. - internal bool AuthenticateRequest(SIP_RequestReceivedEventArgs e, out string userName) - { - userName = null; - - SIP_t_Credentials credentials = SIP_Utils.GetCredentials(e.Request, m_pStack.Realm); - // No credentials for our realm. - if (credentials == null) - { - SIP_Response notAuthenticatedResponse = - m_pStack.CreateResponse(SIP_ResponseCodes.x407_Proxy_Authentication_Required, e.Request); - notAuthenticatedResponse.ProxyAuthenticate.Add( - new Auth_HttpDigest(m_pStack.Realm, m_pStack.DigestNonceManager.CreateNonce(), m_Opaque). - ToChallange()); - - e.ServerTransaction.SendResponse(notAuthenticatedResponse); - return false; - } - - Auth_HttpDigest auth = new Auth_HttpDigest(credentials.AuthData, e.Request.RequestLine.Method); - // Check opaque validity. - if (auth.Opaque != m_Opaque) - { - SIP_Response notAuthenticatedResponse = - m_pStack.CreateResponse( - SIP_ResponseCodes.x407_Proxy_Authentication_Required + ": Opaque value won't match !", - e.Request); - notAuthenticatedResponse.ProxyAuthenticate.Add( - new Auth_HttpDigest(m_pStack.Realm, m_pStack.DigestNonceManager.CreateNonce(), m_Opaque). - ToChallange()); - - // Send response - e.ServerTransaction.SendResponse(notAuthenticatedResponse); - return false; - } - // Check nonce validity. - if (!m_pStack.DigestNonceManager.NonceExists(auth.Nonce)) - { - SIP_Response notAuthenticatedResponse = - m_pStack.CreateResponse( - SIP_ResponseCodes.x407_Proxy_Authentication_Required + ": Invalid nonce value !", - e.Request); - notAuthenticatedResponse.ProxyAuthenticate.Add( - new Auth_HttpDigest(m_pStack.Realm, m_pStack.DigestNonceManager.CreateNonce(), m_Opaque). - ToChallange()); - - // Send response - e.ServerTransaction.SendResponse(notAuthenticatedResponse); - return false; - } - // Valid nonce, consume it so that nonce can't be used any more. - else - { - m_pStack.DigestNonceManager.RemoveNonce(auth.Nonce); - } - - SIP_AuthenticateEventArgs eArgs = OnAuthenticate(auth); - // Authenticate failed. - if (!eArgs.Authenticated) - { - SIP_Response notAuthenticatedResponse = - m_pStack.CreateResponse( - SIP_ResponseCodes.x407_Proxy_Authentication_Required + ": Authentication failed.", - e.Request); - notAuthenticatedResponse.ProxyAuthenticate.Add( - new Auth_HttpDigest(m_pStack.Realm, m_pStack.DigestNonceManager.CreateNonce(), m_Opaque). - ToChallange()); - - // Send response - e.ServerTransaction.SendResponse(notAuthenticatedResponse); - return false; - } - - userName = auth.UserName; - - return true; - } - - /// - /// Gets if this proxy server is responsible for specified route. - /// - /// Route value to check. - /// Returns trues if server route, otherwise false. - /// Is raised when uri is null reference. - internal bool IsLocalRoute(SIP_Uri uri) - { - if (uri == null) - { - throw new ArgumentNullException("uri"); - } - - // Not a route. - if (uri.User != null) - { - return false; - } - - // Consider any IP address as local route, because if server behind NAT we can't do IP check. - if (Net_Utils.IsIPAddress(uri.Host)) - { - return true; - } - else - { - foreach (IPBindInfo bind in m_pStack.BindInfo) - { - if (uri.Host.ToLower() == bind.HostName.ToLower()) - { - return true; - } - } - } - - return false; - } - - // REMOVE ME: - - /* - /// - /// Creates new Contact header field for b2bua forward request. - /// - /// Address. - /// Returns new Contact value. - /// Is raised when address is null. - /// Is raised when any of the arguments has invalid value. - internal SIP_t_NameAddress CreateContactX(SIP_t_NameAddress address) - { - if(address == null){ - throw new ArgumentNullException("address"); - } - - if(address.IsSipOrSipsUri){ - SIP_Uri uri = SIP_Uri.Parse(address.Uri.ToString()); - uri.Host = m_pStack.TransportLayer.GetEndPoint(address.Uri); - uri.Port = -1; - - SIP_t_NameAddress contact = new SIP_t_NameAddress(); - contact.Uri = uri; - - return contact; - } - else{ - throw new ArgumentException("Not SIP URI !"); - } - }*/ - - /// - /// Raises 'IsLocalUri' event. - /// - /// Request URI. - /// Returns true if server local URI, otherwise false. - internal bool OnIsLocalUri(string uri) - { - if (IsLocalUri != null) - { - return IsLocalUri(uri); - } - - return true; - } - - /// - /// Is called by SIP proxy or registrar server when it needs to authenticate user. - /// - /// Authentication context. - /// - internal SIP_AuthenticateEventArgs OnAuthenticate(Auth_HttpDigest auth) - { - SIP_AuthenticateEventArgs eArgs = new SIP_AuthenticateEventArgs(auth); - if (Authenticate != null) - { - Authenticate(eArgs); - } - - return eArgs; - } - - /// - /// Is called by SIP proxy if it needs to check if specified address exists. - /// - /// SIP address to check. - /// Returns true if specified address exists, otherwise false. - internal bool OnAddressExists(string address) - { - if (AddressExists != null) - { - return AddressExists(address); - } - - return false; - } - - #endregion - - /// - /// Is called by SIP proxy when SIP proxy needs to get gateways for non-SIP URI. - /// - /// Non-SIP URI scheme which gateways to get. - /// Authenticated user name. - /// Returns event data. - protected SIP_GatewayEventArgs OnGetGateways(string uriScheme, string userName) - { - SIP_GatewayEventArgs e = new SIP_GatewayEventArgs(uriScheme, userName); - if (GetGateways != null) - { - GetGateways(e); - } - return e; - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_ProxyMode.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_ProxyMode.cs deleted file mode 100644 index 5d19c70ff..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_ProxyMode.cs +++ /dev/null @@ -1,61 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Proxy -{ - #region usings - - using System; - - #endregion - - /// - /// Specifies SIP proxy mode. - /// - /// All flags may be combined, except Stateless,Statefull,B2BUA. - /// For example: (Stateless | Statefull) not allowed, but (Registrar | Presence | Statefull) is allowed. - /// - /// - [Flags] - public enum SIP_ProxyMode - { - /// - /// Proxy implements SIP registrar. - /// - Registrar = 1, - - /// - /// Proxy implements SIP presence server. - /// - Presence = 2, - - /// - /// Proxy runs in stateless mode. - /// - Stateless = 4, - - /// - /// Proxy runs in statefull mode. - /// - Statefull = 8, - - /// - /// Proxy runs in B2BUA(back to back user agent) mode. - /// - B2BUA = 16, - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_ProxyTarget.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_ProxyTarget.cs deleted file mode 100644 index ee0b96c77..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_ProxyTarget.cs +++ /dev/null @@ -1,87 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Proxy -{ - #region usings - - using System; - using Stack; - - #endregion - - /// - /// Represents SIP proxy target in the SIP proxy "target set". Defined in RFC 3261 16. - /// - public class SIP_ProxyTarget - { - #region Members - - private readonly SIP_Flow m_pFlow; - private readonly SIP_Uri m_pTargetUri; - - #endregion - - #region Properties - - /// - /// Gets target URI. - /// - public SIP_Uri TargetUri - { - get { return m_pTargetUri; } - } - - /// - /// Gets data flow. Value null means that new flow must created. - /// - public SIP_Flow Flow - { - get { return m_pFlow; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Target request-URI. - /// Is raised when targetUri is null reference. - public SIP_ProxyTarget(SIP_Uri targetUri) : this(targetUri, null) {} - - /// - /// Default constructor. - /// - /// Target request-URI. - /// Data flow to try for forwarding.. - /// Is raised when targetUri is null reference. - public SIP_ProxyTarget(SIP_Uri targetUri, SIP_Flow flow) - { - if (targetUri == null) - { - throw new ArgumentNullException("targetUri"); - } - - m_pTargetUri = targetUri; - m_pFlow = flow; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_Registrar.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_Registrar.cs deleted file mode 100644 index ae06a4403..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_Registrar.cs +++ /dev/null @@ -1,585 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Proxy -{ - #region usings - - using System; - using System.Timers; - using Message; - using Stack; - - #endregion - - #region Delegates - - /// - /// Represents the method that will handle the SIP_Registrar.CanRegister event. - /// - /// Authenticated user name. - /// Address to be registered. - /// Returns true if specified user can register specified address, otherwise false. - public delegate bool SIP_CanRegisterEventHandler(string userName, string address); - - #endregion - - /// - /// This class implements SIP registrar server. Defined in RFC 3261 10.3. - /// - public class SIP_Registrar - { - #region Events - - /// - /// This event is raised when new AOR(address of record) has been registered. - /// - public event EventHandler AorRegistered = null; - - /// - /// This event is raised when AOR(address of record) has been unregistered. - /// - public event EventHandler AorUnregistered = null; - - /// - /// This event is raised when AOR(address of record) has been updated. - /// - public event EventHandler AorUpdated = null; - - /// - /// This event is raised when SIP registrar need to check if specified user can register specified address. - /// - public event SIP_CanRegisterEventHandler CanRegister = null; - - #endregion - - #region Members - - private bool m_IsDisposed; - private SIP_ProxyCore m_pProxy; - private SIP_RegistrationCollection m_pRegistrations; - private SIP_Stack m_pStack; - private Timer m_pTimer; - - #endregion - - #region Properties - - /// - /// Gets owner proxy core. - /// - public SIP_ProxyCore Proxy - { - get { return m_pProxy; } - } - - /// - /// Gets current SIP registrations. - /// - public SIP_Registration[] Registrations - { - get - { - lock (m_pRegistrations) - { - SIP_Registration[] retVal = new SIP_Registration[m_pRegistrations.Count]; - m_pRegistrations.Values.CopyTo(retVal, 0); - - return retVal; - } - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner proxy. - /// Is raised when proxy is null reference. - internal SIP_Registrar(SIP_ProxyCore proxy) - { - if (proxy == null) - { - throw new ArgumentNullException("proxy"); - } - - m_pProxy = proxy; - m_pStack = m_pProxy.Stack; - - m_pRegistrations = new SIP_RegistrationCollection(); - - m_pTimer = new Timer(15000); - m_pTimer.Elapsed += m_pTimer_Elapsed; - m_pTimer.Enabled = true; - } - - #endregion - - #region Methods - - /// - /// Gets specified registration. Returns null if no such registration. - /// - /// Address of record of registration which to get. - /// Returns SIP registration or null if no match. - public SIP_Registration GetRegistration(string aor) - { - return m_pRegistrations[aor]; - } - - /// - /// Add or updates specified SIP registration info. - /// - /// Registration address of record. - /// Registration address of record contacts to update. - public void SetRegistration(string aor, SIP_t_ContactParam[] contacts) - { - SetRegistration(aor, contacts, null); - } - - /// - /// Add or updates specified SIP registration info. - /// - /// Registration address of record. - /// Registration address of record contacts to update. - /// SIP proxy local data flow what accpeted this contact registration. - public void SetRegistration(string aor, SIP_t_ContactParam[] contacts, SIP_Flow flow) - { - lock (m_pRegistrations) - { - SIP_Registration registration = m_pRegistrations[aor]; - if (registration == null) - { - registration = new SIP_Registration("system", aor); - m_pRegistrations.Add(registration); - OnAorRegistered(registration); - } - - registration.AddOrUpdateBindings(flow, "", 1, contacts); - } - } - - /// - /// Deletes specified registration and all it's contacts. - /// - /// Registration address of record what to remove. - public void DeleteRegistration(string addressOfRecord) - { - m_pRegistrations.Remove(addressOfRecord); - } - - #endregion - - #region Event handlers - - private void m_pTimer_Elapsed(object sender, ElapsedEventArgs e) - { - m_pRegistrations.RemoveExpired(); - } - - #endregion - - #region Utility methods - - /// - /// Raises AorRegistered event. - /// - /// SIP registration. - private void OnAorRegistered(SIP_Registration registration) - { - if (AorRegistered != null) - { - AorRegistered(this, new SIP_RegistrationEventArgs(registration)); - } - } - - /// - /// Raises AorUnregistered event. - /// - /// SIP registration. - private void OnAorUnregistered(SIP_Registration registration) - { - if (AorUnregistered != null) - { - AorUnregistered(this, new SIP_RegistrationEventArgs(registration)); - } - } - - /// - /// Raises AorUpdated event. - /// - /// SIP registration. - private void OnAorUpdated(SIP_Registration registration) - { - if (AorUpdated != null) - { - AorUpdated(this, new SIP_RegistrationEventArgs(registration)); - } - } - - #endregion - - #region Internal methods - - /// - /// Cleans up any resources being used. - /// - internal void Dispose() - { - if (m_IsDisposed) - { - return; - } - m_IsDisposed = true; - - CanRegister = null; - AorRegistered = null; - AorUnregistered = null; - AorUpdated = null; - - m_pProxy = null; - m_pStack = null; - m_pRegistrations = null; - if (m_pTimer != null) - { - m_pTimer.Dispose(); - m_pTimer = null; - } - } - - /// - /// Handles REGISTER method. - /// - /// Request event arguments. - internal void Register(SIP_RequestReceivedEventArgs e) - { - /* RFC 3261 10.3 Processing REGISTER Requests. - 1. The registrar inspects the Request-URI to determine whether it - has access to bindings for the domain identified in the - Request-URI. If not, and if the server also acts as a proxy - server, the server SHOULD forward the request to the addressed - domain, following the general behavior for proxying messages - described in Section 16. - - 2. To guarantee that the registrar supports any necessary extensions, - the registrar MUST process the Require header field. - - 3. A registrar SHOULD authenticate the UAC. - - 4. The registrar SHOULD determine if the authenticated user is - authorized to modify registrations for this address-of-record. - For example, a registrar might consult an authorization - database that maps user names to a list of addresses-of-record - for which that user has authorization to modify bindings. If - the authenticated user is not authorized to modify bindings, - the registrar MUST return a 403 (Forbidden) and skip the - remaining steps. - - 5. The registrar extracts the address-of-record from the To header - field of the request. If the address-of-record is not valid - for the domain in the Request-URI, the registrar MUST send a - 404 (Not Found) response and skip the remaining steps. The URI - MUST then be converted to a canonical form. To do that, all - URI parameters MUST be removed (including the user-param), and - any escaped characters MUST be converted to their unescaped - form. The result serves as an index into the list of bindings. - - 6. The registrar checks whether the request contains the Contact - header field. If not, it skips to the last step. If the - Contact header field is present, the registrar checks if there - is one Contact field value that contains the special value "*" - and an Expires field. If the request has additional Contact - fields or an expiration time other than zero, the request is - invalid, and the server MUST return a 400 (Invalid Request) and - skip the remaining steps. If not, the registrar checks whether - the Call-ID agrees with the value stored for each binding. If - not, it MUST remove the binding. If it does agree, it MUST - remove the binding only if the CSeq in the request is higher - than the value stored for that binding. Otherwise, the update - MUST be aborted and the request fails. - - 7. The registrar now processes each contact address in the Contact - header field in turn. For each address, it determines the - expiration interval as follows: - - - If the field value has an "expires" parameter, that value - MUST be taken as the requested expiration. - - - If there is no such parameter, but the request has an - Expires header field, that value MUST be taken as the requested expiration. - - - If there is neither, a locally-configured default value MUST - be taken as the requested expiration. - - The registrar MAY choose an expiration less than the requested - expiration interval. If and only if the requested expiration - interval is greater than zero AND smaller than one hour AND - less than a registrar-configured minimum, the registrar MAY - reject the registration with a response of 423 (Interval Too - Brief). This response MUST contain a Min-Expires header field - that states the minimum expiration interval the registrar is - willing to honor. It then skips the remaining steps. - - For each address, the registrar then searches the list of - current bindings using the URI comparison rules. If the - binding does not exist, it is tentatively added. If the - binding does exist, the registrar checks the Call-ID value. If - the Call-ID value in the existing binding differs from the - Call-ID value in the request, the binding MUST be removed if - the expiration time is zero and updated otherwise. If they are - the same, the registrar compares the CSeq value. If the value - is higher than that of the existing binding, it MUST update or - remove the binding as above. If not, the update MUST be - aborted and the request fails. - - This algorithm ensures that out-of-order requests from the same - UA are ignored. - - Each binding record records the Call-ID and CSeq values from - the request. - - The binding updates MUST be committed (that is, made visible to - the proxy or redirect server) if and only if all binding - updates and additions succeed. If any one of them fails (for - example, because the back-end database commit failed), the - request MUST fail with a 500 (Server Error) response and all - tentative binding updates MUST be removed. - - 8. The registrar returns a 200 (OK) response. The response MUST - contain Contact header field values enumerating all current - bindings. Each Contact value MUST feature an "expires" - parameter indicating its expiration interval chosen by the - registrar. The response SHOULD include a Date header field. - */ - - SIP_ServerTransaction transaction = e.ServerTransaction; - SIP_Request request = e.Request; - SIP_Uri to = null; - string userName = ""; - - // Probably we need to do validate in SIP stack. - - #region Validate request - - if (SIP_Utils.IsSipOrSipsUri(request.To.Address.Uri.ToString())) - { - to = (SIP_Uri) request.To.Address.Uri; - } - else - { - transaction.SendResponse( - m_pStack.CreateResponse( - SIP_ResponseCodes.x400_Bad_Request + ": To: value must be SIP or SIPS URI.", request)); - return; - } - - #endregion - - #region 1. Check if we are responsible for Request-URI domain - - // if(m_pProxy.OnIsLocalUri(e.Request.Uri)){ - // } - // TODO: - - #endregion - - #region 2. Check that all required extentions supported - - #endregion - - #region 3. Authenticate request - - if (!m_pProxy.AuthenticateRequest(e, out userName)) - { - return; - } - - #endregion - - #region 4. Check if user user is authorized to modify registrations - - // We do this in next step(5.). - - #endregion - - #region 5. Check if address of record exists - - if (!m_pProxy.OnAddressExists(to.Address)) - { - transaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x404_Not_Found, request)); - return; - } - else if (!OnCanRegister(userName, to.Address)) - { - transaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x403_Forbidden, request)); - return; - } - - #endregion - - #region 6. Process * Contact if exists - - // Check if we have star contact. - SIP_t_ContactParam starContact = null; - foreach (SIP_t_ContactParam c in request.Contact.GetAllValues()) - { - if (c.IsStarContact) - { - starContact = c; - break; - } - } - - // We have star contact. - if (starContact != null) - { - if (request.Contact.GetAllValues().Length > 1) - { - transaction.SendResponse( - m_pStack.CreateResponse( - SIP_ResponseCodes.x400_Bad_Request + - ": RFC 3261 10.3.6 -> If star(*) present, only 1 contact allowed.", - request)); - return; - } - else if (starContact.Expires != 0) - { - transaction.SendResponse( - m_pStack.CreateResponse( - SIP_ResponseCodes.x400_Bad_Request + - ": RFC 3261 10.3.6 -> star(*) contact parameter 'expires' value must be always '0'.", - request)); - return; - } - - // Remove bindings. - SIP_Registration reg = m_pRegistrations[to.Address]; - if (reg != null) - { - foreach (SIP_RegistrationBinding b in reg.Bindings) - { - if (request.CallID != b.CallID || request.CSeq.SequenceNumber > b.CSeqNo) - { - b.Remove(); - } - } - } - } - - #endregion - - #region 7. Process Contact values - - if (starContact == null) - { - SIP_Registration reg = m_pRegistrations[to.Address]; - if (reg == null) - { - reg = new SIP_Registration(userName, to.Address); - m_pRegistrations.Add(reg); - } - - // We may do updates in batch only. - // We just validate all values then do update(this ensures that update doesn't fail). - - // Check expires and CSeq. - foreach (SIP_t_ContactParam c in request.Contact.GetAllValues()) - { - if (c.Expires == -1) - { - c.Expires = request.Expires; - } - if (c.Expires == -1) - { - c.Expires = m_pProxy.Stack.MinimumExpireTime; - } - // We must accept 0 values - means remove contact. - if (c.Expires != 0 && c.Expires < m_pProxy.Stack.MinimumExpireTime) - { - SIP_Response resp = m_pStack.CreateResponse( - SIP_ResponseCodes.x423_Interval_Too_Brief, request); - resp.MinExpires = m_pProxy.Stack.MinimumExpireTime; - transaction.SendResponse(resp); - return; - } - - SIP_RegistrationBinding currentBinding = reg.GetBinding(c.Address.Uri); - if (currentBinding != null && currentBinding.CallID == request.CallID && - request.CSeq.SequenceNumber < currentBinding.CSeqNo) - { - transaction.SendResponse( - m_pStack.CreateResponse( - SIP_ResponseCodes.x400_Bad_Request + ": CSeq value out of order.", request)); - return; - } - } - - // Do binding updates. - reg.AddOrUpdateBindings(e.ServerTransaction.Flow, - request.CallID, - request.CSeq.SequenceNumber, - request.Contact.GetAllValues()); - } - - #endregion - - #region 8. Create 200 OK response and return all current bindings - - SIP_Response response = m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok, request); - response.Date = DateTime.Now; - SIP_Registration registration = m_pRegistrations[to.Address]; - if (registration != null) - { - foreach (SIP_RegistrationBinding b in registration.Bindings) - { - // Don't list expired bindings what wait to be disposed. - if (b.TTL > 1) - { - response.Header.Add("Contact:", b.ToContactValue()); - } - } - } - // Add Authentication-Info:, then client knows next nonce. - response.AuthenticationInfo.Add("qop=\"auth\",nextnonce=\"" + - m_pStack.DigestNonceManager.CreateNonce() + "\""); - transaction.SendResponse(response); - - #endregion - } - - /// - /// Is called by SIP registrar if it needs to check if specified user can register specified address. - /// - /// Authenticated user name. - /// Address to be registered. - /// Returns true if specified user can register specified address, otherwise false. - internal bool OnCanRegister(string userName, string address) - { - if (CanRegister != null) - { - return CanRegister(userName, address); - } - - return false; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_Registration.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_Registration.cs deleted file mode 100644 index 6bd9affb5..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_Registration.cs +++ /dev/null @@ -1,247 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Proxy -{ - #region usings - - using System; - using System.Collections.Generic; - using Message; - using Stack; - - #endregion - - /// - /// This class implements SIP registrar registration entry. Defined in RFC 3261 10.3. - /// - public class SIP_Registration - { - #region Members - - private readonly string m_AOR = ""; - private readonly DateTime m_CreateTime; - private readonly List m_pBindings; - private readonly object m_pLock = new object(); - private readonly string m_UserName = ""; - - #endregion - - #region Properties - - /// - /// Gets time when this registration entry was created. - /// - public DateTime CreateTime - { - get { return m_CreateTime; } - } - - /// - /// Gets user name who owns this registration. - /// - public string UserName - { - get { return m_UserName; } - } - - /// - /// Gets registration address of record. - /// - public string AOR - { - get { return m_AOR; } - } - - /// - /// Gets this registration priority ordered bindings. - /// - public SIP_RegistrationBinding[] Bindings - { - get - { - SIP_RegistrationBinding[] retVal = m_pBindings.ToArray(); - - // Sort by qvalue, higer qvalue means higher priority. - Array.Sort(retVal); - - return retVal; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// User name who owns this registration. - /// Address of record. For example: john.doe@lumisoft.ee. - /// Is raised when userName or aor is null reference. - public SIP_Registration(string userName, string aor) - { - if (userName == null) - { - throw new ArgumentNullException("userName"); - } - if (aor == null) - { - throw new ArgumentNullException("aor"); - } - if (aor == "") - { - throw new ArgumentException("Argument 'aor' value must be specified."); - } - - m_UserName = userName; - m_AOR = aor; - - m_CreateTime = DateTime.Now; - m_pBindings = new List(); - } - - #endregion - - #region Methods - - /// - /// Gets matching binding. Returns null if no match. - /// - /// URI to match. - /// Returns matching binding. Returns null if no match. - /// Is raised when contactUri is null reference. - public SIP_RegistrationBinding GetBinding(AbsoluteUri contactUri) - { - if (contactUri == null) - { - throw new ArgumentNullException("contactUri"); - } - - lock (m_pLock) - { - foreach (SIP_RegistrationBinding binding in m_pBindings) - { - if (contactUri.Equals(binding.ContactURI)) - { - return binding; - } - } - - return null; - } - } - - /// - /// Adds or updates matching bindings. - /// - /// SIP data flow what updates this binding. This value is null if binding was not added through network or - /// flow has disposed. - /// Call-ID header field value. - /// CSeq header field sequence number value. - /// Contacts to add or update. - /// Is raised when callID or contacts is null reference. - public void AddOrUpdateBindings(SIP_Flow flow, - string callID, - int cseqNo, - SIP_t_ContactParam[] contacts) - { - if (callID == null) - { - throw new ArgumentNullException("callID"); - } - if (cseqNo < 0) - { - throw new ArgumentException("Argument 'cseqNo' value must be >= 0."); - } - if (contacts == null) - { - throw new ArgumentNullException("contacts"); - } - - lock (m_pLock) - { - foreach (SIP_t_ContactParam contact in contacts) - { - SIP_RegistrationBinding binding = GetBinding(contact.Address.Uri); - // Add binding. - if (binding == null) - { - binding = new SIP_RegistrationBinding(this, contact.Address.Uri); - m_pBindings.Add(binding); - } - - // Update binding. - binding.Update(flow, - contact.Expires == -1 ? 3600 : contact.Expires, - contact.QValue == -1 ? 1.0 : contact.QValue, - callID, - cseqNo); - } - } - } - - /// - /// Removes specified binding. - /// - /// Registration binding. - /// Is raised when binding is null reference. - public void RemoveBinding(SIP_RegistrationBinding binding) - { - if (binding == null) - { - throw new ArgumentNullException("binding"); - } - - lock (m_pLock) - { - m_pBindings.Remove(binding); - } - } - - /// - /// Removes all this registration bindings. - /// - public void RemoveAllBindings() - { - lock (m_pLock) - { - m_pBindings.Clear(); - } - } - - /// - /// Removes all expired bindings. - /// - public void RemoveExpiredBindings() - { - lock (m_pLock) - { - for (int i = 0; i < m_pBindings.Count; i++) - { - if (m_pBindings[i].IsExpired) - { - m_pBindings.RemoveAt(i); - i--; - } - } - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_RegistrationBinding.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_RegistrationBinding.cs deleted file mode 100644 index 7289672ef..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_RegistrationBinding.cs +++ /dev/null @@ -1,247 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Proxy -{ - #region usings - - using System; - using Message; - using Stack; - - #endregion - - /// - /// This class represents SIP registrar registration binding entry. Defined in RFC 3261 10.3. - /// - public class SIP_RegistrationBinding : IComparable - { - #region Members - - private readonly AbsoluteUri m_ContactURI; - private readonly SIP_Registration m_pRegistration; - private string m_CallID = ""; - private int m_CSeqNo = 1; - private int m_Expires = 3600; - private DateTime m_LastUpdate; - private SIP_Flow m_pFlow; - private double m_QValue = 1.0; - - #endregion - - #region Properties - - /// - /// Gets the last time when the binding was updated. - /// - public DateTime LastUpdate - { - get { return m_LastUpdate; } - } - - /// - /// Gets if binding has expired. - /// - public bool IsExpired - { - get { return TTL <= 0; } - } - - /// - /// Gets how many seconds binding has time to live. This is live calulated value, so it decreases every second. - /// - public int TTL - { - get - { - if (DateTime.Now > m_LastUpdate.AddSeconds(m_Expires)) - { - return 0; - } - else - { - return (int) ((m_LastUpdate.AddSeconds(m_Expires) - DateTime.Now)).TotalSeconds; - } - } - } - - /// - /// Gets data flow what added this binding. This value is null if binding was not added through network or - /// flow has disposed. - /// - public SIP_Flow Flow - { - get { return m_pFlow; } - } - - /// - /// Gets contact URI what can be used to contact the registration. - /// - public AbsoluteUri ContactURI - { - get { return m_ContactURI; } - } - - /// - /// Gets binding priority. Higher value means greater priority. - /// - public double QValue - { - get { return m_QValue; } - } - - /// - /// Gets Call-ID header field value which added this binding. - /// - public string CallID - { - get { return m_CallID; } - } - - /// - /// Gets CSeq header field sequence number value which added this binding. - /// - public int CSeqNo - { - get { return m_CSeqNo; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner registration. - /// Contact URI what can be used to contact the registration. - /// Is raised when owner or contactUri is null reference. - internal SIP_RegistrationBinding(SIP_Registration owner, AbsoluteUri contactUri) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - if (contactUri == null) - { - throw new ArgumentNullException("contactUri"); - } - - m_pRegistration = owner; - m_ContactURI = contactUri; - } - - #endregion - - #region Methods - - /// - /// Updates specified binding. - /// - /// SIP data flow what updates this binding. This value is null if binding was not added through network or - /// flow has disposed. - /// Time in seconds when binding will expire. - /// Binding priority. Higher value means greater priority. - /// Call-ID header field value which added/updated this binding. - /// CSeq header field sequence number value which added/updated this binding. - public void Update(SIP_Flow flow, int expires, double qvalue, string callID, int cseqNo) - { - if (expires < 0) - { - throw new ArgumentException("Argument 'expires' value must be >= 0."); - } - if (qvalue < 0 || qvalue > 1) - { - throw new ArgumentException("Argument 'qvalue' value must be >= 0.000 and <= 1.000"); - } - if (callID == null) - { - throw new ArgumentNullException("callID"); - } - if (cseqNo < 0) - { - throw new ArgumentException("Argument 'cseqNo' value must be >= 0."); - } - - m_pFlow = flow; - m_Expires = expires; - m_QValue = qvalue; - m_CallID = callID; - m_CSeqNo = cseqNo; - - m_LastUpdate = DateTime.Now; - } - - /// - /// Removes this binding from the registration. - /// - public void Remove() - { - m_pRegistration.RemoveBinding(this); - } - - /// - /// Converts ContactUri to valid Contact header value. - /// - /// Returns contact header value. - public string ToContactValue() - { - SIP_t_ContactParam retVal = new SIP_t_ContactParam(); - retVal.Parse(new StringReader(m_ContactURI.ToString())); - retVal.Expires = m_Expires; - - return retVal.ToStringValue(); - } - - /// - /// Compares the current instance with another object of the same type. - /// - /// An object to compare with this instance. - /// Returns 0 if two objects equal, -1 if this instance is less or 1 this instance is greater. - public int CompareTo(object obj) - { - if (obj == null) - { - return -1; - } - if (!(obj is SIP_RegistrationBinding)) - { - return -1; - } - - // We must reverse values, because greater value mean higer priority. - - SIP_RegistrationBinding compareValue = (SIP_RegistrationBinding) obj; - if (compareValue.QValue == QValue) - { - return 0; - } - else if (compareValue.QValue > QValue) - { - return 1; - } - else if (compareValue.QValue < QValue) - { - return -1; - } - - return -1; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_RegistrationCollection.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_RegistrationCollection.cs deleted file mode 100644 index 5bd55f37f..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_RegistrationCollection.cs +++ /dev/null @@ -1,188 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Proxy -{ - #region usings - - using System; - using System.Collections; - using System.Collections.Generic; - - #endregion - - /// - /// SIP registration collection. - /// - public class SIP_RegistrationCollection : IEnumerable - { - #region Members - - private readonly List m_pRegistrations; - - #endregion - - #region Properties - - /// - /// Gets number of registrations in the collection. - /// - public int Count - { - get { return m_pRegistrations.Count; } - } - - /// - /// Gets registration with specified registration name. Returns null if specified registration doesn't exist. - /// - /// Address of record of resgistration. - /// - public SIP_Registration this[string addressOfRecord] - { - get - { - lock (m_pRegistrations) - { - foreach (SIP_Registration registration in m_pRegistrations) - { - if (registration.AOR.ToLower() == addressOfRecord.ToLower()) - { - return registration; - } - } - return null; - } - } - } - - /// - /// Gets SIP registrations what in the collection. - /// - public SIP_Registration[] Values - { - get { return m_pRegistrations.ToArray(); } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public SIP_RegistrationCollection() - { - m_pRegistrations = new List(); - } - - #endregion - - #region Methods - - /// - /// Adds specified registration to collection. - /// - /// Registration to add. - public void Add(SIP_Registration registration) - { - lock (m_pRegistrations) - { - if (Contains(registration.AOR)) - { - throw new ArgumentException( - "Registration with specified registration name already exists !"); - } - - m_pRegistrations.Add(registration); - } - } - - /// - /// Deletes specified registration and all it's contacts. - /// - /// Registration address of record what to remove. - public void Remove(string addressOfRecord) - { - lock (m_pRegistrations) - { - foreach (SIP_Registration registration in m_pRegistrations) - { - if (registration.AOR.ToLower() == addressOfRecord.ToLower()) - { - m_pRegistrations.Remove(registration); - break; - } - } - } - } - - /// - /// Gets if registration with specified name already exists. - /// - /// Address of record of resgistration. - /// - public bool Contains(string addressOfRecord) - { - lock (m_pRegistrations) - { - foreach (SIP_Registration registration in m_pRegistrations) - { - if (registration.AOR.ToLower() == addressOfRecord.ToLower()) - { - return true; - } - } - } - - return false; - } - - /// - /// Removes all expired registrations from the collection. - /// - public void RemoveExpired() - { - lock (m_pRegistrations) - { - for (int i = 0; i < m_pRegistrations.Count; i++) - { - SIP_Registration registration = m_pRegistrations[i]; - - // Force registration to remove all its expired contacts. - registration.RemoveExpiredBindings(); - // No bindings left, so we need to remove that registration. - if (registration.Bindings.Length == 0) - { - m_pRegistrations.Remove(registration); - i--; - } - } - } - } - - /// - /// Gets enumerator. - /// - /// - public IEnumerator GetEnumerator() - { - return m_pRegistrations.GetEnumerator(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_RegistrationEventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_RegistrationEventArgs.cs deleted file mode 100644 index b2b1a6d4d..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_RegistrationEventArgs.cs +++ /dev/null @@ -1,68 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Proxy -{ - #region usings - - using System; - - #endregion - - /// - /// This class provides data for SIP_Registrar.AorRegistered,SIP_Registrar.AorUnregistered and SIP_Registrar.AorUpdated event. - /// - public class SIP_RegistrationEventArgs : EventArgs - { - #region Members - - private readonly SIP_Registration m_pRegistration; - - #endregion - - #region Properties - - /// - /// Gets SIP registration. - /// - public SIP_Registration Registration - { - get { return m_pRegistration; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// SIP reggistration. - /// Is raised when registration is null reference. - public SIP_RegistrationEventArgs(SIP_Registration registration) - { - if (registration == null) - { - throw new ArgumentNullException("registration"); - } - - m_pRegistration = registration; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_Route.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_Route.cs deleted file mode 100644 index 30d593993..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Proxy/SIP_Route.cs +++ /dev/null @@ -1,83 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Proxy -{ - #region usings - - using System.Net; - - #endregion - - /// - /// - /// - public class SIP_Route - { - #region Properties - - /// - /// Gets regex match pattern. - /// - public string MatchPattern - { - get { return ""; } - } - - /// - /// Gets matched URI. - /// - public string Uri - { - get { return ""; } - } - - /// - /// Gets SIP targets for Uri. - /// - public string[] Targets - { - get { return null; } - } - - /// - /// Gets targets processing mode. - /// - public SIP_ForkingMode ProcessMode - { - get { return SIP_ForkingMode.Parallel; } - } - - /// - /// Gets if user needs to authenticate to use this route. - /// - public bool RequireAuthentication - { - get { return true; } - } - - /// - /// Gets targets credentials. - /// - public NetworkCredential[] Credentials - { - get { return null; } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/SIP_Utils.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/SIP_Utils.cs deleted file mode 100644 index 2bd9064a0..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/SIP_Utils.cs +++ /dev/null @@ -1,275 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP -{ - #region usings - - using System; - using AUTH; - using Message; - using MIME; - using Stack; - - #endregion - - /// - /// SIP helper methods. - /// - public class SIP_Utils - { - #region Methods - - /// - /// Parses address from SIP To: header field. - /// - /// SIP header To: value. - /// - public static string ParseAddress(string to) - { - try - { - string retVal = to; - if (to.IndexOf('<') > -1 && to.IndexOf('<') < to.IndexOf('>')) - { - retVal = to.Substring(to.IndexOf('<') + 1, to.IndexOf('>') - to.IndexOf('<') - 1); - } - // Remove sip: - if (retVal.IndexOf(':') > -1) - { - retVal = retVal.Substring(retVal.IndexOf(':') + 1).Split(':')[0]; - } - return retVal; - } - catch - { - throw new ArgumentException("Invalid SIP header To: '" + to + "' value !"); - } - } - - /// - /// Converts URI to Request-URI by removing all not allowed Request-URI parameters from URI. - /// - /// URI value. - /// Returns valid Request-URI value. - /// Is raised when uri is null reference. - public static AbsoluteUri UriToRequestUri(AbsoluteUri uri) - { - if (uri == null) - { - throw new ArgumentNullException("uri"); - } - - if (uri is SIP_Uri) - { - // RFC 3261 19.1.2.(Table) - // We need to strip off "method-param" and "header" URI parameters". - // Currently we do it for sip or sips uri, do we need todo it for others too ? - - SIP_Uri sUri = (SIP_Uri) uri; - sUri.Parameters.Remove("method"); - sUri.Header = null; - - return sUri; - } - else - { - return uri; - } - } - - /// - /// Gets if specified value is SIP or SIPS URI. - /// - /// Value to check. - /// Returns true if specified value is SIP or SIPS URI, otherwise false. - public static bool IsSipOrSipsUri(string value) - { - try - { - SIP_Uri.Parse(value); - return true; - } - catch {} - return false; - } - - /// - /// Gets if specified URI is tel: or sip tel URI. There is special case when SIP URI can be tel:, - /// sip:+xxxx and sip:xxx;user=phone. - /// - /// URI to check. - /// Returns true if specified URI is tel: URI. - public static bool IsTelUri(string uri) - { - uri = uri.ToLower(); - - try - { - if (uri.StartsWith("tel:")) - { - return true; - } - else if (IsSipOrSipsUri(uri)) - { - SIP_Uri sipUri = SIP_Uri.Parse(uri); - // RFC 3398 12. If user part starts with +, it's tel: URI. - if (sipUri.User.StartsWith("+")) - { - return true; - } - // RFC 3398 12. - else if (sipUri.Param_User != null && sipUri.Param_User.ToLower() == "phone") - { - return true; - } - } - } - catch {} - - return false; - } - - /// - /// Gets specified realm SIP proxy credentials. Returns null if none exists for specified realm. - /// - /// SIP reques. - /// Realm(domain). - /// Returns specified realm credentials or null if none. - public static SIP_t_Credentials GetCredentials(SIP_Request request, string realm) - { - foreach ( - SIP_SingleValueHF authorization in request.ProxyAuthorization.HeaderFields) - { - if (authorization.ValueX.Method.ToLower() == "digest") - { - Auth_HttpDigest authDigest = new Auth_HttpDigest(authorization.ValueX.AuthData, - request.RequestLine.Method); - if (authDigest.Realm.ToLower() == realm.ToLower()) - { - return authorization.ValueX; - } - } - } - - return null; - } - - /// - /// Gets is specified option tags constains specified option tag. - /// - /// Option tags. - /// Option tag to check. - /// Returns true if specified option tag exists. - public static bool ContainsOptionTag(SIP_t_OptionTag[] tags, string tag) - { - foreach (SIP_t_OptionTag t in tags) - { - if (t.OptionTag.ToLower() == tag) - { - return true; - } - } - - return false; - } - - /// - /// Gets if specified method can establish dialog. - /// - /// SIP method. - /// Returns true if specified SIP method can establish dialog, otherwise false. - /// Is raised when invalid value is passed. - public static bool MethodCanEstablishDialog(string method) - { - if (string.IsNullOrEmpty(method)) - { - throw new ArgumentException("Argument 'method' value can't be null or empty !"); - } - method = method.ToUpper(); - - if (method == SIP_Methods.INVITE) - { - return true; - } - else if (method == SIP_Methods.SUBSCRIBE) - { - return true; - } - else if (method == SIP_Methods.REFER) - { - return true; - } - - return false; - } - - /// - /// Creates tag for tag header filed. For example From:/To: tag value. - /// - /// Returns tag string. - public static string CreateTag() - { - return Guid.NewGuid().ToString().Replace("-", "").Substring(8); - } - - /// - /// Gets if the specified transport is reliable transport. - /// - /// SIP transport. - /// Returns if specified transport is reliable. - /// Is raised when transport is null reference. - public static bool IsReliableTransport(string transport) - { - if (transport == null) - { - throw new ArgumentNullException("transport"); - } - - if (transport.ToUpper() == SIP_Transport.TCP) - { - return true; - } - else if (transport.ToUpper() == SIP_Transport.TLS) - { - return true; - } - else - { - return false; - } - } - - /// - /// Gets if the specified value is "token". - /// - /// Value to check. - /// Returns true if specified valu is token, otherwise false. - /// Is raised when value is null reference. - public static bool IsToken(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - return MIME_Reader.IsToken(value); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/SIP_ValidateRequestEventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/SIP_ValidateRequestEventArgs.cs deleted file mode 100644 index 7d996aff5..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/SIP_ValidateRequestEventArgs.cs +++ /dev/null @@ -1,79 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - #region usings - - using System; - using System.Net; - - #endregion - - /// - /// This class provides data for SIP_Stack.ValidateRequest event. - /// - public class SIP_ValidateRequestEventArgs : EventArgs - { - #region Members - - private readonly IPEndPoint m_pRemoteEndPoint; - private readonly SIP_Request m_pRequest; - - #endregion - - #region Properties - - /// - /// Gets incoming SIP request. - /// - public SIP_Request Request - { - get { return m_pRequest; } - } - - /// - /// Gets IP end point what made request. - /// - public IPEndPoint RemoteEndPoint - { - get { return m_pRemoteEndPoint; } - } - - /// - /// Gets or sets response code. Value null means SIP stack will handle it. - /// - public string ResponseCode { get; set; } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Incoming SIP request. - /// IP end point what made request. - public SIP_ValidateRequestEventArgs(SIP_Request request, IPEndPoint remoteEndpoint) - { - m_pRequest = request; - m_pRemoteEndPoint = remoteEndpoint; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_ClientTransaction.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_ClientTransaction.cs deleted file mode 100644 index 3d30b419a..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_ClientTransaction.cs +++ /dev/null @@ -1,1215 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - #region usings - - using System; - using System.Threading; - using System.Timers; - using Message; - - #endregion - - /// - /// Implements SIP client transaction. Defined in rfc 3261 17.1. - /// - public class SIP_ClientTransaction : SIP_Transaction - { - #region Events - - /// - /// Is raised when transaction received response from remote party. - /// - public event EventHandler ResponseReceived = null; - - #endregion - - #region Members - - private bool m_IsCanceling; - - private TimerEx m_pTimerA; - private TimerEx m_pTimerB; - private TimerEx m_pTimerD; - private TimerEx m_pTimerE; - private TimerEx m_pTimerF; - private TimerEx m_pTimerK; - private int m_RSeq = -1; - - #endregion - - #region Properties - - /// - /// Gets or sets RSeq value. Value -1 means no reliable provisional response received. - /// - internal int RSeq - { - get { return m_RSeq; } - - set { m_RSeq = value; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner SIP stack. - /// SIP data flow which is used to send request. - /// SIP request that transaction will handle. - /// Is raised when stack,flow or request is null reference. - /// Is raised when any of the arguments has invalid value. - internal SIP_ClientTransaction(SIP_Stack stack, SIP_Flow flow, SIP_Request request) - : base(stack, flow, request) - { - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=false] created."); - } - - SetState(SIP_TransactionState.WaitingToStart); - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public override void Dispose() - { - lock (SyncRoot) - { - if (IsDisposed) - { - return; - } - - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=false] disposed."); - } - - // Kill timers. - if (m_pTimerA != null) - { - m_pTimerA.Dispose(); - m_pTimerA = null; - } - if (m_pTimerB != null) - { - m_pTimerB.Dispose(); - m_pTimerB = null; - } - if (m_pTimerD != null) - { - m_pTimerD.Dispose(); - m_pTimerD = null; - } - if (m_pTimerE != null) - { - m_pTimerE.Dispose(); - m_pTimerE = null; - } - if (m_pTimerF != null) - { - m_pTimerF.Dispose(); - m_pTimerF = null; - } - if (m_pTimerK != null) - { - m_pTimerK.Dispose(); - m_pTimerK = null; - } - - ResponseReceived = null; - - base.Dispose(); - } - } - - /// - /// Starts transaction processing. - /// - /// Is raised when this class is Disposed and this method is accessed. - /// Is raised when Start is called other state than 'WaitingToStart'. - public void Start() - { - lock (SyncRoot) - { - if (State == SIP_TransactionState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - else if (State != SIP_TransactionState.WaitingToStart) - { - throw new InvalidOperationException( - "Start method is valid only in 'WaitingToStart' state."); - } - - // Move processing to thread pool. - ThreadPool.QueueUserWorkItem(delegate - { - lock (SyncRoot) - { - #region INVITE - - if (Method == SIP_Methods.INVITE) - { - /* RFC 3261 17.1.1.2. - The initial state, "calling", MUST be entered when the TU - initiates a new client transaction with an INVITE request. The - client transaction MUST pass the request to the transport layer for - transmission (see Section 18). If an unreliable transport is being - used, the client transaction MUST start timer A with a value of T1. - If a reliable transport is being used, the client transaction SHOULD - NOT start timer A (Timer A controls request retransmissions). For - any transport, the client transaction MUST start timer B with a value - of 64*T1 seconds (Timer B controls transaction timeouts). - */ - - SetState(SIP_TransactionState.Calling); - - try - { - // Send initial request. - Stack.TransportLayer.SendRequest(Flow, - Request, - this); - } - catch (Exception x) - { - OnTransportError(x); - // NOTE: TransportError event handler could Dispose this transaction, so we need to check it. - if (State != SIP_TransactionState.Disposed) - { - SetState(SIP_TransactionState.Terminated); - } - return; - } - - // Start timer A for unreliable transports. - if (!Flow.IsReliable) - { - m_pTimerA = new TimerEx( - SIP_TimerConstants.T1, false); - m_pTimerA.Elapsed += m_pTimerA_Elapsed; - m_pTimerA.Enabled = true; - - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + - ID + "';method='" + - Method + - "';IsServer=false] timer A(INVITE request retransmission) started, will triger after " + - m_pTimerA.Interval + - "."); - } - } - - // Start timer B. - m_pTimerB = new TimerEx( - 64*SIP_TimerConstants.T1, false); - m_pTimerB.Elapsed += m_pTimerB_Elapsed; - m_pTimerB.Enabled = true; - - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + - ID + "';method='" + - Method + - "';IsServer=false] timer B(INVITE calling state timeout) started, will triger after " + - m_pTimerB.Interval + "."); - } - } - - #endregion - - #region Non-INVITE - - else - { - /* RFC 3261 17.1.2.2. - The "Trying" state is entered when the TU initiates a new client - transaction with a request. When entering this state, the client - transaction SHOULD set timer F to fire in 64*T1 seconds. The request - MUST be passed to the transport layer for transmission. If an - unreliable transport is in use, the client transaction MUST set timer - E to fire in T1 seconds. - */ - - SetState(SIP_TransactionState.Trying); - - // Start timer F. - m_pTimerF = new TimerEx( - 64*SIP_TimerConstants.T1, false); - m_pTimerF.Elapsed += m_pTimerF_Elapsed; - m_pTimerF.Enabled = true; - - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + - ID + "';method='" + - Method + - "';IsServer=false] timer F(Non-INVITE trying,proceeding state timeout) started, will triger after " + - m_pTimerF.Interval + "."); - } - - try - { - // Send initial request. - Stack.TransportLayer.SendRequest(Flow, - Request, - this); - } - catch (Exception x) - { - OnTransportError(x); - // NOTE: TransportError event handler could Dispose this transaction, so we need to check it. - if (State != SIP_TransactionState.Disposed) - { - SetState(SIP_TransactionState.Terminated); - } - return; - } - - // Start timer E for unreliable transports. - if (!Flow.IsReliable) - { - m_pTimerE = new TimerEx( - SIP_TimerConstants.T1, false); - m_pTimerE.Elapsed += m_pTimerE_Elapsed; - m_pTimerE.Enabled = true; - - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + - ID + "';method='" + - Method + - "';IsServer=false] timer E(Non-INVITE request retransmission) started, will triger after " + - m_pTimerE.Interval + - "."); - } - } - } - - #endregion - } - }); - } - } - - /// - /// Starts canceling transaction. - /// - /// If client transaction has got final response, canel has no effect and will be ignored. - /// Is raised when this class is Disposed and this method is accessed. - public override void Cancel() - { - lock (SyncRoot) - { - if (State == SIP_TransactionState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - else if (State == SIP_TransactionState.WaitingToStart) - { - SetState(SIP_TransactionState.Terminated); - return; - } - else if (m_IsCanceling) - { - return; - } - else if (State == SIP_TransactionState.Terminated) - { - // RFC 3261 9.1. We got final response, nothing to cancel. - return; - } - if (FinalResponse != null) - { - return; - } - - m_IsCanceling = true; - - /* RFC 3261 9.1. - If no provisional response has been received, the CANCEL request MUST - NOT be sent; rather, the client MUST wait for the arrival of a - provisional response before sending the request. - */ - if (Responses.Length == 0) - { - // We set canceling flag, so if provisional response arrives, we do cancel. - } - else - { - SendCancel(); - } - } - } - - #endregion - - #region Event handlers - - /// - /// Is raised when INVITE timer A triggered. - /// - /// Sender. - /// Event data. - private void m_pTimerA_Elapsed(object sender, ElapsedEventArgs e) - { - /* RFC 3261 17.1.1.2. - When timer A fires, the client transaction MUST retransmit the - request by passing it to the transport layer, and MUST reset the - timer with a value of 2*T1. The formal definition of retransmit - within the context of the transaction layer is to take the message - previously sent to the transport layer and pass it to the transport - layer once more. - - When timer A fires 2*T1 seconds later, the request MUST be - retransmitted again (assuming the client transaction is still in this - state). This process MUST continue so that the request is - retransmitted with intervals that double after each transmission. - These retransmissions SHOULD only be done while the client - transaction is in the "calling" state. - */ - - lock (SyncRoot) - { - if (State == SIP_TransactionState.Calling) - { - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=false] timer A(INVITE request retransmission) triggered."); - } - - try - { - // Retransmit request. - Stack.TransportLayer.SendRequest(Flow, Request, this); - } - catch (Exception x) - { - OnTransportError(x); - SetState(SIP_TransactionState.Terminated); - return; - } - - // Update(double current) next transmit time. - m_pTimerA.Interval *= 2; - m_pTimerA.Enabled = true; - - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=false] timer A(INVITE request retransmission) updated, will triger after " + - m_pTimerA.Interval + "."); - } - } - } - } - - /// - /// Is raised when INVITE timer B triggered. - /// - /// Sender. - /// Event data. - private void m_pTimerB_Elapsed(object sender, ElapsedEventArgs e) - { - /* RFC 3261 17.1.1.2. - If the client transaction is still in the "Calling" state when timer - B fires, the client transaction SHOULD inform the TU that a timeout - has occurred. The client transaction MUST NOT generate an ACK. The - value of 64*T1 is equal to the amount of time required to send seven - requests in the case of an unreliable transport. - */ - - lock (SyncRoot) - { - if (State == SIP_TransactionState.Calling) - { - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=false] timer B(INVITE calling state timeout) triggered."); - } - - OnTimedOut(); - SetState(SIP_TransactionState.Terminated); - - // Stop timers A,B. - if (m_pTimerA != null) - { - m_pTimerA.Dispose(); - m_pTimerA = null; - } - if (m_pTimerB != null) - { - m_pTimerB.Dispose(); - m_pTimerB = null; - } - } - } - } - - /// - /// Is raised when INVITE timer D triggered. - /// - /// Sender. - /// Event data. - private void m_pTimerD_Elapsed(object sender, ElapsedEventArgs e) - { - /* RFC 3261 17.1.1.2. - If timer D fires while the client transaction is in the "Completed" - state, the client transaction MUST move to the terminated state. - */ - - lock (SyncRoot) - { - if (State == SIP_TransactionState.Completed) - { - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=false] timer D(INVITE 3xx - 6xx response retransmission wait) triggered."); - } - - SetState(SIP_TransactionState.Terminated); - } - } - } - - /// - /// Is raised when Non-INVITE timer E triggered. - /// - /// Sender. - /// Event data. - private void m_pTimerE_Elapsed(object sender, ElapsedEventArgs e) - { - /* RFC 3261 17.1.2.2. - If timer E fires while in Trying state, the timer is reset, but this time with a value of MIN(2*T1, T2). - When the timer fires again, it is reset to a MIN(4*T1, T2). This process continues so that - retransmissions occur with an exponentially increasing interval that caps at T2. The default - value of T2 is 4s, and it represents the amount of time a non-INVITE server transaction will take to - respond to a request, if it does not respond immediately. For the default values of T1 and T2, - this results in intervals of 500 ms, 1 s, 2 s, 4 s, 4 s, 4 s, etc. - */ - - lock (SyncRoot) - { - if (State == SIP_TransactionState.Trying) - { - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=false] timer E(-NonINVITE request retransmission) triggered."); - } - - try - { - // Retransmit request. - Stack.TransportLayer.SendRequest(Flow, Request, this); - } - catch (Exception x) - { - OnTransportError(x); - SetState(SIP_TransactionState.Terminated); - return; - } - - // Update(double current) next transmit time. - m_pTimerE.Interval = Math.Min(m_pTimerE.Interval*2, SIP_TimerConstants.T2); - m_pTimerE.Enabled = true; - - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=false] timer E(Non-INVITE request retransmission) updated, will triger after " + - m_pTimerE.Interval + "."); - } - } - } - } - - /// - /// Is raised when Non-INVITE timer F triggered. - /// - /// Sender. - /// Event data. - private void m_pTimerF_Elapsed(object sender, ElapsedEventArgs e) - { - /* RFC 3261 17.1.2.2. - If Timer F fires while in the "Trying" state, the client transaction SHOULD inform the TU about the - timeout, and then it SHOULD enter the "Terminated" state. - - If timer F fires while in the "Proceeding" state, the TU MUST be informed of a timeout, and the - client transaction MUST transition to the terminated state. - */ - - lock (SyncRoot) - { - if (State == SIP_TransactionState.Trying || State == SIP_TransactionState.Proceeding) - { - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=false] timer F(Non-INVITE trying,proceeding state timeout) triggered."); - } - - OnTimedOut(); - SetState(SIP_TransactionState.Terminated); - - if (m_pTimerE != null) - { - m_pTimerE.Dispose(); - m_pTimerE = null; - } - if (m_pTimerF != null) - { - m_pTimerF.Dispose(); - m_pTimerF = null; - } - } - } - } - - /// - /// Is raised when Non-INVITE timer K triggered. - /// - /// Sender. - /// Event data. - private void m_pTimerK_Elapsed(object sender, ElapsedEventArgs e) - { - lock (SyncRoot) - { - if (State == SIP_TransactionState.Completed) - { - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=false] timer K(Non-INVITE 3xx - 6xx response retransmission wait) triggered."); - } - - SetState(SIP_TransactionState.Terminated); - } - } - } - - #endregion - - #region Utility methods - - /// - /// Creates and send CANCEL request to remote target. - /// - private void SendCancel() - { - /* RFC 3261 9.1. - The following procedures are used to construct a CANCEL request. The - Request-URI, Call-ID, To, the numeric part of CSeq, and From header - fields in the CANCEL request MUST be identical to those in the - request being cancelled, including tags. A CANCEL constructed by a - client MUST have only a single Via header field value matching the - top Via value in the request being cancelled. Using the same values - for these header fields allows the CANCEL to be matched with the - request it cancels (Section 9.2 indicates how such matching occurs). - However, the method part of the CSeq header field MUST have a value - of CANCEL. This allows it to be identified and processed as a - transaction in its own right (See Section 17). - - If the request being cancelled contains a Route header field, the - CANCEL request MUST include that Route header field's values. - - This is needed so that stateless proxies are able to route CANCEL - requests properly. - */ - - SIP_Request cancelRequest = new SIP_Request(SIP_Methods.CANCEL); - cancelRequest.RequestLine.Uri = Request.RequestLine.Uri; - cancelRequest.Via.Add(Request.Via.GetTopMostValue().ToStringValue()); - cancelRequest.CallID = Request.CallID; - cancelRequest.From = Request.From; - cancelRequest.To = Request.To; - cancelRequest.CSeq = new SIP_t_CSeq(Request.CSeq.SequenceNumber, SIP_Methods.CANCEL); - foreach (SIP_t_AddressParam route in Request.Route.GetAllValues()) - { - cancelRequest.Route.Add(route.ToStringValue()); - } - cancelRequest.MaxForwards = 70; - - // We must use same data flow to send CANCEL what sent initial request. - SIP_ClientTransaction transaction = Stack.TransactionLayer.CreateClientTransaction(Flow, - cancelRequest, - false); - transaction.Start(); - } - - /// - /// Creates and sends ACK for final(3xx - 6xx) failure response. - /// - /// SIP response. - /// Is raised when response is null. - private void SendAck(SIP_Response response) - { - if (response == null) - { - throw new ArgumentNullException("resposne"); - } - - /* RFC 3261 17.1.1.3 Construction of the ACK Request. - The ACK request constructed by the client transaction MUST contain - values for the Call-ID, From, and Request-URI that are equal to the - values of those header fields in the request passed to the transport - by the client transaction (call this the "original request"). The To - header field in the ACK MUST equal the To header field in the - response being acknowledged, and therefore will usually differ from - the To header field in the original request by the addition of the - tag parameter. The ACK MUST contain a single Via header field, and - this MUST be equal to the top Via header field of the original - request. The CSeq header field in the ACK MUST contain the same - value for the sequence number as was present in the original request, - but the method parameter MUST be equal to "ACK". - - If the INVITE request whose response is being acknowledged had Route - header fields, those header fields MUST appear in the ACK. This is - to ensure that the ACK can be routed properly through any downstream - stateless proxies. - */ - - SIP_Request ackRequest = new SIP_Request(SIP_Methods.ACK); - ackRequest.RequestLine.Uri = Request.RequestLine.Uri; - ackRequest.Via.AddToTop(Request.Via.GetTopMostValue().ToStringValue()); - ackRequest.CallID = Request.CallID; - ackRequest.From = Request.From; - ackRequest.To = response.To; - ackRequest.CSeq = new SIP_t_CSeq(Request.CSeq.SequenceNumber, "ACK"); - foreach (SIP_HeaderField h in response.Header.Get("Route:")) - { - ackRequest.Header.Add("Route:", h.Value); - } - ackRequest.MaxForwards = 70; - - try - { - // Send request to target. - Stack.TransportLayer.SendRequest(Flow, ackRequest, this); - } - catch (SIP_TransportException x) - { - OnTransportError(x); - SetState(SIP_TransactionState.Terminated); - } - } - - // FIX ME: - - /// - /// Raises ResponseReceived event. - /// - /// SIP response received. - private void OnResponseReceived(SIP_Response response) - { - if (ResponseReceived != null) - { - ResponseReceived(this, new SIP_ResponseReceivedEventArgs(Stack, this, response)); - } - } - - #endregion - - #region Internal methods - - /// - /// Processes specified response through this transaction. - /// - /// SIP data flow what received response. - /// SIP response to process. - /// Is raised when flow,response is null reference. - internal void ProcessResponse(SIP_Flow flow, SIP_Response response) - { - if (flow == null) - { - throw new ArgumentNullException("flow"); - } - if (response == null) - { - throw new ArgumentNullException("response"); - } - - lock (SyncRoot) - { - if (State == SIP_TransactionState.Disposed) - { - return; - } - /* RFC 3261 9.1. CANCEL. - *) If provisional response, send CANCEL, we should get '478 Request terminated'. - *) If final response, skip canceling, nothing to cancel. - */ - else if (m_IsCanceling && response.StatusCodeType == SIP_StatusCodeType.Provisional) - { - SendCancel(); - return; - } - - // Log - if (Stack.Logger != null) - { - byte[] responseData = response.ToByteData(); - - Stack.Logger.AddRead(Guid.NewGuid().ToString(), - null, - 0, - "Response [transactionID='" + ID + "'; method='" + - response.CSeq.RequestMethod + "'; cseq='" + - response.CSeq.SequenceNumber + "'; " + "transport='" + flow.Transport + - "'; size='" + responseData.Length + "'; statusCode='" + - response.StatusCode + "'; " + "reason='" + response.ReasonPhrase + - "'; received '" + flow.LocalEP + "' <- '" + flow.RemoteEP + "'.", - flow.LocalEP, - flow.RemoteEP, - responseData); - } - - #region INVITE - - /* RFC 3261 17.1.1.2. - |INVITE from TU - Timer A fires |INVITE sent - Reset A, V Timer B fires - INVITE sent +-----------+ or Transport Err. - +---------| |---------------+inform TU - | | Calling | | - +-------->| |-------------->| - +-----------+ 2xx | - | | 2xx to TU | - | |1xx | - 300-699 +---------------+ |1xx to TU | - ACK sent | | | - resp. to TU | 1xx V | - | 1xx to TU -----------+ | - | +---------| | | - | | |Proceeding |-------------->| - | +-------->| | 2xx | - | +-----------+ 2xx to TU | - | 300-699 | | - | ACK sent, | | - | resp. to TU| | - | | | NOTE: - | 300-699 V | - | ACK sent +-----------+Transport Err. | transitions - | +---------| |Inform TU | labeled with - | | | Completed |-------------->| the event - | +-------->| | | over the action - | +-----------+ | to take - | ^ | | - | | | Timer D fires | - +--------------+ | - | - | | - V | - +-----------+ | - | | | - | Terminated|<--------------+ - | | - +-----------+ - - */ - - if (Method == SIP_Methods.INVITE) - { - #region Calling - - if (State == SIP_TransactionState.Calling) - { - // Store response. - AddResponse(response); - - // Stop timer A,B - if (m_pTimerA != null) - { - m_pTimerA.Dispose(); - m_pTimerA = null; - - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=false] timer A(INVITE request retransmission) stoped."); - } - } - if (m_pTimerB != null) - { - m_pTimerB.Dispose(); - m_pTimerB = null; - - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=false] timer B(INVITE calling state timeout) stoped."); - } - } - - // 1xx response. - if (response.StatusCodeType == SIP_StatusCodeType.Provisional) - { - OnResponseReceived(response); - SetState(SIP_TransactionState.Proceeding); - } - // 2xx response. - else if (response.StatusCodeType == SIP_StatusCodeType.Success) - { - OnResponseReceived(response); - SetState(SIP_TransactionState.Terminated); - } - // 3xx - 6xx response. - else - { - SendAck(response); - OnResponseReceived(response); - SetState(SIP_TransactionState.Completed); - - /* RFC 3261 17.1.1.2. - The client transaction SHOULD start timer D when it enters the "Completed" state, - with a value of at least 32 seconds for unreliable transports, and a value of zero - seconds for reliable transports. - */ - m_pTimerD = new TimerEx(Flow.IsReliable ? 0 : Workaround.Definitions.MaxStreamLineLength, false); - m_pTimerD.Elapsed += m_pTimerD_Elapsed; - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=false] timer D(INVITE 3xx - 6xx response retransmission wait) started, will triger after " + - m_pTimerD.Interval + "."); - } - m_pTimerD.Enabled = true; - } - } - - #endregion - - #region Proceeding - - else if (State == SIP_TransactionState.Proceeding) - { - // Store response. - AddResponse(response); - - // 1xx response. - if (response.StatusCodeType == SIP_StatusCodeType.Provisional) - { - OnResponseReceived(response); - } - // 2xx response. - else if (response.StatusCodeType == SIP_StatusCodeType.Success) - { - OnResponseReceived(response); - SetState(SIP_TransactionState.Terminated); - } - // 3xx - 6xx response. - else - { - SendAck(response); - OnResponseReceived(response); - SetState(SIP_TransactionState.Completed); - - /* RFC 3261 17.1.1.2. - The client transaction SHOULD start timer D when it enters the "Completed" state, - with a value of at least 32 seconds for unreliable transports, and a value of zero - seconds for reliable transports. - */ - m_pTimerD = new TimerEx(Flow.IsReliable ? 0 : Workaround.Definitions.MaxStreamLineLength, false); - m_pTimerD.Elapsed += m_pTimerD_Elapsed; - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=false] timer D(INVITE 3xx - 6xx response retransmission wait) started, will triger after " + - m_pTimerD.Interval + "."); - } - m_pTimerD.Enabled = true; - } - } - - #endregion - - #region Completed - - else if (State == SIP_TransactionState.Completed) - { - // 3xx - 6xx - if (response.StatusCode >= 300) - { - SendAck(response); - } - } - - #endregion - - #region Terminated - - else if (State == SIP_TransactionState.Terminated) - { - // We should never reach here, but if so, do nothing. - } - - #endregion - } - - #endregion - - #region Non-INVITE - - /* RFC 3251 17.1.2.2 - |Request from TU - |send request - Timer E V - send request +-----------+ - +---------| |-------------------+ - | | Trying | Timer F | - +-------->| | or Transport Err.| - +-----------+ inform TU | - 200-699 | | | - resp. to TU | |1xx | - +---------------+ |resp. to TU | - | | | - | Timer E V Timer F | - | send req +-----------+ or Transport Err. | - | +---------| | inform TU | - | | |Proceeding |------------------>| - | +-------->| |-----+ | - | +-----------+ |1xx | - | | ^ |resp to TU | - | 200-699 | +--------+ | - | resp. to TU | | - | | | - | V | - | +-----------+ | - | | | | - | | Completed | | - | | | | - | +-----------+ | - | ^ | | - | | | Timer K | - +--------------+ | - | - | | - V | - NOTE: +-----------+ | - | | | - transitions | Terminated|<------------------+ - labeled with | | - the event +-----------+ - over the action - to take - */ - - else - { - #region Trying - - if (State == SIP_TransactionState.Trying) - { - // Store response. - AddResponse(response); - - // Stop timer E - if (m_pTimerE != null) - { - m_pTimerE.Dispose(); - m_pTimerE = null; - - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=false] timer E(Non-INVITE request retransmission) stoped."); - } - } - - // 1xx response. - if (response.StatusCodeType == SIP_StatusCodeType.Provisional) - { - OnResponseReceived(response); - SetState(SIP_TransactionState.Proceeding); - } - // 2xx - 6xx response. - else - { - // Stop timer F - if (m_pTimerF != null) - { - m_pTimerF.Dispose(); - m_pTimerF = null; - - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=false] timer F(Non-INVITE trying,proceeding state timeout) stoped."); - } - } - - OnResponseReceived(response); - SetState(SIP_TransactionState.Completed); - - /* RFC 3261 17.1.2.2. - The client transaction enters the "Completed" state, it MUST set - Timer K to fire in T4 seconds for unreliable transports, and zero - seconds for reliable transports. - */ - m_pTimerK = new TimerEx(Flow.IsReliable ? 1 : SIP_TimerConstants.T4, false); - m_pTimerK.Elapsed += m_pTimerK_Elapsed; - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=false] timer K(Non-INVITE 3xx - 6xx response retransmission wait) started, will triger after " + - m_pTimerK.Interval + "."); - } - m_pTimerK.Enabled = true; - } - } - - #endregion - - #region Proceeding - - else if (State == SIP_TransactionState.Proceeding) - { - // Store response. - AddResponse(response); - - // 1xx response. - if (response.StatusCodeType == SIP_StatusCodeType.Provisional) - { - OnResponseReceived(response); - } - // 2xx - 6xx response. - else - { - // Stop timer F - if (m_pTimerF != null) - { - m_pTimerF.Dispose(); - m_pTimerF = null; - - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=false] timer F(Non-INVITE trying,proceeding state timeout) stoped."); - } - } - - OnResponseReceived(response); - SetState(SIP_TransactionState.Completed); - - /* RFC 3261 17.1.2.2. - The client transaction enters the "Completed" state, it MUST set - Timer K to fire in T4 seconds for unreliable transports, and zero - seconds for reliable transports. - */ - m_pTimerK = new TimerEx(Flow.IsReliable ? 0 : SIP_TimerConstants.T4, false); - m_pTimerK.Elapsed += m_pTimerK_Elapsed; - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=false] timer K(Non-INVITE 3xx - 6xx response retransmission wait) started, will triger after " + - m_pTimerK.Interval + "."); - } - m_pTimerK.Enabled = true; - } - } - - #endregion - - #region Completed - - else if (State == SIP_TransactionState.Completed) - { - // Eat retransmited response. - } - - #endregion - - #region Terminated - - else if (State == SIP_TransactionState.Terminated) - { - // We should never reach here, but if so, do nothing. - } - - #endregion - } - - #endregion - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Dialog.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Dialog.cs deleted file mode 100644 index a77c376d2..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Dialog.cs +++ /dev/null @@ -1,927 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - #region usings - - using System; - using Message; - - #endregion - - /// - /// This class is base class for SIP dialogs. Defined in RFC 3261 12. - /// - public class SIP_Dialog - { - #region Events - - /// - /// This event is raised when Dialog state has changed. - /// - public event EventHandler StateChanged = null; - - /// - /// This event is raised when remote-party terminates dialog with BYE request. - /// - /// This event is useful only if the application is interested in processing the headers in the BYE message. - public event EventHandler TerminatedByRemoteParty = null; - - #endregion - - #region Members - - private readonly DateTime m_CreateTime; - private string m_CallID = ""; - private bool m_IsSecure; - private bool m_IsTerminatedByRemoteParty; - private int m_LocalSeqNo; - private string m_LocalTag = ""; - private SIP_Flow m_pFlow; - private SIP_Uri m_pLocalContact; - private AbsoluteUri m_pLocalUri; - private object m_pLock = new object(); - private SIP_Uri m_pRemoteTarget; - private AbsoluteUri m_pRemoteUri; - private SIP_t_AddressParam[] m_pRouteSet; - private SIP_Stack m_pStack; - private int m_RemoteSeqNo; - private string m_RemoteTag = ""; - private SIP_DialogState m_State = SIP_DialogState.Early; - - #endregion - - #region Properties - - /// - /// Gets an object that can be used to synchronize access to the dialog. - /// - public object SyncRoot - { - get { return m_pLock; } - } - - /// - /// Gets dialog state. - /// - public SIP_DialogState State - { - get { return m_State; } - } - - /// - /// Gets owner stack. - /// - /// Is raised when this class is Disposed and this property is accessed. - public SIP_Stack Stack - { - get - { - if (State == SIP_DialogState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pStack; - } - } - - /// - /// Gets dialog creation time. - /// - /// Is raised when this class is Disposed and this property is accessed. - public DateTime CreateTime - { - get - { - if (State == SIP_DialogState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_CreateTime; - } - } - - /// - /// Gets dialog ID. - /// - /// Is raised when this class is Disposed and this property is accessed. - public string ID - { - get - { - if (State == SIP_DialogState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return CallID + "-" + LocalTag + "-" + RemoteTag; - } - } - - /// - /// Get call ID. - /// - /// Is raised when this class is Disposed and this property is accessed. - public string CallID - { - get - { - if (State == SIP_DialogState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_CallID; - } - } - - /// - /// Gets local-tag. - /// - /// Is raised when this class is Disposed and this property is accessed. - public string LocalTag - { - get - { - if (State == SIP_DialogState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_LocalTag; - } - } - - /// - /// Gets remote-tag. - /// - /// Is raised when this class is Disposed and this property is accessed. - public string RemoteTag - { - get - { - if (State == SIP_DialogState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_RemoteTag; - } - } - - /// - /// Gets local sequence number. - /// - /// Is raised when this class is Disposed and this property is accessed. - public int LocalSeqNo - { - get - { - if (State == SIP_DialogState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_LocalSeqNo; - } - } - - /// - /// Gets remote sequence number. - /// - /// Is raised when this class is Disposed and this property is accessed. - public int RemoteSeqNo - { - get - { - if (State == SIP_DialogState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_RemoteSeqNo; - } - } - - /// - /// Gets local URI. - /// - /// Is raised when this class is Disposed and this property is accessed. - public AbsoluteUri LocalUri - { - get - { - if (State == SIP_DialogState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pLocalUri; - } - } - - /// - /// Gets remote URI. - /// - /// Is raised when this class is Disposed and this property is accessed. - public AbsoluteUri RemoteUri - { - get - { - if (State == SIP_DialogState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pRemoteUri; - } - } - - /// - /// Gets local contact URI. - /// - /// Is raised when this class is Disposed and this property is accessed. - public SIP_Uri LocalContact - { - get - { - if (State == SIP_DialogState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pLocalContact; - } - } - - /// - /// Gets remote target URI. - /// - /// Is raised when this class is Disposed and this property is accessed. - public SIP_Uri RemoteTarget - { - get - { - if (State == SIP_DialogState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pRemoteTarget; - } - } - - /// - /// Gets if dialog uses secure transport. - /// - /// Is raised when this class is Disposed and this property is accessed. - public bool IsSecure - { - get - { - if (State == SIP_DialogState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_IsSecure; - } - } - - /// - /// Gets route set. - /// - /// Is raised when this class is Disposed and this property is accessed. - public SIP_t_AddressParam[] RouteSet - { - get - { - if (State == SIP_DialogState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pRouteSet; - } - } - - /// - /// Gets if dialog was terminated by remote-party. - /// - /// Is raised when this class is Disposed and this property is accessed. - public bool IsTerminatedByRemoteParty - { - get - { - if (State == SIP_DialogState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_IsTerminatedByRemoteParty; - } - } - - /// - /// Gets data flow used to send or receive last SIP message. - /// - /// Is raised when this class is Disposed and this property is accessed. - public SIP_Flow Flow - { - get - { - if (State == SIP_DialogState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pFlow; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public SIP_Dialog() - { - m_CreateTime = DateTime.Now; - m_pRouteSet = new SIP_t_AddressParam[0]; - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public virtual void Dispose() - { - lock (m_pLock) - { - if (State == SIP_DialogState.Disposed) - { - return; - } - - SetState(SIP_DialogState.Disposed, true); - - m_pStack = null; - m_CallID = null; - m_LocalTag = null; - m_RemoteTag = null; - m_pLocalUri = null; - m_pRemoteUri = null; - m_pLocalContact = null; - m_pRemoteTarget = null; - m_pRouteSet = null; - m_pFlow = null; - m_pLock = null; - } - } - - /// - /// Terminates dialog. - /// - /// Is raised when this class is Disposed and this method is accessed. - public void Terminate() - { - Terminate(null, true); - } - - /// - /// Terminates dialog. - /// - /// Termination reason. This value may be null. - /// If true BYE is sent to remote party. - /// Is raised when this class is Disposed and this method is accessed. - public virtual void Terminate(string reason, bool sendBye) - { - lock (m_pLock) - { - if (State == SIP_DialogState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (State == SIP_DialogState.Terminating || State == SIP_DialogState.Terminated) - { - return; - } - - /* RFC 3261 15. - The caller's UA MAY send a BYE for either confirmed or early dialogs, and the callee's UA MAY send a BYE on - confirmed dialogs, but MUST NOT send a BYE on early dialogs. - - RFC 3261 15.1. - Once the BYE is constructed, the UAC core creates a new non-INVITE client transaction, and passes it the BYE request. - The UAC MUST consider the session terminated (and therefore stop sending or listening for media) as soon as the BYE - request is passed to the client transaction. If the response for the BYE is a 481 (Call/Transaction Does Not Exist) - or a 408 (Request Timeout) or no response at all is received for the BYE (that is, a timeout is returned by the - client transaction), the UAC MUST consider the session and the dialog terminated. - */ - - SetState(SIP_DialogState.Terminating, true); - - if (sendBye) - { - // TODO: UAS early - - if (State == SIP_DialogState.Confirmed) - { - SIP_Request bye = CreateRequest(SIP_Methods.BYE); - if (!string.IsNullOrEmpty(reason)) - { - SIP_t_ReasonValue r = new SIP_t_ReasonValue(); - r.Protocol = "SIP"; - r.Text = reason; - bye.Reason.Add(r.ToStringValue()); - } - - // Send BYE, just wait BYE to complete, we don't care about response code. - SIP_RequestSender sender = CreateRequestSender(bye); - sender.Completed += delegate { SetState(SIP_DialogState.Terminated, true); }; - sender.Start(); - } - } - else - { - SetState(SIP_DialogState.Terminated, true); - } - } - } - - /// - /// Creates new SIP request using this dialog info. - /// - /// SIP method. - /// Is raised when this class is Disposed and this method is accessed. - /// Is raised when method is null reference. - /// Is raised when any of the arguments has invalid value. - /// Returns created request. - public SIP_Request CreateRequest(string method) - { - if (State == SIP_DialogState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (method == null) - { - throw new ArgumentNullException("method"); - } - if (method == string.Empty) - { - throw new ArgumentException("Argument 'method' value must be specified."); - } - - /* RFC 3261 12.2.1.1. - A request within a dialog is constructed by using many of the - components of the state stored as part of the dialog. - - The URI in the To field of the request MUST be set to the remote URI - from the dialog state. The tag in the To header field of the request - MUST be set to the remote tag of the dialog ID. The From URI of the - request MUST be set to the local URI from the dialog state. The tag - in the From header field of the request MUST be set to the local tag - of the dialog ID. If the value of the remote or local tags is null, - the tag parameter MUST be omitted from the To or From header fields, - respectively. - - The Call-ID of the request MUST be set to the Call-ID of the dialog. - Requests within a dialog MUST contain strictly monotonically - increasing and contiguous CSeq sequence numbers (increasing-by-one) - in each direction (excepting ACK and CANCEL of course, whose numbers - equal the requests being acknowledged or cancelled). Therefore, if - the local sequence number is not empty, the value of the local - sequence number MUST be incremented by one, and this value MUST be - placed into the CSeq header field. If the local sequence number is - empty, an initial value MUST be chosen using the guidelines of - Section 8.1.1.5. The method field in the CSeq header field value - MUST match the method of the request. - - With a length of 32 bits, a client could generate, within a single - call, one request a second for about 136 years before needing to - wrap around. The initial value of the sequence number is chosen - so that subsequent requests within the same call will not wrap - around. A non-zero initial value allows clients to use a time- - based initial sequence number. A client could, for example, - choose the 31 most significant bits of a 32-bit second clock as an - initial sequence number. - - The UAC uses the remote target and route set to build the Request-URI - and Route header field of the request. - - If the route set is empty, the UAC MUST place the remote target URI - into the Request-URI. The UAC MUST NOT add a Route header field to - the request. - - If the route set is not empty, and the first URI in the route set - contains the lr parameter (see Section 19.1.1), the UAC MUST place - the remote target URI into the Request-URI and MUST include a Route - header field containing the route set values in order, including all - parameters. - - If the route set is not empty, and its first URI does not contain the - lr parameter, the UAC MUST place the first URI from the route set - into the Request-URI, stripping any parameters that are not allowed - in a Request-URI. The UAC MUST add a Route header field containing - the remainder of the route set values in order, including all - parameters. The UAC MUST then place the remote target URI into the - Route header field as the last value. - - For example, if the remote target is sip:user@remoteua and the route - set contains: - ,,, - - The request will be formed with the following Request-URI and Route - header field: - METHOD sip:proxy1 - Route: ,,, - - If the first URI of the route set does not contain the lr - parameter, the proxy indicated does not understand the routing - mechanisms described in this document and will act as specified in - RFC 2543, replacing the Request-URI with the first Route header - field value it receives while forwarding the message. Placing the - Request-URI at the end of the Route header field preserves the - information in that Request-URI across the strict router (it will - be returned to the Request-URI when the request reaches a loose- - router). - - A UAC SHOULD include a Contact header field in any target refresh - requests within a dialog, and unless there is a need to change it, - the URI SHOULD be the same as used in previous requests within the - dialog. If the "secure" flag is true, that URI MUST be a SIPS URI. - As discussed in Section 12.2.2, a Contact header field in a target - refresh request updates the remote target URI. This allows a UA to - provide a new contact address, should its address change during the - duration of the dialog. - - However, requests that are not target refresh requests do not affect - the remote target URI for the dialog. - - The rest of the request is formed as described in Section 8.1.1. - */ - - lock (m_pLock) - { - SIP_Request request = m_pStack.CreateRequest(method, - new SIP_t_NameAddress("", m_pRemoteUri), - new SIP_t_NameAddress("", m_pLocalUri)); - if (m_pRouteSet.Length == 0) - { - request.RequestLine.Uri = m_pRemoteTarget; - } - else - { - SIP_Uri topmostRoute = ((SIP_Uri) m_pRouteSet[0].Address.Uri); - if (topmostRoute.Param_Lr) - { - request.RequestLine.Uri = m_pRemoteTarget; - for (int i = 0; i < m_pRouteSet.Length; i++) - { - request.Route.Add(m_pRouteSet[i].ToStringValue()); - } - } - else - { - request.RequestLine.Uri = SIP_Utils.UriToRequestUri(topmostRoute); - for (int i = 1; i < m_pRouteSet.Length; i++) - { - request.Route.Add(m_pRouteSet[i].ToStringValue()); - } - } - } - request.To.Tag = m_RemoteTag; - request.From.Tag = m_LocalTag; - request.CallID = m_CallID; - request.CSeq.SequenceNumber = ++m_LocalSeqNo; - request.Contact.Add(m_pLocalContact.ToString()); - - return request; - } - } - - /// - /// Creates SIP request sender for the specified request. - /// - /// All requests sent through this dialog SHOULD use this request sender to send out requests. - /// SIP request. - /// Returns created sender. - /// Is raised when this class is Disposed and this method is accessed. - /// Is raised when request is null. - public SIP_RequestSender CreateRequestSender(SIP_Request request) - { - lock (m_pLock) - { - if (State == SIP_DialogState.Terminated) - { - throw new ObjectDisposedException(GetType().Name); - } - if (request == null) - { - throw new ArgumentNullException("request"); - } - - SIP_RequestSender sender = m_pStack.CreateRequestSender(request, Flow); - - return sender; - } - } - - #endregion - - #region Virtual methods - - /// - /// Initializes dialog. - /// - /// Owner stack. - /// Owner transaction. - /// SIP response what caused dialog creation. - /// Is raised when stack,transaction or response. - protected internal virtual void Init(SIP_Stack stack, - SIP_Transaction transaction, - SIP_Response response) - { - if (stack == null) - { - throw new ArgumentNullException("stack"); - } - if (transaction == null) - { - throw new ArgumentNullException("transaction"); - } - if (response == null) - { - throw new ArgumentNullException("response"); - } - - m_pStack = stack; - - #region UAS - - /* RFC 3261 12.1.1. - The UAS then constructs the state of the dialog. This state MUST be - maintained for the duration of the dialog. - - If the request arrived over TLS, and the Request-URI contained a SIPS - URI, the "secure" flag is set to TRUE. - - The route set MUST be set to the list of URIs in the Record-Route - header field from the request, taken in order and preserving all URI - parameters. If no Record-Route header field is present in the - request, the route set MUST be set to the empty set. This route set, - even if empty, overrides any pre-existing route set for future - requests in this dialog. The remote target MUST be set to the URI - from the Contact header field of the request. - - The remote sequence number MUST be set to the value of the sequence - number in the CSeq header field of the request. The local sequence - number MUST be empty. The call identifier component of the dialog ID - MUST be set to the value of the Call-ID in the request. The local - tag component of the dialog ID MUST be set to the tag in the To field - in the response to the request (which always includes a tag), and the - remote tag component of the dialog ID MUST be set to the tag from the - From field in the request. A UAS MUST be prepared to receive a - request without a tag in the From field, in which case the tag is - considered to have a value of null. - - This is to maintain backwards compatibility with RFC 2543, which - did not mandate From tags. - - The remote URI MUST be set to the URI in the From field, and the - local URI MUST be set to the URI in the To field. - */ - - if (transaction is SIP_ServerTransaction) - { - // TODO: Validate request or client transaction must do it ? - - m_IsSecure = ((SIP_Uri) transaction.Request.RequestLine.Uri).IsSecure; - m_pRouteSet = - (SIP_t_AddressParam[]) Core.ReverseArray(transaction.Request.RecordRoute.GetAllValues()); - m_pRemoteTarget = (SIP_Uri) transaction.Request.Contact.GetTopMostValue().Address.Uri; - m_RemoteSeqNo = transaction.Request.CSeq.SequenceNumber; - m_LocalSeqNo = 0; - m_CallID = transaction.Request.CallID; - m_LocalTag = response.To.Tag; - m_RemoteTag = transaction.Request.From.Tag; - m_pRemoteUri = transaction.Request.From.Address.Uri; - m_pLocalUri = transaction.Request.To.Address.Uri; - m_pLocalContact = (SIP_Uri) response.Contact.GetTopMostValue().Address.Uri; - } - - #endregion - - #region UAC - - /* RFC 3261 12.1.2. - When a UAC receives a response that establishes a dialog, it - constructs the state of the dialog. This state MUST be maintained - for the duration of the dialog. - - If the request was sent over TLS, and the Request-URI contained a - SIPS URI, the "secure" flag is set to TRUE. - - The route set MUST be set to the list of URIs in the Record-Route - header field from the response, taken in reverse order and preserving - all URI parameters. If no Record-Route header field is present in - the response, the route set MUST be set to the empty set. This route - set, even if empty, overrides any pre-existing route set for future - requests in this dialog. The remote target MUST be set to the URI - from the Contact header field of the response. - - The local sequence number MUST be set to the value of the sequence - number in the CSeq header field of the request. The remote sequence - number MUST be empty (it is established when the remote UA sends a - request within the dialog). The call identifier component of the - dialog ID MUST be set to the value of the Call-ID in the request. - The local tag component of the dialog ID MUST be set to the tag in - the From field in the request, and the remote tag component of the - dialog ID MUST be set to the tag in the To field of the response. A - UAC MUST be prepared to receive a response without a tag in the To - field, in which case the tag is considered to have a value of null. - - This is to maintain backwards compatibility with RFC 2543, which - did not mandate To tags. - - The remote URI MUST be set to the URI in the To field, and the local - URI MUST be set to the URI in the From field. - */ - - else - { - // TODO: Validate request or client transaction must do it ? - - m_IsSecure = ((SIP_Uri) transaction.Request.RequestLine.Uri).IsSecure; - m_pRouteSet = (SIP_t_AddressParam[]) Core.ReverseArray(response.RecordRoute.GetAllValues()); - m_pRemoteTarget = (SIP_Uri) response.Contact.GetTopMostValue().Address.Uri; - m_LocalSeqNo = transaction.Request.CSeq.SequenceNumber; - m_RemoteSeqNo = 0; - m_CallID = transaction.Request.CallID; - m_LocalTag = transaction.Request.From.Tag; - m_RemoteTag = response.To.Tag; - m_pRemoteUri = transaction.Request.To.Address.Uri; - m_pLocalUri = transaction.Request.From.Address.Uri; - m_pLocalContact = (SIP_Uri) transaction.Request.Contact.GetTopMostValue().Address.Uri; - } - - #endregion - - m_pFlow = transaction.Flow; - } - - /// - /// Processes specified request through this dialog. - /// - /// Method arguments. - /// Returns true if this dialog processed specified response, otherwise false. - /// Is raised when e is null reference. - protected internal virtual bool ProcessRequest(SIP_RequestReceivedEventArgs e) - { - if (e == null) - { - throw new ArgumentNullException("e"); - } - - if (e.Request.RequestLine.Method == SIP_Methods.BYE) - { - e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok, e.Request)); - - m_IsTerminatedByRemoteParty = true; - OnTerminatedByRemoteParty(e); - SetState(SIP_DialogState.Terminated, true); - - return true; - } - - return false; - } - - /// - /// Processes specified response through this dialog. - /// - /// SIP response to process. - /// Returns true if this dialog processed specified response, otherwise false. - /// Is raised when response is null. - protected internal virtual bool ProcessResponse(SIP_Response response) - { - if (response == null) - { - throw new ArgumentNullException("response"); - } - - return false; - } - - #endregion - - #region Utility methods - - /// - /// Raises StateChanged event. - /// - private void OnStateChanged() - { - if (StateChanged != null) - { - StateChanged(this, new EventArgs()); - } - } - - /// - /// Raises TerminatedByRemoteParty event. - /// - /// BYE request. - private void OnTerminatedByRemoteParty(SIP_RequestReceivedEventArgs bye) - { - if (TerminatedByRemoteParty != null) - { - TerminatedByRemoteParty(this, bye); - } - } - - #endregion - - /// - /// Gets if sepcified request method is target-refresh method. - /// - /// SIP request method. - /// Returns true if specified method is target-refresh method. - /// Is raised when method is null reference. - protected bool IsTargetRefresh(string method) - { - if (method == null) - { - throw new ArgumentNullException("method"); - } - - method = method.ToUpper(); - - // RFC 5057 5.4. Target Refresh Requests. - if (method == SIP_Methods.INVITE) - { - return true; - } - else if (method == SIP_Methods.UPDATE) - { - return true; - } - else if (method == SIP_Methods.SUBSCRIBE) - { - return true; - } - else if (method == SIP_Methods.NOTIFY) - { - return true; - } - else if (method == SIP_Methods.REFER) - { - return true; - } - - return false; - } - - /// - /// Sets dialog state. - /// - /// New dialog state, - /// If true, StateChanged event is raised after state change. - protected void SetState(SIP_DialogState state, bool raiseEvent) - { - m_State = state; - - if (raiseEvent) - { - OnStateChanged(); - } - - if (m_State == SIP_DialogState.Terminated) - { - Dispose(); - } - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_DialogState.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_DialogState.cs deleted file mode 100644 index f96a9268e..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_DialogState.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - /// - /// Specifies dialog state. - /// - public enum SIP_DialogState - { - /// - /// Dialog isn't established yet. - /// - Early, - - /// - /// Dialog has got 2xx response. - /// - Confirmed, - - /// - /// Dialog is terminating. - /// - Terminating, - - /// - /// Dialog is terminated. - /// - Terminated, - - /// - /// Dialog is disposed. - /// - Disposed, - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Dialog_Invite.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Dialog_Invite.cs deleted file mode 100644 index f64dbdd65..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Dialog_Invite.cs +++ /dev/null @@ -1,978 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - #region usings - - using System; - using System.Collections.Generic; - using System.Timers; - using Message; - using SDP; - - #endregion - - /// - /// This class represent INVITE dialog. Defined in RFC 3261. - /// - public class SIP_Dialog_Invite : SIP_Dialog - { - #region Nested type: UacInvite2xxRetransmissionWaiter - - /// - /// This class waits INVITE 2xx response retransmissions and answers with ACK request to them. - /// For more info see RFC 3261 13.2.2.4. - /// - private class UacInvite2xxRetransmissionWaiter - { - #region Members - - private bool m_IsDisposed; - private SIP_Dialog_Invite m_pDialog; - private SIP_Request m_pInvite; - private object m_pLock = ""; - private TimerEx m_pTimer; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner INVITE dialog. - /// INVITE request which 2xx retransmission to wait. - /// Is raised when dialog or invite is null reference. - public UacInvite2xxRetransmissionWaiter(SIP_Dialog_Invite dialog, SIP_Request invite) - { - if (dialog == null) - { - throw new ArgumentNullException("dialog"); - } - if (invite == null) - { - throw new ArgumentNullException("invite"); - } - - m_pDialog = dialog; - m_pInvite = invite; - - /* RFC 3261 13.2.2.4. - The UAC core considers the INVITE transaction completed 64*T1 seconds - after the reception of the first 2xx response. - */ - m_pTimer = new TimerEx(64*SIP_TimerConstants.T1, false); - m_pTimer.Elapsed += m_pTimer_Elapsed; - m_pTimer.Enabled = true; - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public void Dispose() - { - lock (m_pLock) - { - if (m_IsDisposed) - { - return; - } - m_IsDisposed = true; - - m_pDialog.m_pUacInvite2xxRetransmitWaits.Remove(this); - - m_pDialog = null; - m_pInvite = null; - if (m_pTimer != null) - { - m_pTimer.Dispose(); - m_pTimer = null; - } - } - } - - /// - /// Checks if specified response matches this 2xx response retransmission wait entry. - /// - /// INVITE 2xx response. - /// Returns true if response matches, othwerwise false. - /// Is raised when response is null reference value. - public bool Match(SIP_Response response) - { - if (response == null) - { - throw new ArgumentNullException("response"); - } - - if (m_pInvite.CSeq.RequestMethod == response.CSeq.RequestMethod && - m_pInvite.CSeq.SequenceNumber == response.CSeq.SequenceNumber) - { - return true; - } - else - { - return false; - } - } - - /// - /// Processes retransmited INVITE 2xx response. - /// - /// INVITE 2xx response. - /// Is raised when response is null reference. - public void Process(SIP_Response response) - { - if (response == null) - { - throw new ArgumentNullException("response"); - } - - lock (m_pLock) - { - SIP_Request ack = CreateAck(); - - try - { - // Try existing flow. - m_pDialog.Flow.Send(ack); - - // Log - if (m_pDialog.Stack.Logger != null) - { - byte[] ackBytes = ack.ToByteData(); - - m_pDialog.Stack.Logger.AddWrite(m_pDialog.ID, - null, - ackBytes.Length, - "Dialog [id='" + m_pDialog.ID + - "] ACK sent for 2xx response.", - m_pDialog.Flow.LocalEP, - m_pDialog.Flow.RemoteEP, - ackBytes); - } - } - catch - { - /* RFC 3261 13.2.2.4. - Once the ACK has been constructed, the procedures of [4] are used to - determine the destination address, port and transport. However, the - request is passed to the transport layer directly for transmission, - rather than a client transaction. - */ - try - { - m_pDialog.Stack.TransportLayer.SendRequest(ack); - } - catch (Exception x) - { - // Log - if (m_pDialog.Stack.Logger != null) - { - m_pDialog.Stack.Logger.AddText("Dialog [id='" + m_pDialog.ID + - "'] ACK send for 2xx response failed: " + - x.Message + "."); - } - } - } - } - } - - #endregion - - #region Event handlers - - /// - /// Is raised when INVITE 2xx retrasmission wait time expired. - /// - /// Sender. - /// Event data. - private void m_pTimer_Elapsed(object sender, ElapsedEventArgs e) - { - Dispose(); - } - - #endregion - - #region Utility methods - - /// - /// Creates ACK request for pending INVITE. - /// - /// Returns created ACK request. - private SIP_Request CreateAck() - { - /* RFC 3261 13.2.2.4. - The header fields of the ACK are constructed - in the same way as for any request sent within a dialog (see Section 12) with the - exception of the CSeq and the header fields related to authentication. The sequence - number of the CSeq header field MUST be the same as the INVITE being acknowledged, - but the CSeq method MUST be ACK. The ACK MUST contain the same credentials as the INVITE. - - ACK must have same branch. - */ - - SIP_Request ackRequest = m_pDialog.CreateRequest(SIP_Methods.ACK); - ackRequest.Via.AddToTop(m_pInvite.Via.GetTopMostValue().ToStringValue()); - ackRequest.CSeq = new SIP_t_CSeq(m_pInvite.CSeq.SequenceNumber, SIP_Methods.ACK); - // Authorization - foreach (SIP_HeaderField h in m_pInvite.Authorization.HeaderFields) - { - ackRequest.Authorization.Add(h.Value); - } - // Proxy-Authorization - foreach (SIP_HeaderField h in m_pInvite.ProxyAuthorization.HeaderFields) - { - ackRequest.Authorization.Add(h.Value); - } - - return ackRequest; - } - - #endregion - } - - #endregion - - #region Nested type: UasInvite2xxRetransmit - - /// - /// This class is responsible for retransmitting INVITE 2xx response while ACK is received form target UAC. - /// For more info see RFC 3261 13.3.1.4. - /// - private class UasInvite2xxRetransmit - { - #region Members - - private bool m_IsDisposed; - private SIP_Dialog_Invite m_pDialog; - private object m_pLock = ""; - private SIP_Response m_pResponse; - private TimerEx m_pTimer; - private DateTime m_StartTime = DateTime.Now; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner INVITE dialog. - /// INVITE 2xx response. - /// Is raised when dialog or response is null reference. - public UasInvite2xxRetransmit(SIP_Dialog_Invite dialog, SIP_Response response) - { - if (dialog == null) - { - throw new ArgumentNullException("dialog"); - } - if (response == null) - { - throw new ArgumentNullException("response"); - } - - m_pDialog = dialog; - m_pResponse = response; - - /* RFC 3261 13.3.1.4. - Once the response has been constructed, it is passed to the INVITE - server transaction. Note, however, that the INVITE server - transaction will be destroyed as soon as it receives this final - response and passes it to the transport. Therefore, it is necessary - to periodically pass the response directly to the transport until the - ACK arrives. The 2xx response is passed to the transport with an - interval that starts at T1 seconds and doubles for each - retransmission until it reaches T2 seconds (T1 and T2 are defined in - Section 17). Response retransmissions cease when an ACK request for - the response is received. This is independent of whatever transport - protocols are used to send the response. - - Since 2xx is retransmitted end-to-end, there may be hops between - UAS and UAC that are UDP. To ensure reliable delivery across - these hops, the response is retransmitted periodically even if the - transport at the UAS is reliable. - */ - - m_pTimer = new TimerEx(SIP_TimerConstants.T1, false); - m_pTimer.Elapsed += m_pTimer_Elapsed; - m_pTimer.Enabled = true; - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public void Dispose() - { - lock (m_pLock) - { - if (m_IsDisposed) - { - return; - } - m_IsDisposed = true; - - m_pDialog.m_pUasInvite2xxRetransmits.Remove(this); - - if (m_pTimer != null) - { - m_pTimer.Dispose(); - m_pTimer = null; - } - m_pDialog = null; - m_pResponse = null; - } - } - - /// - /// Checks if specified ACK request matches this 2xx response retransmission. - /// - /// ACK request. - /// Returns true if ACK matches, othwerwise false. - /// Is raised when ackRequest is null reference value. - public bool MatchAck(SIP_Request ackRequest) - { - if (ackRequest == null) - { - throw new ArgumentNullException("ackRequest"); - } - - if (ackRequest.CSeq.SequenceNumber == m_pResponse.CSeq.SequenceNumber) - { - return true; - } - else - { - return false; - } - } - - #endregion - - #region Event handlers - - /// - /// This method is called when INVITE 2xx retransmit timer triggered. - /// - /// Sender. - /// Event data. - private void m_pTimer_Elapsed(object sender, ElapsedEventArgs e) - { - /* RFC 3261 13.3.1.4. - Once the response has been constructed, it is passed to the INVITE - server transaction. Note, however, that the INVITE server - transaction will be destroyed as soon as it receives this final - response and passes it to the transport. Therefore, it is necessary - to periodically pass the response directly to the transport until the - ACK arrives. The 2xx response is passed to the transport with an - interval that starts at T1 seconds and doubles for each - retransmission until it reaches T2 seconds (T1 and T2 are defined in - Section 17). Response retransmissions cease when an ACK request for - the response is received. This is independent of whatever transport - protocols are used to send the response. - - Since 2xx is retransmitted end-to-end, there may be hops between - UAS and UAC that are UDP. To ensure reliable delivery across - these hops, the response is retransmitted periodically even if the - transport at the UAS is reliable. - - If the server retransmits the 2xx response for 64*T1 seconds without - receiving an ACK, the dialog is confirmed, but the session SHOULD be - terminated. This is accomplished with a BYE, as described in Section - 15. - */ - - lock (m_pLock) - { - if (m_StartTime.AddMilliseconds(64*SIP_TimerConstants.T1) < DateTime.Now) - { - // Log - if (m_pDialog.Stack.Logger != null) - { - m_pDialog.Stack.Logger.AddText("Dialog [id='" + m_pDialog.ID + - "'] ACK was not received for (re-)INVITE, terminating INVITE session."); - } - - m_pDialog.Terminate("Dialog Error: ACK was not received for (re-)INVITE.", true); - Dispose(); - } - else - { - // Retransmit response. - try - { - m_pDialog.Flow.Send(m_pResponse); - - // Log - if (m_pDialog.Stack.Logger != null) - { - m_pDialog.Stack.Logger.AddText("Dialog [id='" + m_pDialog.ID + "';statusCode=" + - m_pResponse.StatusCode + - "] UAS 2xx response retransmited"); - } - } - catch (Exception x) - { - // Log - if (m_pDialog.Stack.Logger != null) - { - m_pDialog.Stack.Logger.AddText("Dialog [id='" + m_pDialog.ID + - "'] UAS 2xx response retransmission failed: " + - x.Message); - } - } - - m_pTimer.Interval = Math.Min(m_pTimer.Interval*2, SIP_TimerConstants.T2); - m_pTimer.Enabled = true; - } - } - } - - #endregion - } - - #endregion - - #region Events - - /// - /// Is raised when re-INVITE is received. - /// - /// INVITE dialog consumer always should respond to this event(accept or decline it). - public event EventHandler Reinvite = null; - - #endregion - - #region Members - - private SIP_Transaction m_pActiveInvite; - private List m_pUacInvite2xxRetransmitWaits; - private List m_pUasInvite2xxRetransmits; - - #endregion - - #region Properties - - /// - /// Gets if dialog has pending INVITE transaction. - /// - /// Is raised when this class is Disposed and this property is accessed. - public bool HasPendingInvite - { - get - { - if (State == SIP_DialogState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (m_pActiveInvite != null) - { - return true; - } - else - { - return false; - } - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - internal SIP_Dialog_Invite() - { - m_pUasInvite2xxRetransmits = new List(); - m_pUacInvite2xxRetransmitWaits = new List(); - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public override void Dispose() - { - lock (SyncRoot) - { - if (State == SIP_DialogState.Disposed) - { - return; - } - - foreach (UasInvite2xxRetransmit t in m_pUasInvite2xxRetransmits.ToArray()) - { - t.Dispose(); - } - m_pUasInvite2xxRetransmits = null; - - foreach (UacInvite2xxRetransmissionWaiter w in m_pUacInvite2xxRetransmitWaits.ToArray()) - { - w.Dispose(); - } - m_pUacInvite2xxRetransmitWaits = null; - - m_pActiveInvite = null; - - base.Dispose(); - } - } - - /// - /// Starts terminating dialog. - /// - /// Termination reason. This value may be null. - /// If true BYE is sent to remote party. - /// Is raised when this class is Disposed and this method is accessed. - public override void Terminate(string reason, bool sendBye) - { - lock (SyncRoot) - { - if (State == SIP_DialogState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (State == SIP_DialogState.Terminating || State == SIP_DialogState.Terminated) - { - return; - } - - /* RFC 3261 15. - The caller's UA MAY send a BYE for either confirmed or early dialogs, and the callee's UA MAY send a BYE on - confirmed dialogs, but MUST NOT send a BYE on early dialogs. - - RFC 3261 15.1. - Once the BYE is constructed, the UAC core creates a new non-INVITE client transaction, and passes it the BYE request. - The UAC MUST consider the session terminated (and therefore stop sending or listening for media) as soon as the BYE - request is passed to the client transaction. If the response for the BYE is a 481 (Call/Transaction Does Not Exist) - or a 408 (Request Timeout) or no response at all is received for the BYE (that is, a timeout is returned by the - client transaction), the UAC MUST consider the session and the dialog terminated. - */ - - if (sendBye) - { - if ((State == SIP_DialogState.Early && m_pActiveInvite is SIP_ClientTransaction) || - State == SIP_DialogState.Confirmed) - { - SetState(SIP_DialogState.Terminating, true); - - SIP_Request bye = CreateRequest(SIP_Methods.BYE); - if (!string.IsNullOrEmpty(reason)) - { - SIP_t_ReasonValue r = new SIP_t_ReasonValue(); - r.Protocol = "SIP"; - r.Text = reason; - bye.Reason.Add(r.ToStringValue()); - } - - // Send BYE, just wait BYE to complete, we don't care about response code. - SIP_RequestSender sender = CreateRequestSender(bye); - sender.Completed += delegate { SetState(SIP_DialogState.Terminated, true); }; - sender.Start(); - } - else - { - /* We are "early" UAS dialog, we need todo follwoing: - *) If we havent sent final response, send '408 Request terminated' and we are done. - *) We have sen't final response, we need to wait ACK to arrive or timeout. - If will ACK arrives or timeout, send BYE. - */ - - if (m_pActiveInvite != null && m_pActiveInvite.FinalResponse == null) - { - Stack.CreateResponse(SIP_ResponseCodes.x408_Request_Timeout, - m_pActiveInvite.Request); - - SetState(SIP_DialogState.Terminated, true); - } - else - { - // Wait ACK to arrive or timeout. - - SetState(SIP_DialogState.Terminating, true); - } - } - } - else - { - SetState(SIP_DialogState.Terminated, true); - } - } - } - - /// - /// Re-invites remote party. - /// - /// New contact value. Value null means current contact value used. - /// SDP media offer. - /// Is raised when this class is Disposed and this method is accessed. - /// Is raised when there is pending invite and this method is called or dialog is in invalid state. - /// Is raised when sdp is null reference. - public void ReInvite(SIP_Uri contact, SDP_Message sdp) - { - if (State == SIP_DialogState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (HasPendingInvite) - { - throw new InvalidOperationException("There is pending INVITE."); - } - if (State != SIP_DialogState.Confirmed) - { - throw new InvalidOperationException("ReInvite is only available in Confirmed state."); - } - if (sdp == null) - { - throw new ArgumentNullException("sdp"); - } - - lock (SyncRoot) - { - // TODO: - - SIP_Request reinvite = CreateRequest(SIP_Methods.INVITE); - if (contact != null) - { - reinvite.Contact.RemoveAll(); - reinvite.Contact.Add(contact.ToString()); - } - reinvite.ContentType = "application/sdp"; - // reinvite.Data = sdp.ToStringData(); - - // TODO: Create request sender - // TODO: Try to reuse existing data flow - //SIP_RequestSender sender = this.Stack.CreateRequestSender(reinvite); - //sender.Start(); - } - } - - #endregion - - #region Overrides - - /// - /// Initializes dialog. - /// - /// Owner stack. - /// Owner transaction. - /// SIP response what caused dialog creation. - /// Is raised when stack,transaction or response. - protected internal override void Init(SIP_Stack stack, - SIP_Transaction transaction, - SIP_Response response) - { - if (stack == null) - { - throw new ArgumentNullException("stack"); - } - if (transaction == null) - { - throw new ArgumentNullException("transaction"); - } - if (response == null) - { - throw new ArgumentNullException("response"); - } - - base.Init(stack, transaction, response); - - if (transaction is SIP_ServerTransaction) - { - if (response.StatusCodeType == SIP_StatusCodeType.Success) - { - SetState(SIP_DialogState.Early, false); - - // We need to retransmit 2xx response while we get ACK or timeout. (RFC 3261 13.3.1.4.) - m_pUasInvite2xxRetransmits.Add(new UasInvite2xxRetransmit(this, response)); - } - else if (response.StatusCodeType == SIP_StatusCodeType.Provisional) - { - SetState(SIP_DialogState.Early, false); - - m_pActiveInvite = transaction; - m_pActiveInvite.StateChanged += delegate - { - if (m_pActiveInvite != null && - m_pActiveInvite.State == - SIP_TransactionState.Terminated) - { - m_pActiveInvite = null; - } - }; - // Once we send 2xx response, we need to retransmit it while get ACK or timeout. (RFC 3261 13.3.1.4.) - ((SIP_ServerTransaction) m_pActiveInvite).ResponseSent += - delegate(object s, SIP_ResponseSentEventArgs a) - { - if (a.Response.StatusCodeType == SIP_StatusCodeType.Success) - { - m_pUasInvite2xxRetransmits.Add(new UasInvite2xxRetransmit(this, a.Response)); - } - }; - } - else - { - throw new ArgumentException( - "Argument 'response' has invalid status code, 1xx - 2xx is only allowed."); - } - } - else - { - if (response.StatusCodeType == SIP_StatusCodeType.Success) - { - SetState(SIP_DialogState.Confirmed, false); - - // Wait for retransmited 2xx responses. (RFC 3261 13.2.2.4.) - m_pUacInvite2xxRetransmitWaits.Add(new UacInvite2xxRetransmissionWaiter(this, - transaction. - Request)); - } - else if (response.StatusCodeType == SIP_StatusCodeType.Provisional) - { - SetState(SIP_DialogState.Early, false); - - m_pActiveInvite = transaction; - m_pActiveInvite.StateChanged += delegate - { - if (m_pActiveInvite != null && - m_pActiveInvite.State == - SIP_TransactionState.Terminated) - { - m_pActiveInvite = null; - } - }; - // Once we receive 2xx response, we need to wait for retransmitted 2xx responses. (RFC 3261 13.2.2.4) - ((SIP_ClientTransaction) m_pActiveInvite).ResponseReceived += - delegate(object s, SIP_ResponseReceivedEventArgs a) - { - if (a.Response.StatusCodeType == SIP_StatusCodeType.Success) - { - UacInvite2xxRetransmissionWaiter waiter = - new UacInvite2xxRetransmissionWaiter(this, m_pActiveInvite.Request); - m_pUacInvite2xxRetransmitWaits.Add(waiter); - // Force to send initial ACK to 2xx response. - waiter.Process(a.Response); - - SetState(SIP_DialogState.Confirmed, true); - } - }; - } - else - { - throw new ArgumentException( - "Argument 'response' has invalid status code, 1xx - 2xx is only allowed."); - } - } - } - - /// - /// Processes specified request through this dialog. - /// - /// Method arguments. - /// Returns true if this dialog processed specified request, otherwise false. - /// Is raised when e is null reference. - protected internal override bool ProcessRequest(SIP_RequestReceivedEventArgs e) - { - if (e == null) - { - throw new ArgumentNullException("e"); - } - - if (base.ProcessRequest(e)) - { - return true; - } - - // We must support: INVITE(re-invite),UPDATE,ACK, [BYE will be handled by base class] - - #region INVITE - - if (e.Request.RequestLine.Method == SIP_Methods.INVITE) - { - /* RFC 3261 14.2. - A UAS that receives a second INVITE before it sends the final - response to a first INVITE with a lower CSeq sequence number on the - same dialog MUST return a 500 (Server Internal Error) response to the - second INVITE and MUST include a Retry-After header field with a - randomly chosen value of between 0 and 10 seconds. - - A UAS that receives an INVITE on a dialog while an INVITE it had sent - on that dialog is in progress MUST return a 491 (Request Pending) - response to the received INVITE. - */ - - if (m_pActiveInvite != null && m_pActiveInvite is SIP_ServerTransaction && - (m_pActiveInvite).Request.CSeq.SequenceNumber < e.Request.CSeq.SequenceNumber) - { - SIP_Response response = - Stack.CreateResponse( - SIP_ResponseCodes.x500_Server_Internal_Error + - ": INVITE with a lower CSeq is pending(RFC 3261 14.2).", - e.Request); - response.RetryAfter = new SIP_t_RetryAfter("10"); - e.ServerTransaction.SendResponse(response); - - return true; - } - if (m_pActiveInvite != null && m_pActiveInvite is SIP_ClientTransaction) - { - e.ServerTransaction.SendResponse( - Stack.CreateResponse(SIP_ResponseCodes.x491_Request_Pending, e.Request)); - - return true; - } - - // Force server transaction creation and set it as active INVITE transaction. - m_pActiveInvite = e.ServerTransaction; - m_pActiveInvite.StateChanged += delegate - { - if (m_pActiveInvite.State == - SIP_TransactionState.Terminated) - { - m_pActiveInvite = null; - } - }; - // Once we send 2xx response, we need to retransmit it while get ACK or timeout. (RFC 3261 13.3.1.4.) - ((SIP_ServerTransaction) m_pActiveInvite).ResponseSent += - delegate(object s, SIP_ResponseSentEventArgs a) - { - if (a.Response.StatusCodeType == SIP_StatusCodeType.Success) - { - m_pUasInvite2xxRetransmits.Add(new UasInvite2xxRetransmit(this, a.Response)); - } - }; - - OnReinvite(((SIP_ServerTransaction) m_pActiveInvite)); - - return true; - } - - #endregion - - #region ACK - - else if (e.Request.RequestLine.Method == SIP_Methods.ACK) - { - // Search corresponding INVITE 2xx retransmit entry and dispose it. - foreach (UasInvite2xxRetransmit t in m_pUasInvite2xxRetransmits) - { - if (t.MatchAck(e.Request)) - { - t.Dispose(); - if (State == SIP_DialogState.Early) - { - SetState(SIP_DialogState.Confirmed, true); - - // TODO: If Terminating - } - - return true; - } - } - - return false; - } - - #endregion - - #region UPDATE - - //else if(request.RequestLine.Method == SIP_Methods.UPDATE){ - // TODO: - //} - - #endregion - - // RFC 5057 5.6. Refusing New Usages. Decline(603 Decline) new dialog usages. - else if (SIP_Utils.MethodCanEstablishDialog(e.Request.RequestLine.Method)) - { - e.ServerTransaction.SendResponse( - Stack.CreateResponse( - SIP_ResponseCodes.x603_Decline + " : New dialog usages not allowed (RFC 5057).", - e.Request)); - - return true; - } - else - { - return false; - } - } - - /// - /// Processes specified response through this dialog. - /// - /// SIP response to process. - /// Returns true if this dialog processed specified response, otherwise false. - /// Is raised when response is null. - protected internal override bool ProcessResponse(SIP_Response response) - { - if (response == null) - { - throw new ArgumentNullException("response"); - } - - if (response.StatusCodeType == SIP_StatusCodeType.Success) - { - // Search pending INVITE 2xx response retransmission waite entry. - foreach (UacInvite2xxRetransmissionWaiter w in m_pUacInvite2xxRetransmitWaits) - { - if (w.Match(response)) - { - w.Process(response); - - return true; - } - } - } - - return false; - } - - #endregion - - #region Utility methods - - /// - /// Raises Reinvite event. - /// - /// Re-INVITE server transaction. - private void OnReinvite(SIP_ServerTransaction reinvite) - { - if (Reinvite != null) - { - Reinvite(this, new EventArgs()); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Dialog_Subscribe.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Dialog_Subscribe.cs deleted file mode 100644 index c7389886f..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Dialog_Subscribe.cs +++ /dev/null @@ -1,58 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - #region usings - - using System; - - #endregion - - /// - /// This class represent SUBSCRIBE dialog. Defined in RFC 3265. - /// - public class SIP_Dialog_Subscribe - { - #region Constructor - - /// - /// Default constructor. - /// - internal SIP_Dialog_Subscribe() {} - - #endregion - - #region Methods - - /// - /// Sends notify request to remote end point. - /// - /// SIP NOTIFY request. - public void Notify(SIP_Request notify) - { - if (notify == null) - { - throw new ArgumentNullException("notify"); - } - - // TODO: - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Flow.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Flow.cs deleted file mode 100644 index f08bb0e5a..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Flow.cs +++ /dev/null @@ -1,737 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - #region usings - - using System; - using System.IO; - using System.Net; - using System.Threading; - using IO; - using Mime; - using TCP; - using UDP; - - #endregion - - /// - /// Implements SIP Flow. Defined in draft-ietf-sip-outbound. - /// - /// A Flow is a network protocol layer (layer 4) association - /// between two hosts that is represented by the network address and - /// port number of both ends and by the protocol. For TCP, a flow is - /// equivalent to a TCP connection. For UDP a flow is a bidirectional - /// stream of datagrams between a single pair of IP addresses and - /// ports of both peers. - /// - public class SIP_Flow : IDisposable - { - #region Events - - /// - /// Is raised when flow is disposing. - /// - public event EventHandler IsDisposing = null; - - #endregion - - #region Members - - private readonly DateTime m_CreateTime; - private readonly string m_ID = ""; - private readonly bool m_IsServer; - private readonly IPEndPoint m_pLocalEP; - private readonly object m_pLock = new object(); - private readonly IPEndPoint m_pRemoteEP; - private readonly SIP_Stack m_pStack; - private readonly string m_Transport = ""; - private long m_BytesWritten; - private bool m_IsDisposed; - private DateTime m_LastActivity; - private bool m_LastCRLF; - private TimerEx m_pKeepAliveTimer; - private MemoryStream m_pMessage; - private TCP_Session m_pTcpSession; - - #endregion - - #region Properties - - /// - /// Gets if this object is disposed. - /// - public bool IsDisposed - { - get { return m_IsDisposed; } - } - - /// - /// Gets if this flow is server flow or client flow. - /// - /// Is raised when this object is disposed and this property is accessed. - public bool IsServer - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_IsServer; - } - } - - /// - /// Gets time when flow was created. - /// - /// Is raised when this object is disposed and this property is accessed. - public DateTime CreateTime - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_CreateTime; - } - } - - /// - /// Gets flow ID. - /// - /// Is raised when this object is disposed and this property is accessed. - public string ID - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_ID; - } - } - - /// - /// Gets flow local IP end point. - /// - /// Is raised when this object is disposed and this property is accessed. - public IPEndPoint LocalEP - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pLocalEP; - } - } - - /// - /// Gets flow remote IP end point. - /// - /// Is raised when this object is disposed and this property is accessed. - public IPEndPoint RemoteEP - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pRemoteEP; - } - } - - /// - /// Gets flow transport. - /// - /// Is raised when this object is disposed and this property is accessed. - public string Transport - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_Transport; - } - } - - /// - /// Gets if flow is reliable transport. - /// - /// Is raised when this object is disposed and this property is accessed. - public bool IsReliable - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_Transport != SIP_Transport.UDP; - } - } - - /// - /// Gets if this connection is secure. - /// - /// Is raised when this object is disposed and this property is accessed. - public bool IsSecure - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (m_Transport == SIP_Transport.TLS) - { - return true; - } - else - { - return false; - } - } - } - - /// - /// Gets or sets if flow sends keep-alive packets. - /// - /// Is raised when this object is disposed and this property is accessed. - public bool SendKeepAlives - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pKeepAliveTimer != null; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value) - { - if (m_pKeepAliveTimer == null) - { - m_pKeepAliveTimer = new TimerEx(15000, true); - m_pKeepAliveTimer.Elapsed += delegate - { - // Log: - if (m_pStack.TransportLayer.Stack.Logger != null) - { - m_pStack.TransportLayer.Stack.Logger.AddWrite - ("", - null, - 2, - "Flow [id='" + ID + "'] sent \"ping\"", - LocalEP, - RemoteEP); - } - - try - { - SendInternal(new[] - { - (byte) '\r', (byte) '\n', - (byte) '\r', (byte) '\n' - }); - } - catch - { - // We don't care about errors here. - } - }; - m_pKeepAliveTimer.Enabled = true; - } - } - else - { - if (m_pKeepAliveTimer != null) - { - m_pKeepAliveTimer.Dispose(); - m_pKeepAliveTimer = null; - } - } - } - } - - /// - /// Gets when flow had last(send or receive) activity. - /// - /// Is raised when this object is disposed and this property is accessed. - public DateTime LastActivity - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (m_Transport == SIP_Transport.TCP || m_Transport == SIP_Transport.TLS) - { - return m_pTcpSession.LastActivity; - } - else - { - return m_LastActivity; - } - } - } - - // TODO: BytesReaded - - /// - /// Gets how many bytes this flow has sent to remote party. - /// - /// Is raised when this object is disposed and this property is accessed. - public long BytesWritten - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_BytesWritten; - } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner stack. - /// Specifies if flow is server or client flow. - /// Local IP end point. - /// Remote IP end point. - /// SIP transport. - /// Is raised when stack,localEP,remoteEP or transport is null reference. - /// Is raised whena any of the arguments has invalid value. - internal SIP_Flow(SIP_Stack stack, - bool isServer, - IPEndPoint localEP, - IPEndPoint remoteEP, - string transport) - { - if (stack == null) - { - throw new ArgumentNullException("stack"); - } - if (localEP == null) - { - throw new ArgumentNullException("localEP"); - } - if (remoteEP == null) - { - throw new ArgumentNullException("remoteEP"); - } - if (transport == null) - { - throw new ArgumentNullException("transport"); - } - - m_pStack = stack; - m_IsServer = isServer; - m_pLocalEP = localEP; - m_pRemoteEP = remoteEP; - m_Transport = transport.ToUpper(); - - m_CreateTime = DateTime.Now; - m_LastActivity = DateTime.Now; - m_ID = m_pLocalEP + "-" + m_pRemoteEP + "-" + m_Transport; - m_pMessage = new MemoryStream(); - } - - /// - /// Server TCP,TLS constructor. - /// - /// Owner stack. - /// TCP session. - /// Is raised when stack or session is null reference. - internal SIP_Flow(SIP_Stack stack, TCP_Session session) - { - if (stack == null) - { - throw new ArgumentNullException("stack"); - } - if (session == null) - { - throw new ArgumentNullException("session"); - } - - m_pStack = stack; - m_pTcpSession = session; - - m_IsServer = true; - m_pLocalEP = session.LocalEndPoint; - m_pRemoteEP = session.RemoteEndPoint; - m_Transport = session.IsSecureConnection ? SIP_Transport.TLS : SIP_Transport.TCP; - m_CreateTime = DateTime.Now; - m_LastActivity = DateTime.Now; - m_ID = m_pLocalEP + "-" + m_pRemoteEP + "-" + m_Transport; - m_pMessage = new MemoryStream(); - - BeginReadHeader(); - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public void Dispose() - { - lock (m_pLock) - { - if (m_IsDisposed) - { - return; - } - OnDisposing(); - m_IsDisposed = true; - - if (m_pTcpSession != null) - { - m_pTcpSession.Dispose(); - m_pTcpSession = null; - } - m_pMessage = null; - if (m_pKeepAliveTimer != null) - { - m_pKeepAliveTimer.Dispose(); - m_pKeepAliveTimer = null; - } - } - } - - /// - /// Sends specified request to flow remote end point. - /// - /// SIP request to send. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when request is null reference. - public void Send(SIP_Request request) - { - lock (m_pLock) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (request == null) - { - throw new ArgumentNullException("request"); - } - - SendInternal(request.ToByteData()); - } - } - - /// - /// Sends specified response to flow remote end point. - /// - /// SIP response to send. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when response is null reference. - public void Send(SIP_Response response) - { - lock (m_pLock) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (response == null) - { - throw new ArgumentNullException("response"); - } - - SendInternal(response.ToByteData()); - } - } - - #endregion - - #region Utility methods - - /// - /// Starts reading SIP message header. - /// - private void BeginReadHeader() - { - // Clear old data. - m_pMessage.SetLength(0); - - // Start reading SIP message header. - m_pTcpSession.TcpStream.BeginReadHeader(m_pMessage, - m_pStack.TransportLayer.Stack.MaximumMessageSize, - SizeExceededAction.JunkAndThrowException, - BeginReadHeader_Completed, - null); - } - - /// - /// This method is called when SIP message header reading has completed. - /// - /// An IAsyncResult that represents an asynchronous call. - private void BeginReadHeader_Completed(IAsyncResult asyncResult) - { - try - { - int countStored = m_pTcpSession.TcpStream.EndReadHeader(asyncResult); - - // We got CRLF(ping or pong). - if (countStored == 0) - { - // We have ping request. - if (IsServer) - { - // We have full ping request. - if (m_LastCRLF) - { - m_LastCRLF = false; - - m_pStack.TransportLayer.OnMessageReceived(this, - new[] - { - (byte) '\r', (byte) '\n', - (byte) '\r', (byte) '\n' - }); - } - // We have first CRLF of ping request. - else - { - m_LastCRLF = true; - } - } - // We got pong to our ping request. - else - { - m_pStack.TransportLayer.OnMessageReceived(this, new[] {(byte) '\r', (byte) '\n'}); - } - - // Wait for new SIP message. - BeginReadHeader(); - } - // We have SIP message header. - else - { - m_LastCRLF = false; - - // Add header terminator blank line. - m_pMessage.Write(new[] {(byte) '\r', (byte) '\n'}, 0, 2); - - m_pMessage.Position = 0; - string contentLengthValue = MimeUtils.ParseHeaderField("Content-Length:", m_pMessage); - m_pMessage.Position = m_pMessage.Length; - - int contentLength = 0; - - // Read message body. - if (contentLengthValue != "") - { - contentLength = Convert.ToInt32(contentLengthValue); - } - - // Start reading message body. - if (contentLength > 0) - { - // Read body data. - m_pTcpSession.TcpStream.BeginReadFixedCount(m_pMessage, - contentLength, - BeginReadData_Completed, - null); - } - // Message with no body. - else - { - byte[] messageData = m_pMessage.ToArray(); - // Wait for new SIP message. - BeginReadHeader(); - - m_pStack.TransportLayer.OnMessageReceived(this, messageData); - } - } - } - catch - { - Dispose(); - } - } - - /// - /// This method is called when SIP message data reading has completed. - /// - /// An IAsyncResult that represents an asynchronous call. - private void BeginReadData_Completed(IAsyncResult asyncResult) - { - try - { - m_pTcpSession.TcpStream.EndReadFixedCount(asyncResult); - - byte[] messageData = m_pMessage.ToArray(); - // Wait for new SIP message. - BeginReadHeader(); - - m_pStack.TransportLayer.OnMessageReceived(this, messageData); - } - catch - { - Dispose(); - } - } - - /// - /// Raises Disposed event. - /// - private void OnDisposing() - { - if (IsDisposing != null) - { - IsDisposing(this, new EventArgs()); - } - } - - #endregion - - #region Internal methods - - /// - /// Starts flow processing. - /// - internal void Start() - { - // Move processing to thread pool. - AutoResetEvent startLock = new AutoResetEvent(false); - ThreadPool.QueueUserWorkItem(delegate - { - lock (m_pLock) - { - startLock.Set(); - - // TCP / TLS client, connect to remote end point. - if (!m_IsServer && m_Transport != SIP_Transport.UDP) - { - try - { - TCP_Client client = new TCP_Client(); - client.Connect(m_pLocalEP, - m_pRemoteEP, - m_Transport == SIP_Transport.TLS); - - m_pTcpSession = client; - - BeginReadHeader(); - } - catch - { - Dispose(); - } - } - } - }); - startLock.WaitOne(); - } - - /// - /// Sends specified data to the remote end point. - /// - /// Data to send. - /// Is raised when data is null reference. - internal void SendInternal(byte[] data) - { - if (data == null) - { - throw new ArgumentNullException("data"); - } - - try - { - if (m_Transport == SIP_Transport.UDP) - { - m_pStack.TransportLayer.UdpServer.SendPacket(m_pLocalEP, data, 0, data.Length, m_pRemoteEP); - } - else if (m_Transport == SIP_Transport.TCP) - { - m_pTcpSession.TcpStream.Write(data, 0, data.Length); - } - else if (m_Transport == SIP_Transport.TLS) - { - m_pTcpSession.TcpStream.Write(data, 0, data.Length); - } - - m_BytesWritten += data.Length; - } - catch (IOException x) - { - Dispose(); - - throw x; - } - } - - /// - /// This method is called when flow gets new UDP packet. - /// - /// UDP data. - /// Is raised when e is null reference. - internal void OnUdpPacketReceived(UDP_PacketEventArgs e) - { - if (e == null) - { - throw new ArgumentNullException("e"); - } - - m_LastActivity = DateTime.Now; - - m_pStack.TransportLayer.OnMessageReceived(this, e.Data); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Hop.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Hop.cs deleted file mode 100644 index 058d3e129..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Hop.cs +++ /dev/null @@ -1,136 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - #region usings - - using System; - using System.Net; - - #endregion - - /// - /// Implements SIP hop(address,port,transport). Defined in RFC 3261. - /// - public class SIP_Hop - { - #region Members - - private readonly IPEndPoint m_pEndPoint; - private readonly string m_Transport = ""; - - #endregion - - #region Properties - - /// - /// Gets target IP end point. - /// - public IPEndPoint EndPoint - { - get { return m_pEndPoint; } - } - - /// - /// Gets target IP address. - /// - public IPAddress IP - { - get { return m_pEndPoint.Address; } - } - - /// - /// Gets target port. - /// - public int Port - { - get { return m_pEndPoint.Port; } - } - - /// - /// Gets target SIP transport. - /// - public string Transport - { - get { return m_Transport; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// IP end point. - /// SIP transport to use. - /// Is raised when ep or transport is null reference. - /// Is raised when any of the arguments has invalid value. - public SIP_Hop(IPEndPoint ep, string transport) - { - if (ep == null) - { - throw new ArgumentNullException("ep"); - } - if (transport == null) - { - throw new ArgumentNullException("transport"); - } - if (transport == "") - { - throw new ArgumentException("Argument 'transport' value must be specified."); - } - - m_pEndPoint = ep; - m_Transport = transport; - } - - /// - /// Default constructor. - /// - /// IP address. - /// Destination port. - /// SIP transport to use. - /// Is raised when ip or transport is null reference. - /// Is raised when any of the arguments has invalid value. - public SIP_Hop(IPAddress ip, int port, string transport) - { - if (ip == null) - { - throw new ArgumentNullException("ip"); - } - if (port < 1) - { - throw new ArgumentException("Argument 'port' value must be >= 1."); - } - if (transport == null) - { - throw new ArgumentNullException("transport"); - } - if (transport == "") - { - throw new ArgumentException("Argument 'transport' value must be specified."); - } - - m_pEndPoint = new IPEndPoint(ip, port); - m_Transport = transport; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Methods.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Methods.cs deleted file mode 100644 index 23fdb4bc6..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Methods.cs +++ /dev/null @@ -1,99 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - /// - /// This class represents known SIP request methods. - /// - public class SIP_Methods - { - #region Constants - - /// - /// ACK method. Defined in RFC 3261. - /// - public const string ACK = "ACK"; - - /// - /// BYE method. Defined in RFC 3261. - /// - public const string BYE = "BYE"; - - /// - /// CANCEL method. Defined in RFC 3261. - /// - public const string CANCEL = "CANCEL"; - - /// - /// INFO method. Defined in RFC 2976. - /// - public const string INFO = "INFO"; - - /// - /// INVITE method. Defined in RFC 3261. - /// - public const string INVITE = "INVITE"; - - /// - /// MESSAGE method. Defined in RFC 3428. - /// - public const string MESSAGE = "MESSAGE"; - - /// - /// NOTIFY method. Defined in RFC 3265. - /// - public const string NOTIFY = "NOTIFY"; - - /// - /// OPTIONS method. Defined in RFC 3261. - /// - public const string OPTIONS = "OPTIONS"; - - /// - /// PRACK method. Defined in RFC 3262. - /// - public const string PRACK = "PRACK"; - - /// - /// PUBLISH method. Defined in RFC 3903. - /// - public const string PUBLISH = "PUBLISH"; - - /// - /// REFER method. Defined in RFC 3515. - /// - public const string REFER = "REFER"; - - /// - /// REGISTER method. Defined in RFC 3261. - /// - public const string REGISTER = "REGISTER"; - - /// - /// SUBSCRIBE method. Defined in RFC 3265. - /// - public const string SUBSCRIBE = "SUBSCRIBE"; - - /// - /// UPDATE method. Defined in RFC 3311. - /// - public const string UPDATE = "UPDATE"; - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Request.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Request.cs deleted file mode 100644 index 91d43362c..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Request.cs +++ /dev/null @@ -1,302 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - #region usings - - using System; - using System.IO; - using System.Net; - using System.Text; - using Message; - - #endregion - - /// - /// SIP server request. Related RFC 3261. - /// - public class SIP_Request : SIP_Message - { - #region Members - - private readonly SIP_RequestLine m_pRequestLine; - private SIP_Flow m_pFlow; - private IPEndPoint m_pLocalEP; - private IPEndPoint m_pRemoteEP; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// SIP request method. - /// Is raised when method is null reference. - public SIP_Request(string method) - { - if (method == null) - { - throw new ArgumentNullException("method"); - } - - m_pRequestLine = new SIP_RequestLine(method, new AbsoluteUri()); - } - - #endregion - - #region Properties - - /// - /// Gets request-line. - /// - public SIP_RequestLine RequestLine - { - get { return m_pRequestLine; } - } - - /// - /// Gets or sets flow what received or sent this request. Returns null if this request isn't sent or received. - /// - internal SIP_Flow Flow - { - get { return m_pFlow; } - - set { m_pFlow = value; } - } - - /// - /// Gets or sets local end point what sent/received this request. Returns null if this request isn't sent or received. - /// - internal IPEndPoint LocalEndPoint - { - get { return m_pLocalEP; } - - set { m_pLocalEP = value; } - } - - /// - /// Gets or sets remote end point what sent/received this request. Returns null if this request isn't sent or received. - /// - internal IPEndPoint RemoteEndPoint - { - get { return m_pRemoteEP; } - - set { m_pRemoteEP = value; } - } - - #endregion - - #region Methods - - /// - /// Parses SIP_Request from byte array. - /// - /// Valid SIP request data. - /// Returns parsed SIP_Request obeject. - /// Raised when data is null. - /// Raised when invalid SIP message. - public static SIP_Request Parse(byte[] data) - { - if (data == null) - { - throw new ArgumentNullException("data"); - } - - return Parse(new MemoryStream(data)); - } - - /// - /// Parses SIP_Request from stream. - /// - /// Stream what contains valid SIP request. - /// Returns parsed SIP_Request obeject. - /// Raised when stream is null. - /// Raised when invalid SIP message. - public static SIP_Request Parse(Stream stream) - { - /* Syntax: - SIP-Method SIP-URI SIP-Version - SIP-Message - */ - - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - // Parse Response-line - StreamLineReader r = new StreamLineReader(stream); - r.Encoding = "utf-8"; - string[] method_uri_version = r.ReadLineString().Split(' '); - if (method_uri_version.Length != 3) - { - throw new Exception( - "Invalid SIP request data ! Method line doesn't contain: SIP-Method SIP-URI SIP-Version."); - } - SIP_Request retVal = new SIP_Request(method_uri_version[0]); - retVal.RequestLine.Uri = AbsoluteUri.Parse(method_uri_version[1]); - retVal.RequestLine.Version = method_uri_version[2]; - - // Parse SIP message - retVal.InternalParse(stream); - - return retVal; - } - - /// - /// Clones this request. - /// - /// Returns new cloned request. - public SIP_Request Copy() - { - SIP_Request retVal = Parse(ToByteData()); - retVal.Flow = m_pFlow; - retVal.LocalEndPoint = m_pLocalEP; - retVal.RemoteEndPoint = m_pRemoteEP; - - return retVal; - } - - /// - /// Checks if SIP request has all required values as request line,header fields and their values. - /// Throws Exception if not valid SIP request. - /// - public void Validate() - { - // Request SIP version - // Via: + branch prameter - // To: - // From: - // CallID: - // CSeq - // Max-Forwards RFC 3261 8.1.1. - - if (!RequestLine.Version.ToUpper().StartsWith("SIP/2.0")) - { - throw new SIP_ParseException("Not supported SIP version '" + RequestLine.Version + "' !"); - } - - if (Via.GetTopMostValue() == null) - { - throw new SIP_ParseException("Via: header field is missing !"); - } - if (Via.GetTopMostValue().Branch == null) - { - throw new SIP_ParseException("Via: header field branch parameter is missing !"); - } - - if (To == null) - { - throw new SIP_ParseException("To: header field is missing !"); - } - - if (From == null) - { - throw new SIP_ParseException("From: header field is missing !"); - } - - if (CallID == null) - { - throw new SIP_ParseException("CallID: header field is missing !"); - } - - if (CSeq == null) - { - throw new SIP_ParseException("CSeq: header field is missing !"); - } - - if (MaxForwards == -1) - { - // We can fix it by setting it to default value 70. - MaxForwards = 70; - } - - /* RFC 3261 12.1.2 - When a UAC sends a request that can establish a dialog (such as an INVITE) it MUST - provide a SIP or SIPS URI with global scope (i.e., the same SIP URI can be used in - messages outside this dialog) in the Contact header field of the request. If the - request has a Request-URI or a topmost Route header field value with a SIPS URI, the - Contact header field MUST contain a SIPS URI. - */ - if (SIP_Utils.MethodCanEstablishDialog(RequestLine.Method)) - { - if (Contact.GetAllValues().Length == 0) - { - throw new SIP_ParseException( - "Contact: header field is missing, method that can establish a dialog MUST provide a SIP or SIPS URI !"); - } - if (Contact.GetAllValues().Length > 1) - { - throw new SIP_ParseException( - "There may be only 1 Contact: header for the method that can establish a dialog !"); - } - if (!Contact.GetTopMostValue().Address.IsSipOrSipsUri) - { - throw new SIP_ParseException( - "Method that can establish a dialog MUST have SIP or SIPS uri in Contact: header !"); - } - } - - // TODO: Invite must have From:/To: tag - - // TODO: Check that request-Method equals CSeq method - - // TODO: PRACK must have RAck and RSeq header fields. - - // TODO: get in transport made request, so check if sips and sip set as needed. - } - - /// - /// Stores SIP_Request to specified stream. - /// - /// Stream where to store. - public void ToStream(Stream stream) - { - // Add request-line - byte[] responseLine = Encoding.UTF8.GetBytes(m_pRequestLine.ToString()); - stream.Write(responseLine, 0, responseLine.Length); - - // Add SIP-message - InternalToStream(stream); - } - - /// - /// Converts this request to raw srver request data. - /// - /// - public byte[] ToByteData() - { - MemoryStream retVal = new MemoryStream(); - ToStream(retVal); - - return retVal.ToArray(); - } - - /// - /// Returns request as string. - /// - /// Returns request as string. - public override string ToString() - { - return Encoding.UTF8.GetString(ToByteData()); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_RequestLine.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_RequestLine.cs deleted file mode 100644 index 432a7e15f..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_RequestLine.cs +++ /dev/null @@ -1,157 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - #region usings - - using System; - - #endregion - - /// - /// Implements SIP Request-Line. Defined in RFC 3261. - /// - public class SIP_RequestLine - { - #region Members - - private string m_Method = ""; - private AbsoluteUri m_pUri; - private string m_Version = ""; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// SIP method. - /// Request URI. - /// Is raised when method or uri is null reference. - /// Is raised when any of the arguments has invalid value. - public SIP_RequestLine(string method, AbsoluteUri uri) - { - if (method == null) - { - throw new ArgumentNullException("method"); - } - if (!SIP_Utils.IsToken(method)) - { - throw new ArgumentException("Argument 'method' value must be token."); - } - if (uri == null) - { - throw new ArgumentNullException("uri"); - } - - m_Method = method.ToUpper(); - m_pUri = uri; - m_Version = "SIP/2.0"; - } - - #endregion - - #region Properties - - /// - /// Gets or sets request method. This value is always in upper-case. - /// - /// Is raised when value is null reference. - /// Is raised when value has invalid value. - public string Method - { - get { return m_Method; } - - set - { - if (value == null) - { - throw new ArgumentNullException("Method"); - } - if (!SIP_Utils.IsToken(value)) - { - throw new ArgumentException("Property 'Method' value must be token."); - } - - m_Method = value.ToUpper(); - } - } - - /// - /// Gets or sets request URI. - /// - /// Is raised when value is null reference. - public AbsoluteUri Uri - { - get { return m_pUri; } - - set - { - if (value == null) - { - throw new ArgumentNullException("Uri"); - } - - m_pUri = value; - } - } - - /// - /// Gets or sets SIP version. - /// - /// Is raised when value is null reference. - /// Is raised when value has invalid value. - public string Version - { - get { return m_Version; } - - set - { - if (value == null) - { - throw new ArgumentNullException("Version"); - } - if (value == "") - { - throw new ArgumentException("Property 'Version' value must be specified."); - } - - m_Version = value; - } - } - - #endregion - - #region Methods - - /// - /// Returns Request-Line string. - /// - /// Returns Request-Line string. - public override string ToString() - { - // RFC 3261 25. - // Request-Line = Method SP Request-URI SP SIP-Version CRLF - - return m_Method + " " + m_pUri + " " + m_Version + "\r\n"; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_RequestReceivedEventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_RequestReceivedEventArgs.cs deleted file mode 100644 index 9001513d0..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_RequestReceivedEventArgs.cs +++ /dev/null @@ -1,121 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - #region usings - - using System; - - #endregion - - /// - /// This class provides data for SIP_Dialog.RequestReceived event and SIP_Core.OnRequestReceived> method. - /// - public class SIP_RequestReceivedEventArgs : EventArgs - { - #region Members - - private readonly SIP_Dialog m_pDialog; - private readonly SIP_Flow m_pFlow; - private readonly SIP_Request m_pRequest; - private readonly SIP_Stack m_pStack; - private SIP_ServerTransaction m_pTransaction; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Reference to SIP stack. - /// SIP data flow. - /// Recieved request. - internal SIP_RequestReceivedEventArgs(SIP_Stack stack, SIP_Flow flow, SIP_Request request) - : this(stack, flow, request, null, null) {} - - /// - /// Default constructor. - /// - /// Reference to SIP stack. - /// SIP data flow. - /// Recieved request. - /// SIP dialog which received request. - /// SIP server transaction which must be used to send response back to request maker. - internal SIP_RequestReceivedEventArgs(SIP_Stack stack, - SIP_Flow flow, - SIP_Request request, - SIP_Dialog dialog, - SIP_ServerTransaction transaction) - { - m_pStack = stack; - m_pFlow = flow; - m_pRequest = request; - m_pDialog = dialog; - m_pTransaction = transaction; - } - - #endregion - - #region Properties - - /// - /// Gets SIP dialog where Request belongs to. Returns null if Request doesn't belong any dialog. - /// - public SIP_Dialog Dialog - { - get { return m_pDialog; } - } - - /// - /// Gets request received by SIP stack. - /// - public SIP_Request Request - { - get { return m_pRequest; } - } - - /// - /// Gets server transaction for that request. Server transaction is created when this property is - /// first accessed. If you don't need server transaction for that request, for example statless proxy, - /// just don't access this property. For ACK method, this method always return null, because ACK - /// doesn't create transaction ! - /// - public SIP_ServerTransaction ServerTransaction - { - get - { - // ACK never creates transaction. - if (m_pRequest.RequestLine.Method == SIP_Methods.ACK) - { - return null; - } - - // Create server transaction for that request. - if (m_pTransaction == null) - { - m_pTransaction = m_pStack.TransactionLayer.EnsureServerTransaction(m_pFlow, m_pRequest); - } - - return m_pTransaction; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_RequestSender.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_RequestSender.cs deleted file mode 100644 index b38a331b4..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_RequestSender.cs +++ /dev/null @@ -1,814 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - #region usings - - using System; - using System.Collections.Generic; - using System.Net; - using System.Threading; - using AUTH; - using Message; - - #endregion - - /// - /// This class implements SIP request sender. - /// - /// - /// Request is sent using following methods:
    - /// *) If there is active data flow, it is used. - /// *) Request is sent as described in RFC 3261 [4](RFC 3263). - ///
    - public class SIP_RequestSender : IDisposable - { - #region Events - - /// - /// Is raised when sender has finished processing(got final-response or error). - /// - public event EventHandler Completed = null; - - /// - /// Is raised when this object has disposed. - /// - public event EventHandler Disposed = null; - - /// - /// Is raised when this transaction has got response from target end point. - /// - public event EventHandler ResponseReceived = null; - - #endregion - - #region Members - - private bool m_IsStarted; - private List m_pCredentials; - private SIP_Flow m_pFlow; - private Queue m_pHops; - private object m_pLock = new object(); - private SIP_Request m_pRequest; - private SIP_Stack m_pStack; - private SIP_ClientTransaction m_pTransaction; - private SIP_RequestSenderState m_State = SIP_RequestSenderState.Initial; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner stack. - /// SIP request. - /// Active data flow what to try before RFC 3261 [4](RFC 3263) methods to use to send request. - /// This value can be null. - /// Is raised when stack or request is null. - internal SIP_RequestSender(SIP_Stack stack, SIP_Request request, SIP_Flow flow) - { - if (stack == null) - { - throw new ArgumentNullException("stack"); - } - if (request == null) - { - throw new ArgumentNullException("request"); - } - - m_pStack = stack; - m_pRequest = request; - m_pFlow = flow; - - m_pCredentials = new List(); - m_pHops = new Queue(); - } - - #endregion - - #region Properties - - /// - /// Gets credentials collection. - /// - /// Is raised when this object is disposed and and this property is accessed. - public List Credentials - { - get - { - if (m_State == SIP_RequestSenderState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pCredentials; - } - } - - /// - /// Gets SIP flow what was used to send request or null if request is not sent yet. - /// - /// Is raised when this object is disposed and and this property is accessed. - public SIP_Flow Flow - { - get - { - if (m_State == SIP_RequestSenderState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pFlow; - } - } - - /// - /// Gets if this object is disposed. - /// - public bool IsDisposed - { - get { return m_State == SIP_RequestSenderState.Disposed; } - } - - /// - /// Gets if request sending has been started. - /// - /// Is raised when this object is disposed and and this property is accessed. - public bool IsStarted - { - get - { - if (m_State == SIP_RequestSenderState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_IsStarted; - } - } - - /// - /// Gets SIP request. - /// - /// Is raised when this object is disposed and and this property is accessed. - public SIP_Request Request - { - get - { - if (m_State == SIP_RequestSenderState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pRequest; - } - } - - /// - /// Gets owner stack. - /// - /// Is raised when this object is disposed and and this property is accessed. - public SIP_Stack Stack - { - get - { - if (m_State == SIP_RequestSenderState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pStack; - } - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public void Dispose() - { - lock (m_pLock) - { - if (m_State == SIP_RequestSenderState.Disposed) - { - return; - } - m_State = SIP_RequestSenderState.Disposed; - - OnDisposed(); - - ResponseReceived = null; - Completed = null; - Disposed = null; - - m_pStack = null; - m_pRequest = null; - m_pCredentials = null; - m_pHops = null; - m_pTransaction = null; - m_pLock = null; - } - } - - /// - /// Starts sending request. - /// - /// Is raised when this object is disposed and and this method is accessed. - /// Is raised when Start method has alredy called. - /// Is raised when no transport hop(s) for request. - public void Start() - { - lock (m_pLock) - { - if (m_State == SIP_RequestSenderState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (m_IsStarted) - { - throw new InvalidOperationException("Start method has been already called."); - } - m_IsStarted = true; - m_State = SIP_RequestSenderState.Starting; - - // Start may take so, process it on thread pool. - ThreadPool.QueueUserWorkItem(delegate - { - lock (m_pLock) - { - if (m_State == SIP_RequestSenderState.Disposed) - { - return; - } - - /* RFC 3261 8.1.2 Sending the Request - The destination for the request is then computed. Unless there is - local policy specifying otherwise, the destination MUST be determined - by applying the DNS procedures described in [4] as follows. If the - first element in the route set indicated a strict router (resulting - in forming the request as described in Section 12.2.1.1), the - procedures MUST be applied to the Request-URI of the request. - Otherwise, the procedures are applied to the first Route header field - value in the request (if one exists), or to the request's Request-URI - if there is no Route header field present. These procedures yield an - ordered set of address, port, and transports to attempt. Independent - of which URI is used as input to the procedures of [4], if the - Request-URI specifies a SIPS resource, the UAC MUST follow the - procedures of [4] as if the input URI were a SIPS URI. - - The UAC SHOULD follow the procedures defined in [4] for stateful - elements, trying each address until a server is contacted. Each try - constitutes a new transaction, and therefore each carries a different - topmost Via header field value with a new branch parameter. - Furthermore, the transport value in the Via header field is set to - whatever transport was determined for the target server. - */ - - // We never use strict, only loose route. - bool isStrictRoute = false; - - SIP_Uri uri = null; - if (isStrictRoute) - { - uri = (SIP_Uri) m_pRequest.RequestLine.Uri; - } - else if (m_pRequest.Route.GetTopMostValue() != null) - { - uri = - (SIP_Uri) - m_pRequest.Route.GetTopMostValue().Address. - Uri; - } - else - { - uri = (SIP_Uri) m_pRequest.RequestLine.Uri; - } - //uri.Param_Transport = "TCP"; - - // Queue hops. - foreach (SIP_Hop hop in - m_pStack.GetHops(uri, - m_pRequest.ToByteData().Length, - ((SIP_Uri) - m_pRequest.RequestLine.Uri). - IsSecure)) - { - m_pHops.Enqueue(hop); - } - - if (m_pHops.Count == 0) - { - OnTransportError( - new SIP_TransportException("No hops for '" + - uri + "'.")); - OnCompleted(); - } - else - { - m_State = SIP_RequestSenderState.Started; - - try - { - if (m_pFlow != null) - { - SendToFlow(m_pFlow, m_pRequest.Copy()); - - return; - } - } - catch - { - // Sending to specified flow failed, probably disposed, just try send to first hop. - } - - SendToNextHop(); - } - } - }); - } - } - - /// - /// Cancels current request sending. - /// - /// Is raised when this object is disposed and and this method is accessed. - /// Is raised when request sending has not been started by Start method. - public void Cancel() - { - // If sender is in starting state, we must wait that state to complete. - while (m_State == SIP_RequestSenderState.Starting) - { - Thread.Sleep(5); - } - - lock (m_pLock) - { - if (m_State == SIP_RequestSenderState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!m_IsStarted) - { - throw new InvalidOperationException("Request sending has not started, nothing to cancel."); - } - if (m_State != SIP_RequestSenderState.Started) - { - return; - } - - m_pHops.Clear(); - } - - // We may not call m_pTransaction.Cancel() in lock block, because deadlock can happen when transaction get response at same time. - // Transaction waits lock for us and we wait lock to transaction. - m_pTransaction.Cancel(); - } - - #endregion - - #region Utility methods - - /// - /// Is called when client transactions receives response. - /// - /// Sender. - /// Event data. - private void ClientTransaction_ResponseReceived(object sender, SIP_ResponseReceivedEventArgs e) - { - lock (m_pLock) - { - m_pFlow = e.ClientTransaction.Request.Flow; - - if (e.Response.StatusCode == 401 || e.Response.StatusCode == 407) - { - // Check if authentication failed(We sent authorization data and it's challenged again, - // probably user name or password inccorect) - bool hasFailedAuthorization = false; - foreach (SIP_t_Challenge challange in e.Response.WWWAuthenticate.GetAllValues()) - { - foreach (SIP_t_Credentials credentials in - m_pTransaction.Request.Authorization.GetAllValues()) - { - if (new Auth_HttpDigest(challange.AuthData, "").Realm == - new Auth_HttpDigest(credentials.AuthData, "").Realm) - { - hasFailedAuthorization = true; - break; - } - } - } - foreach (SIP_t_Challenge challange in e.Response.ProxyAuthenticate.GetAllValues()) - { - foreach (SIP_t_Credentials credentials in - m_pTransaction.Request.ProxyAuthorization.GetAllValues()) - { - if (new Auth_HttpDigest(challange.AuthData, "").Realm == - new Auth_HttpDigest(credentials.AuthData, "").Realm) - { - hasFailedAuthorization = true; - break; - } - } - } - - // Authorization failed, pass response to UA. - if (hasFailedAuthorization) - { - OnResponseReceived(e.Response); - } - // Try to authorize challanges. - else - { - SIP_Request request = m_pRequest.Copy(); - - /* RFC 3261 22.2. - When a UAC resubmits a request with its credentials after receiving a - 401 (Unauthorized) or 407 (Proxy Authentication Required) response, - it MUST increment the CSeq header field value as it would normally - when sending an updated request. - */ - request.CSeq = new SIP_t_CSeq(m_pStack.ConsumeCSeq(), request.CSeq.RequestMethod); - - // All challanges authorized, resend request. - if (Authorize(request, e.Response, Credentials.ToArray())) - { - SIP_Flow flow = m_pTransaction.Flow; - CleanUpActiveTransaction(); - SendToFlow(flow, request); - } - // We don't have credentials for one or more challenges. - else - { - OnResponseReceived(e.Response); - } - } - } - else - { - OnResponseReceived(e.Response); - if (e.Response.StatusCodeType != SIP_StatusCodeType.Provisional) - { - OnCompleted(); - } - } - } - } - - /// - /// Is called when client transaction has timed out. - /// - /// Sender. - /// Event data. - private void ClientTransaction_TimedOut(object sender, EventArgs e) - { - lock (m_pLock) - { - /* RFC 3261 8.1.2. - The UAC SHOULD follow the procedures defined in [4] for stateful - elements, trying each address until a server is contacted. Each try - constitutes a new transaction, and therefore each carries a different - topmost Via header field value with a new branch parameter. - Furthermore, the transport value in the Via header field is set to - whatever transport was determined for the target server. - */ - if (m_pHops.Count > 0) - { - CleanUpActiveTransaction(); - SendToNextHop(); - } - /* 8.1.3.1 Transaction Layer Errors - In some cases, the response returned by the transaction layer will - not be a SIP message, but rather a transaction layer error. When a - timeout error is received from the transaction layer, it MUST be - treated as if a 408 (Request Timeout) status code has been received. - If a fatal transport error is reported by the transport layer - (generally, due to fatal ICMP errors in UDP or connection failures in - TCP), the condition MUST be treated as a 503 (Service Unavailable) - status code. - */ - else - { - OnResponseReceived(m_pStack.CreateResponse(SIP_ResponseCodes.x408_Request_Timeout, - m_pRequest)); - OnCompleted(); - } - } - } - - /// - /// Is called when client transaction encountered transport error. - /// - /// Sender. - /// Event data. - private void ClientTransaction_TransportError(object sender, EventArgs e) - { - lock (m_pLock) - { - /* RFC 3261 8.1.2. - The UAC SHOULD follow the procedures defined in [4] for stateful - elements, trying each address until a server is contacted. Each try - constitutes a new transaction, and therefore each carries a different - topmost Via header field value with a new branch parameter. - Furthermore, the transport value in the Via header field is set to - whatever transport was determined for the target server. - */ - if (m_pHops.Count > 0) - { - CleanUpActiveTransaction(); - SendToNextHop(); - } - /* RFC 3261 8.1.3.1 Transaction Layer Errors - In some cases, the response returned by the transaction layer will - not be a SIP message, but rather a transaction layer error. When a - timeout error is received from the transaction layer, it MUST be - treated as if a 408 (Request Timeout) status code has been received. - If a fatal transport error is reported by the transport layer - (generally, due to fatal ICMP errors in UDP or connection failures in - TCP), the condition MUST be treated as a 503 (Service Unavailable) - status code. - */ - else - { - OnResponseReceived( - m_pStack.CreateResponse( - SIP_ResponseCodes.x503_Service_Unavailable + ": Transport error.", m_pRequest)); - OnCompleted(); - } - } - } - - /// - /// Creates authorization for each challange in response. - /// - /// SIP request where to add authorization values. - /// SIP response which challanges to authorize. - /// Credentials for authorization. - /// Returns true if all challanges were authorized. If any of the challanges was not authorized, returns false. - private bool Authorize(SIP_Request request, SIP_Response response, NetworkCredential[] credentials) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - if (response == null) - { - throw new ArgumentNullException("response"); - } - if (credentials == null) - { - throw new ArgumentNullException("credentials"); - } - - bool allAuthorized = true; - - #region WWWAuthenticate - - foreach (SIP_t_Challenge challange in response.WWWAuthenticate.GetAllValues()) - { - Auth_HttpDigest authDigest = new Auth_HttpDigest(challange.AuthData, - request.RequestLine.Method); - - // Serach credential for the specified challange. - NetworkCredential credential = null; - foreach (NetworkCredential c in credentials) - { - if (c.Domain.ToLower() == authDigest.Realm.ToLower()) - { - credential = c; - break; - } - } - // We don't have credential for this challange. - if (credential == null) - { - allAuthorized = false; - } - // Authorize challange. - else - { - authDigest.UserName = credential.UserName; - authDigest.Password = credential.Password; - authDigest.CNonce = Auth_HttpDigest.CreateNonce(); - - request.Authorization.Add(authDigest.ToAuthorization()); - } - } - - #endregion - - #region ProxyAuthenticate - - foreach (SIP_t_Challenge challange in response.ProxyAuthenticate.GetAllValues()) - { - Auth_HttpDigest authDigest = new Auth_HttpDigest(challange.AuthData, - request.RequestLine.Method); - - // Serach credential for the specified challange. - NetworkCredential credential = null; - foreach (NetworkCredential c in credentials) - { - if (c.Domain.ToLower() == authDigest.Realm.ToLower()) - { - credential = c; - break; - } - } - // We don't have credential for this challange. - if (credential == null) - { - allAuthorized = false; - } - // Authorize challange. - else - { - authDigest.UserName = credential.UserName; - authDigest.Password = credential.Password; - authDigest.CNonce = Auth_HttpDigest.CreateNonce(); - - request.ProxyAuthorization.Add(authDigest.ToAuthorization()); - } - } - - #endregion - - return allAuthorized; - } - - /// - /// Starts sending request to next hop in queue. - /// - /// Is raised when no next hop available(m_pHops.Count == 0) and this method is accessed. - private void SendToNextHop() - { - if (m_pHops.Count == 0) - { - throw new InvalidOperationException("No more hop(s)."); - } - - SIP_Hop hop = m_pHops.Dequeue(); - SendToFlow(m_pStack.TransportLayer.GetOrCreateFlow(hop.Transport, null, hop.EndPoint), - m_pRequest.Copy()); - } - - /// - /// Sends specified request to the specified data flow. - /// - /// SIP data flow. - /// SIP request to send. - /// Is raised when flow or request is null reference. - private void SendToFlow(SIP_Flow flow, SIP_Request request) - { - if (flow == null) - { - throw new ArgumentNullException("flow"); - } - if (request == null) - { - throw new ArgumentNullException("request"); - } - - #region Contact (RFC 3261 8.1.1.8) - - /* - The Contact header field provides a SIP or SIPS URI that can be used - to contact that specific instance of the UA for subsequent requests. - The Contact header field MUST be present and contain exactly one SIP - or SIPS URI in any request that can result in the establishment of a - dialog. For the methods defined in this specification, that includes - only the INVITE request. For these requests, the scope of the - Contact is global. That is, the Contact header field value contains - the URI at which the UA would like to receive requests, and this URI - MUST be valid even if used in subsequent requests outside of any - dialogs. - - If the Request-URI or top Route header field value contains a SIPS - URI, the Contact header field MUST contain a SIPS URI as well. - */ - - SIP_t_ContactParam contact = request.Contact.GetTopMostValue(); - - // Add contact header If request-Method can establish dialog and contact header not present. - if (SIP_Utils.MethodCanEstablishDialog(request.RequestLine.Method) && contact == null) - { - SIP_Uri from = (SIP_Uri) request.From.Address.Uri; - - request.Contact.Add((flow.IsSecure ? "sips:" : "sip:") + from.User + "@" + - m_pStack.TransportLayer.GetContactHost(flow)); - } - // If contact SIP URI and host = auto-allocate, allocate it as needed. - else if (contact != null && contact.Address.Uri is SIP_Uri && - ((SIP_Uri) contact.Address.Uri).Host == "auto-allocate") - { - ((SIP_Uri) contact.Address.Uri).Host = - m_pStack.TransportLayer.GetContactHost(flow).ToString(); - } - - #endregion - - m_pTransaction = m_pStack.TransactionLayer.CreateClientTransaction(flow, request, true); - m_pTransaction.ResponseReceived += ClientTransaction_ResponseReceived; - m_pTransaction.TimedOut += ClientTransaction_TimedOut; - m_pTransaction.TransportError += ClientTransaction_TransportError; - - // Start transaction processing. - m_pTransaction.Start(); - } - - /// - /// Cleans up active transaction. - /// - private void CleanUpActiveTransaction() - { - if (m_pTransaction != null) - { - // Don't dispose transaction, transaction will dispose itself when done. - // Otherwise for example failed INVITE won't linger in "Completed" state as it must be. - // We just release Events processing, because you don't care about them any more. - m_pTransaction.ResponseReceived -= ClientTransaction_ResponseReceived; - m_pTransaction.TimedOut -= ClientTransaction_TimedOut; - m_pTransaction.TransportError -= ClientTransaction_TransportError; - - m_pTransaction = null; - } - } - - /// - /// Raises ResponseReceived event. - /// - /// SIP response received. - private void OnResponseReceived(SIP_Response response) - { - if (ResponseReceived != null) - { - ResponseReceived(this, new SIP_ResponseReceivedEventArgs(m_pStack, m_pTransaction, response)); - } - } - - /// - /// Raises event TransportError. - /// - /// Excption happened. - private void OnTransportError(Exception exception) - { - // TODO: - } - - /// - /// Raises event Completed. - /// - private void OnCompleted() - { - m_State = SIP_RequestSenderState.Completed; - - if (Completed != null) - { - Completed(this, new EventArgs()); - } - } - - /// - /// Raises Disposed event. - /// - private void OnDisposed() - { - if (Disposed != null) - { - Disposed(this, new EventArgs()); - } - } - - #endregion - - #region Nested type: SIP_RequestSenderState - - private enum SIP_RequestSenderState - { - Initial, - Starting, - Started, - Completed, - Disposed - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Response.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Response.cs deleted file mode 100644 index 24f79daea..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Response.cs +++ /dev/null @@ -1,378 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - #region usings - - using System; - using System.Globalization; - using System.IO; - using System.Text; - using Message; - - #endregion - - /// - /// SIP server response. Related RFC 3261. - /// - public class SIP_Response : SIP_Message - { - #region Members - - private readonly SIP_Request m_pRequest; - private string m_ReasonPhrase = ""; - private double m_SipVersion = 2.0d; - private int m_StatusCode = 100; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public SIP_Response() {} - - /// - /// SIP_Request.CreateResponse constructor. - /// - /// Owner request. - internal SIP_Response(SIP_Request request) - { - m_pRequest = request; - } - - #endregion - - #region Properties - - /// - /// Gets or sets reponse reasong phrase. This just StatusCode describeing text. - /// - public string ReasonPhrase - { - get { return m_ReasonPhrase; } - - set - { - if (value == null) - { - throw new ArgumentNullException("ReasonPhrase"); - } - - m_ReasonPhrase = value; - } - } - - /// - /// Gets SIP request which response it is. This value is null if this is stateless response. - /// - public SIP_Request Request - { - get { return m_pRequest; } - } - - /// - /// Gets or sets SIP version. - /// - /// Is raised when invalid SIP version value passed. - public double SipVersion - { - get { return m_SipVersion; } - - set - { - if (value < 1) - { - throw new ArgumentException("Property SIP version must be >= 1.0 !"); - } - - m_SipVersion = value; - } - } - - /// - /// Gets or sets response status code. This value must be between 100 and 999. - /// - /// Is raised when value is out of allowed range. - public int StatusCode - { - get { return m_StatusCode; } - - set - { - if (value < 1 || value > 999) - { - throw new ArgumentException("Property 'StatusCode' value must be >= 100 && <= 999 !"); - } - - m_StatusCode = value; - } - } - - /// - /// Gets or sets SIP Status-Code with Reason-Phrase (Status-Code SP Reason-Phrase). - /// - public string StatusCode_ReasonPhrase - { - get { return m_StatusCode + " " + m_ReasonPhrase; } - - set - { - // Status-Code SP Reason-Phrase - - if (value == null) - { - throw new ArgumentNullException("StatusCode_ReasonPhrase"); - } - - string[] code_reason = value.Split(new[] {' '}, 2); - if (code_reason.Length != 2) - { - throw new ArgumentException( - "Invalid property 'StatusCode_ReasonPhrase' Reason-Phrase value !"); - } - try - { - StatusCode = Convert.ToInt32(code_reason[0]); - } - catch - { - throw new ArgumentException( - "Invalid property 'StatusCode_ReasonPhrase' Status-Code value !"); - } - ReasonPhrase = code_reason[1]; - } - } - - /// - /// Gets SIP status code type. - /// - public SIP_StatusCodeType StatusCodeType - { - get - { - if (m_StatusCode >= 100 && m_StatusCode < 200) - { - return SIP_StatusCodeType.Provisional; - } - else if (m_StatusCode >= 200 && m_StatusCode < 300) - { - return SIP_StatusCodeType.Success; - } - else if (m_StatusCode >= 300 && m_StatusCode < 400) - { - return SIP_StatusCodeType.Redirection; - } - else if (m_StatusCode >= 400 && m_StatusCode < 500) - { - return SIP_StatusCodeType.RequestFailure; - } - else if (m_StatusCode >= 500 && m_StatusCode < 600) - { - return SIP_StatusCodeType.ServerFailure; - } - else if (m_StatusCode >= 600 && m_StatusCode < 700) - { - return SIP_StatusCodeType.GlobalFailure; - } - else - { - throw new Exception("Unknown SIP StatusCodeType !"); - } - } - } - - #endregion - - #region Methods - - /// - /// Parses SIP_Response from byte array. - /// - /// Valid SIP response data. - /// Returns parsed SIP_Response obeject. - /// Raised when data is null. - /// Raised when invalid SIP message. - public static SIP_Response Parse(byte[] data) - { - if (data == null) - { - throw new ArgumentNullException("data"); - } - - return Parse(new MemoryStream(data)); - } - - /// - /// Parses SIP_Response from stream. - /// - /// Stream what contains valid SIP response. - /// Returns parsed SIP_Response obeject. - /// Raised when stream is null. - /// Raised when invalid SIP message. - public static SIP_Response Parse(Stream stream) - { - /* Syntax: - SIP-Version SP Status-Code SP Reason-Phrase - SIP-Message - */ - - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - - SIP_Response retVal = new SIP_Response(); - - // Parse Response-line - StreamLineReader r = new StreamLineReader(stream); - r.Encoding = "utf-8"; - string[] version_code_text = r.ReadLineString().Split(new[] {' '}, 3); - if (version_code_text.Length != 3) - { - throw new SIP_ParseException( - "Invalid SIP Status-Line syntax ! Syntax: {SIP-Version SP Status-Code SP Reason-Phrase}."); - } - // SIP-Version - try - { - retVal.SipVersion = Convert.ToDouble(version_code_text[0].Split('/')[1], - NumberFormatInfo.InvariantInfo); - } - catch - { - throw new SIP_ParseException("Invalid Status-Line SIP-Version value !"); - } - - // Status-Code - try - { - retVal.StatusCode = Convert.ToInt32(version_code_text[1]); - } - catch - { - throw new SIP_ParseException("Invalid Status-Line Status-Code value !"); - } - - // Reason-Phrase - retVal.ReasonPhrase = version_code_text[2]; - - // Parse SIP-Message - retVal.InternalParse(stream); - - return retVal; - } - - /// - /// Clones this request. - /// - /// Returns new cloned request. - public SIP_Response Copy() - { - SIP_Response retVal = Parse(ToByteData()); - - return retVal; - } - - /// - /// Checks if SIP response has all required values as response line,header fields and their values. - /// Throws Exception if not valid SIP response. - /// - public void Validate() - { - // Via: + branch prameter - // To: - // From: - // CallID: - // CSeq - - if (Via.GetTopMostValue() == null) - { - throw new SIP_ParseException("Via: header field is missing !"); - } - if (Via.GetTopMostValue().Branch == null) - { - throw new SIP_ParseException("Via: header fields branch parameter is missing !"); - } - - if (To == null) - { - throw new SIP_ParseException("To: header field is missing !"); - } - - if (From == null) - { - throw new SIP_ParseException("From: header field is missing !"); - } - - if (CallID == null) - { - throw new SIP_ParseException("CallID: header field is missing !"); - } - - if (CSeq == null) - { - throw new SIP_ParseException("CSeq: header field is missing !"); - } - - // TODO: INVITE 2xx must have only 1 contact header with SIP or SIPS. - } - - /// - /// Stores SIP_Response to specified stream. - /// - /// Stream where to store. - public void ToStream(Stream stream) - { - // Status-Line = SIP-Version SP Status-Code SP Reason-Phrase CRLF - - // Add response-line - byte[] responseLine = - Encoding.UTF8.GetBytes("SIP/" + SipVersion.ToString("f1").Replace(',', '.') + " " + StatusCode + - " " + ReasonPhrase + "\r\n"); - stream.Write(responseLine, 0, responseLine.Length); - - // Add SIP-message - InternalToStream(stream); - } - - /// - /// Converts this response to raw srver response data. - /// - /// - public byte[] ToByteData() - { - MemoryStream retVal = new MemoryStream(); - ToStream(retVal); - - return retVal.ToArray(); - } - - /// - /// Returns response as string. - /// - /// Returns response as string. - public override string ToString() - { - return Encoding.UTF8.GetString(ToByteData()); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_ResponseCodes.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_ResponseCodes.cs deleted file mode 100644 index a579e5527..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_ResponseCodes.cs +++ /dev/null @@ -1,380 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - /// - /// This class holds SIP respnse codes. - /// - public class SIP_ResponseCodes - { - #region Members - - /// - /// This response indicates that the request has been received by the - /// next-hop server and that some unspecified action is being taken on - /// behalf of this call (for example, a database is being consulted). - /// - public static readonly string x100_Trying = "100 Trying"; - - /// - /// The UA receiving the INVITE is trying to alert the user. - /// - public static readonly string x180_Ringing = "180 Ringing"; - - /// - /// A server MAY use this status code to indicate that the call is being - /// forwarded to a different set of destinations. - /// - public static readonly string x181_Call_Forwarded = "181 Call Is Being Forwarded"; - - /// - /// The called party is temporarily unavailable, but the server has - /// decided to queue the call rather than reject it. When the callee - /// becomes available, it will return the appropriate final status response. - /// - public static readonly string x182_Queued = "182 Queued"; - - /// - /// The 183 (Session Progress) response is used to convey information - /// about the progress of the call that is not otherwise classified. - /// - public static readonly string x183_Session_Progress = "183 Session Progress"; - - /// - /// The request has succeeded. - /// - public static readonly string x200_Ok = "200 OK"; - - /// - /// The request has accepted. Defined in rfc 3265. - /// - public static readonly string x202_Ok = "202 Accepted"; - - /// - /// The request could not be understood due to malformed syntax. The - /// Reason-Phrase SHOULD identify the syntax problem in more detail, for - /// example, "Missing Call-ID header field". - /// - public static readonly string x400_Bad_Request = "400 Bad Request"; - - /// - /// The request requires user authentication. - /// - public static readonly string x401_Unauthorized = "401 Unauthorized"; - - /// - /// The server understood the request, but is refusing to fulfill it. - /// Authorization will not help, and the request SHOULD NOT be repeated. - /// - public static readonly string x403_Forbidden = "403 Forbidden"; - - /// - /// The server has definitive information that the user does not exist at - /// the domain specified in the Request-URI. - /// - public static readonly string x404_Not_Found = "404 Not Found"; - - /// - /// The method specified in the Request-Line is understood, but not - /// allowed for the address identified by the Request-URI. - /// - public static readonly string x405_Method_Not_Allowed = "405 Method Not Allowed"; - - /// - /// The resource identified by the request is only capable of generating - /// response entities that have content characteristics not acceptable - /// according to the Accept header field sent in the request. - /// - public static readonly string x406_Not_Acceptable = "406 Not Acceptable"; - - /// - /// This code is similar to 401 (Unauthorized), but indicates that the - /// client MUST first authenticate itself with the proxy. - /// - public static readonly string x407_Proxy_Authentication_Required = "407 Proxy Authentication Required"; - - /// - /// The server could not produce a response within a suitable amount of - /// time, for example, if it could not determine the location of the user in time. - /// - public static readonly string x408_Request_Timeout = "408 Request Timeout"; - - /// - /// The requested resource is no longer available at the server and no - /// forwarding address is known. This condition is expected to be - /// considered permanent. - /// - public static readonly string x410_Gone = "410 Gone"; - - /// - /// Is used to indicate that the precondition given for the request has failed. Defined in rfc 3903. - /// - public static readonly string x412_Conditional_Request_Failed = "412 Conditional Request Failed"; - - /// - /// The server is refusing to process a request because the request - /// entity-body is larger than the server is willing or able to process. - /// The server MAY close the connection to prevent the client from - /// continuing the request. - /// - public static readonly string x413_Request_Entity_Too_Large = "413 Request Entity Too Large"; - - /// - /// The server is refusing to service the request because the Request-URI - /// is longer than the server is willing to interpret. - /// - public static readonly string x414_RequestURI_Too_Long = "414 Request-URI Too Long"; - - /// - /// The server is refusing to service the request because the message - /// body of the request is in a format not supported by the server for - /// the requested method. The server MUST return a list of acceptable - /// formats using the Accept, Accept-Encoding, or Accept-Language header - /// field, depending on the specific problem with the content. - /// - public static readonly string x415_Unsupported_Media_Type = "415 Unsupported Media Type"; - - /// - /// The server cannot process the request because the scheme of the URI - /// in the Request-URI is unknown to the server. - /// - public static readonly string x416_Unsupported_URI_Scheme = "416 Unsupported URI Scheme"; - - /// - /// TODO: add description. Defined in rfc 4412. - /// - public static readonly string x417_Unknown_Resource_Priority = "417 Unknown Resource-Priority"; - - /// - /// The server did not understand the protocol extension specified in a - /// Proxy-Require or Require header field. - /// - public static readonly string x420_Bad_Extension = "420 Bad Extension"; - - /// - /// The UAS needs a particular extension to process the request, but this - /// extension is not listed in a Supported header field in the request. - /// Responses with this status code MUST contain a Require header field - /// listing the required extensions. - /// - public static readonly string x421_Extension_Required = "421 Extension Required"; - - /// - /// It is generated by a UAS or proxy when a request contains a Session-Expires header field - /// with a duration below the minimum timer for the server. The 422 response MUST contain a Min-SE - /// header field with the minimum timer for that server. - /// - public static readonly string x422_Session_Interval_Too_Small = "422 Session Interval Too Small"; - - /// - /// The server is rejecting the request because the expiration time of - /// the resource refreshed by the request is too short. This response - /// can be used by a registrar to reject a registration whose Contact - /// header field expiration time was too small. - /// - public static readonly string x423_Interval_Too_Brief = "423 Interval Too Brief"; - - /// - /// It is used when the verifier receives a message with an Identity signature that does not - /// correspond to the digest-string calculated by the verifier. Defined in rfc 4474. - /// - public static readonly string x428_Use_Identity_Header = "428 Use Identity Header"; - - /// - /// TODO: add description. Defined in rfc 3892. - /// - public static readonly string x429_Provide_Referrer_Identity = "429 Provide Referrer Identity"; - - /// - /// It is used when the Identity-Info header contains a URI that cannot be dereferenced by the - /// verifier (either the URI scheme is unsupported by the verifier, or the resource designated by - /// the URI is otherwise unavailable). Defined in rfc 4474. - /// - public static readonly string x436_Bad_Identity_Info = "436 Bad Identity-Info"; - - /// - /// It is used when the verifier cannot validate the certificate referenced by the URI of the - /// Identity-Info header, because, for example, the certificate is self-signed, or signed by a - /// root certificate authority for whom the verifier does not possess a root certificate. - /// Defined in rfc 4474. - /// - public static readonly string x437_Unsupported_Certificate = "437 Unsupported Certificate"; - - /// - /// It is used when the verifier receives a message with an Identity signature that does not - /// correspond to the digest-string calculated by the verifier. Defined in rfc 4474. - /// - public static readonly string x438_Invalid_Identity_Header = "438 Invalid Identity Header"; - - /// - /// The callee's end system was contacted successfully but the callee is - /// currently unavailable (for example, is not logged in, logged in but - /// in a state that precludes communication with the callee, or has - /// activated the "do not disturb" feature). - /// - public static readonly string x480_Temporarily_Unavailable = "480 Temporarily Unavailable"; - - /// - /// This status indicates that the UAS received a request that does not - /// match any existing dialog or transaction. - /// - public static readonly string x481_Call_Transaction_Does_Not_Exist = - "481 Call/Transaction Does Not Exist"; - - /// - /// The server has detected a loop. - /// - public static readonly string x482_Loop_Detected = "482 Loop Detected"; - - /// - /// The server received a request that contains a Max-Forwards. - /// - public static readonly string x483_Too_Many_Hops = "483 Too Many Hops"; - - /// - /// The server received a request with a Request-URI that was incomplete. - /// Additional information SHOULD be provided in the reason phrase. - /// - public static readonly string x484_Address_Incomplete = "484 Address Incomplete"; - - /// - /// The Request-URI was ambiguous. - /// - public static readonly string x485_Ambiguous = "485 Ambiguous"; - - /// - /// The callee's end system was contacted successfully, but the callee is - /// currently not willing or able to take additional calls at this end - /// system. The response MAY indicate a better time to call in the - /// Retry-After header field. - /// - public static readonly string x486_Busy_Here = "486 Busy Here"; - - /// - /// The request was terminated by a BYE or CANCEL request. This response - /// is never returned for a CANCEL request itself. - /// - public static readonly string x487_Request_Terminated = "487 Request Terminated"; - - /// - /// The response has the same meaning as 606 (Not Acceptable), but only - /// applies to the specific resource addressed by the Request-URI and the - /// request may succeed elsewhere. - /// - public static readonly string x488_Not_Acceptable_Here = "488 Not Acceptable Here"; - - /// - /// Is used to indicate that the server did not understand the event package specified - /// in a "Event" header field. Defined in rfc 3265. - /// - public static readonly string x489_Bad_Event = "489 Bad Event"; - - /// - /// The request was received by a UAS that had a pending request within - /// the same dialog. - /// - public static readonly string x491_Request_Pending = "491 Request Pending"; - - /// - /// The request was received by a UAS that contained an encrypted MIME - /// body for which the recipient does not possess or will not provide an - /// appropriate decryption key. - /// - public static readonly string x493_Undecipherable = "493 Undecipherable"; - - /// - /// TODO: add description. Defined in rfc 3329. - /// - public static readonly string x494_Security_Agreement_Required = "494 Security Agreement Required"; - - /// - /// The server encountered an unexpected condition that prevented it from - /// fulfilling the request. - /// - public static readonly string x500_Server_Internal_Error = "500 Server Internal Error"; - - /// - /// The server does not support the functionality required to fulfill the request. - /// - public static readonly string x501_Not_Implemented = "501 Not Implemented"; - - /// - /// The server, while acting as a gateway or proxy, received an invalid - /// response from the downstream server it accessed in attempting to - /// fulfill the request. - /// - public static readonly string x502_Bad_Gateway = "502 Bad Gateway"; - - /// - /// The server is temporarily unable to process the request due to a - /// temporary overloading or maintenance of the server. - /// - public static readonly string x503_Service_Unavailable = "503 Service Unavailable"; - - /// - /// The server did not receive a timely response from an external server - /// it accessed in attempting to process the request. - /// - public static readonly string x504_Timeout = "504 Server Time-out"; - - /// - /// The server does not support, or refuses to support, the SIP protocol - /// version that was used in the request. - /// - public static readonly string x504_Version_Not_Supported = "505 Version Not Supported"; - - /// - /// The server was unable to process the request since the message length - /// exceeded its capabilities. - /// - public static readonly string x513_Message_Too_Large = "513 Message Too Large"; - - /// - /// When a UAS, acting as an answerer, cannot or is not willing to meet the preconditions - /// in the offer. Defined in rfc 3312. - /// - public static readonly string x580_Precondition_Failure = "580 Precondition Failure"; - - /// - /// The callee's end system was contacted successfully but the callee is - /// busy and does not wish to take the call at this time. - /// - public static readonly string x600_Busy_Everywhere = "600 Busy Everywhere"; - - /// - /// The callee's machine was successfully contacted but the user - /// explicitly does not wish to or cannot participate. - /// - public static readonly string x603_Decline = "603 Decline"; - - /// - /// The server has authoritative information that the user indicated in - /// the Request-URI does not exist anywhere - /// - public static readonly string x604_Does_Not_Exist_Anywhere = "604 Does Not Exist Anywhere"; - - /// - /// The user's agent was contacted successfully but some aspects of the - /// session description such as the requested media, bandwidth, or - /// addressing style were not acceptable. - /// - public static readonly string x606_Not_Acceptable = "606 Not Acceptable"; - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_ResponseReceivedEventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_ResponseReceivedEventArgs.cs deleted file mode 100644 index 65e66a7b5..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_ResponseReceivedEventArgs.cs +++ /dev/null @@ -1,87 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - #region usings - - using System; - - #endregion - - /// - /// This class provides data for ResponseReceived events. - /// - public class SIP_ResponseReceivedEventArgs : EventArgs - { - #region Members - - private readonly SIP_Response m_pResponse; - private readonly SIP_Stack m_pStack; - private readonly SIP_ClientTransaction m_pTransaction; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Reference to SIP stack. - /// Client transaction what response it is. This value can be null if no matching client response. - /// Received response. - internal SIP_ResponseReceivedEventArgs(SIP_Stack stack, - SIP_ClientTransaction transaction, - SIP_Response response) - { - m_pStack = stack; - m_pResponse = response; - m_pTransaction = transaction; - } - - #endregion - - #region Properties - - /// - /// Gets client transaction which response it is. This value is null if no matching client transaction. - /// If this core is staless proxy then it's allowed, otherwise core MUST discard that response. - /// - public SIP_ClientTransaction ClientTransaction - { - get { return m_pTransaction; } - } - - /// - /// Gets SIP dialog where Response belongs to. Returns null if Response doesn't belong any dialog. - /// - public SIP_Dialog Dialog - { - get { return m_pStack.TransactionLayer.MatchDialog(m_pResponse); } - } - - /// - /// Gets response received by SIP stack. - /// - public SIP_Response Response - { - get { return m_pResponse; } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_ResponseSentEventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_ResponseSentEventArgs.cs deleted file mode 100644 index 89ecc9f3d..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_ResponseSentEventArgs.cs +++ /dev/null @@ -1,83 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - #region usings - - using System; - - #endregion - - /// - /// This class provides data for SIP_ServerTransaction.ResponseSent method. - /// - public class SIP_ResponseSentEventArgs : EventArgs - { - #region Members - - private readonly SIP_Response m_pResponse; - private readonly SIP_ServerTransaction m_pTransaction; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Server transaction. - /// SIP response. - /// Is raised when any of the arguments is null. - public SIP_ResponseSentEventArgs(SIP_ServerTransaction transaction, SIP_Response response) - { - if (transaction == null) - { - throw new ArgumentNullException("transaction"); - } - if (response == null) - { - throw new ArgumentNullException("response"); - } - - m_pTransaction = transaction; - m_pResponse = response; - } - - #endregion - - #region Properties - - /// - /// Gets response which was sent. - /// - public SIP_Response Response - { - get { return m_pResponse; } - } - - /// - /// Gets server transaction which sent response. - /// - public SIP_ServerTransaction ServerTransaction - { - get { return m_pTransaction; } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_ServerTransaction.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_ServerTransaction.cs deleted file mode 100644 index 2c8a13670..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_ServerTransaction.cs +++ /dev/null @@ -1,934 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - #region usings - - using System; - using System.Timers; - using Message; - - #endregion - - /// - /// Implements SIP server transaction. Defined in rfc 3261 17.2. - /// - public class SIP_ServerTransaction : SIP_Transaction - { - #region Events - - /// - /// Is raised when transaction has canceled. - /// - public event EventHandler Canceled = null; - - /// - /// Is raised when transaction has sent response to remote party. - /// - public event EventHandler ResponseSent = null; - - #endregion - - #region Members - - private TimerEx m_pTimer100; - private TimerEx m_pTimerG; - private TimerEx m_pTimerH; - private TimerEx m_pTimerI; - private TimerEx m_pTimerJ; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner SIP stack. - /// SIP data flow which received request. - /// SIP request that transaction will handle. - /// Is raised when stack,flow or request is null reference. - /// Is raised when any of the arguments has invalid value. - public SIP_ServerTransaction(SIP_Stack stack, SIP_Flow flow, SIP_Request request) - : base(stack, flow, request) - { - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=true] created."); - } - - Start(); - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public override void Dispose() - { - lock (SyncRoot) - { - if (m_pTimer100 != null) - { - m_pTimer100.Dispose(); - m_pTimer100 = null; - } - if (m_pTimerG != null) - { - m_pTimerG.Dispose(); - m_pTimerG = null; - } - if (m_pTimerH != null) - { - m_pTimerH.Dispose(); - m_pTimerH = null; - } - if (m_pTimerI != null) - { - m_pTimerI.Dispose(); - m_pTimerI = null; - } - if (m_pTimerJ != null) - { - m_pTimerJ.Dispose(); - m_pTimerJ = null; - } - } - } - - /// - /// Sends specified response to remote party. - /// - /// SIP response to send. - /// Is raised when this class is Disposed and this method is accessed. - /// Is raised when response is null reference. - public void SendResponse(SIP_Response response) - { - lock (SyncRoot) - { - if (State == SIP_TransactionState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (response == null) - { - throw new ArgumentNullException("response"); - } - - try - { - #region INVITE - - /* RFC 3261 17.2.1. - |INVITE - |pass INV to TU - INVITE V send 100 if TU won't in 200ms - send response+-----------+ - +--------| |--------+101-199 from TU - | | Proceeding| |send response - +------->| |<-------+ - | | Transport Err. - | | Inform TU - | |--------------->+ - +-----------+ | - 300-699 from TU | |2xx from TU | - send response | |send response | - | +------------------>+ - | | - INVITE V Timer G fires | - send response+-----------+ send response | - +--------| |--------+ | - | | Completed | | | - +------->| |<-------+ | - +-----------+ | - | | | - ACK | | | - - | +------------------>+ - | Timer H fires | - V or Transport Err.| - +-----------+ Inform TU | - | | | - | Confirmed | | - | | | - +-----------+ | - | | - |Timer I fires | - |- | - | | - V | - +-----------+ | - | | | - | Terminated|<---------------+ - | | - +-----------+ - */ - - if (Method == SIP_Methods.INVITE) - { - #region Proceeding - - if (State == SIP_TransactionState.Proceeding) - { - AddResponse(response); - - // 1xx - if (response.StatusCodeType == SIP_StatusCodeType.Provisional) - { - Stack.TransportLayer.SendResponse(this, response); - OnResponseSent(response); - } - // 2xx - else if (response.StatusCodeType == SIP_StatusCodeType.Success) - { - Stack.TransportLayer.SendResponse(this, response); - OnResponseSent(response); - SetState(SIP_TransactionState.Terminated); - } - // 3xx - 6xx - else - { - Stack.TransportLayer.SendResponse(this, response); - OnResponseSent(response); - SetState(SIP_TransactionState.Completed); - - /* RFC 3261 17.2.1. - For unreliable transports, timer G is set to fire in T1 seconds, and is not set to fire for reliable transports. - */ - if (!Flow.IsReliable) - { - m_pTimerG = new TimerEx(SIP_TimerConstants.T1, false); - m_pTimerG.Elapsed += m_pTimerG_Elapsed; - m_pTimerG.Enabled = true; - - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + - Method + - "';IsServer=true] timer G(INVITE response(3xx - 6xx) retransmission) started, will triger after " + - m_pTimerG.Interval + "."); - } - } - - /* RFC 3261 17.2.1. - When the "Completed" state is entered, timer H MUST be set to fire in 64*T1 seconds for all transports. - */ - m_pTimerH = new TimerEx(64*SIP_TimerConstants.T1); - m_pTimerH.Elapsed += m_pTimerH_Elapsed; - m_pTimerH.Enabled = true; - - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=true] timer H(INVITE ACK wait) started, will triger after " + - m_pTimerH.Interval + "."); - } - } - } - - #endregion - - #region Completed - - else if (State == SIP_TransactionState.Completed) - { - // We do nothing here, we just wait ACK to arrive. - } - - #endregion - - #region Confirmed - - else if (State == SIP_TransactionState.Confirmed) - { - // We do nothing, just wait ACK retransmissions. - } - - #endregion - - #region Terminated - - else if (State == SIP_TransactionState.Terminated) - { - // We should never rreach here, but if so, skip it. - } - - #endregion - } - - #endregion - - #region Non-INVITE - - /* RFC 3261 17.2.2. - |Request received - |pass to TU - V - +-----------+ - | | - | Trying |-------------+ - | | | - +-----------+ |200-699 from TU - | |send response - |1xx from TU | - |send response | - | | - Request V 1xx from TU | - send response+-----------+send response| - +--------| |--------+ | - | | Proceeding| | | - +------->| |<-------+ | - +<--------------| | | - |Trnsprt Err +-----------+ | - |Inform TU | | - | | | - | |200-699 from TU | - | |send response | - | Request V | - | send response+-----------+ | - | +--------| | | - | | | Completed |<------------+ - | +------->| | - +<--------------| | - |Trnsprt Err +-----------+ - |Inform TU | - | |Timer J fires - | |- - | | - | V - | +-----------+ - | | | - +-------------->| Terminated| - | | - +-----------+ - */ - - else - { - #region Trying - - if (State == SIP_TransactionState.Trying) - { - AddResponse(response); - - // 1xx - if (response.StatusCodeType == SIP_StatusCodeType.Provisional) - { - Stack.TransportLayer.SendResponse(this, response); - OnResponseSent(response); - SetState(SIP_TransactionState.Proceeding); - } - // 2xx - 6xx - else - { - Stack.TransportLayer.SendResponse(this, response); - OnResponseSent(response); - SetState(SIP_TransactionState.Completed); - - /* RFC 3261 17.2.2. - When the server transaction enters the "Completed" state, it MUST set - Timer J to fire in 64*T1 seconds for unreliable transports, and zero - seconds for reliable transports. - */ - m_pTimerJ = new TimerEx(64*SIP_TimerConstants.T1, false); - m_pTimerJ.Elapsed += m_pTimerJ_Elapsed; - m_pTimerJ.Enabled = true; - - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=true] timer J(Non-INVITE request retransmission wait) started, will triger after " + - m_pTimerJ.Interval + "."); - } - } - } - - #endregion - - #region Proceeding - - else if (State == SIP_TransactionState.Proceeding) - { - AddResponse(response); - - // 1xx - if (response.StatusCodeType == SIP_StatusCodeType.Provisional) - { - Stack.TransportLayer.SendResponse(this, response); - OnResponseSent(response); - } - // 2xx - 6xx - else - { - Stack.TransportLayer.SendResponse(this, response); - OnResponseSent(response); - SetState(SIP_TransactionState.Completed); - - /* RFC 3261 17.2.2. - When the server transaction enters the "Completed" state, it MUST set - Timer J to fire in 64*T1 seconds for unreliable transports, and zero - seconds for reliable transports. - */ - m_pTimerJ = new TimerEx(64*SIP_TimerConstants.T1, false); - m_pTimerJ.Elapsed += m_pTimerJ_Elapsed; - m_pTimerJ.Enabled = true; - - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=true] timer J(Non-INVITE request retransmission wait) started, will triger after " + - m_pTimerJ.Interval + "."); - } - } - } - - #endregion - - #region Completed - - else if (State == SIP_TransactionState.Completed) - { - // Do nothing. - } - - #endregion - - #region Terminated - - else if (State == SIP_TransactionState.Terminated) - { - // Do nothing. - } - - #endregion - } - - #endregion - } - catch (SIP_TransportException x) - { - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=true] transport exception: " + x.Message); - } - - OnTransportError(x); - SetState(SIP_TransactionState.Terminated); - } - } - } - - /// - /// Cancels current transaction processing and sends '487 Request Terminated'. - /// - /// Is raised when this class is Disposed and this method is accessed. - /// Is raised when final response is sent and Cancel method is called after it. - public override void Cancel() - { - lock (SyncRoot) - { - if (State == SIP_TransactionState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (FinalResponse != null) - { - throw new InvalidOperationException("Final response is already sent, CANCEL not allowed."); - } - - try - { - SIP_Response response = Stack.CreateResponse(SIP_ResponseCodes.x487_Request_Terminated, - Request); - Stack.TransportLayer.SendResponse(this, response); - - OnCanceled(); - } - catch (SIP_TransportException x) - { - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=true] transport exception: " + x.Message); - } - - OnTransportError(x); - SetState(SIP_TransactionState.Terminated); - } - } - } - - #endregion - - #region Internal methods - - /// - /// Processes specified request through this transaction. - /// - /// SIP data flow. - /// SIP request. - /// Is raised when flow or request is null reference. - internal void ProcessRequest(SIP_Flow flow, SIP_Request request) - { - if (flow == null) - { - throw new ArgumentNullException("flow"); - } - if (request == null) - { - throw new ArgumentNullException("request"); - } - - lock (SyncRoot) - { - if (State == SIP_TransactionState.Disposed) - { - return; - } - - try - { - // Log - if (Stack.Logger != null) - { - byte[] requestData = request.ToByteData(); - - Stack.Logger.AddRead(Guid.NewGuid().ToString(), - null, - 0, - "Request [transactionID='" + ID + "'; method='" + - request.RequestLine.Method + "'; cseq='" + - request.CSeq.SequenceNumber + "'; " + "transport='" + - flow.Transport + "'; size='" + requestData.Length + - "'; received '" + flow.LocalEP + "' <- '" + flow.RemoteEP + "'.", - flow.LocalEP, - flow.RemoteEP, - requestData); - } - - #region INVITE - - if (Method == SIP_Methods.INVITE) - { - #region INVITE - - if (request.RequestLine.Method == SIP_Methods.INVITE) - { - if (State == SIP_TransactionState.Proceeding) - { - /* RFC 3261 17.2.1. - If a request retransmission is received while in the "Proceeding" state, the most recent provisional - response that was received from the TU MUST be passed to the transport layer for retransmission. - */ - SIP_Response response = LastProvisionalResponse; - if (response != null) - { - Stack.TransportLayer.SendResponse(this, response); - } - } - else if (State == SIP_TransactionState.Completed) - { - /* RFC 3261 17.2.1. - While in the "Completed" state, if a request retransmission is received, the server SHOULD - pass the response to the transport for retransmission. - */ - Stack.TransportLayer.SendResponse(this, FinalResponse); - } - } - - #endregion - - #region ACK - - else if (request.RequestLine.Method == SIP_Methods.ACK) - { - /* RFC 3261 17.2.1 - If an ACK is received while the server transaction is in the "Completed" state, the server transaction - MUST transition to the "Confirmed" state. As Timer G is ignored in this state, any retransmissions of the - response will cease. - - When this state is entered, timer I is set to fire in T4 seconds for unreliable transports, - and zero seconds for reliable transports. - */ - - if (State == SIP_TransactionState.Completed) - { - SetState(SIP_TransactionState.Confirmed); - - // Stop timers G,H - if (m_pTimerG != null) - { - m_pTimerG.Dispose(); - m_pTimerG = null; - - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + - Method + - "';IsServer=true] timer G(INVITE response(3xx - 6xx) retransmission) stoped."); - } - } - if (m_pTimerH != null) - { - m_pTimerH.Dispose(); - m_pTimerH = null; - - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + - Method + - "';IsServer=true] timer H(INVITE ACK wait) stoped."); - } - } - - // Start timer I. - m_pTimerI = new TimerEx((flow.IsReliable ? 0 : SIP_TimerConstants.T4), false); - m_pTimerI.Elapsed += m_pTimerI_Elapsed; - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=true] timer I(INVITE ACK retransission wait) started, will triger after " + - m_pTimerI.Interval + "."); - } - m_pTimerI.Enabled = true; - } - } - - #endregion - } - - #endregion - - #region Non-INVITE - - else - { - // Non-INVITE transaction may have only request retransmission requests. - if (Method == request.RequestLine.Method) - { - if (State == SIP_TransactionState.Proceeding) - { - /* RFC 3261 17.2.2. - If a retransmission of the request is received while in the "Proceeding" state, the most - recently sent provisional response MUST be passed to the transport layer for retransmission. - */ - Stack.TransportLayer.SendResponse(this, LastProvisionalResponse); - } - else if (State == SIP_TransactionState.Completed) - { - /* RFC 3261 17.2.2. - While in the "Completed" state, the server transaction MUST pass the final response to the transport - layer for retransmission whenever a retransmission of the request is received. - */ - Stack.TransportLayer.SendResponse(this, FinalResponse); - } - } - } - - #endregion - } - catch (SIP_TransportException x) - { - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=true] transport exception: " + x.Message); - } - - OnTransportError(x); - SetState(SIP_TransactionState.Terminated); - } - } - } - - #endregion - - #region Utility methods - - /// - /// Is raised when INVITE 100 (Trying) response must be sent if no response sent by transaction user. - /// - /// Sender. - /// Event data. - private void m_pTimer100_Elapsed(object sender, ElapsedEventArgs e) - { - lock (SyncRoot) - { - // RFC 3261 17.2.1. TU didn't generate response in 200 ms, send '100 Trying' to stop request retransmission. - if (State == SIP_TransactionState.Proceeding && Responses.Length == 0) - { - /* RFC 3261 17.2.1. - The 100 (Trying) response is constructed according to the procedures in Section 8.2.6, except that the - insertion of tags in the To header field of the response (when none was present in the request) - is downgraded from MAY to SHOULD NOT. - * - RFC 3261 8.2.6. - When a 100 (Trying) response is generated, any Timestamp header field present in the request MUST - be copied into this 100 (Trying) response. If there is a delay in generating the response, the UAS - SHOULD add a delay value into the Timestamp value in the response. This value MUST contain the difference - between the time of sending of the response and receipt of the request, measured in seconds. - */ - - SIP_Response tryingResponse = Stack.CreateResponse(SIP_ResponseCodes.x100_Trying, Request); - if (Request.Timestamp != null) - { - tryingResponse.Timestamp = new SIP_t_Timestamp(Request.Timestamp.Time, - (DateTime.Now - CreateTime).Seconds); - } - - try - { - Stack.TransportLayer.SendResponse(this, tryingResponse); - } - catch (Exception x) - { - OnTransportError(x); - SetState(SIP_TransactionState.Terminated); - return; - } - } - - if (m_pTimer100 != null) - { - m_pTimer100.Dispose(); - m_pTimer100 = null; - } - } - } - - /// - /// Is raised when INVITE timer G triggered. - /// - /// Sender. - /// Event data. - private void m_pTimerG_Elapsed(object sender, ElapsedEventArgs e) - { - /* RFC 3261 17.2.1. - If timer G fires, the response is passed to the transport layer once more for retransmission, and - timer G is set to fire in MIN(2*T1, T2) seconds. From then on, when timer G fires, the response is - passed to the transport again for transmission, and timer G is reset with a value that doubles, unless - that value exceeds T2, in which case it is reset with the value of T2. - */ - - lock (SyncRoot) - { - if (State == SIP_TransactionState.Completed) - { - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=true] timer G(INVITE response(3xx - 6xx) retransmission) triggered."); - } - - try - { - Stack.TransportLayer.SendResponse(this, FinalResponse); - - // Update(double current) next transmit time. - m_pTimerG.Interval *= Math.Min(m_pTimerG.Interval*2, SIP_TimerConstants.T2); - m_pTimerG.Enabled = true; - - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=false] timer G(INVITE response(3xx - 6xx) retransmission) updated, will triger after " + - m_pTimerG.Interval + "."); - } - } - catch (Exception x) - { - OnTransportError(x); - SetState(SIP_TransactionState.Terminated); - } - } - } - } - - /// - /// Is raised when INVITE timer H triggered. - /// - /// Sender. - /// Event data. - private void m_pTimerH_Elapsed(object sender, ElapsedEventArgs e) - { - /* RFC 3261 17.2.1. - If timer H fires while in the "Completed" state, it implies that the - ACK was never received. In this case, the server transaction MUST - transition to the "Terminated" state, and MUST indicate to the TU - that a transaction failure has occurred. - */ - - lock (SyncRoot) - { - if (State == SIP_TransactionState.Completed) - { - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=true] timer H(INVITE ACK wait) triggered."); - } - - OnTransactionError("ACK was never received."); - SetState(SIP_TransactionState.Terminated); - } - } - } - - /// - /// Is raised when INVITE timer I triggered. - /// - /// Sender. - /// Event data. - private void m_pTimerI_Elapsed(object sender, ElapsedEventArgs e) - { - /* RFC 3261 17.2.1. - Once timer I fires, the server MUST transition to the "Terminated" state. - */ - - lock (SyncRoot) - { - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=true] timer I(INVITE ACK retransmission wait) triggered."); - } - - SetState(SIP_TransactionState.Terminated); - } - } - - /// - /// Is raised when INVITE timer J triggered. - /// - /// Sender. - /// Event data. - private void m_pTimerJ_Elapsed(object sender, ElapsedEventArgs e) - { - /* RFC 3261 172.2.2. - Timer J fires, at which point it MUST transition to the "Terminated" state. - */ - - lock (SyncRoot) - { - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + - "';IsServer=true] timer I(Non-INVITE request retransmission wait) triggered."); - } - - SetState(SIP_TransactionState.Terminated); - } - } - - /// - /// Starts transaction processing. - /// - private void Start() - { - #region INVITE - - if (Method == SIP_Methods.INVITE) - { - /* RFC 3261 17.2.1. - When a server transaction is constructed for a request, it enters the "Proceeding" state. The server - transaction MUST generate a 100 (Trying) response unless it knows that the TU will generate a provisional - or final response within 200 ms, in which case it MAY generate a 100 (Trying) response. - */ - - SetState(SIP_TransactionState.Proceeding); - - m_pTimer100 = new TimerEx(200, false); - m_pTimer100.Elapsed += m_pTimer100_Elapsed; - m_pTimer100.Enabled = true; - } - - #endregion - - #region Non-INVITE - - else - { - // RFC 3261 17.2.2. The state machine is initialized in the "Trying" state. - SetState(SIP_TransactionState.Trying); - } - - #endregion - } - - /// - /// Raises ResponseSent event. - /// - /// SIP response. - private void OnResponseSent(SIP_Response response) - { - if (ResponseSent != null) - { - ResponseSent(this, new SIP_ResponseSentEventArgs(this, response)); - } - } - - /// - /// Raises Canceled event. - /// - private void OnCanceled() - { - if (Canceled != null) - { - Canceled(this, new EventArgs()); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Stack.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Stack.cs deleted file mode 100644 index ef30dca47..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Stack.cs +++ /dev/null @@ -1,1528 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - #region usings - - using System; - using System.Collections.Generic; - using System.Net; - using System.Threading; - using AUTH; - using Dns.Client; - using Log; - using Message; - - #endregion - - /// - /// Implements SIP stack. - /// - public class SIP_Stack - { - #region Events - - /// - /// This event is raised by any SIP element when unknown/unhandled error happened. - /// - public event EventHandler Error = null; - - /// - /// This event is raised when new SIP request is received. - /// - public event EventHandler RequestReceived = null; - - /// - /// This event is raised when new stray SIP response is received. - /// Stray response means that response doesn't match to any transaction. - /// - public event EventHandler ResponseReceived = null; - - /// - /// This event is raised when new incoming SIP request is received. You can control incoming requests - /// with that event. For example you can require authentication or send what ever error to request maker user. - /// - public event EventHandler ValidateRequest = null; - - #endregion - - #region Members - - private readonly List m_pCredentials; - private readonly Dns_Client m_pDnsClient; - private readonly Logger m_pLogger; - - private readonly Auth_HttpDigest_NonceManager m_pNonceManager; - private readonly List m_pProxyServers; - private readonly List m_pRegistrations; - private readonly SIP_TransactionLayer m_pTransactionLayer; - private readonly SIP_TransportLayer m_pTransportLayer; - private readonly SIP_t_CallID m_RegisterCallID; - private int m_CSeq = 1; - private bool m_IsDisposed; - private bool m_IsRunning; - private int m_MaxForwards = 70; - private int m_MaximumConnections; - private int m_MaximumMessageSize = 1000000; - private int m_MinExpireTime = 1800; - private int m_MinSessionExpires = 90; - private string m_Realm = ""; - private int m_SessionExpires = 1800; - private int MTU = 1400; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public SIP_Stack() - { - m_pTransportLayer = new SIP_TransportLayer(this); - m_pTransactionLayer = new SIP_TransactionLayer(this); - m_pNonceManager = new Auth_HttpDigest_NonceManager(); - m_pProxyServers = new List(); - m_pRegistrations = new List(); - m_pCredentials = new List(); - m_RegisterCallID = SIP_t_CallID.CreateCallID(); - - m_pLogger = new Logger(); - - m_pDnsClient = new Dns_Client(); - } - - #endregion - - #region Properties - - /// - /// Gets or sets socket bind info. Use this property to specify on which protocol,IP,port server - /// listnes and also if connections is SSL. - /// - /// Is raised when this class is Disposed and this property is accessed. - public IPBindInfo[] BindInfo - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pTransportLayer.BindInfo; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - m_pTransportLayer.BindInfo = value; - } - } - - /// - /// Gets credentials collection. - /// - /// Is raised when this object is disposed and and this property is accessed. - public List Credentials - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pCredentials; - } - } - - /// - /// Gets digest authentication nonce manager. - /// - /// Is raised when this class is Disposed and this property is accessed. - public Auth_HttpDigest_NonceManager DigestNonceManager - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pNonceManager; - } - } - - /// - /// Gets stack DNS client. - /// - /// Is raised when this class is Disposed and this property is accessed. - public Dns_Client Dns - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pDnsClient; - } - } - - /// - /// Gets or sets if SIP stack is running. - /// - /// Is raised when this class is Disposed and this property is accessed. - public bool Enabled - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_IsRunning; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value) - { - Start(); - } - else - { - Stop(); - } - } - } - - /// - /// Gets if SIP stack has started. - /// - /// Is raised when this class is Disposed and this property is accessed. - public bool IsRunning - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_IsRunning; - } - } - - /// - /// Gets SIP logger. - /// - /// Is raised when this class is Disposed and this property is accessed. - public Logger Logger - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pLogger; - } - } - - /// - /// Gets or sets maximum forwards SIP request may have. - /// - /// Is raised when value contains invalid value. - /// Is raised when this object is disposed and and this property is accessed. - public int MaxForwards - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_MaxForwards; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (value < 1) - { - throw new ArgumentException("Value must be > 0."); - } - - m_MaxForwards = value; - } - } - - /// - /// Gets or sets how many cunncurent connections allowed. Value 0 means not limited. This is used only for TCP based connections. - /// - /// Is raised when this class is Disposed and this property is accessed. - public int MaximumConnections - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_MaximumConnections; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (value < 1) - { - m_MaximumConnections = 0; - } - else - { - m_MaximumConnections = value; - } - } - } - - /// - /// Gets or sets maximum allowed SIP message size in bytes. This is used only for TCP based connections. - /// Value 0 means unlimited. - /// - /// Is raised when this class is Disposed and this property is accessed. - public int MaximumMessageSize - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_MaximumMessageSize; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (value < 1) - { - m_MaximumMessageSize = 0; - } - else - { - m_MaximumMessageSize = value; - } - } - } - - /// - /// Gets or sets minimum expire time in seconds what server allows. - /// - /// Is raised when this class is Disposed and this property is accessed. - public int MinimumExpireTime - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_MinExpireTime; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (value < 10) - { - throw new ArgumentException("Property MinimumExpireTime value must be >= 10 !"); - } - - m_MinExpireTime = value; - } - } - - /// - /// Gets or sets minimum session expires value in seconds. NOTE: Minimum value is 90 ! - /// - /// Is raised when this class is Disposed and this property is accessed. - /// Is raised when invalid value is passed. - public int MinimumSessionExpries - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_MinSessionExpires; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (value < 90) - { - throw new ArgumentException("Minimum session expires value must be >= 90 !"); - } - - m_MinSessionExpires = value; - } - } - - /// - /// Gets SIP outbound proxy servers collection. - /// - /// Is raised when this object is disposed and and this property is accessed. - public List ProxyServers - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pProxyServers; - } - } - - /// - /// Gets or sets SIP realm value. Mostly this value is used by digest authentication. - /// - /// Is raised when this object is disposed and and this property is accessed. - /// Is raised when null reference is passed. - public string Realm - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_Realm; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (value == null) - { - throw new ArgumentNullException(); - } - - m_Realm = value; - } - } - - /// - /// Gets current registrations. - /// - /// Is raised when this object is disposed and and this property is accessed. - public SIP_UA_Registration[] Registrations - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pRegistrations.ToArray(); - } - } - - /// - /// Gets or sets session expires value in seconds. NOTE: This value can't be smaller than MinimumSessionExpries. - /// - /// Is raised when this class is Disposed and this property is accessed. - /// Is raised when invalid value is passed. - public int SessionExpries - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_SessionExpires; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (value < 90) - { - throw new ArgumentException( - "Session expires value can't be < MinimumSessionExpries value !"); - } - - m_SessionExpires = value; - } - } - - /// - /// Gets or sets STUN server name or IP address. This value must be filled if SIP stack is running behind a NAT. - /// - public string StunServer - { - get { return m_pTransportLayer.StunServer; } - - set { m_pTransportLayer.StunServer = value; } - } - - /// - /// Gets transaction layer. - /// - /// Is raised when this class is Disposed and this property is accessed. - public SIP_TransactionLayer TransactionLayer - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pTransactionLayer; - } - } - - /// - /// Gets transport layer what is used to receive and send requests and responses. - /// - /// Is raised when this class is Disposed and this property is accessed. - public SIP_TransportLayer TransportLayer - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pTransportLayer; - } - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public void Dispose() - { - if (m_IsDisposed) - { - return; - } - - Stop(); - - m_IsDisposed = true; - - // TODO: "clean" clean up with disposing state, wait some time transaction/dialogs to die, block new ones. - - // Release events. - RequestReceived = null; - ResponseReceived = null; - Error = null; - - if (m_pTransactionLayer != null) - { - m_pTransactionLayer.Dispose(); - } - if (m_pTransportLayer != null) - { - m_pTransportLayer.Dispose(); - } - if (m_pNonceManager != null) - { - m_pNonceManager.Dispose(); - } - if (m_pLogger != null) - { - m_pLogger.Dispose(); - } - } - - /// - /// Starts SIP stack. - /// - public void Start() - { - if (m_IsRunning) - { - return; - } - m_IsRunning = true; - - m_pTransportLayer.Start(); - } - - /// - /// Stops SIP stack. - /// - public void Stop() - { - if (!m_IsRunning) - { - return; - } - - // Unregister registrations. - foreach (SIP_UA_Registration reg in m_pRegistrations.ToArray()) - { - reg.BeginUnregister(true); - } - - // Wait till all registrations disposed or wait timeout reached. - DateTime start = DateTime.Now; - while (m_pRegistrations.Count > 0) - { - Thread.Sleep(500); - - // Timeout, just kill all UA. - if (((DateTime.Now - start)).Seconds > 15) - { - break; - } - } - - m_IsRunning = false; - - m_pTransportLayer.Stop(); - } - - /// - /// Creates new out-off dialog SIP request. - /// - /// SIP request-method. - /// Recipient address. For example: sip:user@domain.com - /// Senders address. For example: sip:user@domain.com - /// Returns created request. - /// Is raised when this object is disposed and and this method is accessed. - /// Is raised when method,to or from is null. - /// Is raised when any of the arguments has invalid value. - public SIP_Request CreateRequest(string method, SIP_t_NameAddress to, SIP_t_NameAddress from) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (method == null) - { - throw new ArgumentNullException("method"); - } - if (method == "") - { - throw new ArgumentException("Argument 'method' value must be specified."); - } - if (to == null) - { - throw new ArgumentNullException("to"); - } - if (from == null) - { - throw new ArgumentNullException("from"); - } - - method = method.ToUpper(); - - /* RFC 3261 8.1.1 Generating the Request - - A valid SIP request formulated by a UAC MUST, at a minimum, contain - the following header fields: To, From, CSeq, Call-ID, Max-Forwards, - and Via; all of these header fields are mandatory in all SIP - requests. These six header fields are the fundamental building - blocks of a SIP message, as they jointly provide for most of the - critical message routing services including the addressing of - messages, the routing of responses, limiting message propagation, - ordering of messages, and the unique identification of transactions. - These header fields are in addition to the mandatory request line, - which contains the method, Request-URI, and SIP version. - */ - - SIP_Request request = new SIP_Request(method); - - #region Request-URI (section 8.1.1.1) - - /* - The initial Request-URI of the message SHOULD be set to the value of - the URI in the To field. One notable exception is the REGISTER - method; behavior for setting the Request-URI of REGISTER is given in - Section 10. - */ - - request.RequestLine.Uri = to.Uri; - - #endregion - - #region To (section 8.1.1.2) - - /* - The To header field first and foremost specifies the desired - "logical" recipient of the request, or the address-of-record of the - user or resource that is the target of this request. This may or may - not be the ultimate recipient of the request. The To header field - MAY contain a SIP or SIPS URI, but it may also make use of other URI - schemes (the tel URL (RFC 2806 [9]), for example) when appropriate. - */ - - SIP_t_To t = new SIP_t_To(to); - request.To = t; - - #endregion - - #region From (section 8.1.1.3) - - /* - The From header field indicates the logical identity of the initiator - of the request, possibly the user's address-of-record. Like the To - header field, it contains a URI and optionally a display name. It is - used by SIP elements to determine which processing rules to apply to - a request (for example, automatic call rejection). As such, it is - very important that the From URI not contain IP addresses or the FQDN - of the host on which the UA is running, since these are not logical - names. - - The From header field allows for a display name. A UAC SHOULD use - the display name "Anonymous", along with a syntactically correct, but - otherwise meaningless URI (like sip:thisis@anonymous.invalid), if the - identity of the client is to remain hidden. - - The From field MUST contain a new "tag" parameter, chosen by the UAC. - See Section 19.3 for details on choosing a tag. - */ - - SIP_t_From f = new SIP_t_From(from); - f.Tag = SIP_Utils.CreateTag(); - request.From = f; - - #endregion - - #region CallID (section 8.1.1.4) - - /* - The Call-ID header field acts as a unique identifier to group - together a series of messages. It MUST be the same for all requests - and responses sent by either UA in a dialog. It SHOULD be the same - in each registration from a UA. - */ - - if (method == SIP_Methods.REGISTER) - { - request.CallID = m_RegisterCallID.ToStringValue(); - } - else - { - request.CallID = SIP_t_CallID.CreateCallID().ToStringValue(); - } - - #endregion - - #region CSeq (section 8.1.1.5) - - /* - The CSeq header field serves as a way to identify and order - transactions. It consists of a sequence number and a method. The - method MUST match that of the request. For non-REGISTER requests - outside of a dialog, the sequence number value is arbitrary. The - sequence number value MUST be expressible as a 32-bit unsigned - integer and MUST be less than 2**31. As long as it follows the above - guidelines, a client may use any mechanism it would like to select - CSeq header field values. - */ - - request.CSeq = new SIP_t_CSeq(ConsumeCSeq(), method); - - #endregion - - #region Max-Forwards (section 8.1.1.6) - - request.MaxForwards = m_MaxForwards; - - #endregion - - #region Pre-configured route (proxy server) - - // section 8.1.2 suggests to use pre-configured route for proxy. - - foreach (SIP_Uri proxy in m_pProxyServers) - { - request.Route.Add(proxy.ToString()); - } - - #endregion - - // TODO: RFC 3261 13.2.1 INVITE special headers - // INVITE should have - // Allow,Supported - - // TODO: Subscribe special headers. - // Expires is mandatory header. - - return request; - } - - /// - /// Creates SIP request sender for the specified request. - /// - /// SIP request. - /// Is raised when this object is disposed and and this method is accessed. - /// Is raised when request is null reference. - public SIP_RequestSender CreateRequestSender(SIP_Request request) - { - return CreateRequestSender(request, null); - } - - /// - /// Consumes current CSeq number and increments it by 1. - /// - /// Returns CSeq number. - public int ConsumeCSeq() - { - return m_CSeq++; - } - - /// - /// Creates response for the specified request. - /// - /// Status-code reasontext. - /// SIP request. - /// Returns created response. - /// Is raised when statusCode_reasonText or request is null reference. - /// Is raised when request is ACK-request. ACK request is response less. - public SIP_Response CreateResponse(string statusCode_reasonText, SIP_Request request) - { - return CreateResponse(statusCode_reasonText, request, null); - } - - /// - /// Creates response for the specified request. - /// - /// Status-code reasontext. - /// SIP request. - /// Data flow what sends response. This value is used to construct Contact: header value. - /// This value can be null, but then adding Contact: header is response sender responsibility. - /// Returns created response. - /// Is raised when statusCode_reasonText or request is null reference. - /// Is raised when request is ACK-request. ACK request is response less. - public SIP_Response CreateResponse(string statusCode_reasonText, SIP_Request request, SIP_Flow flow) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - if (request.RequestLine.Method == SIP_Methods.ACK) - { - throw new InvalidOperationException("ACK is responseless request !"); - } - - /* RFC 3261 8.2.6.1. - When a 100 (Trying) response is generated, any Timestamp header field - present in the request MUST be copied into this 100 (Trying) - response. - - RFC 3261 8.2.6.2. - The From field of the response MUST equal the From header field of - the request. The Call-ID header field of the response MUST equal the - Call-ID header field of the request. The CSeq header field of the - response MUST equal the CSeq field of the request. The Via header - field values in the response MUST equal the Via header field values - in the request and MUST maintain the same ordering. - - If a request contained a To tag in the request, the To header field - in the response MUST equal that of the request. However, if the To - header field in the request did not contain a tag, the URI in the To - header field in the response MUST equal the URI in the To header - field; additionally, the UAS MUST add a tag to the To header field in - the response (with the exception of the 100 (Trying) response, in - which a tag MAY be present). This serves to identify the UAS that is - responding, possibly resulting in a component of a dialog ID. The - same tag MUST be used for all responses to that request, both final - and provisional (again excepting the 100 (Trying)). Procedures for - the generation of tags are defined in Section 19.3. - - RFC 3261 12.1.1. - When a UAS responds to a request with a response that establishes a - dialog (such as a 2xx to INVITE), the UAS MUST copy all Record-Route - header field values from the request into the response (including the - URIs, URI parameters, and any Record-Route header field parameters, - whether they are known or unknown to the UAS) and MUST maintain the - order of those values. - */ - - SIP_Response response = new SIP_Response(request); - response.StatusCode_ReasonPhrase = statusCode_reasonText; - foreach (SIP_t_ViaParm via in request.Via.GetAllValues()) - { - response.Via.Add(via.ToStringValue()); - } - response.From = request.From; - response.To = request.To; - if (request.To.Tag == null) - { - response.To.Tag = SIP_Utils.CreateTag(); - } - response.CallID = request.CallID; - response.CSeq = request.CSeq; - - // TODO: Allow: / Supported: - - if (SIP_Utils.MethodCanEstablishDialog(request.RequestLine.Method)) - { - foreach (SIP_t_AddressParam route in request.RecordRoute.GetAllValues()) - { - response.RecordRoute.Add(route.ToStringValue()); - } - - if (response.StatusCodeType == SIP_StatusCodeType.Success && - response.Contact.GetTopMostValue() == null && flow != null) - { - try - { - string user = ((SIP_Uri) response.To.Address.Uri).User; - response.Contact.Add((flow.IsSecure ? "sips:" : "sip:") + user + "@" + - TransportLayer.Resolve(flow)); - } - catch - { - // TODO: Log - } - } - } - - return response; - } - - /// - /// Gets target hops(address,port,transport) of the specified URI. - /// - /// Target URI. - /// SIP message size. - /// If true only TLS hops are returned. - /// Returns target hops(address,port,transport) of the specified URI. - /// Is raised when uri is null reference. - public SIP_Hop[] GetHops(SIP_Uri uri, int messageSize, bool forceTLS) - { - if (uri == null) - { - throw new ArgumentNullException("uri"); - } - - List retVal = new List(); - string transport = ""; - bool transportSetExplicitly = false; - List targetSRV = new List(); - - #region RFC 3263 4.1 Selecting a Transport Protocol - - /* 4.1 Selecting a Transport Protocol - - If the URI specifies a transport protocol in the transport parameter, - that transport protocol SHOULD be used. - - Otherwise, if no transport protocol is specified, but the TARGET is a - numeric IP address, the client SHOULD use UDP for a SIP URI, and TCP - for a SIPS URI. Similarly, if no transport protocol is specified, - and the TARGET is not numeric, but an explicit port is provided, the - client SHOULD use UDP for a SIP URI, and TCP for a SIPS URI. This is - because UDP is the only mandatory transport in RFC 2543 [6], and thus - the only one guaranteed to be interoperable for a SIP URI. It was - also specified as the default transport in RFC 2543 when no transport - was present in the SIP URI. However, another transport, such as TCP, - MAY be used if the guidelines of SIP mandate it for this particular - request. That is the case, for example, for requests that exceed the - path MTU. - - Otherwise, if no transport protocol or port is specified, and the - target is not a numeric IP address, the client SHOULD perform a NAPTR - query for the domain in the URI. The services relevant for the task - of transport protocol selection are those with NAPTR service fields - with values "SIP+D2X" and "SIPS+D2X", where X is a letter that - corresponds to a transport protocol supported by the domain. This - specification defines D2U for UDP, D2T for TCP, and D2S for SCTP. We - also establish an IANA registry for NAPTR service name to transport - protocol mappings. - - These NAPTR records provide a mapping from a domain to the SRV record - for contacting a server with the specific transport protocol in the - NAPTR services field. The resource record will contain an empty - regular expression and a replacement value, which is the SRV record - for that particular transport protocol. If the server supports - multiple transport protocols, there will be multiple NAPTR records, - each with a different service value. As per RFC 2915 [3], the client - discards any records whose services fields are not applicable. For - the purposes of this specification, several rules are defined. - - First, a client resolving a SIPS URI MUST discard any services that - do not contain "SIPS" as the protocol in the service field. The - converse is not true, however. A client resolving a SIP URI SHOULD - retain records with "SIPS" as the protocol, if the client supports - TLS. Second, a client MUST discard any service fields that identify - a resolution service whose value is not "D2X", for values of X that - indicate transport protocols supported by the client. The NAPTR - processing as described in RFC 2915 will result in the discovery of - the most preferred transport protocol of the server that is supported - by the client, as well as an SRV record for the server. It will also - allow the client to discover if TLS is available and its preference - for its usage. - - As an example, consider a client that wishes to resolve - sip:user@example.com. The client performs a NAPTR query for that - domain, and the following NAPTR records are returned: - - ; order pref flags service regexp replacement - IN NAPTR 50 50 "s" "SIPS+D2T" "" _sips._tcp.example.com. - IN NAPTR 90 50 "s" "SIP+D2T" "" _sip._tcp.example.com - IN NAPTR 100 50 "s" "SIP+D2U" "" _sip._udp.example.com. - - This indicates that the server supports TLS over TCP, TCP, and UDP, - in that order of preference. Since the client supports TCP and UDP, - TCP will be used, targeted to a host determined by an SRV lookup of - _sip._tcp.example.com. That lookup would return: - - ;; Priority Weight Port Target - IN SRV 0 1 5060 server1.example.com - IN SRV 0 2 5060 server2.example.com - - If a SIP proxy, redirect server, or registrar is to be contacted - through the lookup of NAPTR records, there MUST be at least three - records - one with a "SIP+D2T" service field, one with a "SIP+D2U" - service field, and one with a "SIPS+D2T" service field. The records - with SIPS as the protocol in the service field SHOULD be preferred - (i.e., have a lower value of the order field) above records with SIP - as the protocol in the service field. A record with a "SIPS+D2U" - service field SHOULD NOT be placed into the DNS, since it is not - possible to use TLS over UDP. - - It is not necessary for the domain suffixes in the NAPTR replacement - field to match the domain of the original query (i.e., example.com - above). However, for backwards compatibility with RFC 2543, a domain - MUST maintain SRV records for the domain of the original query, even - if the NAPTR record is in a different domain. As an example, even - though the SRV record for TCP is _sip._tcp.school.edu, there MUST - also be an SRV record at _sip._tcp.example.com. - - RFC 2543 will look up the SRV records for the domain directly. If - these do not exist because the NAPTR replacement points to a - different domain, the client will fail. - - For NAPTR records with SIPS protocol fields, (if the server is using - a site certificate), the domain name in the query and the domain name - in the replacement field MUST both be valid based on the site - certificate handed out by the server in the TLS exchange. Similarly, - the domain name in the SRV query and the domain name in the target in - the SRV record MUST both be valid based on the same site certificate. - Otherwise, an attacker could modify the DNS records to contain - replacement values in a different domain, and the client could not - validate that this was the desired behavior or the result of an - attack. - - If no NAPTR records are found, the client constructs SRV queries for - those transport protocols it supports, and does a query for each. - Queries are done using the service identifier "_sip" for SIP URIs and - "_sips" for SIPS URIs. A particular transport is supported if the - query is successful. The client MAY use any transport protocol it - desires which is supported by the server. - - This is a change from RFC 2543. It specified that a client would - lookup SRV records for all transports it supported, and merge the - priority values across those records. Then, it would choose the - most preferred record. - - If no SRV records are found, the client SHOULD use TCP for a SIPS - URI, and UDP for a SIP URI. However, another transport protocol, - such as TCP, MAY be used if the guidelines of SIP mandate it for this - particular request. That is the case, for example, for requests that - exceed the path MTU. - */ - - // TLS usage demanded explicitly. - if (forceTLS) - { - transportSetExplicitly = true; - transport = SIP_Transport.TLS; - } - // If the URI specifies a transport protocol in the transport parameter, that transport protocol SHOULD be used. - else if (uri.Param_Transport != null) - { - transportSetExplicitly = true; - transport = uri.Param_Transport; - } - /* If no transport protocol is specified, but the TARGET is a numeric IP address, - the client SHOULD use UDP for a SIP URI, and TCP for a SIPS URI. Similarly, - if no transport protocol is specified, and the TARGET is not numeric, but - an explicit port is provided, the client SHOULD use UDP for a SIP URI, and - TCP for a SIPS URI. However, another transport, such as TCP, MAY be used if - the guidelines of SIP mandate it for this particular request. That is the case, - for example, for requests that exceed the path MTU. - */ - else if (Net_Utils.IsIPAddress(uri.Host) || uri.Port != -1) - { - if (uri.IsSecure) - { - transport = SIP_Transport.TLS; - } - else if (messageSize > MTU) - { - transport = SIP_Transport.TCP; - } - else - { - transport = SIP_Transport.UDP; - } - } - else - { - DnsServerResponse response = null; - /* - DnsServerResponse response = m_pDnsClient.Query(uri.Host,QTYPE.NAPTR); - // NAPTR records available. - if(response.GetNAPTRRecords().Length > 0){ - // TODO: Choose suitable here - // 1) If SIPS get SIPS if possible. - // 2) If message size > MTU, try to use TCP. - // 3) Otherwise use UDP. - if(uri.IsSecure){ - // Get SIPS+D2T records. - } - else if(messageSize > MTU){ - // Get SIP+D2T records. - } - else{ - // Get SIP+D2U records. - } - } - else{*/ - Dictionary supportedTransports = new Dictionary(); - bool srvRecordsAvailable = false; - - // Query SRV to see what protocols are supported. - response = m_pDnsClient.Query("_sips._tcp." + uri.Host, QTYPE.SRV); - if (response.GetSRVRecords().Length > 0) - { - srvRecordsAvailable = true; - supportedTransports.Add(SIP_Transport.TLS, response.GetSRVRecords()); - } - response = m_pDnsClient.Query("_sip._tcp." + uri.Host, QTYPE.SRV); - if (response.GetSRVRecords().Length > 0) - { - srvRecordsAvailable = true; - supportedTransports.Add(SIP_Transport.TCP, response.GetSRVRecords()); - } - response = m_pDnsClient.Query("_sip._udp." + uri.Host, QTYPE.SRV); - if (response.GetSRVRecords().Length > 0) - { - srvRecordsAvailable = true; - supportedTransports.Add(SIP_Transport.UDP, response.GetSRVRecords()); - } - - // SRV records available. - if (srvRecordsAvailable) - { - if (uri.IsSecure) - { - if (supportedTransports.ContainsKey(SIP_Transport.TLS)) - { - transport = SIP_Transport.TLS; - targetSRV.AddRange(supportedTransports[SIP_Transport.TLS]); - } - // Target won't support SIPS. - else - { - // TODO: What to do ? - } - } - else if (messageSize > MTU) - { - if (supportedTransports.ContainsKey(SIP_Transport.TCP)) - { - transport = SIP_Transport.TCP; - targetSRV.AddRange(supportedTransports[SIP_Transport.TCP]); - } - else if (supportedTransports.ContainsKey(SIP_Transport.TLS)) - { - transport = SIP_Transport.TLS; - targetSRV.AddRange(supportedTransports[SIP_Transport.TLS]); - } - // Target support UDP only, but TCP is required. - else - { - // TODO: What to do ? - } - } - else - { - if (supportedTransports.ContainsKey(SIP_Transport.UDP)) - { - transport = SIP_Transport.UDP; - targetSRV.AddRange(supportedTransports[SIP_Transport.UDP]); - } - else if (supportedTransports.ContainsKey(SIP_Transport.TCP)) - { - transport = SIP_Transport.TCP; - targetSRV.AddRange(supportedTransports[SIP_Transport.TCP]); - } - else - { - transport = SIP_Transport.TLS; - targetSRV.AddRange(supportedTransports[SIP_Transport.TLS]); - } - } - } - /* If no SRV records are found, the client SHOULD use TCP for a SIPS - URI, and UDP for a SIP URI. However, another transport protocol, - such as TCP, MAY be used if the guidelines of SIP mandate it for this - particular request. That is the case, for example, for requests that - exceed the path MTU. - */ - else - { - if (uri.IsSecure) - { - transport = SIP_Transport.TLS; - } - else if (messageSize > MTU) - { - transport = SIP_Transport.TCP; - } - else - { - transport = SIP_Transport.UDP; - } - } - //} - } - - #endregion - - #region RFC 3263 4.2 Determining Port and IP Address - - /* 4.2 Determining Port and IP Address - - If TARGET is a numeric IP address, the client uses that address. If - the URI also contains a port, it uses that port. If no port is - specified, it uses the default port for the particular transport - protocol. - - If the TARGET was not a numeric IP address, but a port is present in - the URI, the client performs an A or AAAA record lookup of the domain - name. The result will be a list of IP addresses, each of which can - be contacted at the specific port from the URI and transport protocol - determined previously. The client SHOULD try the first record. If - an attempt should fail, based on the definition of failure in Section - 4.3, the next SHOULD be tried, and if that should fail, the next - SHOULD be tried, and so on. - - This is a change from RFC 2543. Previously, if the port was - explicit, but with a value of 5060, SRV records were used. Now, A - or AAAA records will be used. - - If the TARGET was not a numeric IP address, and no port was present - in the URI, the client performs an SRV query on the record returned - from the NAPTR processing of Section 4.1, if such processing was - performed. If it was not, because a transport was specified - explicitly, the client performs an SRV query for that specific - transport, using the service identifier "_sips" for SIPS URIs. For a - SIP URI, if the client wishes to use TLS, it also uses the service - identifier "_sips" for that specific transport, otherwise, it uses - "_sip". If the NAPTR processing was not done because no NAPTR - records were found, but an SRV query for a supported transport - protocol was successful, those SRV records are selected. Irregardless - of how the SRV records were determined, the procedures of RFC 2782, - as described in the section titled "Usage rules" are followed, - augmented by the additional procedures of Section 4.3 of this - document. - - If no SRV records were found, the client performs an A or AAAA record - lookup of the domain name. The result will be a list of IP - addresses, each of which can be contacted using the transport - protocol determined previously, at the default port for that - transport. Processing then proceeds as described above for an - explicit port once the A or AAAA records have been looked up. - */ - - if (Net_Utils.IsIPAddress(uri.Host)) - { - if (uri.Port != -1) - { - retVal.Add(new SIP_Hop(IPAddress.Parse(uri.Host), uri.Port, transport)); - } - else if (forceTLS || uri.IsSecure) - { - retVal.Add(new SIP_Hop(IPAddress.Parse(uri.Host), 5061, transport)); - } - else - { - retVal.Add(new SIP_Hop(IPAddress.Parse(uri.Host), 5060, transport)); - } - } - else if (uri.Port != -1) - { - foreach (IPAddress ip in m_pDnsClient.GetHostAddresses(uri.Host)) - { - retVal.Add(new SIP_Hop(ip, uri.Port, transport)); - } - } - else - { - //if(naptrRecords){ - // We need to get (IP:Port)'s foreach SRV record. - //DnsServerResponse response = m_pDnsClient.Query("??? need NAPTR value here",QTYPE.SRV); - //} - if (transportSetExplicitly) - { - DnsServerResponse response = null; - if (transport == SIP_Transport.TLS) - { - response = m_pDnsClient.Query("_sips._tcp." + uri.Host, QTYPE.SRV); - } - else if (transport == SIP_Transport.TCP) - { - response = m_pDnsClient.Query("_sip._tcp." + uri.Host, QTYPE.SRV); - } - else - { - response = m_pDnsClient.Query("_sip._udp." + uri.Host, QTYPE.SRV); - } - targetSRV.AddRange(response.GetSRVRecords()); - } - - // We have SRV records, resovle them to (IP:Port)'s. - if (targetSRV.Count > 0) - { - foreach (DNS_rr_SRV record in targetSRV) - { - if (Net_Utils.IsIPAddress(record.Target)) - { - retVal.Add(new SIP_Hop(IPAddress.Parse(record.Target), record.Port, transport)); - } - else - { - foreach (IPAddress ip in m_pDnsClient.GetHostAddresses(record.Target)) - { - retVal.Add(new SIP_Hop(ip, record.Port, transport)); - } - } - } - } - // No SRV recors, just use A and AAAA records. - else - { - int port = 5060; - if (transport == SIP_Transport.TLS) - { - port = 5061; - } - - foreach (IPAddress ip in m_pDnsClient.GetHostAddresses(uri.Host)) - { - retVal.Add(new SIP_Hop(ip, port, transport)); - } - } - } - - #endregion - - return retVal.ToArray(); - } - - /// - /// Creates new registration. - /// - /// Registrar server URI. For example: sip:domain.com. - /// Registration address of record. For example: user@domain.com. - /// Contact URI. - /// Gets after how many seconds reigisration expires. - /// Returns created registration. - /// Is raised when this object is disposed and and this method is accessed. - /// Is raised when server,aor or contact is null reference. - /// Is raised when any of the arguments has invalid value. - public SIP_UA_Registration CreateRegistration(SIP_Uri server, - string aor, - AbsoluteUri contact, - int expires) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (server == null) - { - throw new ArgumentNullException("server"); - } - if (aor == null) - { - throw new ArgumentNullException("aor"); - } - if (aor == string.Empty) - { - throw new ArgumentException("Argument 'aor' value must be specified."); - } - if (contact == null) - { - throw new ArgumentNullException("contact"); - } - - lock (m_pRegistrations) - { - SIP_UA_Registration registration = new SIP_UA_Registration(this, server, aor, contact, expires); - registration.Disposed += delegate - { - if (!m_IsDisposed) - { - m_pRegistrations.Remove(registration); - } - }; - m_pRegistrations.Add(registration); - - return registration; - } - } - - #endregion - - #region Internal methods - - /// - /// Creates SIP request sender for the specified request. - /// - /// SIP request. - /// Data flow. - /// Is raised when this object is disposed and and this method is accessed. - /// Is raised when request is null reference. - internal SIP_RequestSender CreateRequestSender(SIP_Request request, SIP_Flow flow) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (request == null) - { - throw new ArgumentNullException("request"); - } - - SIP_RequestSender sender = new SIP_RequestSender(this, request, flow); - sender.Credentials.AddRange(m_pCredentials); - - return sender; - } - - /// - /// Is called by Transport layer when new incoming SIP request is received. - /// - /// Incoming SIP request. - /// Request maker IP end point. - /// - internal SIP_ValidateRequestEventArgs OnValidateRequest(SIP_Request request, IPEndPoint remoteEndPoint) - { - SIP_ValidateRequestEventArgs eArgs = new SIP_ValidateRequestEventArgs(request, remoteEndPoint); - if (ValidateRequest != null) - { - ValidateRequest(this, eArgs); - } - - return eArgs; - } - - /// - /// Raises RequestReceived event. - /// - /// Event data. - internal void OnRequestReceived(SIP_RequestReceivedEventArgs e) - { - if (RequestReceived != null) - { - RequestReceived(this, e); - } - } - - /// - /// Raises ResponseReceived event. - /// - /// Event data. - internal void OnResponseReceived(SIP_ResponseReceivedEventArgs e) - { - if (ResponseReceived != null) - { - ResponseReceived(this, e); - } - } - - /// - /// Is called when ever unknown error happens. - /// - /// Exception happened. - internal void OnError(Exception x) - { - if (Error != null) - { - Error(this, new ExceptionEventArgs(x)); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_StatusCodeType.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_StatusCodeType.cs deleted file mode 100644 index 816fa2195..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_StatusCodeType.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - /// - /// Specifies SIP status code type. Defined in rfc 3261. - /// - public enum SIP_StatusCodeType - { - /// - /// Request received, continuing to process the request. 1xx status code. - /// - Provisional, - - /// - /// Action was successfully received, understood, and accepted. 2xx status code. - /// - Success, - - /// - /// Request must be redirected(forwarded). 3xx status code. - /// - Redirection, - - /// - /// Request contains bad syntax or cannot be fulfilled at this server. 4xx status code. - /// - RequestFailure, - - /// - /// Server failed to fulfill a valid request. 5xx status code. - /// - ServerFailure, - - /// - /// Request cannot be fulfilled at any server. 6xx status code. - /// - GlobalFailure - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_StatusLine.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_StatusLine.cs deleted file mode 100644 index 34127083d..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_StatusLine.cs +++ /dev/null @@ -1,148 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - #region usings - - using System; - - #endregion - - /// - /// Implements SIP Status-Line. Defined in RFC 3261. - /// - public class SIP_StatusLine - { - #region Members - - private string m_Reason = ""; - private int m_StatusCode; - private string m_Version = ""; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Status code. - /// Reason text. - /// Is raised when - /// Is raised when reason is null reference. - public SIP_StatusLine(int statusCode, string reason) - { - if (statusCode < 100 || statusCode > 699) - { - throw new ArgumentException("Argument 'statusCode' value must be >= 100 and <= 699."); - } - if (reason == null) - { - throw new ArgumentNullException("reason"); - } - - m_Version = "SIP/2.0"; - m_StatusCode = statusCode; - m_Reason = reason; - } - - #endregion - - #region Properties - - /// - /// Gets or sets reason phrase. - /// - /// Is raised when value is null reference. - public string Reason - { - get { return m_Reason; } - - set - { - if (Reason == null) - { - throw new ArgumentNullException("Reason"); - } - - m_Reason = value; - } - } - - /// - /// Gets or sets status code. - /// - /// Is raised when value has invalid value. - public int StatusCode - { - get { return m_StatusCode; } - - set - { - if (value < 100 || value > 699) - { - throw new ArgumentException("Argument 'statusCode' value must be >= 100 and <= 699."); - } - - m_StatusCode = value; - } - } - - /// - /// Gets or sets SIP version. - /// - /// Is raised when value is null reference. - /// Is raised when value has invalid value. - public string Version - { - get { return m_Version; } - - set - { - if (value == null) - { - throw new ArgumentNullException("Version"); - } - if (value == "") - { - throw new ArgumentException("Property 'Version' value must be specified."); - } - - m_Version = value; - } - } - - #endregion - - #region Methods - - /// - /// Returns Status-Line string. - /// - /// Returns Status-Line string. - public override string ToString() - { - // RFC 3261 25. - // Status-Line = SIP-Version SP Status-Code SP Reason-Phrase CRLF - - return m_Version + " " + m_StatusCode + " " + m_Reason + "\r\n"; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_TimerConstants.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_TimerConstants.cs deleted file mode 100644 index 500e65e59..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_TimerConstants.cs +++ /dev/null @@ -1,33 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - /// - /// This class holds known SIP timer constant values. - /// - internal class SIP_TimerConstants - { - #region Constants - - public const int T1 = 500; - public const int T2 = 4000; - public const int T4 = 5000; - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Transaction.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Transaction.cs deleted file mode 100644 index 00e1abf0e..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Transaction.cs +++ /dev/null @@ -1,527 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - #region usings - - using System; - using System.Collections.Generic; - using Message; - - #endregion - - /// - /// This is base class for SIP client and server transaction. - /// - public abstract class SIP_Transaction : IDisposable - { - #region Events - - /// - /// Is raised when transaction is disposed. - /// - public event EventHandler Disposed = null; - - /// - /// Is raised when transaction state has changed. - /// - public event EventHandler StateChanged = null; - - /// - /// Is raised if transaction is timed out. - /// - public event EventHandler TimedOut = null; - - /// - /// Is raised when there is transaction error. For example this is raised when server transaction never - /// gets ACK. - /// - public event EventHandler TransactionError = null; - - /// - /// Is raised when there is transport error. - /// - public event EventHandler TransportError = null; - - #endregion - - #region Members - - private readonly DateTime m_CreateTime; - - private readonly string m_ID = ""; - private readonly string m_Key = ""; - private readonly string m_Method = ""; - private readonly object m_pLock = new object(); - private readonly List m_pResponses; - private SIP_Flow m_pFlow; - private SIP_Request m_pRequest; - private SIP_Stack m_pStack; - private SIP_TransactionState m_State; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner SIP stack. - /// Transaction data flow. - /// SIP request that transaction will handle. - /// Is raised when stack,flow or request is null reference. - public SIP_Transaction(SIP_Stack stack, SIP_Flow flow, SIP_Request request) - { - if (stack == null) - { - throw new ArgumentNullException("stack"); - } - if (flow == null) - { - throw new ArgumentNullException("flow"); - } - if (request == null) - { - throw new ArgumentNullException("request"); - } - - m_pStack = stack; - m_pFlow = flow; - m_pRequest = request; - m_Method = request.RequestLine.Method; - m_CreateTime = DateTime.Now; - m_pResponses = new List(); - - // Validate Via: - SIP_t_ViaParm via = request.Via.GetTopMostValue(); - if (via == null) - { - throw new ArgumentException("Via: header is missing !"); - } - if (via.Branch == null) - { - throw new ArgumentException("Via: header 'branch' parameter is missing !"); - } - - m_ID = via.Branch; - - if (this is SIP_ServerTransaction) - { - /* - We use branch and sent-by as indexing key for transaction, the only special what we need to - do is to handle CANCEL, because it has same branch as transaction to be canceled. - For avoiding key collision, we add branch + '-' + 'sent-by' + CANCEL for cancel index key. - ACK has also same branch, but we won't do transaction for ACK, so it isn't problem. - */ - string key = request.Via.GetTopMostValue().Branch + '-' + request.Via.GetTopMostValue().SentBy; - if (request.RequestLine.Method == SIP_Methods.CANCEL) - { - key += "-CANCEL"; - } - m_Key = key; - } - else - { - m_Key = m_ID + "-" + request.RequestLine.Method; - } - } - - #endregion - - #region Properties - - /// - /// Gets time when this transaction was created. - /// - /// Is raised when this class is Disposed and this property is accessed. - public DateTime CreateTime - { - get - { - if (State == SIP_TransactionState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_CreateTime; - } - } - - /// - /// Gets transaction related SIP dialog. Returns null if no dialog available. - /// - public SIP_Dialog Dialog - { - // FIX ME: - get { return null; } - } - - /// - /// Gets transaction final(2xx - 699) response from responses collection. Returns null if no final responses. - /// - /// Is raised when this class is Disposed and this property is accessed. - public SIP_Response FinalResponse - { - get - { - if (State == SIP_TransactionState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - foreach (SIP_Response response in Responses) - { - if (response.StatusCodeType != SIP_StatusCodeType.Provisional) - { - return response; - } - } - - return null; - } - } - - /// - /// Gets transaction data flow. - /// - /// Is raised when this class is Disposed and this property is accessed. - public SIP_Flow Flow - { - get - { - if (State == SIP_TransactionState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pFlow; - } - } - - /// - /// Gets if transaction has any provisional(1xx) in responses collection. - /// - /// Is raised when this class is Disposed and this property is accessed. - public bool HasProvisionalResponse - { - get - { - if (State == SIP_TransactionState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - foreach (SIP_Response response in m_pResponses) - { - if (response.StatusCodeType == SIP_StatusCodeType.Provisional) - { - return true; - } - } - - return false; - } - } - - /// - /// Gets transaction ID (Via: branch parameter value). - /// - /// Is raised when this class is Disposed and this property is accessed. - public string ID - { - get - { - if (State == SIP_TransactionState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_ID; - } - } - - /// - /// Gets if transaction is disposed. - /// - public bool IsDisposed - { - get { return m_State == SIP_TransactionState.Disposed; } - } - - /// - /// Gets transaction final(1xx) response from responses collection. Returns null if no provisional responses. - /// - /// Is raised when this class is Disposed and this property is accessed. - public SIP_Response LastProvisionalResponse - { - get - { - if (State == SIP_TransactionState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - for (int i = Responses.Length - 1; i > -1; i--) - { - if (Responses[i].StatusCodeType == SIP_StatusCodeType.Provisional) - { - return Responses[i]; - } - } - - return null; - } - } - - /// - /// Gets request method that transaction handles. - /// - /// Is raised when this class is Disposed and this property is accessed. - public string Method - { - get - { - if (State == SIP_TransactionState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_Method; - } - } - - /// - /// Gets SIP request what caused this transaction creation. - /// - /// Is raised when this class is Disposed and this property is accessed. - public SIP_Request Request - { - get - { - if (State == SIP_TransactionState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pRequest; - } - } - - /// - /// Gets transaction processed responses. - /// - /// Is raised when this class is Disposed and this property is accessed. - public SIP_Response[] Responses - { - get - { - if (State == SIP_TransactionState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pResponses.ToArray(); - } - } - - /// - /// Gets owner SIP stack. - /// - /// Is raised when this class is Disposed and this property is accessed. - public SIP_Stack Stack - { - get - { - if (State == SIP_TransactionState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pStack; - } - } - - /// - /// Gets current transaction state. - /// - public SIP_TransactionState State - { - get { return m_State; } - } - - /// - /// Gets an object that can be used to synchronize access to the dialog. - /// - public object SyncRoot - { - get { return m_pLock; } - } - - /// - /// Gets or sets user data. - /// - public object Tag { get; set; } - - /// - /// Gets transaction indexing key. - /// - internal string Key - { - get { return m_Key; } - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public virtual void Dispose() - { - SetState(SIP_TransactionState.Disposed); - OnDisposed(); - - m_pStack = null; - m_pFlow = null; - m_pRequest = null; - - StateChanged = null; - Disposed = null; - TimedOut = null; - TransportError = null; - } - - /// - /// Cancels current transaction. - /// - public abstract void Cancel(); - - #endregion - - #region Utility methods - - /// - /// Raises event StateChanged. - /// - private void OnStateChanged() - { - if (StateChanged != null) - { - StateChanged(this, new EventArgs()); - } - } - - #endregion - - /// - /// Changes transaction state. - /// - /// New transaction state. - protected void SetState(SIP_TransactionState state) - { - // Log - if (Stack.Logger != null) - { - Stack.Logger.AddText(ID, - "Transaction [branch='" + ID + "';method='" + Method + "';IsServer=" + - (this is SIP_ServerTransaction) + "] swtiched to '" + state + "' state."); - } - - m_State = state; - - OnStateChanged(); - - if (m_State == SIP_TransactionState.Terminated) - { - Dispose(); - } - } - - /// - /// Adds specified response to transaction responses collection. - /// - /// SIP response. - /// Is raised when response is null reference. - protected void AddResponse(SIP_Response response) - { - if (response == null) - { - throw new ArgumentNullException("response"); - } - - // Don't store more than 15 responses, otherwise hacker may try todo buffer overrun with provisional responses. - if (m_pResponses.Count < 15 || response.StatusCode >= 200) - { - m_pResponses.Add(response); - } - } - - /// - /// Raises event Disposed. - /// - protected void OnDisposed() - { - if (Disposed != null) - { - Disposed(this, new EventArgs()); - } - } - - /// - /// Raises TimedOut event. - /// - protected void OnTimedOut() - { - if (TimedOut != null) - { - TimedOut(this, new EventArgs()); - } - } - - /// - /// Raises TimedOut event. - /// - /// Transport exception. - /// Is raised when exception is null reference. - protected void OnTransportError(Exception exception) - { - if (exception == null) - { - throw new ArgumentNullException("exception"); - } - - if (TransportError != null) - { - TransportError(this, new ExceptionEventArgs(exception)); - } - } - - /// - /// Raises TransactionError event. - /// - /// Text describing error. - protected void OnTransactionError(string errorText) - { - if (TransactionError != null) - { - TransactionError(this, new EventArgs()); - } - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_TransactionLayer.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_TransactionLayer.cs deleted file mode 100644 index da9efaca8..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_TransactionLayer.cs +++ /dev/null @@ -1,609 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - #region usings - - using System; - using System.Collections.Generic; - using System.Timers; - using Message; - - #endregion - - /// - /// Implements SIP transaction layer. Defined in RFC 3261. - /// Transaction layer manages client,server transactions and dialogs. - /// - public class SIP_TransactionLayer - { - #region Members - - private readonly Dictionary m_pClientTransactions; - private readonly Dictionary m_pDialogs; - private readonly Dictionary m_pServerTransactions; - private readonly SIP_Stack m_pStack; - private bool m_IsDisposed; - private Timer m_pTimer; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Reference to SIP stack. - /// Is raised when stack is null reference. - internal SIP_TransactionLayer(SIP_Stack stack) - { - if (stack == null) - { - throw new ArgumentNullException("stack"); - } - - m_pStack = stack; - - m_pClientTransactions = new Dictionary(); - m_pServerTransactions = new Dictionary(); - m_pDialogs = new Dictionary(); - - m_pTimer = new Timer(20000); - m_pTimer.AutoReset = true; - m_pTimer.Elapsed += m_pTimer_Elapsed; - } - - #endregion - - #region Properties - - /// - /// Gets all available client transactions. This method is thread-safe. - /// - public SIP_ClientTransaction[] ClientTransactions - { - get - { - lock (m_pClientTransactions) - { - SIP_ClientTransaction[] retVal = - new SIP_ClientTransaction[m_pClientTransactions.Values.Count]; - m_pClientTransactions.Values.CopyTo(retVal, 0); - - return retVal; - } - } - } - - /// - /// Gets active dialogs. This method is thread-safe. - /// - public SIP_Dialog[] Dialogs - { - get - { - lock (m_pDialogs) - { - SIP_Dialog[] retVal = new SIP_Dialog[m_pDialogs.Values.Count]; - m_pDialogs.Values.CopyTo(retVal, 0); - - return retVal; - } - } - } - - /// - /// Gets all available server transactions. This method is thread-safe. - /// - public SIP_ServerTransaction[] ServerTransactions - { - get - { - lock (m_pServerTransactions) - { - SIP_ServerTransaction[] retVal = - new SIP_ServerTransaction[m_pServerTransactions.Values.Count]; - m_pServerTransactions.Values.CopyTo(retVal, 0); - - return retVal; - } - } - } - - #endregion - - #region Methods - - /// - /// Creates new client transaction. - /// - /// SIP data flow which is used to send request. - /// SIP request that transaction will handle. - /// If true, transaction will add Via: header, otherwise it's user responsibility. - /// Is raised when this class is Disposed and this method is accessed. - /// Is raised when flow or request is null reference. - /// Returns created transaction. - public SIP_ClientTransaction CreateClientTransaction(SIP_Flow flow, SIP_Request request, bool addVia) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (flow == null) - { - throw new ArgumentNullException("flow"); - } - if (request == null) - { - throw new ArgumentNullException("request"); - } - - // Add Via: - if (addVia) - { - SIP_t_ViaParm via = new SIP_t_ViaParm(); - via.ProtocolName = "SIP"; - via.ProtocolVersion = "2.0"; - via.ProtocolTransport = flow.Transport; - via.SentBy = new HostEndPoint("transport_layer_will_replace_it", -1); - via.Branch = SIP_t_ViaParm.CreateBranch(); - via.RPort = 0; - request.Via.AddToTop(via.ToStringValue()); - } - - lock (m_pClientTransactions) - { - SIP_ClientTransaction transaction = new SIP_ClientTransaction(m_pStack, flow, request); - m_pClientTransactions.Add(transaction.Key, transaction); - transaction.StateChanged += delegate - { - if (transaction.State == SIP_TransactionState.Terminated) - { - lock (m_pClientTransactions) - { - m_pClientTransactions.Remove(transaction.Key); - } - } - }; - - return transaction; - } - } - - /// - /// Creates new SIP server transaction for specified request. - /// - /// SIP data flow which is used to receive request. - /// SIP request. - /// Returns added server transaction. - /// Is raised when this class is Disposed and this method is accessed. - /// Is raised when flow or request is null reference. - public SIP_ServerTransaction CreateServerTransaction(SIP_Flow flow, SIP_Request request) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (flow == null) - { - throw new ArgumentNullException("flow"); - } - if (request == null) - { - throw new ArgumentNullException("request"); - } - - lock (m_pServerTransactions) - { - SIP_ServerTransaction transaction = new SIP_ServerTransaction(m_pStack, flow, request); - m_pServerTransactions.Add(transaction.Key, transaction); - transaction.StateChanged += delegate - { - if (transaction.State == SIP_TransactionState.Terminated) - { - lock (m_pClientTransactions) - { - m_pServerTransactions.Remove(transaction.Key); - } - } - }; - - return transaction; - } - } - - /// - /// Ensures that specified request has matching server transaction. If server transaction doesn't exist, - /// it will be created, otherwise existing transaction will be returned. - /// - /// SIP data flow which is used to receive request. - /// SIP request. - /// Returns matching transaction. - /// Is raised when flow or request is null. - /// Is raised when request.Method is ACK request. - public SIP_ServerTransaction EnsureServerTransaction(SIP_Flow flow, SIP_Request request) - { - if (flow == null) - { - throw new ArgumentNullException("flow"); - } - if (request == null) - { - throw new ArgumentNullException("request"); - } - if (request.RequestLine.Method == SIP_Methods.ACK) - { - throw new InvalidOperationException( - "ACK request is transaction less request, can't create transaction for it."); - } - - /* - We use branch and sent-by as indexing key for transaction, the only special what we need to - do is to handle CANCEL, because it has same branch as transaction to be canceled. - For avoiding key collision, we add branch + '-' + 'sent-by' + CANCEL for cancel index key. - ACK has also same branch, but we won't do transaction for ACK, so it isn't problem. - */ - string key = request.Via.GetTopMostValue().Branch + '-' + request.Via.GetTopMostValue().SentBy; - if (request.RequestLine.Method == SIP_Methods.CANCEL) - { - key += "-CANCEL"; - } - - lock (m_pServerTransactions) - { - SIP_ServerTransaction retVal = null; - m_pServerTransactions.TryGetValue(key, out retVal); - // We don't have transaction, create it. - if (retVal == null) - { - retVal = CreateServerTransaction(flow, request); - } - - return retVal; - } - } - - /// - /// Matches CANCEL requst to SIP server non-CANCEL transaction. Returns null if no match. - /// - /// SIP CANCEL request. - /// Returns CANCEL matching server transaction or null if no match. - /// Is raised when cancelTransaction is null. - /// Is raised when cancelTransaction has invalid. - public SIP_ServerTransaction MatchCancelToTransaction(SIP_Request cancelRequest) - { - if (cancelRequest == null) - { - throw new ArgumentNullException("cancelRequest"); - } - if (cancelRequest.RequestLine.Method != SIP_Methods.CANCEL) - { - throw new ArgumentException("Argument 'cancelRequest' is not SIP CANCEL request."); - } - - SIP_ServerTransaction retVal = null; - - // NOTE: There we don't add '-CANCEL' because we want to get CANCEL matching transaction, not CANCEL - // transaction itself. - string key = cancelRequest.Via.GetTopMostValue().Branch + '-' + - cancelRequest.Via.GetTopMostValue().SentBy; - lock (m_pServerTransactions) - { - m_pServerTransactions.TryGetValue(key, out retVal); - } - - return retVal; - } - - /// - /// Gets existing or creates new dialog. - /// - /// Owner transaction what forces to create dialog. - /// Response what forces to create dialog. - /// Returns dialog. - /// Is raised when transaction or response is null. - public SIP_Dialog GetOrCreateDialog(SIP_Transaction transaction, SIP_Response response) - { - if (transaction == null) - { - throw new ArgumentNullException("transaction"); - } - if (response == null) - { - throw new ArgumentNullException("response"); - } - - string dialogID = ""; - if (transaction is SIP_ServerTransaction) - { - dialogID = response.CallID + "-" + response.To.Tag + "-" + response.From.Tag; - } - else - { - dialogID = response.CallID + "-" + response.From.Tag + "-" + response.To.Tag; - } - - lock (m_pDialogs) - { - SIP_Dialog dialog = null; - m_pDialogs.TryGetValue(dialogID, out dialog); - // Dialog doesn't exist, create it. - if (dialog == null) - { - if (response.CSeq.RequestMethod.ToUpper() == SIP_Methods.INVITE) - { - dialog = new SIP_Dialog_Invite(); - dialog.Init(m_pStack, transaction, response); - dialog.StateChanged += delegate - { - if (dialog.State == SIP_DialogState.Terminated) - { - m_pDialogs.Remove(dialog.ID); - } - }; - m_pDialogs.Add(dialog.ID, dialog); - } - else - { - throw new ArgumentException("Method '" + response.CSeq.RequestMethod + - "' has no dialog handler."); - } - } - - return dialog; - } - } - - #endregion - - #region Internal methods - - /// - /// Cleans up any resources being used. - /// - internal void Dispose() - { - if (m_IsDisposed) - { - return; - } - - if (m_pTimer != null) - { - m_pTimer.Dispose(); - m_pTimer = null; - } - - foreach (SIP_ClientTransaction tr in ClientTransactions) - { - try - { - tr.Dispose(); - } - catch {} - } - foreach (SIP_ServerTransaction tr in ServerTransactions) - { - try - { - tr.Dispose(); - } - catch {} - } - foreach (SIP_Dialog dialog in Dialogs) - { - try - { - dialog.Dispose(); - } - catch {} - } - - m_IsDisposed = true; - } - - /// - /// Matches SIP response to client transaction. If not matching transaction found, returns null. - /// - /// SIP response to match. - internal SIP_ClientTransaction MatchClientTransaction(SIP_Response response) - { - /* RFC 3261 17.1.3 Matching Responses to Client Transactions. - 1. If the response has the same value of the branch parameter in - the top Via header field as the branch parameter in the top - Via header field of the request that created the transaction. - - 2. If the method parameter in the CSeq header field matches the - method of the request that created the transaction. The - method is needed since a CANCEL request constitutes a - different transaction, but shares the same value of the branch - parameter. - */ - - SIP_ClientTransaction retVal = null; - - string transactionID = response.Via.GetTopMostValue().Branch + "-" + response.CSeq.RequestMethod; - lock (m_pClientTransactions) - { - m_pClientTransactions.TryGetValue(transactionID, out retVal); - } - - return retVal; - } - - /// - /// Matches SIP request to server transaction. If not matching transaction found, returns null. - /// - /// SIP request to match. - /// Returns matching transaction or null if no match. - internal SIP_ServerTransaction MatchServerTransaction(SIP_Request request) - { - /* RFC 3261 17.2.3 Matching Requests to Server Transactions. - This matching rule applies to both INVITE and non-INVITE transactions. - - 1. the branch parameter in the request is equal to the one in the top Via header - field of the request that created the transaction, and - - 2. the sent-by value in the top Via of the request is equal to the - one in the request that created the transaction, and - - 3. the method of the request matches the one that created the transaction, except - for ACK, where the method of the request that created the transaction is INVITE. - - Internal implementation notes: - Inernally we use branch + '-' + sent-by for non-CANCEL and for CANCEL - branch + '-' + sent-by + '-' CANCEL. This is because method matching is actually - needed for CANCEL only (CANCEL shares cancelable transaction branch ID). - */ - - SIP_ServerTransaction retVal = null; - - /* - We use branch and sent-by as indexing key for transaction, the only special what we need to - do is to handle CANCEL, because it has same branch as transaction to be canceled. - For avoiding key collision, we add branch + '-' + 'sent-by' + CANCEL for cancel index key. - ACK has also same branch, but we won't do transaction for ACK, so it isn't problem. - */ - string key = request.Via.GetTopMostValue().Branch + '-' + request.Via.GetTopMostValue().SentBy; - if (request.RequestLine.Method == SIP_Methods.CANCEL) - { - key += "-CANCEL"; - } - - lock (m_pServerTransactions) - { - m_pServerTransactions.TryGetValue(key, out retVal); - } - - // Don't match ACK for terminated transaction, in that case ACK must be passed to "core". - if (retVal != null && request.RequestLine.Method == SIP_Methods.ACK && - retVal.State == SIP_TransactionState.Terminated) - { - retVal = null; - } - - return retVal; - } - - /// - /// Removes specified dialog from dialogs collection. - /// - /// SIP dialog to remove. - internal void RemoveDialog(SIP_Dialog dialog) - { - lock (m_pDialogs) - { - m_pDialogs.Remove(dialog.ID); - } - } - - /// - /// Matches specified SIP request to SIP dialog. If no matching dialog found, returns null. - /// - /// SIP request. - /// Returns matched SIP dialog or null in no match found. - /// Is raised when request is null. - internal SIP_Dialog MatchDialog(SIP_Request request) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - SIP_Dialog dialog = null; - - try - { - string callID = request.CallID; - string localTag = request.To.Tag; - string remoteTag = request.From.Tag; - if (callID != null && localTag != null && remoteTag != null) - { - string dialogID = callID + "-" + localTag + "-" + remoteTag; - lock (m_pDialogs) - { - m_pDialogs.TryGetValue(dialogID, out dialog); - } - } - } - catch {} - - return dialog; - } - - /// - /// Matches specified SIP response to SIP dialog. If no matching dialog found, returns null. - /// - /// SIP response. - /// Returns matched SIP dialog or null in no match found. - /// Is raised when response is null. - internal SIP_Dialog MatchDialog(SIP_Response response) - { - if (response == null) - { - throw new ArgumentNullException("response"); - } - - SIP_Dialog dialog = null; - - try - { - string callID = response.CallID; - string fromTag = response.From.Tag; - string toTag = response.To.Tag; - if (callID != null && fromTag != null && toTag != null) - { - string dialogID = callID + "-" + fromTag + "-" + toTag; - lock (m_pDialogs) - { - m_pDialogs.TryGetValue(dialogID, out dialog); - } - } - } - catch {} - - return dialog; - } - - #endregion - - #region Utility methods - - private void m_pTimer_Elapsed(object sender, ElapsedEventArgs e) - { - foreach (SIP_Dialog dialog in Dialogs) - { - // Terminate early dialog after 5 minutes, normally there must be any, but just in case ... . - if (dialog.State == SIP_DialogState.Early) - { - if (dialog.CreateTime.AddMinutes(5) < DateTime.Now) - { - dialog.Terminate(); - } - } - // - else if (dialog.State == SIP_DialogState.Confirmed) - { - // TODO: - } - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_TransactionState.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_TransactionState.cs deleted file mode 100644 index dd14c7edd..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_TransactionState.cs +++ /dev/null @@ -1,65 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - /// - /// This enum holds SIP transaction states. Defined in RFC 3261. - /// - public enum SIP_TransactionState - { - /// - /// Client transaction waits Start method to be called. - /// - WaitingToStart, - - /// - /// Calling to recipient. This is used only by INVITE client transaction. - /// - Calling, - - /// - /// This is transaction initial state. Used only in Non-INVITE transaction. - /// - Trying, - - /// - /// This is INVITE server transaction initial state. Used only in INVITE server transaction. - /// - Proceeding, - - /// - /// Transaction has got final response. - /// - Completed, - - /// - /// Transation has got ACK from request maker. This is used only by INVITE server transaction. - /// - Confirmed, - - /// - /// Transaction has terminated and waits disposing. - /// - Terminated, - - /// - /// Transaction has disposed. - /// - Disposed, - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Transport.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Transport.cs deleted file mode 100644 index 704efdda5..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_Transport.cs +++ /dev/null @@ -1,44 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - /// - /// This class holds SIP transports. Defined in RFC 3261. - /// - public class SIP_Transport - { - #region Constants - - /// - /// TCP protocol. - /// - public const string TCP = "TCP"; - - /// - /// TCP + SSL protocol. - /// - public const string TLS = "TLS"; - - /// - /// UDP protocol. - /// - public const string UDP = "UDP"; - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_TransportException.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_TransportException.cs deleted file mode 100644 index 971b0ef25..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_TransportException.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - #region usings - - using System; - - #endregion - - /// - /// The exception that is thrown when a transport error occurs. - /// - public class SIP_TransportException : Exception - { - #region Constructor - - /// - /// Default constructor. - /// - /// Error text describing error. - public SIP_TransportException(string errorText) : base(errorText) {} - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_TransportLayer.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_TransportLayer.cs deleted file mode 100644 index b7bae1b89..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_TransportLayer.cs +++ /dev/null @@ -1,2074 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - #region usings - - using System; - using System.Collections.Generic; - using System.Net; - using System.Net.Sockets; - using System.Text; - using System.Threading; - using System.Timers; - using Dns.Client; - using Message; - using TCP; - using UDP; - - #endregion - - /// - /// Implements SIP transport layer. Defined in RFC 3261. - /// - public class SIP_TransportLayer - { - #region Members - - private readonly SIP_FlowManager m_pFlowManager; - private readonly CircleCollection m_pLocalIPv4; - private readonly CircleCollection m_pLocalIPv6; - private readonly SIP_Stack m_pStack; - private bool m_IsDisposed; - private bool m_IsRunning; - private IPBindInfo[] m_pBinds; - private Random m_pRandom; - private TCP_Server m_pTcpServer; - private UDP_Server m_pUdpServer; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner SIP stack. - /// Is raised when stack is null reference. - internal SIP_TransportLayer(SIP_Stack stack) - { - if (stack == null) - { - throw new ArgumentNullException("stack"); - } - - m_pStack = stack; - - m_pUdpServer = new UDP_Server(); - m_pUdpServer.ProcessMode = UDP_ProcessMode.Parallel; - m_pUdpServer.PacketReceived += m_pUdpServer_PacketReceived; - m_pUdpServer.Error += m_pUdpServer_Error; - - m_pTcpServer = new TCP_Server(); - m_pTcpServer.SessionCreated += m_pTcpServer_SessionCreated; - - m_pFlowManager = new SIP_FlowManager(this); - - m_pBinds = new IPBindInfo[] {}; - - m_pRandom = new Random(); - - m_pLocalIPv4 = new CircleCollection(); - m_pLocalIPv6 = new CircleCollection(); - } - - #endregion - - #region Properties - - /// - /// Gets or sets socket bind info. Use this property to specify on which protocol,IP,port server - /// listnes and also if connections is SSL. - /// - public IPBindInfo[] BindInfo - { - get { return m_pBinds; } - - set - { - if (value == null) - { - throw new ArgumentNullException("BindInfo"); - } - - //--- See binds has changed -------------- - bool changed = false; - if (m_pBinds.Length != value.Length) - { - changed = true; - } - else - { - for (int i = 0; i < m_pBinds.Length; i++) - { - if (!m_pBinds[i].Equals(value[i])) - { - changed = true; - break; - } - } - } - - if (changed) - { - m_pBinds = value; - - // Create listening points. - List udpListeningPoints = new List(); - List tcpListeningPoints = new List(); - foreach (IPBindInfo bindInfo in m_pBinds) - { - if (bindInfo.Protocol == BindInfoProtocol.UDP) - { - udpListeningPoints.Add(new IPEndPoint(bindInfo.IP, bindInfo.Port)); - } - else - { - tcpListeningPoints.Add(bindInfo); - } - } - m_pUdpServer.Bindings = udpListeningPoints.ToArray(); - m_pTcpServer.Bindings = tcpListeningPoints.ToArray(); - - // Build possible local TCP/TLS IP addresses. - foreach (IPEndPoint ep in m_pTcpServer.LocalEndPoints) - { - if (ep.AddressFamily == AddressFamily.InterNetwork) - { - m_pLocalIPv4.Add(ep.Address); - } - else if (ep.AddressFamily == AddressFamily.InterNetwork) - { - m_pLocalIPv6.Add(ep.Address); - } - } - } - } - } - - /// - /// Gets currently active flows. - /// - public SIP_Flow[] Flows - { - get { return m_pFlowManager.Flows; } - } - - /// - /// Gets if transport layer is running. - /// - public bool IsRunning - { - get { return m_IsRunning; } - } - - /// - /// Gets owner SIP stack. - /// - public SIP_Stack Stack - { - get { return m_pStack; } - } - - /// - /// Gets or sets STUN server name or IP address. This value must be filled if SIP stack is running behind a NAT. - /// - internal string StunServer { get; set; } - - /// - /// Gets UDP server. - /// - internal UDP_Server UdpServer - { - get { return m_pUdpServer; } - } - - #endregion - - #region Methods - - /// - /// Gets existing flow or if flow doesn't exist, new one is created and returned. - /// - /// SIP transport. - /// Local end point. Value null means system will allocate it. - /// Remote end point. - /// Returns data flow. - /// Is raised when this object is disposed and and this method is accessed. - /// Is raised when remoteEP. - public SIP_Flow GetOrCreateFlow(string transport, IPEndPoint localEP, IPEndPoint remoteEP) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (remoteEP == null) - { - throw new ArgumentNullException("remoteEP"); - } - - if (localEP == null) - { - if (transport == SIP_Transport.UDP) - { - // Get load-balanched local endpoint. - localEP = m_pUdpServer.GetLocalEndPoint(remoteEP); - } - else if (transport == SIP_Transport.TCP) - { - // Get load-balanched local IP for TCP and create random port. - if (remoteEP.AddressFamily == AddressFamily.InterNetwork) - { - localEP = new IPEndPoint(m_pLocalIPv4.Next(), m_pRandom.Next(10000, 65000)); - } - else - { - localEP = new IPEndPoint(m_pLocalIPv4.Next(), m_pRandom.Next(10000, 65000)); - } - } - else if (transport == SIP_Transport.TLS) - { - // Get load-balanched local IP for TLS and create random port. - if (remoteEP.AddressFamily == AddressFamily.InterNetwork) - { - localEP = new IPEndPoint(m_pLocalIPv4.Next(), m_pRandom.Next(10000, 65000)); - } - else - { - localEP = new IPEndPoint(m_pLocalIPv4.Next(), m_pRandom.Next(10000, 65000)); - } - } - } - - return m_pFlowManager.GetOrCreateFlow(false, localEP, remoteEP, transport); - } - - /// - /// Returns specified flow or null if no such flow. - /// - /// Data flow ID. - /// Returns specified flow or null if no such flow. - /// Is raised when this object is disposed and and this method is accessed. - /// Is raised when flowID is null reference. - public SIP_Flow GetFlow(string flowID) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (flowID == null) - { - throw new ArgumentNullException("flowID"); - } - - return m_pFlowManager.GetFlow(flowID); - } - - /// - /// Sends request using methods as described in RFC 3261 [4](RFC 3263). - /// - /// SIP request to send. - /// Is raised when this object is disposed and and this method is accessed. - /// Is raised when request is null. - /// Is raised when transport error happens. - public void SendRequest(SIP_Request request) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (request == null) - { - throw new ArgumentNullException("request"); - } - - SIP_Hop[] hops = m_pStack.GetHops((SIP_Uri) request.RequestLine.Uri, - request.ToByteData().Length, - false); - if (hops.Length == 0) - { - throw new SIP_TransportException("No target hops for URI '" + request.RequestLine.Uri + "'."); - } - - SIP_TransportException lastException = null; - foreach (SIP_Hop hop in hops) - { - try - { - SendRequest(request, null, hop); - - return; - } - catch (SIP_TransportException x) - { - lastException = x; - } - } - - // If we reach so far, send failed, return last error. - throw lastException; - } - - /// - /// Sends request to the specified hop. - /// - /// SIP request. - /// Local end point. Value null means system will allocate it. - /// Target hop. - /// Is raised when this object is disposed and and this method is accessed. - /// Is raised when request or hop is null reference. - public void SendRequest(SIP_Request request, IPEndPoint localEP, SIP_Hop hop) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (request == null) - { - throw new ArgumentNullException("request"); - } - if (hop == null) - { - throw new ArgumentNullException("hop"); - } - - SendRequest(GetOrCreateFlow(hop.Transport, localEP, hop.EndPoint), request); - } - - /// - /// Sends request to the specified flow. - /// - /// Data flow. - /// SIP request. - /// Is raised when this object is disposed and and this method is accessed. - /// Is raised when flow or request is null reference. - /// Is raised when any of the arguments contains invalid value. - public void SendRequest(SIP_Flow flow, SIP_Request request) - { - SendRequest(flow, request, null); - } - - /// - /// Sends specified response back to request maker using RFC 3261 18. rules. - /// - /// SIP response. - /// Is raised when this object is disposed and and this method is accessed. - /// Is raised when stack ahs not been started and this method is accessed. - /// Is raised when response is null reference. - /// Is raised when any of the arguments has invalid value. - /// Is raised when response sending has failed. - /// Use this method to send SIP responses from stateless SIP elements, like stateless proxy. - /// Otherwise SIP_ServerTransaction.SendResponse method should be used. - public void SendResponse(SIP_Response response) - { - SendResponse(response, null); - } - - /// - /// Sends specified response back to request maker using RFC 3261 18. rules. - /// - /// SIP response. - /// Local IP end point to use for sending resposne. Value null means system will allocate it. - /// Is raised when this object is disposed and and this method is accessed. - /// Is raised when stack ahs not been started and this method is accessed. - /// Is raised when response is null reference. - /// Is raised when any of the arguments has invalid value. - /// Is raised when response sending has failed. - /// Use this method to send SIP responses from stateless SIP elements, like stateless proxy. - /// Otherwise SIP_ServerTransaction.SendResponse method should be used. - public void SendResponse(SIP_Response response, IPEndPoint localEP) - { - // NOTE: all paramter / state validations are done in SendResponseInternal. - - SendResponseInternal(null, response, localEP); - } - - #endregion - - #region Internal methods - - /// - /// Cleans up any resources being used. - /// - internal void Dispose() - { - if (m_IsDisposed) - { - return; - } - m_IsDisposed = true; - - Stop(); - - m_IsRunning = false; - m_pBinds = null; - m_pRandom = null; - - m_pTcpServer.Dispose(); - m_pTcpServer = null; - - m_pUdpServer.Dispose(); - m_pUdpServer = null; - } - - /// - /// Starts listening incoming requests and responses. - /// - internal void Start() - { - if (m_IsRunning) - { - return; - } - // Set this flag before running thread, otherwise thead may exist before you set this flag. - m_IsRunning = true; - - m_pUdpServer.Start(); - m_pTcpServer.Start(); - } - - /// - /// Stops listening incoming requests and responses. - /// - internal void Stop() - { - if (!m_IsRunning) - { - return; - } - m_IsRunning = false; - - m_pUdpServer.Stop(); - m_pTcpServer.Stop(); - } - - /// - /// Is called when specified SIP flow has got new SIP message. - /// - /// SIP flow. - /// Received message. - /// Is raised when flow or message is null reference. - internal void OnMessageReceived(SIP_Flow flow, byte[] message) - { - if (flow == null) - { - throw new ArgumentNullException("flow"); - } - if (message == null) - { - throw new ArgumentNullException("message"); - } - - // TODO: Log - - try - { - #region Ping / pong - - // We have "ping"(CRLFCRLF) request, response with "pong". - if (message.Length == 4) - { - if (Stack.Logger != null) - { - Stack.Logger.AddRead("", - null, - 2, - "Flow [id='" + flow.ID + "'] received \"ping\"", - flow.LocalEP, - flow.RemoteEP); - } - - // Send "pong". - flow.SendInternal(new[] {(byte) '\r', (byte) '\n'}); - - if (Stack.Logger != null) - { - Stack.Logger.AddWrite("", - null, - 2, - "Flow [id='" + flow.ID + "'] sent \"pong\"", - flow.LocalEP, - flow.RemoteEP); - } - - return; - } - // We have pong(CRLF), do nothing. - else if (message.Length == 2) - { - if (Stack.Logger != null) - { - Stack.Logger.AddRead("", - null, - 2, - "Flow [id='" + flow.ID + "'] received \"pong\"", - flow.LocalEP, - flow.RemoteEP); - } - - return; - } - - #endregion - - #region Response - - if (Encoding.UTF8.GetString(message, 0, 3).ToUpper().StartsWith("SIP")) - { - #region Parse and validate response - - SIP_Response response = null; - try - { - response = SIP_Response.Parse(message); - } - catch (Exception x) - { - if (m_pStack.Logger != null) - { - m_pStack.Logger.AddText("Skipping message, parse error: " + x); - } - - return; - } - - try - { - response.Validate(); - } - catch (Exception x) - { - if (m_pStack.Logger != null) - { - m_pStack.Logger.AddText("Response validation failed: " + x); - } - - return; - } - - #endregion - - /* RFC 3261 18.1.2 Receiving Responses. - When a response is received, the client transport examines the top - Via header field value. If the value of the "sent-by" parameter in - that header field value does not correspond to a value that the - client transport is configured to insert into requests, the response - MUST be silently discarded. - - If there are any client transactions in existence, the client - transport uses the matching procedures of Section 17.1.3 to attempt - to match the response to an existing transaction. If there is a - match, the response MUST be passed to that transaction. Otherwise, - the response MUST be passed to the core (whether it be stateless - proxy, stateful proxy, or UA) for further processing. Handling of - these "stray" responses is dependent on the core (a proxy will - forward them, while a UA will discard, for example). - */ - - SIP_ClientTransaction transaction = - m_pStack.TransactionLayer.MatchClientTransaction(response); - // Allow client transaction to process response. - if (transaction != null) - { - transaction.ProcessResponse(flow, response); - } - else - { - // Pass response to dialog. - SIP_Dialog dialog = m_pStack.TransactionLayer.MatchDialog(response); - if (dialog != null) - { - dialog.ProcessResponse(response); - } - // Pass response to core. - else - { - m_pStack.OnResponseReceived(new SIP_ResponseReceivedEventArgs(m_pStack, - null, - response)); - } - } - } - - #endregion - - #region Request - - // SIP request. - else - { - #region Parse and validate request - - SIP_Request request = null; - try - { - request = SIP_Request.Parse(message); - } - catch (Exception x) - { - // Log - if (m_pStack.Logger != null) - { - m_pStack.Logger.AddText("Skipping message, parse error: " + x.Message); - } - - return; - } - - try - { - request.Validate(); - } - catch (Exception x) - { - if (m_pStack.Logger != null) - { - m_pStack.Logger.AddText("Request validation failed: " + x); - } - - // Bad request, send error to request maker. - SendResponse( - m_pStack.CreateResponse(SIP_ResponseCodes.x400_Bad_Request + ". " + x.Message, - request)); - - return; - } - - #endregion - - // TODO: Is that needed, core can reject message as it would like. - SIP_ValidateRequestEventArgs eArgs = m_pStack.OnValidateRequest(request, flow.RemoteEP); - // Request rejected, return response. - if (eArgs.ResponseCode != null) - { - SendResponse(m_pStack.CreateResponse(eArgs.ResponseCode, request)); - - return; - } - - request.Flow = flow; - request.LocalEndPoint = flow.LocalEP; - request.RemoteEndPoint = flow.RemoteEP; - - /* RFC 3261 18.2.1. - When the server transport receives a request over any transport, it - MUST examine the value of the "sent-by" parameter in the top Via - header field value. If the host portion of the "sent-by" parameter - contains a domain name, or if it contains an IP address that differs - from the packet source address, the server MUST add a "received" - parameter to that Via header field value. This parameter MUST - contain the source address from which the packet was received. This - is to assist the server transport layer in sending the response, - since it must be sent to the source IP address from which the request - came. - - Next, the server transport attempts to match the request to a server - transaction. It does so using the matching rules described in - Section 17.2.3. If a matching server transaction is found, the - request is passed to that transaction for processing. If no match is - found, the request is passed to the core, which may decide to - construct a new server transaction for that request. Note that when - a UAS core sends a 2xx response to INVITE, the server transaction is - destroyed. This means that when the ACK arrives, there will be no - matching server transaction, and based on this rule, the ACK is - passed to the UAS core, where it is processed. - */ - - /* RFC 3581 4. - When a server compliant to this specification (which can be a proxy - or UAS) receives a request, it examines the topmost Via header field - value. If this Via header field value contains an "rport" parameter - with no value, it MUST set the value of the parameter to the source - port of the request. This is analogous to the way in which a server - will insert the "received" parameter into the topmost Via header - field value. In fact, the server MUST insert a "received" parameter - containing the source IP address that the request came from, even if - it is identical to the value of the "sent-by" component. Note that - this processing takes place independent of the transport protocol. - */ - - SIP_t_ViaParm via = request.Via.GetTopMostValue(); - via.Received = flow.RemoteEP.Address; - if (via.RPort == 0) - { - via.RPort = flow.RemoteEP.Port; - } - - bool processed = false; - SIP_ServerTransaction transaction = - m_pStack.TransactionLayer.MatchServerTransaction(request); - // Pass request to matched server transaction. - if (transaction != null) - { - transaction.ProcessRequest(flow, request); - - processed = true; - } - else - { - SIP_Dialog dialog = m_pStack.TransactionLayer.MatchDialog(request); - // Pass request to dialog. - if (dialog != null) - { - processed = - dialog.ProcessRequest(new SIP_RequestReceivedEventArgs(m_pStack, flow, request)); - } - } - - // Request not proecced by dialog or transaction, pass request to TU. - if (!processed) - { - // Log - if (m_pStack.Logger != null) - { - byte[] requestData = request.ToByteData(); - - m_pStack.Logger.AddRead(Guid.NewGuid().ToString(), - null, - 0, - "Request [method='" + request.RequestLine.Method + - "'; cseq='" + request.CSeq.SequenceNumber + "'; " + - "transport='" + flow.Transport + "'; size='" + - requestData.Length + "'; " + "received '" + flow.RemoteEP + - "' -> '" + flow.LocalEP + "'.", - flow.LocalEP, - flow.RemoteEP, - requestData); - } - - m_pStack.OnRequestReceived(new SIP_RequestReceivedEventArgs(m_pStack, flow, request)); - } - } - - #endregion - } - catch (SocketException s) - { - // Skip all socket errors here - string dummy = s.Message; - } - //catch(ArgumentException x){ - // m_pStack.OnError(x); - //} - catch (Exception x) - { - m_pStack.OnError(x); - } - } - - /// - /// Sends request to the specified flow. - /// - /// Data flow. - /// SIP request. - /// Owner client transaction or null if stateless sending. - /// Is raised when this object is disposed and and this method is accessed. - /// Is raised when flow or request is null reference. - /// Is raised when any of the arguments contains invalid value. - internal void SendRequest(SIP_Flow flow, SIP_Request request, SIP_ClientTransaction transaction) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (flow == null) - { - throw new ArgumentNullException("flow"); - } - if (request == null) - { - throw new ArgumentNullException("request"); - } - if (request.Via.GetTopMostValue() == null) - { - throw new ArgumentException("Argument 'request' doesn't contain required Via: header field."); - } - - // Set sent-by - SIP_t_ViaParm via = request.Via.GetTopMostValue(); - via.ProtocolTransport = flow.Transport; - // Via sent-by is used only to send responses when request maker data flow is not active. - // Normally this never used, so just report first local listening point as sent-by. - HostEndPoint sentBy = null; - foreach (IPBindInfo bind in BindInfo) - { - if (flow.Transport == SIP_Transport.UDP && bind.Protocol == BindInfoProtocol.UDP) - { - if (!string.IsNullOrEmpty(bind.HostName)) - { - sentBy = new HostEndPoint(bind.HostName, bind.Port); - } - else - { - sentBy = new HostEndPoint(flow.LocalEP.Address.ToString(), bind.Port); - } - break; - } - else if (flow.Transport == SIP_Transport.TLS && bind.Protocol == BindInfoProtocol.TCP && - bind.SslMode == SslMode.SSL) - { - if (!string.IsNullOrEmpty(bind.HostName)) - { - sentBy = new HostEndPoint(bind.HostName, bind.Port); - } - else - { - sentBy = new HostEndPoint(flow.LocalEP.Address.ToString(), bind.Port); - } - break; - } - else if (flow.Transport == SIP_Transport.TCP && bind.Protocol == BindInfoProtocol.TCP) - { - if (!string.IsNullOrEmpty(bind.HostName)) - { - sentBy = new HostEndPoint(bind.HostName, bind.Port); - } - else - { - sentBy = new HostEndPoint(flow.LocalEP.Address.ToString(), bind.Port); - } - break; - } - } - // No local end point for sent-by, just use flow local end point for it. - if (sentBy == null) - { - via.SentBy = new HostEndPoint(flow.LocalEP); - } - else - { - via.SentBy = sentBy; - } - - // Send request. - flow.Send(request); - - // Log. - if (m_pStack.Logger != null) - { - byte[] requestData = request.ToByteData(); - - m_pStack.Logger.AddWrite(Guid.NewGuid().ToString(), - null, - 0, - "Request [" + - (transaction == null ? "" : "transactionID='" + transaction.ID + "';") + - "method='" + request.RequestLine.Method + "'; cseq='" + - request.CSeq.SequenceNumber + "'; " + "transport='" + flow.Transport + - "'; size='" + requestData.Length + "'; sent '" + flow.LocalEP + - "' -> '" + flow.RemoteEP + "'.", - flow.LocalEP, - flow.RemoteEP, - requestData); - } - } - - /// - /// Sends specified response back to request maker using RFC 3261 18. rules. - /// - /// SIP server transaction which response to send. - /// SIP response. - /// Is raised when this object is disposed and and this method is accessed. - /// Is raised when stack ahs not been started and this method is accessed. - /// Is raised when transaction or response is null reference. - /// Is raised when any of the arguments has invalid value. - /// Is raised when response sending has failed. - internal void SendResponse(SIP_ServerTransaction transaction, SIP_Response response) - { - if (transaction == null) - { - throw new ArgumentNullException("transaction"); - } - // NOTE: all other paramter / state validations are done in SendResponseInternal. - - SendResponseInternal(transaction, response, null); - } - - /// - /// Resolves data flow local NATed IP end point to public IP end point. - /// - /// Data flow. - /// Returns public IP end point of local NATed IP end point. - /// Is raised flow is null reference. - internal IPEndPoint Resolve(SIP_Flow flow) - { - if (flow == null) - { - throw new ArgumentNullException("flow"); - } - - IPEndPoint resolvedEP = null; - AutoResetEvent completionWaiter = new AutoResetEvent(false); - // Create OPTIONS request - SIP_Request optionsRequest = m_pStack.CreateRequest(SIP_Methods.OPTIONS, - new SIP_t_NameAddress("sip:ping@publicIP.com"), - new SIP_t_NameAddress("sip:ping@publicIP.com")); - SIP_ClientTransaction optionsTransaction = m_pStack.TransactionLayer.CreateClientTransaction( - flow, optionsRequest, true); - optionsTransaction.ResponseReceived += delegate(object s, SIP_ResponseReceivedEventArgs e) - { - SIP_t_ViaParm via = - e.Response.Via.GetTopMostValue(); - - resolvedEP = - new IPEndPoint( - via.Received == null - ? flow.LocalEP.Address - : via.Received, - via.RPort > 0 - ? via.RPort - : flow.LocalEP.Port); - - completionWaiter.Set(); - }; - optionsTransaction.StateChanged += delegate - { - if (optionsTransaction.State == - SIP_TransactionState.Terminated) - { - completionWaiter.Set(); - } - }; - optionsTransaction.Start(); - - // Wait OPTIONS request to complete. - completionWaiter.WaitOne(); - - if (resolvedEP != null) - { - return resolvedEP; - } - else - { - return flow.LocalEP; - } - } - - /// - /// Gets contact URI host parameter suitable to the specified flow. - /// - /// Data flow. - /// Returns contact URI host parameter suitable to the specified flow. - internal HostEndPoint GetContactHost(SIP_Flow flow) - { - if (flow == null) - { - throw new ArgumentNullException("flow"); - } - - HostEndPoint retVal = null; - - // Find suitable listening point for flow. - foreach (IPBindInfo bind in BindInfo) - { - if (bind.Protocol == BindInfoProtocol.UDP && flow.Transport == SIP_Transport.UDP) - { - // For UDP flow localEP is also listeining EP, so use it. - if (bind.IP.AddressFamily == flow.LocalEP.AddressFamily && bind.Port == flow.LocalEP.Port) - { - retVal = - new HostEndPoint( - (string.IsNullOrEmpty(bind.HostName) - ? flow.LocalEP.Address.ToString() - : bind.HostName), - bind.Port); - break; - } - } - else if (bind.Protocol == BindInfoProtocol.TCP && bind.SslMode == SslMode.SSL && - flow.Transport == SIP_Transport.TLS) - { - // Just use first matching listening point. - // TODO: Probably we should imporve it with load-balanched local end point. - if (bind.IP.AddressFamily == flow.LocalEP.AddressFamily) - { - if (bind.IP == IPAddress.Any || bind.IP == IPAddress.IPv6Any) - { - retVal = - new HostEndPoint( - (string.IsNullOrEmpty(bind.HostName) - ? flow.LocalEP.Address.ToString() - : bind.HostName), - bind.Port); - } - else - { - retVal = - new HostEndPoint( - (string.IsNullOrEmpty(bind.HostName) - ? bind.IP.ToString() - : bind.HostName), - bind.Port); - } - break; - } - } - else if (bind.Protocol == BindInfoProtocol.TCP && flow.Transport == SIP_Transport.TCP) - { - // Just use first matching listening point. - // TODO: Probably we should imporve it with load-balanched local end point. - if (bind.IP.AddressFamily == flow.LocalEP.AddressFamily) - { - if (bind.IP.Equals(IPAddress.Any) || bind.IP.Equals(IPAddress.IPv6Any)) - { - retVal = - new HostEndPoint( - (string.IsNullOrEmpty(bind.HostName) - ? flow.LocalEP.Address.ToString() - : bind.HostName), - bind.Port); - } - else - { - retVal = - new HostEndPoint( - (string.IsNullOrEmpty(bind.HostName) - ? bind.IP.ToString() - : bind.HostName), - bind.Port); - } - break; - } - } - } - - // We don't have suitable listening point for active flow. - // RFC 3261 forces to have, but for TCP based protocls + NAT, server can't connect to use anyway, - // so just ignore it and report flow local EP. - if (retVal == null) - { - retVal = new HostEndPoint(flow.LocalEP); - } - - // If flow remoteEP is public IP and our localEP is private IP, resolve localEP to public. - if (retVal.IsIPAddress && Core.IsPrivateIP(IPAddress.Parse(retVal.Host)) && - !Core.IsPrivateIP(flow.RemoteEP.Address)) - { - retVal = new HostEndPoint(Resolve(flow)); - } - - return retVal; - } - - /// - /// Gets Record-Route for the specified transport. - /// - /// SIP transport. - /// Returns Record-Route ro or null if no record route possible. - internal string GetRecordRoute(string transport) - { - foreach (IPBindInfo bind in m_pBinds) - { - if (!string.IsNullOrEmpty(bind.HostName)) - { - if (bind.Protocol == BindInfoProtocol.TCP && bind.SslMode != SslMode.None && - transport == SIP_Transport.TLS) - { - return ""; - } - else if (bind.Protocol == BindInfoProtocol.TCP && transport == SIP_Transport.TCP) - { - return ""; - } - else if (bind.Protocol == BindInfoProtocol.UDP && transport == SIP_Transport.UDP) - { - return ""; - } - } - } - - return null; - } - - #endregion - - #region Utility methods - - /// - /// This method is called when new SIP UDP packet has received. - /// - /// Event data. - private void m_pUdpServer_PacketReceived(UDP_PacketEventArgs e) - { - try - { - SIP_Flow flow = m_pFlowManager.GetOrCreateFlow(true, - e.LocalEndPoint, - e.RemoteEndPoint, - SIP_Transport.UDP); - flow.OnUdpPacketReceived(e); - } - catch (Exception x) - { - m_pStack.OnError(x); - } - } - - /// - /// This method is called when UDP server unknown error. - /// - /// Sender. - /// Event data. - private void m_pUdpServer_Error(object sender, Error_EventArgs e) - { - m_pStack.OnError(e.Exception); - } - - /// - /// This method is called when SIP stack has got new incoming connection. - /// - /// Sender. - /// Event data. - private void m_pTcpServer_SessionCreated(object sender, - TCP_ServerSessionEventArgs e) - { - m_pFlowManager.CreateFromSession(e.Session); - } - - /// - /// Sends response to request maker using RFC 3261 18. rules. - /// - /// Owner server transaction. Can be null if stateless response sending. - /// SIP response to send. - /// Local IP end point to use for sending resposne. Value null means system will allocate it. - /// Is raised when this object is disposed and and this method is accessed. - /// Is raised when stack ahs not been started and this method is accessed. - /// Is raised when response is null reference. - /// Is raised when any of the arguments has invalid value. - /// Is raised when response sending has failed. - private void SendResponseInternal(SIP_ServerTransaction transaction, - SIP_Response response, - IPEndPoint localEP) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!m_IsRunning) - { - throw new InvalidOperationException("Stack has not been started."); - } - if (response == null) - { - throw new ArgumentNullException("response"); - } - - /* RFC 3261 18.2.2. - The server transport uses the value of the top Via header field in - order to determine where to send a response. It MUST follow the - following process: - - o If the "sent-protocol" is a reliable transport protocol such as - TCP or SCTP, or TLS over those, the response MUST be sent using - the existing connection to the source of the original request - that created the transaction, if that connection is still open. - This requires the server transport to maintain an association - between server transactions and transport connections. If that - connection is no longer open, the server SHOULD open a - connection to the IP address in the "received" parameter, if - present, using the port in the "sent-by" value, or the default - port for that transport, if no port is specified. If that - connection attempt fails, the server SHOULD use the procedures - in [4] for servers in order to determine the IP address and - port to open the connection and send the response to. - - o Otherwise, if the Via header field value contains a "maddr" - parameter, the response MUST be forwarded to the address listed - there, using the port indicated in "sent-by", or port 5060 if - none is present. If the address is a multicast address, the - response SHOULD be sent using the TTL indicated in the "ttl" - parameter, or with a TTL of 1 if that parameter is not present. - - o Otherwise (for unreliable unicast transports), if the top Via - has a "received" parameter, the response MUST be sent to the - address in the "received" parameter, using the port indicated - in the "sent-by" value, or using port 5060 if none is specified - explicitly. If this fails, for example, elicits an ICMP "port - unreachable" response, the procedures of Section 5 of [4] - SHOULD be used to determine where to send the response. - - o Otherwise, if it is not receiver-tagged, the response MUST be - sent to the address indicated by the "sent-by" value, using the - procedures in Section 5 of [4]. - */ - - /* RFC 3581 4. (Adds new processing between RFC 3261 18.2.2. bullet 2 and 3) - When a server attempts to send a response, it examines the topmost - Via header field value of that response. If the "sent-protocol" - component indicates an unreliable unicast transport protocol, such as - UDP, and there is no "maddr" parameter, but there is both a - "received" parameter and an "rport" parameter, the response MUST be - sent to the IP address listed in the "received" parameter, and the - port in the "rport" parameter. The response MUST be sent from the - same address and port that the corresponding request was received on. - This effectively adds a new processing step between bullets two and - three in Section 18.2.2 of SIP [1]. - - The response must be sent from the same address and port that the - request was received on in order to traverse symmetric NATs. When a - server is listening for requests on multiple ports or interfaces, it - will need to remember the one on which the request was received. For - a stateful proxy, storing this information for the duration of the - transaction is not an issue. However, a stateless proxy does not - store state between a request and its response, and therefore cannot - remember the address and port on which a request was received. To - properly implement this specification, a stateless proxy can encode - the destination address and port of a request into the Via header - field value that it inserts. When the response arrives, it can - extract this information and use it to forward the response. - */ - - SIP_t_ViaParm via = response.Via.GetTopMostValue(); - if (via == null) - { - throw new ArgumentException("Argument 'response' does not contain required Via: header field."); - } - - // TODO: If transport is not supported. - //throw new SIP_TransportException("Not supported transport '" + via.ProtocolTransport + "'."); - - string logID = Guid.NewGuid().ToString(); - string transactionID = transaction == null ? "" : transaction.ID; - - // Try to get local IP end point which we should use to send response back. - if (transaction != null && transaction.Request.LocalEndPoint != null) - { - localEP = transaction.Request.LocalEndPoint; - } - - // TODO: no "localEP" at moment - - // TODO: Stateless should use flowID instead. - - // Our stateless proxy add 'localEP' parameter to Via: if so normally we can get it from there. - else if (via.Parameters["localEP"] != null) - { - localEP = Net_Utils.ParseIPEndPoint(via.Parameters["localEP"].Value); - } - - byte[] responseData = response.ToByteData(); - - #region Try existing flow first - - /* First try active flow to send response, thats not 100% as RFC says, but works better in any case. - RFC says that for TCP and TLS only, we do it for any transport. - */ - - if (transaction != null) - { - try - { - SIP_Flow flow = transaction.Flow; - flow.Send(response); - - if (m_pStack.Logger != null) - { - m_pStack.Logger.AddWrite(logID, - null, - 0, - "Response [flowReuse=true; transactionID='" + transactionID + - "'; method='" + response.CSeq.RequestMethod + "'; cseq='" + - response.CSeq.SequenceNumber + "'; " + "transport='" + - flow.Transport + "'; size='" + responseData.Length + - "'; statusCode='" + response.StatusCode + "'; " + "reason='" + - response.ReasonPhrase + "'; sent '" + flow.LocalEP + "' -> '" + - flow.RemoteEP + "'.", - localEP, - flow.RemoteEP, - responseData); - } - - return; - } - catch - { - // Do nothing, processing will continue. - } - } - - #endregion - - #region Reliable TCP,TLS, ... - - if (SIP_Utils.IsReliableTransport(via.ProtocolTransport)) - { - // Get original request remote end point. - IPEndPoint remoteEP = null; - if (transaction != null && transaction.Request.RemoteEndPoint != null) - { - remoteEP = transaction.Request.RemoteEndPoint; - } - else if (via.Received != null) - { - remoteEP = new IPEndPoint(via.Received, via.SentBy.Port == -1 ? 5060 : via.SentBy.Port); - } - - #region If original request connection alive, use it - - try - { - SIP_Flow flow = null; - - // Statefull - if (transaction != null) - { - if (transaction.Request.Flow != null && !transaction.Request.Flow.IsDisposed) - { - flow = transaction.Request.Flow; - } - } - // Stateless - else - { - string flowID = via.Parameters["connectionID"].Value; - if (flowID != null) - { - flow = m_pFlowManager[flowID]; - } - } - - if (flow != null) - { - flow.Send(response); - - if (m_pStack.Logger != null) - { - m_pStack.Logger.AddWrite(logID, - null, - 0, - "Response [flowReuse=true; transactionID='" + - transactionID + "'; method='" + - response.CSeq.RequestMethod + "'; cseq='" + - response.CSeq.SequenceNumber + "'; " + "transport='" + - flow.Transport + "'; size='" + responseData.Length + - "'; statusCode='" + response.StatusCode + "'; " + - "reason='" + response.ReasonPhrase + "'; sent '" + - flow.RemoteEP + "' -> '" + flow.LocalEP + "'.", - localEP, - remoteEP, - responseData); - } - - return; - } - } - catch - { - // Do nothing, processing will continue. - // Override RFC, if there is any existing connection and it gives error, try always RFC 3261 18.2.2(recieved) and 3265 5. - } - - #endregion - - #region Send RFC 3261 18.2.2(recieved) - - if (remoteEP != null) - { - try - { - SendResponseToHost(logID, - transactionID, - null, - remoteEP.Address.ToString(), - remoteEP.Port, - via.ProtocolTransport, - response); - } - catch - { - // Do nothing, processing will continue -> "RFC 3265 5.". - } - } - - #endregion - - #region Send RFC 3265 5. - - SendResponse_RFC_3263_5(logID, transactionID, localEP, response); - - #endregion - } - - #endregion - - #region UDP Via: maddr parameter - - else if (via.Maddr != null) - { - throw new SIP_TransportException( - "Sending responses to multicast address(Via: 'maddr') is not supported."); - } - - #endregion - - #region RFC 3581 4. UDP Via: received and rport parameters - - else if (via.Maddr == null && via.Received != null && via.RPort > 0) - { - SendResponseToHost(logID, - transactionID, - localEP, - via.Received.ToString(), - via.RPort, - via.ProtocolTransport, - response); - } - - #endregion - - #region UDP Via: received parameter - - else if (via.Received != null) - { - SendResponseToHost(logID, - transactionID, - localEP, - via.Received.ToString(), - via.SentByPortWithDefault, - via.ProtocolTransport, - response); - } - - #endregion - - #region UDP - - else - { - SendResponse_RFC_3263_5(logID, transactionID, localEP, response); - } - - #endregion - } - - /// - /// Sends specified response back to request maker using RFC 3263 5. rules. - /// - /// Log ID. - /// Transaction ID. If null, then stateless response sending. - /// UDP local end point to use for sending. If null, system will use default. - /// SIP response. - /// Is raised when response sending has failed. - private void SendResponse_RFC_3263_5(string logID, - string transactionID, - IPEndPoint localEP, - SIP_Response response) - { - /* RFC 3263 5. - RFC 3261 [1] defines procedures for sending responses from a server - back to the client. Typically, for unicast UDP requests, the - response is sent back to the source IP address where the request came - from, using the port contained in the Via header. For reliable - transport protocols, the response is sent over the connection the - request arrived on. However, it is important to provide failover - support when the client element fails between sending the request and - receiving the response. - - A server, according to RFC 3261 [1], will send a response on the - connection it arrived on (in the case of reliable transport - protocols), and for unreliable transport protocols, to the source - address of the request, and the port in the Via header field. The - procedures here are invoked when a server attempts to send to that - location and that response fails (the specific conditions are - detailed in RFC 3261). "Fails" is defined as any closure of the - transport connection the request came in on before the response can - be sent, or communication of a fatal error from the transport layer. - - In these cases, the server examines the value of the sent-by - construction in the topmost Via header. If it contains a numeric IP - address, the server attempts to send the response to that address, - using the transport protocol from the Via header, and the port from - sent-by, if present, else the default for that transport protocol. - The transport protocol in the Via header can indicate "TLS", which - refers to TLS over TCP. When this value is present, the server MUST - use TLS over TCP to send the response. - - If, however, the sent-by field contained a domain name and a port - number, the server queries for A or AAAA records with that name. It - tries to send the response to each element on the resulting list of - IP addresses, using the port from the Via, and the transport protocol - from the Via (again, a value of TLS refers to TLS over TCP). As in - the client processing, the next entry in the list is tried if the one - before it results in a failure. - - If, however, the sent-by field contained a domain name and no port, - the server queries for SRV records at that domain name using the - service identifier "_sips" if the Via transport is "TLS", "_sip" - otherwise, and the transport from the topmost Via header ("TLS" - implies that the transport protocol in the SRV query is TCP). The - resulting list is sorted as described in [2], and the response is - sent to the topmost element on the new list described there. If that - results in a failure, the next entry on the list is tried. - */ - - SIP_t_ViaParm via = response.Via.GetTopMostValue(); - - #region Sent-By is IP address - - if (via.SentBy.IsIPAddress) - { - SendResponseToHost(logID, - transactionID, - localEP, - via.SentBy.Host, - via.SentByPortWithDefault, - via.ProtocolTransport, - response); - } - - #endregion - - #region Sent-By is host name with port number - - else if (via.SentBy.Port != -1) - { - SendResponseToHost(logID, - transactionID, - localEP, - via.SentBy.Host, - via.SentByPortWithDefault, - via.ProtocolTransport, - response); - } - - #endregion - - #region Sent-By is just host name - - else - { - try - { - // Query SRV records. - string srvQuery = ""; - if (via.ProtocolTransport == SIP_Transport.UDP) - { - srvQuery = "_sip._udp." + via.SentBy.Host; - } - else if (via.ProtocolTransport == SIP_Transport.TCP) - { - srvQuery = "_sip._tcp." + via.SentBy.Host; - } - else if (via.ProtocolTransport == SIP_Transport.UDP) - { - srvQuery = "_sips._tcp." + via.SentBy.Host; - } - DnsServerResponse dnsResponse = m_pStack.Dns.Query(srvQuery, QTYPE.SRV); - if (dnsResponse.ResponseCode != RCODE.NO_ERROR) - { - throw new SIP_TransportException("Dns error: " + dnsResponse.ResponseCode); - } - DNS_rr_SRV[] srvRecords = dnsResponse.GetSRVRecords(); - - // Use SRV records. - if (srvRecords.Length > 0) - { - for (int i = 0; i < srvRecords.Length; i++) - { - DNS_rr_SRV srv = srvRecords[i]; - try - { - if (m_pStack.Logger != null) - { - m_pStack.Logger.AddText(logID, - "Starts sending response to DNS SRV record '" + - srv.Target + "'."); - } - - SendResponseToHost(logID, - transactionID, - localEP, - srv.Target, - srv.Port, - via.ProtocolTransport, - response); - } - catch - { - // Generate error, all SRV records has failed. - if (i == (srvRecords.Length - 1)) - { - if (m_pStack.Logger != null) - { - m_pStack.Logger.AddText(logID, - "Failed to send response to DNS SRV record '" + - srv.Target + "'."); - } - - throw new SIP_TransportException("Host '" + via.SentBy.Host + - "' is not accessible."); - } - // For loop will try next SRV record. - else - { - if (m_pStack.Logger != null) - { - m_pStack.Logger.AddText(logID, - "Failed to send response to DNS SRV record '" + - srv.Target + "', will try next."); - } - } - } - } - } - // If no SRV, use A and AAAA records. (Thats not in 3263 5., but we need to todo it so.) - else - { - if (m_pStack.Logger != null) - { - m_pStack.Logger.AddText(logID, - "No DNS SRV records found, starts sending to Via: sent-by host '" + - via.SentBy.Host + "'."); - } - - SendResponseToHost(logID, - transactionID, - localEP, - via.SentBy.Host, - via.SentByPortWithDefault, - via.ProtocolTransport, - response); - } - } - catch (DNS_ClientException dnsX) - { - throw new SIP_TransportException("Dns error: " + dnsX.ErrorCode); - } - } - - #endregion - } - - /// - /// Sends response to the specified host. - /// - /// Log ID. - /// Transaction ID. If null, then stateless response sending. - /// UDP local end point to use for sending. If null, system will use default. - /// Host name or IP address where to send response. - /// Target host port. - /// SIP transport to use. - /// SIP response to send. - private void SendResponseToHost(string logID, - string transactionID, - IPEndPoint localEP, - string host, - int port, - string transport, - SIP_Response response) - { - try - { - IPAddress[] targets = null; - if (Net_Utils.IsIPAddress(host)) - { - targets = new[] {IPAddress.Parse(host)}; - } - else - { - targets = m_pStack.Dns.GetHostAddresses(host); - if (targets.Length == 0) - { - throw new SIP_TransportException("Invalid Via: Sent-By host name '" + host + - "' could not be resolved."); - } - } - - byte[] responseData = response.ToByteData(); - - for (int i = 0; i < targets.Length; i++) - { - IPEndPoint remoteEP = new IPEndPoint(targets[i], port); - try - { - SIP_Flow flow = GetOrCreateFlow(transport, localEP, remoteEP); - flow.Send(response); - // localEP = flow.LocalEP; - - if (m_pStack.Logger != null) - { - m_pStack.Logger.AddWrite(logID, - null, - 0, - "Response [transactionID='" + transactionID + - "'; method='" + response.CSeq.RequestMethod + "'; cseq='" + - response.CSeq.SequenceNumber + "'; " + "transport='" + - transport + "'; size='" + responseData.Length + - "'; statusCode='" + response.StatusCode + "'; " + - "reason='" + response.ReasonPhrase + "'; sent '" + - localEP + "' -> '" + remoteEP + "'.", - localEP, - remoteEP, - responseData); - } - - // If we reach so far, send succeeded. - return; - } - catch - { - // Generate error, all IP addresses has failed. - if (i == (targets.Length - 1)) - { - if (m_pStack.Logger != null) - { - m_pStack.Logger.AddText(logID, - "Failed to send response to host '" + host + - "' IP end point '" + remoteEP + "'."); - } - - throw new SIP_TransportException("Host '" + host + ":" + port + - "' is not accessible."); - } - // For loop will try next IP address. - else - { - if (m_pStack.Logger != null) - { - m_pStack.Logger.AddText(logID, - "Failed to send response to host '" + host + - "' IP end point '" + remoteEP + - "', will try next A record."); - } - } - } - } - } - catch (DNS_ClientException dnsX) - { - throw new SIP_TransportException("Dns error: " + dnsX.ErrorCode); - } - } - - #endregion - - #region Nested type: SIP_FlowManager - - /// - /// Implements SIP flow manager. - /// - private class SIP_FlowManager : IDisposable - { - #region Members - - private readonly object m_pLock = new object(); - private int m_IdelTimeout = 60*5; - private bool m_IsDisposed; - private Dictionary m_pFlows; - private SIP_TransportLayer m_pOwner; - private TimerEx m_pTimeoutTimer; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner transport layer. - /// Is raised when owner is null reference. - internal SIP_FlowManager(SIP_TransportLayer owner) - { - if (owner == null) - { - throw new ArgumentNullException("owner"); - } - - m_pOwner = owner; - - m_pFlows = new Dictionary(); - - m_pTimeoutTimer = new TimerEx(15000); - m_pTimeoutTimer.AutoReset = true; - m_pTimeoutTimer.Elapsed += m_pTimeoutTimer_Elapsed; - m_pTimeoutTimer.Enabled = true; - } - - #endregion - - #region Properties - - /// - /// Gets number of flows in the collection. - /// - /// Is raised when this object is disposed and this property is accessed. - public int Count - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pFlows.Count; - } - } - - /// - /// Gets active flows. - /// - public SIP_Flow[] Flows - { - get - { - lock (m_pLock) - { - SIP_Flow[] retVal = new SIP_Flow[m_pFlows.Count]; - m_pFlows.Values.CopyTo(retVal, 0); - - return retVal; - } - } - } - - /// - /// Gets if this object is disposed. - /// - public bool IsDisposed - { - get { return m_IsDisposed; } - } - - /// - /// Gets a flow with the specified flow ID. - /// - /// SIP flow ID. - /// Returns flow with the specified flow ID or null if not found. - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when flowID is null reference value. - public SIP_Flow this[string flowID] - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (flowID == null) - { - throw new ArgumentNullException("flowID"); - } - - if (m_pFlows.ContainsKey(flowID)) - { - return m_pFlows[flowID]; - } - else - { - return null; - } - } - } - - /// - /// Gets owner transpoprt layer. - /// - /// Is raised when this object is disposed and this property is accessed. - internal SIP_TransportLayer TransportLayer - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pOwner; - } - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public void Dispose() - { - lock (m_pLock) - { - if (m_IsDisposed) - { - return; - } - m_IsDisposed = true; - - foreach (SIP_Flow flow in Flows) - { - flow.Dispose(); - } - - m_pOwner = null; - m_pFlows = null; - m_pTimeoutTimer.Dispose(); - m_pTimeoutTimer = null; - } - } - - /// - /// Returns specified flow or null if no such flow. - /// - /// Data flow ID. - /// Returns specified flow or null if no such flow. - /// Is raised when flowID is null reference. - public SIP_Flow GetFlow(string flowID) - { - if (flowID == null) - { - throw new ArgumentNullException("flowID"); - } - - lock (m_pFlows) - { - SIP_Flow retVal = null; - m_pFlows.TryGetValue(flowID, out retVal); - - return retVal; - } - } - - #endregion - - #region Internal methods - - /// - /// Returns existing flow if exists, otherwise new created flow. - /// - /// Specifies if created flow is server or client flow. This has effect only if flow is created. - /// Local end point. - /// Remote end point. - /// SIP transport. - /// Returns existing flow if exists, otherwise new created flow. - /// Is raised when localEP,remoteEP or transport is null reference. - /// Is raised when any of the arguments has invalid value. - internal SIP_Flow GetOrCreateFlow(bool isServer, - IPEndPoint localEP, - IPEndPoint remoteEP, - string transport) - { - if (localEP == null) - { - throw new ArgumentNullException("localEP"); - } - if (remoteEP == null) - { - throw new ArgumentNullException("remoteEP"); - } - if (transport == null) - { - throw new ArgumentNullException("transport"); - } - - string flowID = localEP + "-" + remoteEP + "-" + transport; - - lock (m_pLock) - { - SIP_Flow flow = null; - if (m_pFlows.TryGetValue(flowID, out flow)) - { - return flow; - } - else - { - flow = new SIP_Flow(m_pOwner.Stack, isServer, localEP, remoteEP, transport); - m_pFlows.Add(flow.ID, flow); - flow.IsDisposing += delegate - { - lock (m_pLock) - { - m_pFlows.Remove(flowID); - } - }; - flow.Start(); - - return flow; - } - } - } - - /// - /// Creates new flow from TCP server session. - /// - /// TCP server session. - /// Returns created flow. - /// Is raised when session is null reference. - internal SIP_Flow CreateFromSession(TCP_ServerSession session) - { - if (session == null) - { - throw new ArgumentNullException("session"); - } - - string flowID = session.LocalEndPoint + "-" + session.RemoteEndPoint + "-" + - (session.IsSecureConnection ? SIP_Transport.TLS : SIP_Transport.TCP); - - lock (m_pLock) - { - SIP_Flow flow = new SIP_Flow(m_pOwner.Stack, session); - m_pFlows.Add(flowID, flow); - flow.IsDisposing += delegate - { - lock (m_pLock) - { - m_pFlows.Remove(flowID); - } - }; - flow.Start(); - - return flow; - } - } - - #endregion - - #region Utility methods - - private void m_pTimeoutTimer_Elapsed(object sender, ElapsedEventArgs e) - { - lock (m_pLock) - { - if (m_IsDisposed) - { - return; - } - - foreach (SIP_Flow flow in Flows) - { - try - { - if (flow.LastActivity.AddSeconds(m_IdelTimeout) < DateTime.Now) - { - flow.Dispose(); - } - } - catch (ObjectDisposedException x) - { - string dummy = x.Message; - } - } - } - } - - #endregion - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_UA_Registration.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_UA_Registration.cs deleted file mode 100644 index 2ab5649ca..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_UA_Registration.cs +++ /dev/null @@ -1,514 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - #region usings - - using System; - using System.Net; - using System.Timers; - using Message; - - #endregion - - /// - /// This class represent SIP UA registration. - /// - public class SIP_UA_Registration - { - #region Events - - /// - /// This event is raised when registration has disposed. - /// - public event EventHandler Disposed = null; - - /// - /// This event is raised when REGISTER/un-REGISTER has failed. - /// - public event EventHandler Error = null; - - /// - /// This event is raised when REGISTER has completed successfully. - /// - public event EventHandler Registered = null; - - /// - /// This event is raised when registration state has changed. - /// - public event EventHandler StateChanged = null; - - /// - /// This event is raised when un-REGISTER has completed successfully. - /// - public event EventHandler Unregistered = null; - - #endregion - - #region Members - - private readonly string m_AOR = ""; - private readonly AbsoluteUri m_pContact; - private readonly SIP_Uri m_pServer; - private readonly int m_RefreshInterval = 300; - private bool m_AutoDispose; - private bool m_AutoRefresh = true; - private bool m_IsDisposed; - private SIP_Flow m_pFlow; - private SIP_RequestSender m_pRegisterSender; - private SIP_Stack m_pStack; - private TimerEx m_pTimer; - private SIP_RequestSender m_pUnregisterSender; - private SIP_UA_RegistrationState m_State = SIP_UA_RegistrationState.Unregistered; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner SIP stack. - /// Registrar server URI. For example: sip:domain.com. - /// Address of record. For example: user@domain.com. - /// Contact URI. - /// Gets after how many seconds reigisration expires. - /// Is raised when ua,server,transport,aor or contact is null reference. - /// Is raised when any of the arguments contains invalid value. - internal SIP_UA_Registration(SIP_Stack stack, - SIP_Uri server, - string aor, - AbsoluteUri contact, - int expires) - { - if (stack == null) - { - throw new ArgumentNullException("stack"); - } - if (server == null) - { - throw new ArgumentNullException("server"); - } - if (aor == null) - { - throw new ArgumentNullException("aor"); - } - if (aor == string.Empty) - { - throw new ArgumentException("Argument 'aor' value must be specified."); - } - if (contact == null) - { - throw new ArgumentNullException("contact"); - } - - m_pStack = stack; - m_pServer = server; - m_AOR = aor; - m_pContact = contact; - m_RefreshInterval = expires; - - m_pTimer = new TimerEx((m_RefreshInterval - 15)*1000); - m_pTimer.AutoReset = false; - m_pTimer.Elapsed += m_pTimer_Elapsed; - m_pTimer.Enabled = false; - } - - #endregion - - #region Properties - - /// - /// Gets registration address of record. - /// - /// Is raised when this object is disposed and and this property is accessed. - public string AOR - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_AOR; - } - } - - /// - /// If true and contact is different than received or rport, received and rport is used as contact. - /// - /// Is raised when this object is disposed and and this property is accessed. - public bool AutoFixContact - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - // TODO: - - return false; - } - } - - /// - /// Gets registration contact URI. - /// - /// Is raised when this object is disposed and and this property is accessed. - public AbsoluteUri Contact - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pContact; - } - } - - /// - /// Gets after how many seconds contact expires. - /// - /// Is raised when this object is disposed and and this property is accessed. - public int Expires - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return 3600; - } - } - - /// - /// Gets if this object is disposed. - /// - public bool IsDisposed - { - get { return m_IsDisposed; } - } - - /// - /// Gets registration state. - /// - public SIP_UA_RegistrationState State - { - get { return m_State; } - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public void Dispose() - { - if (m_IsDisposed) - { - return; - } - m_IsDisposed = true; - - m_pStack = null; - m_pTimer.Dispose(); - m_pTimer = null; - - SetState(SIP_UA_RegistrationState.Disposed); - OnDisposed(); - - Registered = null; - Unregistered = null; - Error = null; - Disposed = null; - } - - /// - /// Starts registering. - /// - /// If true, registration takes care of refreshing itself to registrar server. - /// Is raised when this object is disposed and and this method is accessed. - public void BeginRegister(bool autoRefresh) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - // Fix ME: Stack not running, try register on next step. - // In ideal solution we need to start registering when stack starts. - if (!m_pStack.IsRunning) - { - m_pTimer.Enabled = true; - return; - } - - m_AutoRefresh = autoRefresh; - SetState(SIP_UA_RegistrationState.Registering); - - /* RFC 3261 10.1 Constructing the REGISTER Request. - Request-URI: The Request-URI names the domain of the location service for which the registration is meant (for example, - "sip:chicago.com"). The "userinfo" and "@" components of the SIP URI MUST NOT be present. - */ - - SIP_Request register = m_pStack.CreateRequest(SIP_Methods.REGISTER, - new SIP_t_NameAddress(m_pServer.Scheme + ":" + m_AOR), - new SIP_t_NameAddress(m_pServer.Scheme + ":" + m_AOR)); - register.RequestLine.Uri = - SIP_Uri.Parse(m_pServer.Scheme + ":" + m_AOR.Substring(m_AOR.IndexOf('@') + 1)); - register.Route.Add(m_pServer.ToString()); - register.Contact.Add("<" + Contact + ">;expires=" + m_RefreshInterval); - - m_pRegisterSender = m_pStack.CreateRequestSender(register, m_pFlow); - m_pRegisterSender.ResponseReceived += m_pRegisterSender_ResponseReceived; - m_pRegisterSender.Start(); - } - - /// - /// Starts unregistering. - /// - /// If true, registration will be disposed after unregister. - /// Is raised when this object is disposed and and this method is accessed. - public void BeginUnregister(bool dispose) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - m_AutoDispose = dispose; - - // Stop register timer, otherwise we may get register and unregister race condition. - m_pTimer.Enabled = false; - - if (m_State == SIP_UA_RegistrationState.Registered) - { - /* RFC 3261 10.1 Constructing the REGISTER Request. - Request-URI: The Request-URI names the domain of the location service for which the registration is meant (for example, - "sip:chicago.com"). The "userinfo" and "@" components of the SIP URI MUST NOT be present. - */ - - SIP_Request unregister = m_pStack.CreateRequest(SIP_Methods.REGISTER, - new SIP_t_NameAddress(m_pServer.Scheme + ":" + - m_AOR), - new SIP_t_NameAddress(m_pServer.Scheme + ":" + - m_AOR)); - unregister.RequestLine.Uri = - SIP_Uri.Parse(m_pServer.Scheme + ":" + m_AOR.Substring(m_AOR.IndexOf('@') + 1)); - unregister.Route.Add(m_pServer.ToString()); - unregister.Contact.Add("<" + Contact + ">;expires=0"); - - m_pUnregisterSender = m_pStack.CreateRequestSender(unregister, m_pFlow); - m_pUnregisterSender.ResponseReceived += m_pUnregisterSender_ResponseReceived; - m_pUnregisterSender.Start(); - } - else - { - SetState(SIP_UA_RegistrationState.Unregistered); - OnUnregistered(); - - if (m_AutoDispose) - { - Dispose(); - } - - m_pUnregisterSender = null; - } - } - - #endregion - - #region Utility methods - - /// - /// This method is raised when registration needs to refresh server registration. - /// - /// Sender. - /// Event data. - private void m_pTimer_Elapsed(object sender, ElapsedEventArgs e) - { - if (m_pStack.IsRunning) - { - BeginRegister(m_AutoRefresh); - } - } - - /// - /// This method is called when REGISTER has finished. - /// - /// Sender. - /// Event data. - private void m_pRegisterSender_ResponseReceived(object sender, SIP_ResponseReceivedEventArgs e) - { - m_pFlow = e.ClientTransaction.Flow; - - if (e.Response.StatusCodeType == SIP_StatusCodeType.Success) - { - SetState(SIP_UA_RegistrationState.Registered); - - OnRegistered(); - - m_pFlow.SendKeepAlives = true; - } - else - { - SetState(SIP_UA_RegistrationState.Error); - - OnError(e); - } - - // REMOVE ME: - if (AutoFixContact && (m_pContact is SIP_Uri)) - { - // If Via: received or rport paramter won't match to our sent-by, use received and rport to construct new contact value. - - SIP_Uri cContact = ((SIP_Uri) m_pContact); - IPAddress cContactIP = Net_Utils.IsIPAddress(cContact.Host) - ? IPAddress.Parse(cContact.Host) - : null; - SIP_t_ViaParm via = e.Response.Via.GetTopMostValue(); - if (via != null && cContactIP != null) - { - IPEndPoint ep = new IPEndPoint(via.Received != null ? via.Received : cContactIP, - via.RPort > 0 ? via.RPort : cContact.Port); - if (!cContactIP.Equals(ep.Address) || cContact.Port != via.RPort) - { - // Unregister old contact. - BeginUnregister(false); - - // Fix contact. - cContact.Host = ep.Address.ToString(); - cContact.Port = ep.Port; - - m_pRegisterSender.Dispose(); - m_pRegisterSender = null; - - BeginRegister(m_AutoRefresh); - - return; - } - } - } - - if (m_AutoRefresh) - { - // Set registration refresh timer. - m_pTimer.Enabled = true; - } - - m_pRegisterSender.Dispose(); - m_pRegisterSender = null; - } - - /// - /// This method is called when un-REGISTER has finished. - /// - /// Sender. - /// Event data. - private void m_pUnregisterSender_ResponseReceived(object sender, SIP_ResponseReceivedEventArgs e) - { - SetState(SIP_UA_RegistrationState.Unregistered); - OnUnregistered(); - - if (m_AutoDispose) - { - Dispose(); - } - - m_pUnregisterSender = null; - } - - /// - /// Changes current registration state. - /// - /// New registration state. - private void SetState(SIP_UA_RegistrationState newState) - { - m_State = newState; - - OnStateChanged(); - } - - /// - /// Raises event StateChanged. - /// - private void OnStateChanged() - { - if (StateChanged != null) - { - StateChanged(this, new EventArgs()); - } - } - - /// - /// Raises event Registered. - /// - private void OnRegistered() - { - if (Registered != null) - { - Registered(this, new EventArgs()); - } - } - - /// - /// Raises event Unregistered. - /// - private void OnUnregistered() - { - if (Unregistered != null) - { - Unregistered(this, new EventArgs()); - } - } - - /// - /// Raises event Error. - /// - /// Event data. - private void OnError(SIP_ResponseReceivedEventArgs e) - { - if (Error != null) - { - Error(this, e); - } - } - - /// - /// Raises event Disposed. - /// - private void OnDisposed() - { - if (Disposed != null) - { - Disposed(this, new EventArgs()); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_UA_RegistrationState.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_UA_RegistrationState.cs deleted file mode 100644 index 89e2022ec..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/Stack/SIP_UA_RegistrationState.cs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.Stack -{ - /// - /// This class specifies SIP UA registration state. - /// - public enum SIP_UA_RegistrationState - { - /// - /// Registration is currently registering. - /// - Registering, - - /// - /// Registration is active. - /// - Registered, - - /// - /// Registration is not registered to registrar server. - /// - Unregistered, - - /// - /// Registering has failed. - /// - Error, - - /// - /// Registration has disposed. - /// - Disposed - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/UA/SIP_UA.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/UA/SIP_UA.cs deleted file mode 100644 index ce95885fe..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/UA/SIP_UA.cs +++ /dev/null @@ -1,311 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.UA -{ - #region usings - - using System; - using System.Collections.Generic; - using System.Threading; - using Stack; - - #endregion - - /// - /// This class implements SIP UA. Defined in RFC 3261 8.1. - /// - public class SIP_UA : IDisposable - { - #region Events - - /// - /// Is raised when new incoming call. - /// - public event EventHandler IncomingCall = null; - - /// - /// Is raised when user agent get new SIP request. - /// - public event EventHandler RequestReceived = null; - - #endregion - - #region Members - - private readonly List m_pCalls; - private readonly object m_pLock = new object(); - private bool m_IsDisposed; - private SIP_Stack m_pStack; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public SIP_UA() - { - m_pStack = new SIP_Stack(); - m_pStack.RequestReceived += m_pStack_RequestReceived; - - m_pCalls = new List(); - } - - #endregion - - #region Properties - - /// - /// Gets active calls. - /// - /// Is raised when this object is disposed and and this property is accessed. - public SIP_UA_Call[] Calls - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pCalls.ToArray(); - } - } - - /// - /// Gets if this object is disposed. - /// - public bool IsDisposed - { - get { return m_IsDisposed; } - } - - /// - /// Gets SIP stack. - /// - /// Is raised when this object is disposed and and this property is accessed. - public SIP_Stack Stack - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pStack; - } - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public void Dispose() - { - lock (m_pLock) - { - if (m_IsDisposed) - { - return; - } - - // Hang up all calls. - foreach (SIP_UA_Call call in m_pCalls.ToArray()) - { - call.Terminate(); - } - - // Wait till all registrations and calls disposed or wait timeout reached. - DateTime start = DateTime.Now; - while (m_pCalls.Count > 0) - { - Thread.Sleep(500); - - // Timeout, just kill all UA. - if (((DateTime.Now - start)).Seconds > 15) - { - break; - } - } - - m_IsDisposed = true; - - RequestReceived = null; - IncomingCall = null; - - m_pStack.Dispose(); - m_pStack = null; - } - } - - /// - /// Creates call to invite specified recipient. - /// - /// INVITE request. - /// Returns created call. - /// Is raised when invite is null reference. - /// Is raised when any of the argumnets has invalid value. - public SIP_UA_Call CreateCall(SIP_Request invite) - { - if (invite == null) - { - throw new ArgumentNullException("invite"); - } - if (invite.RequestLine.Method != SIP_Methods.INVITE) - { - throw new ArgumentException("Argument 'invite' is not INVITE request."); - } - - lock (m_pLock) - { - SIP_UA_Call call = new SIP_UA_Call(this, invite); - call.StateChanged += Call_StateChanged; - m_pCalls.Add(call); - - return call; - } - } - - #endregion - - #region Utility methods - - /// - /// This method is called when SIP stack received new message. - /// - /// Sender. - /// Event data. - private void m_pStack_RequestReceived(object sender, SIP_RequestReceivedEventArgs e) - { - // TODO: Performance: rise events on thread pool or see if this method called on pool aready, then we may not keep lock for events ? - - if (e.Request.RequestLine.Method == SIP_Methods.CANCEL) - { - /* RFC 3261 9.2. - If the UAS did not find a matching transaction for the CANCEL - according to the procedure above, it SHOULD respond to the CANCEL - with a 481 (Call Leg/Transaction Does Not Exist). - - Regardless of the method of the original request, as long as the - CANCEL matched an existing transaction, the UAS answers the CANCEL - request itself with a 200 (OK) response. - */ - - SIP_ServerTransaction trToCancel = - m_pStack.TransactionLayer.MatchCancelToTransaction(e.Request); - if (trToCancel != null) - { - trToCancel.Cancel(); - e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok, - e.Request)); - } - else - { - e.ServerTransaction.SendResponse( - m_pStack.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist, - e.Request)); - } - } - else if (e.Request.RequestLine.Method == SIP_Methods.BYE) - { - /* RFC 3261 15.1.2. - If the BYE does not match an existing dialog, the UAS core SHOULD generate a 481 - (Call/Transaction Does Not Exist) response and pass that to the server transaction. - */ - // TODO: - - SIP_Dialog dialog = m_pStack.TransactionLayer.MatchDialog(e.Request); - if (dialog != null) - { - e.ServerTransaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x200_Ok, - e.Request)); - dialog.Terminate(); - } - else - { - e.ServerTransaction.SendResponse( - m_pStack.CreateResponse(SIP_ResponseCodes.x481_Call_Transaction_Does_Not_Exist, - e.Request)); - } - } - else if (e.Request.RequestLine.Method == SIP_Methods.INVITE) - { - SIP_ServerTransaction transaction = e.ServerTransaction; - transaction.SendResponse(m_pStack.CreateResponse(SIP_ResponseCodes.x180_Ringing, e.Request)); - - // TODO: See if we support SDP media. - - // Create call. - SIP_UA_Call call = new SIP_UA_Call(this, transaction); - call.StateChanged += Call_StateChanged; - m_pCalls.Add(call); - - OnIncomingCall(call); - } - else - { - OnRequestReceived(e); - } - } - - /// - /// Thsi method is called when call state has chnaged. - /// - /// Sender. - /// Event data. - private void Call_StateChanged(object sender, EventArgs e) - { - SIP_UA_Call call = (SIP_UA_Call) sender; - if (call.State == SIP_UA_CallState.Terminated) - { - m_pCalls.Remove(call); - } - } - - /// - /// Raises event IncomingCall. - /// - /// Incoming call. - private void OnIncomingCall(SIP_UA_Call call) - { - if (IncomingCall != null) - { - IncomingCall(this, new SIP_UA_Call_EventArgs(call)); - } - } - - #endregion - - /// - /// Raises RequestReceived event. - /// - /// SIP request. - protected void OnRequestReceived(SIP_RequestReceivedEventArgs request) - { - if (RequestReceived != null) - { - RequestReceived(this, request); - } - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/UA/SIP_UA_Call.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/UA/SIP_UA_Call.cs deleted file mode 100644 index 5dfb01725..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/UA/SIP_UA_Call.cs +++ /dev/null @@ -1,555 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.UA -{ - #region usings - - using System; - using System.Collections.Generic; - using System.Text; - using Message; - using SDP; - using Stack; - - #endregion - - /// - /// This class represent SIP UA call. - /// - public class SIP_UA_Call : IDisposable - { - #region Events - - /// - /// Is raised when call state has changed. - /// - public event EventHandler StateChanged = null; - - #endregion - - #region Members - - private readonly List m_pEarlyDialogs; - private readonly SIP_ServerTransaction m_pInitialInviteTransaction; - private readonly SIP_Request m_pInvite; - private readonly AbsoluteUri m_pLocalUri; - private readonly AbsoluteUri m_pRemoteUri; - private readonly SIP_UA m_pUA; - private bool m_IsRedirected; - private SIP_Dialog m_pDialog; - private SIP_RequestSender m_pInitialInviteSender; - private object m_pLock = ""; - private SDP_Message m_pRemoteSDP; - private DateTime m_StartTime; - private SIP_UA_CallState m_State = SIP_UA_CallState.WaitingForStart; - - #endregion - - #region Constructor - - /// - /// Default outgoing call constructor. - /// - /// Owner UA. - /// INVITE request. - /// Is riased when ua or invite is null reference. - /// Is raised when any of the argumnets has invalid value. - internal SIP_UA_Call(SIP_UA ua, SIP_Request invite) - { - if (ua == null) - { - throw new ArgumentNullException("ua"); - } - if (invite == null) - { - throw new ArgumentNullException("invite"); - } - if (invite.RequestLine.Method != SIP_Methods.INVITE) - { - throw new ArgumentException("Argument 'invite' is not INVITE request."); - } - - m_pUA = ua; - m_pInvite = invite; - m_pLocalUri = invite.From.Address.Uri; - m_pRemoteUri = invite.To.Address.Uri; - - m_State = SIP_UA_CallState.WaitingForStart; - - m_pEarlyDialogs = new List(); - } - - /// - /// Default incoming call constructor. - /// - /// Owner UA. - /// INVITE server transaction. - /// Is riased when ua or invite is null reference. - internal SIP_UA_Call(SIP_UA ua, SIP_ServerTransaction invite) - { - if (ua == null) - { - throw new ArgumentNullException("ua"); - } - if (invite == null) - { - throw new ArgumentNullException("invite"); - } - - m_pUA = ua; - m_pInitialInviteTransaction = invite; - m_pLocalUri = invite.Request.To.Address.Uri; - m_pRemoteUri = invite.Request.From.Address.Uri; - m_pInitialInviteTransaction.Canceled += delegate - { - // If transaction canceled, terminate call. - SetState(SIP_UA_CallState.Terminated); - }; - - m_State = SIP_UA_CallState.WaitingToAccept; - } - - #endregion - - #region Properties - - /// - /// Gets call duration in seconds. - /// - public int Duration - { - get - { - return ((DateTime.Now - m_StartTime)).Seconds; - - // TODO: if terminated, we need static value here. - } - } - - /// - /// Gets if this object is disposed. - /// - public bool IsDisposed - { - get { return m_State == SIP_UA_CallState.Disposed; } - } - - /// - /// Gets if call is on hold. - /// - public bool IsOnhold - { - // TODO: - - get { return false; } - } - - /// - /// Gets if call has been redirected by remote party. - /// - public bool IsRedirected - { - get { return m_IsRedirected; } - } - - /// - /// Gets call local party URI. - /// - public AbsoluteUri LocalUri - { - get { return m_pLocalUri; } - } - - /// - /// Gets remote SDP. - /// - public SDP_Message RemoteSDP - { - get { return m_pRemoteSDP; } - } - - /// - /// Gets call remote party URI. - /// - public AbsoluteUri RemoteUri - { - get { return m_pRemoteUri; } - } - - /// - /// Gets call start time. - /// - public DateTime StartTime - { - get { return m_StartTime; } - } - - /// - /// Gets current call state. - /// - public SIP_UA_CallState State - { - get { return m_State; } - } - - /// - /// Gets or sets user data. - /// - public object Tag { get; set; } - - #endregion - - #region Methods - - /// - /// Cleans up any resource being used. - /// - public void Dispose() - { - lock (m_pLock) - { - if (m_State == SIP_UA_CallState.Disposed) - { - return; - } - SetState(SIP_UA_CallState.Disposed); - - // TODO: Clean up - - StateChanged = null; - } - } - - /// - /// Starts terminating call. To get when call actually terminates, monitor StateChanged event. - /// - /// Is raised when this object is disposed and and this method is accessed. - public void Terminate() - { - lock (m_pLock) - { - if (m_State == SIP_UA_CallState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (m_State == SIP_UA_CallState.Terminating || m_State == SIP_UA_CallState.Terminated) - { - return; - } - else if (m_State == SIP_UA_CallState.WaitingForStart) - { - SetState(SIP_UA_CallState.Terminated); - } - else if (m_State == SIP_UA_CallState.WaitingToAccept) - { - m_pInitialInviteTransaction.SendResponse( - m_pUA.Stack.CreateResponse(SIP_ResponseCodes.x487_Request_Terminated, - m_pInitialInviteTransaction.Request)); - - SetState(SIP_UA_CallState.Terminated); - } - else if (m_State == SIP_UA_CallState.Active) - { - m_pDialog.Terminate(); - - SetState(SIP_UA_CallState.Terminated); - } - else if (m_pInitialInviteSender != null) - { - /* RFC 3261 15. - If we are caller and call is not active yet, we must do following actions: - *) Send CANCEL, set call Terminating flag. - *) If we get non 2xx final response, we are done. (Normally cancel causes '408 Request terminated') - *) If we get 2xx response (2xx sent by remote party before our CANCEL reached), we must send BYE to active dialog. - */ - - SetState(SIP_UA_CallState.Terminating); - - m_pInitialInviteSender.Cancel(); - } - } - } - - /// - /// Starts calling. - /// - /// Is raised when this object is disposed and and this method is accessed. - /// Is raised when call is not in valid state. - public void Start() - { - lock (m_pLock) - { - if (m_State == SIP_UA_CallState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (m_State != SIP_UA_CallState.WaitingForStart) - { - throw new InvalidOperationException( - "Start method can be called only in 'SIP_UA_CallState.WaitingForStart' state."); - } - - SetState(SIP_UA_CallState.Calling); - - m_pInitialInviteSender = m_pUA.Stack.CreateRequestSender(m_pInvite); - m_pInitialInviteSender.ResponseReceived += m_pInitialInviteSender_ResponseReceived; - m_pInitialInviteSender.Start(); - } - } - - /// - /// Accepts call. - /// - public void Accept() - { - if (m_State == SIP_UA_CallState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (m_State != SIP_UA_CallState.WaitingToAccept) - { - throw new InvalidOperationException( - "Accept method can be called only in 'SIP_UA_CallState.WaitingToAccept' state."); - } - - // TODO: We must add Contact header and SDP to response. - - SIP_Response response = m_pUA.Stack.CreateResponse(SIP_ResponseCodes.x200_Ok, - m_pInitialInviteTransaction.Request, - m_pInitialInviteTransaction.Flow); - response.ContentType = "application/sdp"; - response.Data = - Encoding.Default.GetBytes("v=0\r\n" + "o=LSSIP 333 242452 IN IP4 192.168.1.70\r\n" + - "s=SIP Call\r\n" + "c=IN IP4 192.168.1.70\r\n" + "t=0 0\r\n" + - "m=audio 11000 RTP/AVP 0\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + - "a=sendrecv\r\n"); - m_pInitialInviteTransaction.SendResponse(response); - - SetState(SIP_UA_CallState.Active); - - m_pDialog = m_pUA.Stack.TransactionLayer.GetOrCreateDialog(m_pInitialInviteTransaction, response); - m_pDialog.StateChanged += m_pDialog_StateChanged; - } - - /// - /// Rejects incoming call. - /// - /// Status-code reasonText. - /// Is raised when this object is disposed and and this method is accessed. - /// Is raised when call is not in valid state and this method is called. - /// Is raised when statusCode_reason is null. - public void Reject(string statusCode_reason) - { - lock (m_pLock) - { - if (State == SIP_UA_CallState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (State != SIP_UA_CallState.WaitingToAccept) - { - throw new InvalidOperationException("Call is not in valid state."); - } - if (statusCode_reason == null) - { - throw new ArgumentNullException("statusCode_reason"); - } - - m_pInitialInviteTransaction.SendResponse(m_pUA.Stack.CreateResponse(statusCode_reason, - m_pInitialInviteTransaction - .Request)); - - SetState(SIP_UA_CallState.Terminated); - } - } - - /// - /// Redirects incoming call to speified contact(s). - /// - /// Redirection targets. - /// Is raised when this object is disposed and and this method is accessed. - /// Is raised when call is not in valid state and this method is called. - /// Is raised when contacts is null. - /// Is raised when any of the arguments has invalid value. - public void Redirect(SIP_t_ContactParam[] contacts) - { - lock (m_pLock) - { - if (State == SIP_UA_CallState.Disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (State != SIP_UA_CallState.WaitingToAccept) - { - throw new InvalidOperationException("Call is not in valid state."); - } - if (contacts == null) - { - throw new ArgumentNullException("contacts"); - } - if (contacts.Length == 0) - { - throw new ArgumentException("Arguments 'contacts' must contain at least 1 value."); - } - - // TODO: - //m_pUA.Stack.CreateResponse(SIP_ResponseCodes.,m_pInitialInviteTransaction); - - throw new NotImplementedException(); - } - } - - #endregion - - #region Utility methods - - /// - /// Is called when SIP dialog state has changed. - /// - /// Sender. - /// Event data. - private void m_pDialog_StateChanged(object sender, EventArgs e) - { - if (State == SIP_UA_CallState.Terminated) - { - return; - } - - if (m_pDialog.State == SIP_DialogState.Terminated) - { - SetState(SIP_UA_CallState.Terminated); - - m_pDialog.Dispose(); - } - } - - /// - /// This method is called when initial INVITE sender got response. - /// - /// Sender. - /// Event data. - private void m_pInitialInviteSender_ResponseReceived(object sender, SIP_ResponseReceivedEventArgs e) - { - lock (m_pLock) - { - // If remote party provided SDP, parse it. - if (e.Response.ContentType != null && - e.Response.ContentType.ToLower().IndexOf("application/sdp") > -1) - { - m_pRemoteSDP = SDP_Message.Parse(Encoding.UTF8.GetString(e.Response.Data)); - - // TODO: If parsing failed, end call. - } - - if (e.Response.StatusCodeType == SIP_StatusCodeType.Provisional) - { - if (e.Response.StatusCode == 180) - { - SetState(SIP_UA_CallState.Ringing); - } - else if (e.Response.StatusCode == 182) - { - SetState(SIP_UA_CallState.Queued); - } - // We don't care other status responses. - - /* RFC 3261 13.2.2.1. - Zero, one or multiple provisional responses may arrive before one or - more final responses are received. Provisional responses for an - INVITE request can create "early dialogs". If a provisional response - has a tag in the To field, and if the dialog ID of the response does - not match an existing dialog, one is constructed using the procedures - defined in Section 12.1.2. - */ - if (e.Response.StatusCode > 100 && e.Response.To.Tag != null) - { - m_pEarlyDialogs.Add(m_pUA.Stack.TransactionLayer.GetOrCreateDialog( - e.ClientTransaction, e.Response)); - } - } - else if (e.Response.StatusCodeType == SIP_StatusCodeType.Success) - { - m_StartTime = DateTime.Now; - SetState(SIP_UA_CallState.Active); - - m_pDialog = m_pUA.Stack.TransactionLayer.GetOrCreateDialog(e.ClientTransaction, e.Response); - m_pDialog.StateChanged += m_pDialog_StateChanged; - - /* Exit all all other dialogs created by this call (due to forking). - That is not defined in RFC but, since UAC can send BYE to early and confirmed dialogs, - because of this all 100% valid. - */ - foreach (SIP_Dialog dialog in m_pEarlyDialogs.ToArray()) - { - if (!m_pDialog.Equals(dialog)) - { - dialog.Terminate("Another forking leg accepted.", true); - } - } - } - else - { - /* RFC 3261 13.2.2.3. - All early dialogs are considered terminated upon reception of the non-2xx final response. - */ - foreach (SIP_Dialog dialog in m_pEarlyDialogs.ToArray()) - { - dialog.Terminate( - "All early dialogs are considered terminated upon reception of the non-2xx final response. (RFC 3261 13.2.2.3)", - false); - } - m_pEarlyDialogs.Clear(); - - Error(); - - SetState(SIP_UA_CallState.Terminated); - } - } - } - - /// - /// Changes call state. - /// - /// New call state. - private void SetState(SIP_UA_CallState state) - { - m_State = state; - - OnStateChanged(state); - } - - /// - /// Raises StateChanged event. - /// - /// New call state. - private void OnStateChanged(SIP_UA_CallState state) - { - if (StateChanged != null) - { - StateChanged(this, new EventArgs()); - } - } - - // public event EventHandler Error - - private void Error() {} - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/UA/SIP_UA_CallState.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/UA/SIP_UA_CallState.cs deleted file mode 100644 index 58635e478..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/UA/SIP_UA_CallState.cs +++ /dev/null @@ -1,70 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.UA -{ - /// - /// This enum specifies SIP UA call states. - /// - public enum SIP_UA_CallState - { - /// - /// Outgoing call waits to be started. - /// - WaitingForStart, - - /// - /// Outgoing calling is in progress. - /// - Calling, - - /// - /// Outgoing call remote end party is ringing. - /// - Ringing, - - /// - /// Outgoing call remote end pary queued a call. - /// - Queued, - - /// - /// Incoming call waits to be accepted. - /// - WaitingToAccept, - - /// - /// Call is active. - /// - Active, - - /// - /// Call is terminating. - /// - Terminating, - - /// - /// Call is terminated. - /// - Terminated, - - /// - /// Call has disposed. - /// - Disposed - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/UA/SIP_UA_Call_EventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/UA/SIP_UA_Call_EventArgs.cs deleted file mode 100644 index 99a72242d..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SIP/UA/SIP_UA_Call_EventArgs.cs +++ /dev/null @@ -1,68 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SIP.UA -{ - #region usings - - using System; - - #endregion - - /// - /// This class provides data for SIP_UA.IncomingCall event. - /// - public class SIP_UA_Call_EventArgs : EventArgs - { - #region Members - - private readonly SIP_UA_Call m_pCall; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// SIP UA call. - /// Is called when call is null reference. - public SIP_UA_Call_EventArgs(SIP_UA_Call call) - { - if (call == null) - { - throw new ArgumentNullException("call"); - } - - m_pCall = call; - } - - #endregion - - #region Properties - - /// - /// Gets call. - /// - public SIP_UA_Call Call - { - get { return m_pCall; } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Client/SMTP_Client.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Client/SMTP_Client.cs deleted file mode 100644 index 8d6fd93d2..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Client/SMTP_Client.cs +++ /dev/null @@ -1,1987 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SMTP.Client -{ - #region usings - - using System; - using System.Collections.Generic; - using System.IO; - using System.Net; - using System.Security.Cryptography; - using System.Security.Principal; - using System.Text; - using AUTH; - using Dns.Client; - using Mime; - using TCP; - - #endregion - - /// - /// This class implements SMTP client. Defined in RFC 2821. - /// - /// - /// Simple way: - /// - /// /* - /// To make this code to work, you need to import following namespaces: - /// using LumiSoft.Net.SMTP.Client; - /// */ - /// - /// // You can send any valid SMTP message here, from disk,memory, ... or - /// // you can use LumiSoft.Net.Mime mime classes to compose valid SMTP mail message. - /// - /// // SMTP_Client.QuickSendSmartHost(... - /// or - /// // SMTP_Client.QuickSend(... - /// - /// - /// Advanced way: - /// - /// /* - /// To make this code to work, you need to import following namespaces: - /// using LumiSoft.Net.SMTP.Client; - /// */ - /// - /// using(SMTP_Client smtp = new SMTP_Client()){ - /// // If you have registered DNS host name, set it here before connecting. - /// // That name will be reported to SMTP server. - /// // smtp.LocalHostName = "mail.domain.com"; - /// - /// // You can use SMTP_Client.GetDomainHosts(... to get target receipient SMTP hosts for Connect method. - /// smtp.Connect("hostName",WellKnownPorts.SMTP); - /// // Authenticate if target server requires. - /// // smtp.Authenticate("user","password"); - /// smtp.MailFrom("sender@domain.com"); - /// // Repeat this for all recipients. - /// smtp.RcptTo("to@domain.com"); - /// - /// // Send message to server. - /// // You can send any valid SMTP message here, from disk,memory, ... or - /// // you can use LumiSoft.Net.Mime mieclasses to compose valid SMTP mail message. - /// // smtp.SendMessage(.... . - /// } - /// - /// - public class SMTP_Client : TCP_Client - { - #region Delegates - - /// - /// Internal helper method for asynchronous Authenticate method. - /// - private delegate void AuthenticateDelegate(string userName, string password); - - /// - /// Internal helper method for asynchronous SendMessage method. - /// - private delegate string[] GetDomainHostsDelegate(string domain); - - /// - /// Internal helper method for asynchronous MailFrom method. - /// - private delegate void MailFromDelegate(string from, long messageSize); - - /// - /// Internal helper method for asynchronous Noop method. - /// - private delegate void NoopDelegate(); - - /// - /// Internal helper method for asynchronous RcptTo method. - /// - private delegate void RcptToDelegate(string to); - - /// - /// Internal helper method for asynchronous Reset method. - /// - private delegate void ResetDelegate(); - - /// - /// Internal helper method for asynchronous SendMessage method. - /// - private delegate void SendMessageDelegate(Stream message); - - /// - /// Internal helper method for asynchronous StartTLS method. - /// - private delegate void StartTLSDelegate(); - - #endregion - - #region Members - - private string m_GreetingText = ""; - private bool m_IsEsmtpSupported; - private string m_LocalHostName; - private string m_MailFrom; - private GenericIdentity m_pAuthdUserIdentity; - private List m_pEsmtpFeatures; - private List m_pRecipients; - private string m_RemoteHostName; - - #endregion - - #region Properties - - /// - /// Gets session authenticated user identity, returns null if not authenticated. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when this property is accessed and SMTP client is not connected. - public override GenericIdentity AuthenticatedUserIdentity - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - - return m_pAuthdUserIdentity; - } - } - - /// - /// Gets what ESMTP features are supported by connected SMTP server. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when this property is accessed and SMTP client is not connected. - public string[] EsmtpFeatures - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - - return m_pEsmtpFeatures.ToArray(); - } - } - - /// - /// Gets greeting text which was sent by SMTP server. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when this property is accessed and SMTP client is not connected. - public string GreetingText - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - - return m_GreetingText; - } - } - - /// - /// Gets if connected SMTP server suports ESMTP. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when this property is accessed and SMTP client is not connected. - public bool IsEsmtpSupported - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - - return m_IsEsmtpSupported; - } - } - - /// - /// Gets or sets host name which is reported to SMTP server. If value null, then local computer name is used. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when this property is accessed and SMTP client is connected. - public string LocalHostName - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_LocalHostName; - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (IsConnected) - { - throw new InvalidOperationException( - "Property LocalHostName is available only when SMTP client is not connected."); - } - - m_LocalHostName = value; - } - } - - /// - /// Gets maximum message size in bytes what SMTP server accepts. Value null means not known. - /// - public long MaxAllowedMessageSize - { - get - { - try - { - foreach (string feature in EsmtpFeatures) - { - if (feature.ToUpper().StartsWith(SMTP_ServiceExtensions.SIZE)) - { - return Convert.ToInt64(feature.Split(' ')[1]); - } - } - } - catch - { - // Never should reach here, skip errors here. - } - - return 0; - } - } - - /// - /// Gets SMTP server host name which it reported to us. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when this property is accessed and SMTP client is not connected. - public string RemoteHostName - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - - return m_RemoteHostName; - } - } - - /// - /// Gets SMTP server supported SASL authentication method. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when this property is accessed and SMTP client is not connected. - public string[] SaslAuthMethods - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - - // Search AUTH entry. - foreach (string feature in EsmtpFeatures) - { - if (feature.ToUpper().StartsWith(SMTP_ServiceExtensions.AUTH)) - { - // Remove AUTH and split authentication methods. - return feature.Substring(4).Trim().Split(' '); - } - } - - return new string[0]; - } - } - - #endregion - - #region Methods - - /// - /// Starts getting specified email domain SMTP hosts. - /// - /// Email domain or email address. For example domain.com or user@domain.com. - /// Callback to call when the asynchronous operation is complete. - /// User data. - /// An IAsyncResult that references the asynchronous method. - /// Is raised when domain is null. - /// Is raised when any of the arguments has invalid value. - public static IAsyncResult BeginGetDomainHosts(string domain, AsyncCallback callback, object state) - { - if (domain == null) - { - throw new ArgumentNullException("domain"); - } - if (string.IsNullOrEmpty(domain)) - { - throw new ArgumentException( - "Invalid argument 'domain' value, you need to specify domain value."); - } - - GetDomainHostsDelegate asyncMethod = GetDomainHosts; - AsyncResultState asyncState = new AsyncResultState(null, asyncMethod, callback, state); - asyncState.SetAsyncResult(asyncMethod.BeginInvoke(domain, asyncState.CompletedCallback, null)); - - return asyncState; - } - - /// - /// Ends a pending asynchronous BeginGetDomainHosts request. - /// - /// An IAsyncResult that stores state information and any user defined data for this asynchronous operation. - /// Returns specified email domain SMTP hosts. - /// Is raised when asyncResult is null. - /// Is raised when invalid asyncResult passed to this method. - public static string[] EndGetDomainHosts(IAsyncResult asyncResult) - { - if (asyncResult == null) - { - throw new ArgumentNullException("asyncResult"); - } - - AsyncResultState castedAsyncResult = asyncResult as AsyncResultState; - if (castedAsyncResult == null) - { - throw new ArgumentException( - "Argument asyncResult was not returned by a call to the BeginGetDomainHosts method."); - } - if (castedAsyncResult.IsEndCalled) - { - throw new InvalidOperationException( - "BeginGetDomainHosts was previously called for the asynchronous connection."); - } - - castedAsyncResult.IsEndCalled = true; - if (castedAsyncResult.AsyncDelegate is GetDomainHostsDelegate) - { - return - ((GetDomainHostsDelegate) castedAsyncResult.AsyncDelegate).EndInvoke( - castedAsyncResult.AsyncResult); - } - else - { - throw new ArgumentException( - "Argument asyncResult was not returned by a call to the BeginGetDomainHosts method."); - } - } - - /// - /// Gets specified email domain SMTP hosts. Values are in descending priority order. - /// - /// Domain name. This value can be email address too, then domain parsed automatically. - /// Returns specified email domain SMTP hosts. - /// Is raised when domain is null. - /// Is raised when any of the arguments has invalid value. - /// Is raised when DNS query failure. - public static string[] GetDomainHosts(string domain) - { - if (domain == null) - { - throw new ArgumentNullException("domain"); - } - if (string.IsNullOrEmpty(domain)) - { - throw new ArgumentException( - "Invalid argument 'domain' value, you need to specify domain value."); - } - - // We have email address, parse domain. - if (domain.IndexOf("@") > -1) - { - domain = domain.Substring(domain.IndexOf('@') + 1); - } - - List retVal = new List(); - - // Get MX records. - Dns_Client dns = new Dns_Client(); - DnsServerResponse response = dns.Query(domain, QTYPE.MX); - if (response.ResponseCode == RCODE.NO_ERROR) - { - foreach (DNS_rr_MX mx in response.GetMXRecords()) - { - // Block invalid MX records. - if (!string.IsNullOrEmpty(mx.Host)) - { - retVal.Add(mx.Host); - } - } - } - else - { - throw new DNS_ClientException(response.ResponseCode); - } - - /* RFC 2821 5. - If no MX records are found, but an A RR is found, the A RR is treated as if it - was associated with an implicit MX RR, with a preference of 0, pointing to that host. - */ - if (retVal.Count == 0) - { - retVal.Add(domain); - } - - return retVal.ToArray(); - } - - /// - /// Sends specified mime message. - /// - /// Message to send. - /// Is raised when message is null. - public static void QuickSend(Mime message) - { - if (message == null) - { - throw new ArgumentNullException("message"); - } - - string from = ""; - if (message.MainEntity.From != null && message.MainEntity.From.Count > 0) - { - from = ((MailboxAddress) message.MainEntity.From[0]).EmailAddress; - } - - List recipients = new List(); - if (message.MainEntity.To != null) - { - MailboxAddress[] addresses = message.MainEntity.To.Mailboxes; - foreach (MailboxAddress address in addresses) - { - recipients.Add(address.EmailAddress); - } - } - if (message.MainEntity.Cc != null) - { - MailboxAddress[] addresses = message.MainEntity.Cc.Mailboxes; - foreach (MailboxAddress address in addresses) - { - recipients.Add(address.EmailAddress); - } - } - if (message.MainEntity.Bcc != null) - { - MailboxAddress[] addresses = message.MainEntity.Bcc.Mailboxes; - foreach (MailboxAddress address in addresses) - { - recipients.Add(address.EmailAddress); - } - - // We must hide BCC - message.MainEntity.Bcc.Clear(); - } - - foreach (string recipient in recipients) - { - QuickSend(null, from, recipient, new MemoryStream(message.ToByteData())); - } - } - - /// - /// Sends message directly to email domain. Domain email sever resolve order: MX recordds -> A reords if no MX. - /// - /// Sender email what is reported to SMTP server. - /// Recipient email. - /// Raw message to send. - /// Is raised when from,to or message is null. - /// Is raised when any of the arguments has invalid value. - /// Is raised when SMTP server returns error. - public static void QuickSend(string from, string to, Stream message) - { - QuickSend(null, from, to, message); - } - - /// - /// Sends message directly to email domain. Domain email sever resolve order: MX recordds -> A reords if no MX. - /// - /// Host name which is reported to SMTP server. - /// Sender email what is reported to SMTP server. - /// Recipient email. - /// Raw message to send. - /// Is raised when from,to or message is null. - /// Is raised when any of the arguments has invalid value. - /// Is raised when SMTP server returns error. - public static void QuickSend(string localHost, string from, string to, Stream message) - { - if (from == null) - { - throw new ArgumentNullException("from"); - } - if (from != "" && !SMTP_Utils.IsValidAddress(from)) - { - throw new ArgumentException("Argument 'from' has invalid value."); - } - if (to == null) - { - throw new ArgumentNullException("to"); - } - if (to == "") - { - throw new ArgumentException("Argument 'to' value must be specified."); - } - if (!SMTP_Utils.IsValidAddress(to)) - { - throw new ArgumentException("Argument 'to' has invalid value."); - } - if (message == null) - { - throw new ArgumentNullException("message"); - } - - QuickSendSmartHost(localHost, GetDomainHosts(to)[0], 25, false, from, new[] {to}, message); - } - - /// - /// Sends message by using specified smart host. - /// - /// Host name or IP address. - /// Host port. - /// Sender email what is reported to SMTP server. - /// Recipients email addresses. - /// Raw message to send. - /// Is raised when argument host,from,to or stream is null. - /// Is raised when any of the method arguments has invalid value. - /// Is raised when SMTP server returns error. - public static void QuickSendSmartHost(string host, int port, string from, string[] to, Stream message) - { - QuickSendSmartHost(null, host, port, false, null, null, from, to, message); - } - - /// - /// Sends message by using specified smart host. - /// - /// Host name or IP address. - /// Host port. - /// Specifies if connected via SSL. - /// Sender email what is reported to SMTP server. - /// Recipients email addresses. - /// Raw message to send. - /// Is raised when argument host,from,to or stream is null. - /// Is raised when any of the method arguments has invalid value. - /// Is raised when SMTP server returns error. - public static void QuickSendSmartHost(string host, - int port, - bool ssl, - string from, - string[] to, - Stream message) - { - QuickSendSmartHost(null, host, port, ssl, null, null, from, to, message); - } - - /// - /// Sends message by using specified smart host. - /// - /// Host name which is reported to SMTP server. - /// Host name or IP address. - /// Host port. - /// Specifies if connected via SSL. - /// Sender email what is reported to SMTP server. - /// Recipients email addresses. - /// Raw message to send. - /// Is raised when argument host,from,to or stream is null. - /// Is raised when any of the method arguments has invalid value. - /// Is raised when SMTP server returns error. - public static void QuickSendSmartHost(string localHost, - string host, - int port, - bool ssl, - string from, - string[] to, - Stream message) - { - QuickSendSmartHost(localHost, host, port, ssl, null, null, from, to, message); - } - - /// - /// Sends message by using specified smart host. - /// - /// Host name which is reported to SMTP server. - /// Host name or IP address. - /// Host port. - /// Specifies if connected via SSL. - /// SMTP server user name. This value may be null, then authentication not used. - /// SMTP server password. - /// Sender email what is reported to SMTP server. - /// Recipients email addresses. - /// Raw message to send. - /// Is raised when argument host,from,to or stream is null. - /// Is raised when any of the method arguments has invalid value. - /// Is raised when SMTP server returns error. - public static void QuickSendSmartHost(string localHost, - string host, - int port, - bool ssl, - string userName, - string password, - string from, - string[] to, - Stream message) - { - if (host == null) - { - throw new ArgumentNullException("host"); - } - if (host == "") - { - throw new ArgumentException("Argument 'host' value may not be empty."); - } - if (port < 1) - { - throw new ArgumentException("Argument 'port' value must be >= 1."); - } - if (from == null) - { - throw new ArgumentNullException("from"); - } - if (from != "" && !SMTP_Utils.IsValidAddress(from)) - { - throw new ArgumentException("Argument 'from' has invalid value."); - } - if (to == null) - { - throw new ArgumentNullException("to"); - } - if (to.Length == 0) - { - throw new ArgumentException("Argument 'to' must contain at least 1 recipient."); - } - foreach (string t in to) - { - if (!SMTP_Utils.IsValidAddress(t)) - { - throw new ArgumentException("Argument 'to' has invalid value '" + t + "'."); - } - } - if (message == null) - { - throw new ArgumentNullException("message"); - } - - using (SMTP_Client smtp = new SMTP_Client()) - { - if (!string.IsNullOrEmpty(localHost)) - { - smtp.LocalHostName = localHost; - } - smtp.Connect(host, port, ssl); - if (!string.IsNullOrEmpty(userName)) - { - smtp.Authenticate(userName, password); - } - smtp.MailFrom(from, -1); - foreach (string t in to) - { - smtp.RcptTo(t); - } - smtp.SendMessage(message); - } - } - - /// - /// Clean up any resources being used. - /// - public override void Dispose() - { - base.Dispose(); - } - - /// - /// Closes connection to SMTP server. - /// - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when SMTP client is not connected. - public override void Disconnect() - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("SMTP client is not connected."); - } - - try - { - // Send QUIT command to server. - WriteLine("QUIT"); - } - catch {} - - m_LocalHostName = null; - m_RemoteHostName = null; - m_GreetingText = ""; - m_IsEsmtpSupported = false; - m_pEsmtpFeatures = null; - m_MailFrom = null; - m_pRecipients = null; - m_pAuthdUserIdentity = null; - - try - { - base.Disconnect(); - } - catch {} - } - - /// - /// Starts sending NOOP command to server. This method can be used for keeping connection alive(not timing out). - /// - /// Callback to call when the asynchronous operation is complete. - /// User data. - /// An IAsyncResult that references the asynchronous operation. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when SMTP client is not connected. - public IAsyncResult BeginNoop(AsyncCallback callback, object state) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - - NoopDelegate asyncMethod = Noop; - AsyncResultState asyncState = new AsyncResultState(this, asyncMethod, callback, state); - asyncState.SetAsyncResult(asyncMethod.BeginInvoke(asyncState.CompletedCallback, null)); - - return asyncState; - } - - /// - /// Ends a pending asynchronous Noop request. - /// - /// An IAsyncResult that stores state information and any user defined data for this asynchronous operation. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when asyncResult is null. - /// Is raised when invalid asyncResult passed to this method. - /// Is raised when SMTP server returns error. - public void EndNoop(IAsyncResult asyncResult) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (asyncResult == null) - { - throw new ArgumentNullException("asyncResult"); - } - - AsyncResultState castedAsyncResult = asyncResult as AsyncResultState; - if (castedAsyncResult == null || castedAsyncResult.AsyncObject != this) - { - throw new ArgumentException( - "Argument asyncResult was not returned by a call to the BeginNoop method."); - } - if (castedAsyncResult.IsEndCalled) - { - throw new InvalidOperationException( - "BeginNoop was previously called for the asynchronous connection."); - } - - castedAsyncResult.IsEndCalled = true; - if (castedAsyncResult.AsyncDelegate is NoopDelegate) - { - ((NoopDelegate) castedAsyncResult.AsyncDelegate).EndInvoke(castedAsyncResult.AsyncResult); - } - else - { - throw new ArgumentException( - "Argument asyncResult was not returned by a call to the BeginNoop method."); - } - } - - /// - /// Send NOOP command to server. This method can be used for keeping connection alive(not timing out). - /// - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when SMTP client is not connected. - /// Is raised when SMTP server returns error. - public void Noop() - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - - /* RFC 2821 4.1.1.9 NOOP. - This command does not affect any parameters or previously entered - commands. It specifies no action other than that the receiver send - an OK reply. - - Syntax: - "NOOP" [ SP String ] CRLF - */ - - WriteLine("NOOP"); - - string line = ReadLine(); - if (!line.StartsWith("250")) - { - throw new SMTP_ClientException(line); - } - } - - /// - /// Starts switching to SSL. - /// - /// An IAsyncResult that references the asynchronous operation. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when SMTP client is not connected or is already secure connection. - public IAsyncResult BeginStartTLS(AsyncCallback callback, object state) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (IsSecureConnection) - { - throw new InvalidOperationException("Connection is already secure."); - } - - StartTLSDelegate asyncMethod = StartTLS; - AsyncResultState asyncState = new AsyncResultState(this, asyncMethod, callback, state); - asyncState.SetAsyncResult(asyncMethod.BeginInvoke(asyncState.CompletedCallback, null)); - - return asyncState; - } - - /// - /// Ends a pending asynchronous StartTLS request. - /// - /// An IAsyncResult that stores state information and any user defined data for this asynchronous operation. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when asyncResult is null. - /// Is raised when invalid asyncResult passed to this method. - /// Is raised when SMTP server returns error. - public void EndStartTLS(IAsyncResult asyncResult) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (asyncResult == null) - { - throw new ArgumentNullException("asyncResult"); - } - - AsyncResultState castedAsyncResult = asyncResult as AsyncResultState; - if (castedAsyncResult == null || castedAsyncResult.AsyncObject != this) - { - throw new ArgumentException( - "Argument asyncResult was not returned by a call to the BeginReset method."); - } - if (castedAsyncResult.IsEndCalled) - { - throw new InvalidOperationException( - "BeginReset was previously called for the asynchronous connection."); - } - - castedAsyncResult.IsEndCalled = true; - if (castedAsyncResult.AsyncDelegate is StartTLSDelegate) - { - ((StartTLSDelegate) castedAsyncResult.AsyncDelegate).EndInvoke(castedAsyncResult.AsyncResult); - } - else - { - throw new ArgumentException( - "Argument asyncResult was not returned by a call to the BeginReset method."); - } - } - - /// - /// Switches SMTP connection to SSL. - /// - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when SMTP client is not connected or is already secure connection. - /// Is raised when SMTP server returns error. - public void StartTLS() - { - /* RFC 2487 STARTTLS 5. STARTTLS Command. - STARTTLS with no parameters. - - After the client gives the STARTTLS command, the server responds with - one of the following reply codes: - - 220 Ready to start TLS - 501 Syntax error (no parameters allowed) - 454 TLS not available due to temporary reason - */ - - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (IsSecureConnection) - { - throw new InvalidOperationException("Connection is already secure."); - } - - WriteLine("STARTTLS"); - - string line = ReadLine(); - if (!line.ToUpper().StartsWith("220")) - { - throw new SMTP_ClientException(line); - } - - SwitchToSecure(); - } - - /// - /// Starts authentication. - /// - /// User login name. - /// Password. - /// Callback to call when the asynchronous operation is complete. - /// User data. - /// An IAsyncResult that references the asynchronous operation. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when SMTP client is not connected or is already authenticated. - public IAsyncResult BeginAuthenticate(string userName, - string password, - AsyncCallback callback, - object state) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (IsAuthenticated) - { - throw new InvalidOperationException("Session is already authenticated."); - } - - AuthenticateDelegate asyncMethod = Authenticate; - AsyncResultState asyncState = new AsyncResultState(this, asyncMethod, callback, state); - asyncState.SetAsyncResult(asyncMethod.BeginInvoke(userName, - password, - asyncState.CompletedCallback, - null)); - - return asyncState; - } - - /// - /// Ends a pending asynchronous authentication request. - /// - /// An IAsyncResult that stores state information and any user defined data for this asynchronous operation. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when asyncResult is null. - /// Is raised when invalid asyncResult passed to this method. - /// Is raised when SMTP server returns error. - public void EndAuthenticate(IAsyncResult asyncResult) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (asyncResult == null) - { - throw new ArgumentNullException("asyncResult"); - } - - AsyncResultState castedAsyncResult = asyncResult as AsyncResultState; - if (castedAsyncResult == null || castedAsyncResult.AsyncObject != this) - { - throw new ArgumentException( - "Argument 'asyncResult' was not returned by a call to the BeginAuthenticate method."); - } - if (castedAsyncResult.IsEndCalled) - { - throw new InvalidOperationException( - "BeginAuthenticate was previously called for the asynchronous connection."); - } - - castedAsyncResult.IsEndCalled = true; - if (castedAsyncResult.AsyncDelegate is AuthenticateDelegate) - { - ((AuthenticateDelegate) castedAsyncResult.AsyncDelegate).EndInvoke( - castedAsyncResult.AsyncResult); - } - else - { - throw new ArgumentException( - "Argument asyncResult was not returned by a call to the BeginAuthenticate method."); - } - } - - /// - /// Authenticates user. Authenticate method chooses strongest possible authentication method supported by server, - /// preference order DIGEST-MD5 -> CRAM-MD5 -> LOGIN. - /// - /// User login name. - /// Password. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when SMTP client is not connected or is already authenticated. - /// Is raised when userName is null. - /// Is raised when SMTP server returns error. - public void Authenticate(string userName, string password) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (IsAuthenticated) - { - throw new InvalidOperationException("Session is already authenticated."); - } - if (string.IsNullOrEmpty(userName)) - { - throw new ArgumentNullException("userName"); - } - if (password == null) - { - password = ""; - } - - // Choose authentication method, we consider LOGIN as default. - string authMethod = "LOGIN"; - List authMethods = new List(SaslAuthMethods); - if (authMethods.Contains("DIGEST-MD5")) - { - authMethod = "DIGEST-MD5"; - } - else if (authMethods.Contains("CRAM-MD5")) - { - authMethod = "CRAM-MD5"; - } - - #region AUTH LOGIN - - if (authMethod == "LOGIN") - { - /* LOGIN - Example: - C: AUTH LOGIN - S: 334 VXNlcm5hbWU6 VXNlcm5hbWU6 = base64("USERNAME") - C: base64(username) - S: 334 UGFzc3dvcmQ6 UGFzc3dvcmQ6 = base64("PASSWORD") - C: base64(password) - S: 235 Ok - */ - - WriteLine("AUTH LOGIN"); - - // Read server response. - string line = ReadLine(); - // Response line must start with 334 or otherwise it's error response. - if (!line.StartsWith("334")) - { - throw new SMTP_ClientException(line); - } - - // Send user name to server. - WriteLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(userName))); - - // Read server response. - line = ReadLine(); - // Response line must start with 334 or otherwise it's error response. - if (!line.StartsWith("334")) - { - throw new SMTP_ClientException(line); - } - - // Send password to server. - WriteLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(password))); - - // Read server response. - line = ReadLine(); - // Response line must start with 334 or otherwise it's error response. - if (!line.StartsWith("235")) - { - throw new SMTP_ClientException(line); - } - - m_pAuthdUserIdentity = new GenericIdentity(userName, "LOGIN"); - } - - #endregion - - #region AUTH CRAM-MD5 - - else if (authMethod == "CRAM-MD5") - { - /* CRAM-M5 - Description: - HMACMD5 key is "password". - - Example: - C: AUTH CRAM-MD5 - S: 334 base64(md5_calculation_hash) - C: base64(username password_hash) - S: 235 Ok - */ - - WriteLine("AUTH CRAM-MD5"); - - // Read server response. - string line = ReadLine(); - // Response line must start with 334 or otherwise it's error response. - if (!line.StartsWith("334")) - { - throw new SMTP_ClientException(line); - } - - HMACMD5 kMd5 = new HMACMD5(Encoding.ASCII.GetBytes(password)); - string passwordHash = - Core.ToHexString(kMd5.ComputeHash(Convert.FromBase64String(line.Split(' ')[1]))).ToLower(); - - // Send authentication info to server. - WriteLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(userName + " " + passwordHash))); - - // Read server response. - line = ReadLine(); - // Response line must start with 235 or otherwise it's error response - if (!line.StartsWith("235")) - { - throw new SMTP_ClientException(line); - } - - m_pAuthdUserIdentity = new GenericIdentity(userName, "CRAM-MD5"); - } - - #endregion - - #region AUTH DIGEST-MD5 - - else if (authMethod == "DIGEST-MD5") - { - /* - Example: - C: AUTH DIGEST-MD5 - S: 334 base64(digestChallange) - C: base64(digestAuthorization) - S: 334 base64(serverResponse) - C: - S: 235 Ok - */ - - WriteLine("AUTH DIGEST-MD5"); - - // Read server response. - string line = ReadLine(); - // Response line must start with 334 or otherwise it's error response. - if (!line.StartsWith("334")) - { - throw new SMTP_ClientException(line); - } - - Auth_HttpDigest digestmd5 = - new Auth_HttpDigest( - Encoding.Default.GetString(Convert.FromBase64String(line.Split(' ')[1])), - "AUTHENTICATE"); - digestmd5.CNonce = Auth_HttpDigest.CreateNonce(); - digestmd5.Uri = "smtp/" + digestmd5.Realm; - digestmd5.UserName = userName; - digestmd5.Password = password; - - // Send authentication info to server. - WriteLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(digestmd5.ToAuthorization(false)))); - - // Read server response. - line = ReadLine(); - // Response line must start with 334 or otherwise it's error response. - if (!line.StartsWith("334")) - { - throw new SMTP_ClientException(line); - } - - // Send empty line. - WriteLine(""); - - // Read server response. - line = ReadLine(); - // Response line must start with 235 or otherwise it's error response. - if (!line.StartsWith("235")) - { - throw new SMTP_ClientException(line); - } - - m_pAuthdUserIdentity = new GenericIdentity(userName, "DIGEST-MD5"); - } - - #endregion - } - - /// - /// Starts sending MAIL FROM: command to SMTP server. - /// - /// Sender email address reported to SMTP server. - /// Sendable message size in bytes, -1 if message size unknown. - /// Callback to call when the asynchronous operation is complete. - /// User data. - /// An IAsyncResult that references the asynchronous disconnect. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when SMTP client is not connected. - public IAsyncResult BeginMailFrom(string from, long messageSize, AsyncCallback callback, object state) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - - MailFromDelegate asyncMethod = MailFrom; - AsyncResultState asyncState = new AsyncResultState(this, asyncMethod, callback, state); - asyncState.SetAsyncResult(asyncMethod.BeginInvoke(from, - messageSize, - asyncState.CompletedCallback, - null)); - - return asyncState; - } - - /// - /// Ends a pending asynchronous MailFrom request. - /// - /// An IAsyncResult that stores state information and any user defined data for this asynchronous operation. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when asyncResult is null. - /// Is raised when invalid asyncResult passed to this method. - /// Is raised when SMTP server returns error. - public void EndMailFrom(IAsyncResult asyncResult) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (asyncResult == null) - { - throw new ArgumentNullException("asyncResult"); - } - - AsyncResultState castedAsyncResult = asyncResult as AsyncResultState; - if (castedAsyncResult == null || castedAsyncResult.AsyncObject != this) - { - throw new ArgumentException( - "Argument asyncResult was not returned by a call to the BeginReset method."); - } - if (castedAsyncResult.IsEndCalled) - { - throw new InvalidOperationException( - "BeginReset was previously called for the asynchronous connection."); - } - - castedAsyncResult.IsEndCalled = true; - if (castedAsyncResult.AsyncDelegate is MailFromDelegate) - { - ((MailFromDelegate) castedAsyncResult.AsyncDelegate).EndInvoke(castedAsyncResult.AsyncResult); - } - else - { - throw new ArgumentException( - "Argument asyncResult was not returned by a call to the BeginReset method."); - } - } - - /// - /// Sends MAIL FROM: command to SMTP server. - /// - /// Sender email address reported to SMTP server. - /// Sendable message size in bytes, -1 if message size unknown. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when SMTP client is not connected. - /// Is raised when any of the arguments has invalid value. - /// Is raised when SMTP server returns error. - public void MailFrom(string from, long messageSize) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!SMTP_Utils.IsValidAddress(from)) - { - throw new ArgumentException("Argument from has invalid value."); - } - - /* RFC 2821 4.1.1.2 MAIL - Examples: - MAIL FROM: - - RFC 1870 adds optional SIZE keyword support. - SIZE keyword may only be used if it's reported in EHLO command response. - Examples: - MAIL FROM: SIZE=1000 - */ - - bool isSizeSupported = false; - foreach (string feature in m_pEsmtpFeatures) - { - if (feature.ToLower() == "size") - { - isSizeSupported = true; - break; - } - } - - string line = "MAIL FROM:<" + from + ">"; - if (isSizeSupported && messageSize > 0) - { - line = "MAIL FROM:<" + from + "> SIZE=" + messageSize; - } - WriteLine(line); - - // Read first line of reply, check if it's ok. - line = ReadLine(); - if (!line.StartsWith("250")) - { - throw new SMTP_ClientException(line); - } - - m_MailFrom = from; - } - - /// - /// Starts sending RCPT TO: command to SMTP server. - /// - /// Recipient email address. - /// Callback to call when the asynchronous operation is complete. - /// User data. - /// An IAsyncResult that references the asynchronous disconnect. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when SMTP client is not connected. - public IAsyncResult BeginRcptTo(string to, AsyncCallback callback, object state) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - - RcptToDelegate asyncMethod = RcptTo; - AsyncResultState asyncState = new AsyncResultState(this, asyncMethod, callback, state); - asyncState.SetAsyncResult(asyncMethod.BeginInvoke(to, asyncState.CompletedCallback, null)); - - return asyncState; - } - - /// - /// Ends a pending asynchronous RcptTo request. - /// - /// An IAsyncResult that stores state information and any user defined data for this asynchronous operation. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when asyncResult is null. - /// Is raised when invalid asyncResult passed to this method. - /// Is raised when SMTP server returns error. - public void EndRcptTo(IAsyncResult asyncResult) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (asyncResult == null) - { - throw new ArgumentNullException("asyncResult"); - } - - AsyncResultState castedAsyncResult = asyncResult as AsyncResultState; - if (castedAsyncResult == null || castedAsyncResult.AsyncObject != this) - { - throw new ArgumentException( - "Argument asyncResult was not returned by a call to the BeginReset method."); - } - if (castedAsyncResult.IsEndCalled) - { - throw new InvalidOperationException( - "BeginReset was previously called for the asynchronous connection."); - } - - castedAsyncResult.IsEndCalled = true; - if (castedAsyncResult.AsyncDelegate is RcptToDelegate) - { - ((RcptToDelegate) castedAsyncResult.AsyncDelegate).EndInvoke(castedAsyncResult.AsyncResult); - } - else - { - throw new ArgumentException( - "Argument asyncResult was not returned by a call to the BeginReset method."); - } - } - - /// - /// Sends RCPT TO: command to SMTP server. - /// - /// Recipient email address. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when SMTP client is not connected. - /// Is raised when any of the arguments has invalid value. - /// Is raised when SMTP server returns error. - public void RcptTo(string to) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (!SMTP_Utils.IsValidAddress(to)) - { - throw new ArgumentException("Argument from has invalid value."); - } - - /* RFC 2821 4.1.1.2 RCPT. - Syntax: - "RCPT TO:" ("" / "" / Forward-Path) [SP Rcpt-parameters] CRLF - - Examples: - RCPT TO: - */ - - WriteLine("RCPT TO:<" + to + ">"); - - // Read first line of reply, check if it's ok. - string line = ReadLine(); - if (!line.StartsWith("250")) - { - throw new SMTP_ClientException(line); - } - - if (!m_pRecipients.Contains(to)) - { - m_pRecipients.Add(to); - } - } - - /// - /// Starts resetting SMTP session, all state data will be deleted. - /// - /// Callback to call when the asynchronous operation is complete. - /// User data. - /// An IAsyncResult that references the asynchronous disconnect. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when SMTP client is not connected. - public IAsyncResult BeginReset(AsyncCallback callback, object state) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - - ResetDelegate asyncMethod = Reset; - AsyncResultState asyncState = new AsyncResultState(this, asyncMethod, callback, state); - asyncState.SetAsyncResult(asyncMethod.BeginInvoke(asyncState.CompletedCallback, null)); - - return asyncState; - } - - /// - /// Ends a pending asynchronous reset request. - /// - /// An IAsyncResult that stores state information and any user defined data for this asynchronous operation. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when asyncResult is null. - /// Is raised when invalid asyncResult passed to this method. - /// Is raised when SMTP server returns error. - public void EndReset(IAsyncResult asyncResult) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (asyncResult == null) - { - throw new ArgumentNullException("asyncResult"); - } - - AsyncResultState castedAsyncResult = asyncResult as AsyncResultState; - if (castedAsyncResult == null || castedAsyncResult.AsyncObject != this) - { - throw new ArgumentException( - "Argument asyncResult was not returned by a call to the BeginReset method."); - } - if (castedAsyncResult.IsEndCalled) - { - throw new InvalidOperationException( - "BeginReset was previously called for the asynchronous connection."); - } - - castedAsyncResult.IsEndCalled = true; - if (castedAsyncResult.AsyncDelegate is ResetDelegate) - { - ((ResetDelegate) castedAsyncResult.AsyncDelegate).EndInvoke(castedAsyncResult.AsyncResult); - } - else - { - throw new ArgumentException( - "Argument asyncResult was not returned by a call to the BeginReset method."); - } - } - - /// - /// Resets SMTP session, all state data will be deleted. - /// - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when SMTP client is not connected. - /// Is raised when SMTP server returns error. - public void Reset() - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - - /* RFC 2821 4.1.1.5 RESET (RSET). - This command specifies that the current mail transaction will be - aborted. Any stored sender, recipients, and mail data MUST be - discarded, and all buffers and state tables cleared. The receiver - MUST send a "250 OK" reply to a RSET command with no arguments. A - reset command may be issued by the client at any time. - */ - - WriteLine("RSET"); - - string line = ReadLine(); - if (!line.StartsWith("250")) - { - throw new SMTP_ClientException(line); - } - - m_MailFrom = null; - m_pRecipients.Clear(); - } - - /// - /// Starts sending specified raw message to SMTP server. - /// - /// Message stream. Message will be readed from current stream position and to the end of stream. - /// Callback to call when the asynchronous operation is complete. - /// User data. - /// An IAsyncResult that references the asynchronous method. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when SMTP client is not connected. - /// Is raised when message is null. - public IAsyncResult BeginSendMessage(Stream message, AsyncCallback callback, object state) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (message == null) - { - throw new ArgumentNullException("message"); - } - - SendMessageDelegate asyncMethod = SendMessage; - AsyncResultState asyncState = new AsyncResultState(this, asyncMethod, callback, state); - asyncState.SetAsyncResult(asyncMethod.BeginInvoke(message, asyncState.CompletedCallback, null)); - - return asyncState; - } - - /// - /// Ends a pending asynchronous SendMessage request. - /// - /// An IAsyncResult that stores state information and any user defined data for this asynchronous operation. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when asyncResult is null. - /// Is raised when invalid asyncResult passed to this method. - /// Is raised when SMTP server returns error. - public void EndSendMessage(IAsyncResult asyncResult) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (asyncResult == null) - { - throw new ArgumentNullException("asyncResult"); - } - - AsyncResultState castedAsyncResult = asyncResult as AsyncResultState; - if (castedAsyncResult == null || castedAsyncResult.AsyncObject != this) - { - throw new ArgumentException( - "Argument asyncResult was not returned by a call to the BeginSendMessage method."); - } - if (castedAsyncResult.IsEndCalled) - { - throw new InvalidOperationException( - "BeginSendMessage was previously called for the asynchronous connection."); - } - - castedAsyncResult.IsEndCalled = true; - if (castedAsyncResult.AsyncDelegate is SendMessageDelegate) - { - ((SendMessageDelegate) castedAsyncResult.AsyncDelegate).EndInvoke( - castedAsyncResult.AsyncResult); - } - else - { - throw new ArgumentException( - "Argument asyncResult was not returned by a call to the BeginSendMessage method."); - } - } - - /// - /// Sends specified raw message to SMTP server. - /// - /// Message stream. Message will be readed from current stream position and to the end of stream. - /// The stream must contain data in MIME format, other formats normally are rejected by SMTP server. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when SMTP client is not connected. - /// Is raised when message is null. - /// Is raised when SMTP server returns error. - public void SendMessage(Stream message) - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - if (message == null) - { - throw new ArgumentNullException("message"); - } - - // See if BDAT supported. - bool bdatSupported = false; - foreach (string feature in EsmtpFeatures) - { - if (feature.ToUpper() == SMTP_ServiceExtensions.CHUNKING) - { - bdatSupported = true; - break; - } - } - - #region DATA - - if (!bdatSupported) - { - /* RFC 2821 4.1.1.4 DATA - Notes: - Message must be period handled for DATA command. This meas if message line starts with ., - additional .(period) must be added. - Message send is ended with .. - - Examples: - C: DATA - S: 354 Start sending message, end with . - C: send_message - C: . - S: 250 Ok - */ - - WriteLine("DATA"); - - string line = ReadLine(); - if (line.StartsWith("354")) - { - long writtenCount = TcpStream.WritePeriodTerminated(message); - LogAddWrite(writtenCount, "Wrote " + writtenCount + " bytes."); - - // Read server reply. - line = ReadLine(); - if (!line.StartsWith("250")) - { - throw new SMTP_ClientException(line); - } - } - else - { - throw new SMTP_ClientException(line); - } - } - - #endregion - - #region BDAT - - else - { - /* RFC 3030 BDAT - Syntax: - BDATChunkSize[LAST] - - Exapmle: - C: BDAT 1000 LAST - C: send_1000_byte_message - S: 250 OK - */ - - // TODO: Get rid of BDAT 0 LAST, this is valid syntax but many servers can't handle it. - // We just read 1 buffer ahead, then you see when source stream has EOS. - byte[] buffer1 = new byte[16000]; - byte[] buffer2 = new byte[16000]; - byte[] currentBuffer = buffer1; - byte[] lastBuffer = buffer2; - int lastReadedCount = 0; - - // Buffer first data block. - lastReadedCount = message.Read(lastBuffer, 0, lastBuffer.Length); - - while (true) - { - // Read data block to free buffer. - int readedCount = message.Read(currentBuffer, 0, currentBuffer.Length); - - // End of stream reached, "last data block" is last one. - if (readedCount == 0) - { - WriteLine("BDAT " + lastReadedCount + " LAST"); - TcpStream.Write(lastBuffer, 0, lastReadedCount); - LogAddWrite(readedCount, "Wrote " + lastReadedCount + " bytes."); - - // Read server response. - string line = ReadLine(); - if (!line.StartsWith("250")) - { - throw new SMTP_ClientException(line); - } - - // We are done, exit while. - break; - } - // Send last data block, free it up for reuse. - else - { - WriteLine("BDAT " + lastReadedCount); - TcpStream.Write(lastBuffer, 0, lastReadedCount); - LogAddWrite(readedCount, "Wrote " + lastReadedCount + " bytes."); - - // Read server response. - string line = ReadLine(); - if (!line.StartsWith("250")) - { - throw new SMTP_ClientException(line); - } - - // Mark last buffer as current(free it up), just mark current buffer as last for next while cycle. - byte[] tmp = lastBuffer; - lastBuffer = currentBuffer; - currentBuffer = tmp; - } - - lastReadedCount = readedCount; - } - } - - #endregion - } - - #endregion - - #region Overrides - - /// - /// This method is called after TCP client has sucessfully connected. - /// - protected override void OnConnected() - { - /* - Notes: Greeting may be single or multiline response. - - Examples: - 220SMTP server ready - - 220-SMTP server ready - 220-Addtitional text - 220final row - */ - - StringBuilder response = new StringBuilder(); - string line = ReadLine(); - response.AppendLine(line); - // Read multiline response. - while (line.Length >= 4 && line[3] == '-') - { - line = ReadLine(); - response.AppendLine(line); - } - - if (line.StartsWith("220")) - { - m_GreetingText = response.ToString(); - } - else - { - throw new SMTP_ClientException(response.ToString()); - } - - #region EHLO/HELO - - // If local host name not specified, get local computer name. - string localHostName = m_LocalHostName; - if (string.IsNullOrEmpty(localHostName)) - { - localHostName = Dns.GetHostName(); - } - - // Do EHLO/HELO. - WriteLine("EHLO " + localHostName); - - // Read server response. - line = ReadLine(); - if (line.StartsWith("250")) - { - m_IsEsmtpSupported = true; - - /* RFC 2821 4.1.1.1 EHLO - Examples: - C: EHLO domain - S: 250-domain freeText - S: 250-EHLO_keyword - S: 250 EHLO_keyword - - 250 specifies that last EHLO response line. - */ - - // We may have 250- or 250 SP as domain separator. - // 250- - if (line.StartsWith("250-")) - { - m_RemoteHostName = line.Substring(4).Split(new[] {' '}, 2)[0]; - } - // 250 SP - else - { - m_RemoteHostName = line.Split(new[] {' '}, 3)[1]; - } - - m_pEsmtpFeatures = new List(); - // Read multiline response, EHLO keywords. - while (line.StartsWith("250-")) - { - line = ReadLine(); - - if (line.StartsWith("250-")) - { - m_pEsmtpFeatures.Add(line.Substring(4)); - } - } - } - // Probably EHLO not supported, try HELO. - else - { - m_IsEsmtpSupported = false; - m_pEsmtpFeatures = new List(); - - WriteLine("HELO " + localHostName); - - line = ReadLine(); - if (line.StartsWith("250")) - { - /* Rfc 2821 4.1.1.1 EHLO/HELO - Syntax: "HELO" SP Domain CRLF - - Examples: - C: HELO domain - S: 250 domain freeText - */ - - m_RemoteHostName = line.Split(new[] {' '}, 3)[1]; - } - // Server rejects us for some reason. - else - { - throw new SMTP_ClientException(line); - } - } - - #endregion - - m_pRecipients = new List(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Client/SMTP_ClientException.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Client/SMTP_ClientException.cs deleted file mode 100644 index d531093d6..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Client/SMTP_ClientException.cs +++ /dev/null @@ -1,104 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SMTP.Client -{ - #region usings - - using System; - - #endregion - - /// - /// SMTP client exception. - /// - public class SMTP_ClientException : Exception - { - #region Members - - private readonly string m_ResponseText = ""; - private readonly int m_StatusCode = 500; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// SMTP server response line. - /// Is raised when responseLine is null. - public SMTP_ClientException(string responseLine) : base(responseLine) - { - if (responseLine == null) - { - throw new ArgumentNullException("responseLine"); - } - - string[] code_text = responseLine.Split(new[] {' ', '-'}, 2); - try - { - m_StatusCode = Convert.ToInt32(code_text[0]); - } - catch {} - if (code_text.Length == 2) - { - m_ResponseText = code_text[1]; - } - } - - #endregion - - #region Properties - - /// - /// Gets if it is permanent SMTP(5xx) error. - /// - public bool IsPermanentError - { - get - { - if (m_StatusCode >= 500 && m_StatusCode <= 599) - { - return true; - } - else - { - return false; - } - } - } - - /// - /// Gets SMTP server response text after status code. - /// - public string ResponseText - { - get { return m_ResponseText; } - } - - /// - /// Gets SMTP status code. - /// - public int StatusCode - { - get { return m_StatusCode; } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Relay/Relay_Mode.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Relay/Relay_Mode.cs deleted file mode 100644 index 41c558c7f..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Relay/Relay_Mode.cs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SMTP.Relay -{ - /// - /// Specifies relay mode. - /// - public enum Relay_Mode - { - /// - /// Dns is used to resolve email message target. - /// - Dns = 0, - - /// - /// All messages sent to the specified host. - /// - SmartHost = 1, - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Relay/Relay_Queue.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Relay/Relay_Queue.cs deleted file mode 100644 index 5ed053f20..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Relay/Relay_Queue.cs +++ /dev/null @@ -1,144 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SMTP.Relay -{ - #region usings - - using System; - using System.Collections.Generic; - using System.IO; - - #endregion - - /// - /// This class implements SMTP relay queue. - /// - public class Relay_Queue : IDisposable - { - #region Members - - private readonly string m_Name = ""; - private readonly Queue m_pQueue; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Relay queue name. - /// Is raised when name is null. - /// Is raised when any of the arguments has invalid value. - public Relay_Queue(string name) - { - if (name == null) - { - throw new ArgumentNullException("name"); - } - if (name == "") - { - throw new ArgumentException("Argument 'name' value may not be empty."); - } - - m_Name = name; - m_pQueue = new Queue(); - } - - #endregion - - #region Properties - - /// - /// Gets number of queued items in queue. - /// - public int Count - { - get { return m_pQueue.Count; } - } - - /// - /// Gets queue name. - /// - public string Name - { - get { return m_Name; } - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public void Dispose() {} - - /// - /// Queues message for relay. - /// - /// Sender address. - /// Target recipient address. - /// Message ID. - /// Raw mime message. Message reading starts from current position. - /// User data. - /// Is raised when to,to,messageID or message is null. - /// Is raised when any of the arguments has invalid value. - public void QueueMessage(string from, string to, string messageID, Stream message, object tag) - { - if (messageID == null) - { - throw new ArgumentNullException("messageID"); - } - if (messageID == "") - { - throw new ArgumentException("Argument 'messageID' value must be specified."); - } - if (message == null) - { - throw new ArgumentNullException("message"); - } - - lock (m_pQueue) - { - m_pQueue.Enqueue(new Relay_QueueItem(this, from, to, messageID, message, tag)); - } - } - - /// - /// Dequeues message from queue. If there are no messages, this method returns null. - /// - /// Returns queued relay message or null if no messages. - public Relay_QueueItem DequeueMessage() - { - lock (m_pQueue) - { - if (m_pQueue.Count > 0) - { - return m_pQueue.Dequeue(); - } - else - { - return null; - } - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Relay/Relay_QueueItem.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Relay/Relay_QueueItem.cs deleted file mode 100644 index d3626dbf4..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Relay/Relay_QueueItem.cs +++ /dev/null @@ -1,118 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SMTP.Relay -{ - #region usings - - using System.IO; - - #endregion - - /// - /// Thsi class holds Relay_Queue queued item. - /// - public class Relay_QueueItem - { - #region Members - - private readonly string m_From = ""; - private readonly string m_MessageID = ""; - private readonly Stream m_pMessageStream; - private readonly Relay_Queue m_pQueue; - private readonly string m_To = ""; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Item owner queue. - /// Sender address. - /// Target recipient address. - /// Message ID. - /// Raw mime message. Message reading starts from current position. - /// User data. - internal Relay_QueueItem(Relay_Queue queue, - string from, - string to, - string messageID, - Stream message, - object tag) - { - m_pQueue = queue; - m_From = from; - m_To = to; - m_MessageID = messageID; - m_pMessageStream = message; - Tag = tag; - } - - #endregion - - #region Properties - - /// - /// Gets from address. - /// - public string From - { - get { return m_From; } - } - - /// - /// Gets message ID which is being relayed now. - /// - public string MessageID - { - get { return m_MessageID; } - } - - /// - /// Gets raw mime message which must be relayed. - /// - public Stream MessageStream - { - get { return m_pMessageStream; } - } - - /// - /// Gets this relay item owner queue. - /// - public Relay_Queue Queue - { - get { return m_pQueue; } - } - - /// - /// Gets or sets user data. - /// - public object Tag { get; set; } - - /// - /// Gets target recipient. - /// - public string To - { - get { return m_To; } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Relay/Relay_Server.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Relay/Relay_Server.cs deleted file mode 100644 index 67f44b3cf..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Relay/Relay_Server.cs +++ /dev/null @@ -1,795 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SMTP.Relay -{ - #region usings - - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.Net; - using System.Net.Sockets; - using System.Threading; - using Log; - using TCP; - - #endregion - - #region Delegates - - /// - /// Represents the method that will handle the Relay_Server.SessionCompleted event. - /// - /// Event data. - public delegate void Relay_SessionCompletedEventHandler(Relay_SessionCompletedEventArgs e); - - #endregion - - /// - /// This class implements SMTP relay server. Defined in RFC 2821. - /// - public class Relay_Server : IDisposable - { - #region Events - - /// - /// This event is raised when unhandled exception happens. - /// - public event ErrorEventHandler Error = null; - - /// - /// This event is raised when relay session processing completes. - /// - public event Relay_SessionCompletedEventHandler SessionCompleted = null; - - #endregion - - #region Members - - private bool m_HasBindingsChanged; - - private bool m_IsDisposed; - private bool m_IsRunning; - private long m_MaxConnections; - private long m_MaxConnectionsPerIP; - private IPBindInfo[] m_pBindings = new IPBindInfo[0]; - private Dictionary m_pConnectionsPerIP; - private CircleCollection m_pLocalEndPoints; - private List m_pQueues; - private TCP_SessionCollection m_pSessions; - private CircleCollection m_pSmartHosts; - private Relay_Mode m_RelayMode = Relay_Mode.Dns; - private int m_SessionIdleTimeout = 30; - private BalanceMode m_SmartHostsBalanceMode = BalanceMode.LoadBalance; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public Relay_Server() - { - m_pQueues = new List(); - m_pSmartHosts = new CircleCollection(); - } - - #endregion - - #region Properties - - /// - /// Gets or sets relay server IP bindings. - /// - /// Is raised when this object is disposed and this property is accessed. - public IPBindInfo[] Bindings - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pBindings; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (value == null) - { - value = new IPBindInfo[0]; - } - - //--- See binds has changed -------------- - bool changed = false; - if (m_pBindings.Length != value.Length) - { - changed = true; - } - else - { - for (int i = 0; i < m_pBindings.Length; i++) - { - if (!m_pBindings[i].Equals(value[i])) - { - changed = true; - break; - } - } - } - - if (changed) - { - m_pBindings = value; - m_HasBindingsChanged = true; - } - } - } - - /// - /// Gets if server is disposed. - /// - public bool IsDisposed - { - get { return m_IsDisposed; } - } - - /// - /// Gets if server is running. - /// - public bool IsRunning - { - get { return m_IsRunning; } - } - - /// - /// Gets or sets relay logger. Value null means no logging. - /// - public Logger Logger { get; set; } - - /// - /// Gets or sets maximum allowed concurent connections. Value 0 means unlimited. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when negative value is passed. - public long MaxConnections - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_MaxConnections; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (value < 0) - { - throw new ArgumentException("Property 'MaxConnections' value must be >= 0."); - } - - m_MaxConnections = value; - } - } - - /// - /// Gets or sets maximum allowed connections to 1 IP address. Value 0 means unlimited. - /// - /// Is raised when this object is disposed and this property is accessed. - public long MaxConnectionsPerIP - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_MaxConnectionsPerIP; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (m_MaxConnectionsPerIP < 0) - { - throw new ArgumentException("Property 'MaxConnectionsPerIP' value must be >= 0."); - } - - m_MaxConnectionsPerIP = value; - } - } - - /// - /// Gets relay queues. Queue with lower index number has higher priority. - /// - /// Is raised when this object is disposed and this property is accessed. - public List Queues - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pQueues; - } - } - - /// - /// Gets or sets relay mode. - /// - /// Is raised when this object is disposed and this property is accessed. - public Relay_Mode RelayMode - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_RelayMode; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - m_RelayMode = value; - } - } - - /// - /// Gets or sets session idle time in seconds when it will be timed out. Value 0 means unlimited (strongly not recomended). - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when invalid value is passed. - public int SessionIdleTimeout - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_SessionIdleTimeout; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (m_SessionIdleTimeout < 0) - { - throw new ArgumentException("Property 'SessionIdleTimeout' value must be >= 0."); - } - - m_SessionIdleTimeout = value; - } - } - - /// - /// Gets active relay sessions. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when this property is accessed and relay server is not running. - public TCP_SessionCollection Sessions - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!m_IsRunning) - { - throw new InvalidOperationException("Relay server not running."); - } - - return m_pSessions; - } - } - - /// - /// Gets or sets smart hosts. Smart hosts must be in priority order, lower index number means higher priority. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when null value is passed. - public Relay_SmartHost[] SmartHosts - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pSmartHosts.ToArray(); - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (value == null) - { - throw new ArgumentNullException("SmartHosts"); - } - - m_pSmartHosts.Add(value); - } - } - - /// - /// Gets or sets how smart hosts will be balanced. - /// - /// Is raised when this object is disposed and this property is accessed. - public BalanceMode SmartHostsBalanceMode - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_SmartHostsBalanceMode; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - m_SmartHostsBalanceMode = value; - } - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public void Dispose() - { - if (m_IsDisposed) - { - return; - } - try - { - if (m_IsRunning) - { - Stop(); - } - } - catch {} - m_IsDisposed = true; - - // Release events. - Error = null; - SessionCompleted = null; - - m_pQueues = null; - m_pSmartHosts = null; - } - - /// - /// Starts SMTP relay server. - /// - /// Is raised when this object is disposed and this method is accessed. - public virtual void Start() - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (m_IsRunning) - { - return; - } - m_IsRunning = true; - - m_pLocalEndPoints = new CircleCollection(); - m_pSessions = new TCP_SessionCollection(); - m_pConnectionsPerIP = new Dictionary(); - - Thread tr1 = new Thread(Run); - tr1.Start(); - - Thread tr2 = new Thread(Run_CheckTimedOutSessions); - tr2.Start(); - } - - /// - /// Stops SMTP relay server. - /// - /// Is raised when this object is disposed and this method is accessed. - public virtual void Stop() - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!m_IsRunning) - { - return; - } - m_IsRunning = false; - - // TODO: We need to send notify to all not processed messages, then they can be Disposed as needed. - - // Clean up. - m_pLocalEndPoints = null; - //m_pSessions.Dispose(); - m_pSessions = null; - m_pConnectionsPerIP = null; - } - - #endregion - - #region Virtual methods - - /// - /// Raises SessionCompleted event. - /// - /// Session what completed processing. - /// Exception happened or null if relay completed successfully. - protected internal virtual void OnSessionCompleted(Relay_Session session, Exception exception) - { - if (SessionCompleted != null) - { - SessionCompleted(new Relay_SessionCompletedEventArgs(session, exception)); - } - } - - /// - /// Raises Error event. - /// - /// Exception happned. - protected internal virtual void OnError(Exception x) - { - if (Error != null) - { - Error(this, new Error_EventArgs(x, new StackTrace())); - } - } - - #endregion - - #region Internal methods - - /// - /// Increases specified IP address connactions count if maximum allowed connections to - /// the specified IP address isn't exceeded. - /// - /// IP address. - /// Is raised when ip is null. - /// Returns true if specified IP usage increased, false if maximum allowed connections to the specified IP address is exceeded. - internal bool TryAddIpUsage(IPAddress ip) - { - if (ip == null) - { - throw new ArgumentNullException("ip"); - } - - lock (m_pConnectionsPerIP) - { - long count = 0; - // Specified IP entry exists, increase usage. - if (m_pConnectionsPerIP.TryGetValue(ip, out count)) - { - // Maximum allowed connections to the specified IP address is exceeded. - if (m_MaxConnectionsPerIP > 0 && count >= m_MaxConnectionsPerIP) - { - return false; - } - - m_pConnectionsPerIP[ip] = count + 1; - } - // Specified IP entry doesn't exist, create new entry and increase usage. - else - { - m_pConnectionsPerIP.Add(ip, 1); - } - - return true; - } - } - - /// - /// Decreases specified IP address connactions count. - /// - /// IP address. - /// Is raised when ip is null. - internal void RemoveIpUsage(IPAddress ip) - { - if (ip == null) - { - throw new ArgumentNullException("ip"); - } - - lock (m_pConnectionsPerIP) - { - long count = 0; - // Specified IP entry exists, increase usage. - if (m_pConnectionsPerIP.TryGetValue(ip, out count)) - { - // This is last usage to that IP, remove that IP entry. - if (count == 1) - { - m_pConnectionsPerIP.Remove(ip); - } - // Decrease Ip usage. - else - { - m_pConnectionsPerIP[ip] = count - 1; - } - } - else - { - // No such entry, just skip it. - } - } - } - - /// - /// Gets how many connections to the specified IP address. - /// - /// IP address. - /// Returns number of connections to the specified IP address. - /// Is raised when ip is null. - internal long GetIpUsage(IPAddress ip) - { - if (ip == null) - { - throw new ArgumentNullException("ip"); - } - - lock (m_pConnectionsPerIP) - { - long count = 0; - // Specified IP entry exists, return usage. - if (m_pConnectionsPerIP.TryGetValue(ip, out count)) - { - return count; - } - // No usage to specified IP. - else - { - return 0; - } - } - } - - #endregion - - #region Utility methods - - /// - /// Processes relay queue. - /// - private void Run() - { - while (m_IsRunning) - { - try - { - // Bind info has changed, create new local end points. - if (m_HasBindingsChanged) - { - m_pLocalEndPoints.Clear(); - - foreach (IPBindInfo binding in m_pBindings) - { - if (binding.IP == IPAddress.Any) - { - foreach (IPAddress ip in Dns.GetHostAddresses("")) - { - if (ip.AddressFamily == AddressFamily.InterNetwork) - { - IPBindInfo b = new IPBindInfo(binding.HostName, - binding.Protocol, - ip, - 25); - if (!m_pLocalEndPoints.Contains(b)) - { - m_pLocalEndPoints.Add(b); - } - } - } - } - else if (binding.IP == IPAddress.IPv6Any) - { - foreach (IPAddress ip in Dns.GetHostAddresses("")) - { - if (ip.AddressFamily == AddressFamily.InterNetworkV6) - { - IPBindInfo b = new IPBindInfo(binding.HostName, - binding.Protocol, - ip, - 25); - if (!m_pLocalEndPoints.Contains(b)) - { - m_pLocalEndPoints.Add(b); - } - } - } - } - else - { - IPBindInfo b = new IPBindInfo(binding.HostName, - binding.Protocol, - binding.IP, - 25); - if (!m_pLocalEndPoints.Contains(b)) - { - m_pLocalEndPoints.Add(b); - } - } - } - - m_HasBindingsChanged = false; - } - - // There are no local end points specified. - if (m_pLocalEndPoints.Count == 0) - { - Thread.Sleep(10); - } - // Maximum allowed relay sessions exceeded, skip adding new ones. - else if (m_MaxConnections != 0 && m_pSessions.Count >= m_MaxConnections) - { - Thread.Sleep(10); - } - else - { - Relay_QueueItem item = null; - - // Get next queued message from highest possible priority queue. - foreach (Relay_Queue queue in m_pQueues) - { - item = queue.DequeueMessage(); - // There is queued message. - if (item != null) - { - break; - } - // No messages in this queue, see next lower priority queue. - } - - // There are no messages in any queue. - if (item == null) - { - Thread.Sleep(10); - } - // Create new session for queued relay item. - else - { - // Get round-robin local end point for that session. - // This ensures if multiple network connections, all will be load balanced. - IPBindInfo localBindInfo = m_pLocalEndPoints.Next(); - - if (m_RelayMode == Relay_Mode.Dns) - { - Relay_Session session = new Relay_Session(this, localBindInfo, item); - m_pSessions.Add(session); - ThreadPool.QueueUserWorkItem(session.Start); - } - else if (m_RelayMode == Relay_Mode.SmartHost) - { - // Get smart hosts in balance mode order. - Relay_SmartHost[] smartHosts = null; - if (m_SmartHostsBalanceMode == BalanceMode.FailOver) - { - smartHosts = m_pSmartHosts.ToArray(); - } - else - { - smartHosts = m_pSmartHosts.ToCurrentOrderArray(); - } - - Relay_Session session = new Relay_Session(this, - localBindInfo, - item, - smartHosts); - m_pSessions.Add(session); - ThreadPool.QueueUserWorkItem(session.Start); - } - } - } - } - catch (Exception x) - { - OnError(x); - } - } - } - - /// - /// This method checks timed out relay sessions while server is running. - /// - private void Run_CheckTimedOutSessions() - { - DateTime lastCheck = DateTime.Now; - while (IsRunning) - { - try - { - // Check interval reached. - if (m_SessionIdleTimeout > 0 && lastCheck.AddSeconds(30) < DateTime.Now) - { - foreach (Relay_Session session in Sessions.ToArray()) - { - try - { - if (session.LastActivity.AddSeconds(m_SessionIdleTimeout) < DateTime.Now) - { - session.Dispose(new Exception("Session idle timeout.")); - } - } - catch {} - } - lastCheck = DateTime.Now; - } - // Not check interval yet. - else - { - Thread.Sleep(1000); - } - } - catch (Exception x) - { - OnError(x); - } - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Relay/Relay_Session.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Relay/Relay_Session.cs deleted file mode 100644 index 3555d66a9..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Relay/Relay_Session.cs +++ /dev/null @@ -1,1004 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SMTP.Relay -{ - #region usings - - using System; - using System.Collections.Generic; - using System.IO; - using System.Net; - using System.Security.Principal; - using Client; - using Dns.Client; - using IO; - using Log; - using TCP; - - #endregion - - /// - /// This class implements SMTP relay server session. - /// - public class Relay_Session : TCP_Session - { - #region Members - - private readonly Relay_Mode m_RelayMode = Relay_Mode.Dns; - private readonly DateTime m_SessionCreateTime; - private readonly string m_SessionID = ""; - - private bool m_IsDisposed; - private Relay_Target m_pActiveTarget; - private IPBindInfo m_pLocalBindInfo; - private Relay_QueueItem m_pRelayItem; - private Relay_Server m_pServer; - private Relay_SmartHost[] m_pSmartHosts; - private SMTP_Client m_pSmtpClient; - private List m_pTargets; - - #endregion - - #region Constructor - - /// - /// Dns relay session constructor. - /// - /// Owner relay server. - /// Local bind info. - /// Relay item. - /// Is raised when server,localBindInfo or realyItem is null. - /// Is raised when any of the arguments has invalid value. - internal Relay_Session(Relay_Server server, IPBindInfo localBindInfo, Relay_QueueItem realyItem) - { - if (server == null) - { - throw new ArgumentNullException("server"); - } - if (localBindInfo == null) - { - throw new ArgumentNullException("localBindInfo"); - } - if (realyItem == null) - { - throw new ArgumentNullException("realyItem"); - } - - m_pServer = server; - m_pLocalBindInfo = localBindInfo; - m_pRelayItem = realyItem; - - m_SessionID = Guid.NewGuid().ToString(); - m_SessionCreateTime = DateTime.Now; - m_pTargets = new List(); - } - - /// - /// Smart host relay session constructor. - /// - /// Owner relay server. - /// Local bind info. - /// Relay item. - /// Smart hosts. - /// Is raised when server,localBindInfo,realyItem or smartHostsis null. - /// Is raised when any of the arguments has invalid value. - internal Relay_Session(Relay_Server server, - IPBindInfo localBindInfo, - Relay_QueueItem realyItem, - Relay_SmartHost[] smartHosts) - { - if (server == null) - { - throw new ArgumentNullException("server"); - } - if (localBindInfo == null) - { - throw new ArgumentNullException("localBindInfo"); - } - if (realyItem == null) - { - throw new ArgumentNullException("realyItem"); - } - if (smartHosts == null) - { - throw new ArgumentNullException("smartHosts"); - } - - m_pServer = server; - m_pLocalBindInfo = localBindInfo; - m_pRelayItem = realyItem; - m_pSmartHosts = smartHosts; - - m_RelayMode = Relay_Mode.SmartHost; - m_SessionID = Guid.NewGuid().ToString(); - m_SessionCreateTime = DateTime.Now; - m_pTargets = new List(); - } - - #endregion - - #region Properties - - /// - /// Gets session authenticated user identity, returns null if not authenticated. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when this property is accessed and relay session is not connected. - public override GenericIdentity AuthenticatedUserIdentity - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!m_pSmtpClient.IsConnected) - { - throw new InvalidOperationException("You must connect first."); - } - - return m_pSmtpClient.AuthenticatedUserIdentity; - } - } - - /// - /// Gets the time when session was connected. - /// - /// Is raised when this object is disposed and this property is accessed. - public override DateTime ConnectTime - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pSmtpClient.ConnectTime; - } - } - - /// - /// Gets how many seconds has left before timout is triggered. - /// - /// Is raised when this object is disposed and this property is accessed. - public int ExpectedTimeout - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return - (int) - (m_pServer.SessionIdleTimeout - - ((DateTime.Now.Ticks - TcpStream.LastActivity.Ticks)/10000)); - } - } - - /// - /// Gets from address. - /// - /// Is raised when this object is disposed and this property is accessed. - public string From - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pRelayItem.From; - } - } - - /// - /// Gets session ID. - /// - /// Is raised when this object is disposed and this property is accessed. - public override string ID - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_SessionID; - } - } - - /// - /// Gets if session is connected. - /// - /// Is raised when this object is disposed and this property is accessed. - public override bool IsConnected - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pSmtpClient.IsConnected; - } - } - - /// - /// Gets if this object is disposed. - /// - public bool IsDisposed - { - get { return m_IsDisposed; } - } - - /// - /// Gets the last time when data was sent or received. - /// - /// Is raised when this object is disposed and this property is accessed. - public override DateTime LastActivity - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pSmtpClient.LastActivity; - } - } - - /// - /// Gets session local IP end point. - /// - /// Is raised when this object is disposed and this property is accessed. - public override IPEndPoint LocalEndPoint - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pSmtpClient.LocalEndPoint; - } - } - - /// - /// Gets message ID which is being relayed now. - /// - /// Is raised when this object is disposed and this property is accessed. - public string MessageID - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pRelayItem.MessageID; - } - } - - /// - /// Gets message what is being relayed now. - /// - /// Is raised when this object is disposed and this property is accessed. - public Stream MessageStream - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pRelayItem.MessageStream; - } - } - - /// - /// Gets relay queue which session it is. - /// - /// Is raised when this object is disposed and this property is accessed. - public Relay_Queue Queue - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pRelayItem.Queue; - } - } - - /// - /// Gets user data what was procided to Relay_Queue.QueueMessage method. - /// - /// Is raised when this object is disposed and this property is accessed. - public object QueueTag - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pRelayItem.Tag; - } - } - - /// - /// Gets session remote IP end point. - /// - /// Is raised when this object is disposed and this property is accessed. - public override IPEndPoint RemoteEndPoint - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pSmtpClient.RemoteEndPoint; - } - } - - /// - /// Gets time when relay session created. - /// - /// Is raised when this object is disposed and this property is accessed. - public DateTime SessionCreateTime - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_SessionCreateTime; - } - } - - /// - /// Gets TCP stream which must be used to send/receive data through this session. - /// - /// Is raised when this object is disposed and this property is accessed. - public override SmartStream TcpStream - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pSmtpClient.TcpStream; - } - } - - /// - /// Gets target recipient. - /// - /// Is raised when this object is disposed and this property is accessed. - public string To - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pRelayItem.To; - } - } - - #endregion - - #region Methods - - /// - /// Completes relay session and does clean up. This method is thread-safe. - /// - public override void Dispose() - { - Dispose(new ObjectDisposedException(GetType().Name)); - } - - /// - /// Completes relay session and does clean up. This method is thread-safe. - /// - /// Exception happened or null if relay completed successfully. - public void Dispose(Exception exception) - { - try - { - lock (this) - { - if (m_IsDisposed) - { - return; - } - try - { - m_pServer.OnSessionCompleted(this, exception); - } - catch {} - m_pServer.Sessions.Remove(this); - m_IsDisposed = true; - - m_pLocalBindInfo = null; - m_pRelayItem = null; - m_pSmartHosts = null; - if (m_pSmtpClient != null) - { - m_pSmtpClient.Dispose(); - m_pSmtpClient = null; - } - m_pTargets = null; - if (m_pActiveTarget != null) - { - m_pServer.RemoveIpUsage(m_pActiveTarget.Target.Address); - m_pActiveTarget = null; - } - m_pServer = null; - } - } - catch (Exception x) - { - if (m_pServer != null) - { - m_pServer.OnError(x); - } - } - } - - /// - /// Closes relay connection. - /// - /// Is raised when this object is disposed and this method is accessed. - public override void Disconnect() - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - return; - } - - m_pSmtpClient.Disconnect(); - } - - /// - /// Closes relay connection. - /// - /// Text to send to the connected host. - /// Is raised when this object is disposed and this method is accessed. - public void Disconnect(string text) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - return; - } - - m_pSmtpClient.TcpStream.WriteLine(text); - Disconnect(); - } - - #endregion - - #region Internal methods - - /// - /// Start processing relay message. - /// - /// User data. - internal void Start(object state) - { - try - { - m_pSmtpClient = new SMTP_Client(); - m_pSmtpClient.LocalHostName = m_pLocalBindInfo.HostName; - if (m_pServer.Logger != null) - { - m_pSmtpClient.Logger = new Logger(); - m_pSmtpClient.Logger.WriteLog += SmtpClient_WriteLog; - } - - LogText("Starting to relay message '" + m_pRelayItem.MessageID + "' from '" + - m_pRelayItem.From + "' to '" + m_pRelayItem.To + "'."); - - // Get all possible target hosts for active recipient. - List targetHosts = new List(); - if (m_RelayMode == Relay_Mode.Dns) - { - foreach (string host in SMTP_Client.GetDomainHosts(m_pRelayItem.To)) - { - try - { - foreach (IPAddress ip in Dns_Client.Resolve(host)) - { - m_pTargets.Add(new Relay_Target(new IPEndPoint(ip, 25))); - } - } - catch - { - // Failed to resolve host name. - - LogText("Failed to resolve host '" + host + "' name."); - } - } - } - else if (m_RelayMode == Relay_Mode.SmartHost) - { - foreach (Relay_SmartHost smartHost in m_pSmartHosts) - { - try - { - m_pTargets.Add( - new Relay_Target( - new IPEndPoint(Dns_Client.Resolve(smartHost.Host)[0], smartHost.Port), - smartHost.SslMode, - smartHost.UserName, - smartHost.Password)); - } - catch - { - // Failed to resolve smart host name. - - LogText("Failed to resolve smart host '" + smartHost.Host + "' name."); - } - } - } - - BeginConnect(); - } - catch (Exception x) - { - Dispose(x); - } - } - - #endregion - - #region Utility methods - - /// - /// Thsi method is called when SMTP client has new log entry available. - /// - /// Sender. - /// Event data. - private void SmtpClient_WriteLog(object sender, WriteLogEventArgs e) - { - try - { - if (m_pServer.Logger == null) {} - else if (e.LogEntry.EntryType == LogEntryType.Read) - { - m_pServer.Logger.AddRead(m_SessionID, - e.LogEntry.UserIdentity, - e.LogEntry.Size, - e.LogEntry.Text, - e.LogEntry.LocalEndPoint, - e.LogEntry.RemoteEndPoint); - } - else if (e.LogEntry.EntryType == LogEntryType.Text) - { - m_pServer.Logger.AddText(m_SessionID, - e.LogEntry.UserIdentity, - e.LogEntry.Text, - e.LogEntry.LocalEndPoint, - e.LogEntry.RemoteEndPoint); - } - else if (e.LogEntry.EntryType == LogEntryType.Write) - { - m_pServer.Logger.AddWrite(m_SessionID, - e.LogEntry.UserIdentity, - e.LogEntry.Size, - e.LogEntry.Text, - e.LogEntry.LocalEndPoint, - e.LogEntry.RemoteEndPoint); - } - } - catch {} - } - - /// - /// Starts connecting to best target. - /// - private void BeginConnect() - { - // No tagets, abort relay. - if (m_pTargets.Count == 0) - { - LogText("No relay target(s) for '" + m_pRelayItem.To + "', aborting."); - Dispose(new Exception("No relay target(s) for '" + m_pRelayItem.To + "', aborting.")); - return; - } - - // If maximum connections to specified target exceeded and there are more targets, try to get limit free target. - if (m_pServer.MaxConnectionsPerIP > 0) - { - // For DNS or load-balnced smart host relay, search free target if any. - if (m_pServer.RelayMode == Relay_Mode.Dns || - m_pServer.SmartHostsBalanceMode == BalanceMode.LoadBalance) - { - foreach (Relay_Target t in m_pTargets) - { - // We found free target, stop searching. - if (m_pServer.TryAddIpUsage(m_pTargets[0].Target.Address)) - { - m_pActiveTarget = t; - m_pTargets.Remove(t); - break; - } - } - } - // Smart host fail-over mode, just check if it's free. - else - { - // Smart host IP limit not reached. - if (m_pServer.TryAddIpUsage(m_pTargets[0].Target.Address)) - { - m_pActiveTarget = m_pTargets[0]; - m_pTargets.RemoveAt(0); - } - } - } - // Just get first target. - else - { - m_pActiveTarget = m_pTargets[0]; - m_pTargets.RemoveAt(0); - } - - // If all targets has exeeded maximum allowed connection per IP address, end relay session, - // next relay cycle will try to relay again. - if (m_pActiveTarget == null) - { - LogText("All targets has exeeded maximum allowed connection per IP address, skip relay."); - Dispose( - new Exception( - "All targets has exeeded maximum allowed connection per IP address, skip relay.")); - return; - } - - m_pSmtpClient.BeginConnect(new IPEndPoint(m_pLocalBindInfo.IP, 0), - m_pActiveTarget.Target, - false, - ConnectCallback, - null); - } - - /// - /// This method is called when asynchronous Connect method completes. - /// - /// An IAsyncResult that stores state information and any user defined data for this asynchronous operation. - private void ConnectCallback(IAsyncResult ar) - { - try - { - m_pSmtpClient.EndConnect(ar); - - // Start TLS requested, start switching to SSL. - if (m_pActiveTarget.SslMode == SslMode.TLS) - { - m_pSmtpClient.BeginStartTLS(StartTlsCallback, null); - } - // Authentication requested, start authenticating. - else if (!string.IsNullOrEmpty(m_pActiveTarget.UserName)) - { - m_pSmtpClient.BeginAuthenticate(m_pActiveTarget.UserName, - m_pActiveTarget.Password, - AuthenticateCallback, - null); - } - else - { - long messageSize = -1; - try - { - messageSize = m_pRelayItem.MessageStream.Length - m_pRelayItem.MessageStream.Position; - } - catch - { - // Stream doesn't support seeking. - } - - m_pSmtpClient.BeginMailFrom(From, messageSize, MailFromCallback, null); - } - } - catch (Exception x) - { - try - { - // Release IP usage. - m_pServer.RemoveIpUsage(m_pActiveTarget.Target.Address); - m_pActiveTarget = null; - - // Connect failed, if there are more target IPs, try next one. - if (!IsDisposed && !IsConnected && m_pTargets.Count > 0) - { - BeginConnect(); - } - else - { - Dispose(x); - } - } - catch (Exception xx) - { - Dispose(xx); - } - } - } - - /// - /// This method is called when asynchronous StartTLS method completes. - /// - /// An IAsyncResult that stores state information and any user defined data for this asynchronous operation. - private void StartTlsCallback(IAsyncResult ar) - { - try - { - m_pSmtpClient.EndStartTLS(ar); - - // Authentication requested, start authenticating. - if (!string.IsNullOrEmpty(m_pActiveTarget.UserName)) - { - m_pSmtpClient.BeginAuthenticate(m_pActiveTarget.UserName, - m_pActiveTarget.Password, - AuthenticateCallback, - null); - } - else - { - long messageSize = -1; - try - { - messageSize = m_pRelayItem.MessageStream.Length - m_pRelayItem.MessageStream.Position; - } - catch - { - // Stream doesn't support seeking. - } - - m_pSmtpClient.BeginMailFrom(From, messageSize, MailFromCallback, null); - } - } - catch (Exception x) - { - Dispose(x); - } - } - - /// - /// This method is called when asynchronous Authenticate method completes. - /// - /// An IAsyncResult that stores state information and any user defined data for this asynchronous operation. - private void AuthenticateCallback(IAsyncResult ar) - { - try - { - m_pSmtpClient.EndAuthenticate(ar); - - long messageSize = -1; - try - { - messageSize = m_pRelayItem.MessageStream.Length - m_pRelayItem.MessageStream.Position; - } - catch - { - // Stream doesn't support seeking. - } - - m_pSmtpClient.BeginMailFrom(From, messageSize, MailFromCallback, null); - } - catch (Exception x) - { - Dispose(x); - } - } - - /// - /// This method is called when asynchronous MailFrom method completes. - /// - /// An IAsyncResult that stores state information and any user defined data for this asynchronous operation. - private void MailFromCallback(IAsyncResult ar) - { - try - { - m_pSmtpClient.EndMailFrom(ar); - - m_pSmtpClient.BeginRcptTo(To, RcptToCallback, null); - } - catch (Exception x) - { - Dispose(x); - } - } - - /// - /// This method is called when asynchronous RcptTo method completes. - /// - /// An IAsyncResult that stores state information and any user defined data for this asynchronous operation. - private void RcptToCallback(IAsyncResult ar) - { - try - { - m_pSmtpClient.EndRcptTo(ar); - - m_pSmtpClient.BeginSendMessage(m_pRelayItem.MessageStream, SendMessageCallback, null); - } - catch (Exception x) - { - Dispose(x); - } - } - - /// - /// This method is called when asynchronous SendMessage method completes. - /// - /// An IAsyncResult that stores state information and any user defined data for this asynchronous operation. - private void SendMessageCallback(IAsyncResult ar) - { - try - { - m_pSmtpClient.EndSendMessage(ar); - - // Message relayed successfully. - Dispose(null); - } - catch (Exception x) - { - Dispose(x); - } - } - - /// - /// Logs specified text if logging enabled. - /// - /// Text to log. - private void LogText(string text) - { - if (m_pServer.Logger != null) - { - GenericIdentity identity = null; - try - { - identity = AuthenticatedUserIdentity; - } - catch {} - IPEndPoint localEP = null; - IPEndPoint remoteEP = null; - try - { - localEP = m_pSmtpClient.LocalEndPoint; - remoteEP = m_pSmtpClient.RemoteEndPoint; - } - catch {} - m_pServer.Logger.AddText(m_SessionID, identity, text, localEP, remoteEP); - } - } - - #endregion - - #region Nested type: Relay_Target - - /// - /// This class holds relay target information. - /// - private class Relay_Target - { - #region Members - - private readonly string m_Password; - private readonly IPEndPoint m_pTarget; - private readonly SslMode m_SslMode = SslMode.None; - private readonly string m_UserName; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Target host IP end point. - public Relay_Target(IPEndPoint target) - { - m_pTarget = target; - } - - /// - /// Default constructor. - /// - /// Target host IP end point. - /// SSL mode. - /// Target host user name. - /// Target host password. - public Relay_Target(IPEndPoint target, SslMode sslMode, string userName, string password) - { - m_pTarget = target; - m_SslMode = sslMode; - m_UserName = userName; - m_Password = password; - } - - #endregion - - #region Properties - - /// - /// Gets target server password. - /// - public string Password - { - get { return m_Password; } - } - - /// - /// Gets target SSL mode. - /// - public SslMode SslMode - { - get { return m_SslMode; } - } - - /// - /// Gets specified target IP end point. - /// - public IPEndPoint Target - { - get { return m_pTarget; } - } - - /// - /// Gets target server user name. - /// - public string UserName - { - get { return m_UserName; } - } - - #endregion - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Relay/Relay_SessionCompletedEventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Relay/Relay_SessionCompletedEventArgs.cs deleted file mode 100644 index 51266bbcc..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Relay/Relay_SessionCompletedEventArgs.cs +++ /dev/null @@ -1,79 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SMTP.Relay -{ - #region usings - - using System; - - #endregion - - /// - /// This class provides data for Relay_Server.SessionCompleted event. - /// - public class Relay_SessionCompletedEventArgs - { - #region Members - - private readonly Exception m_pException; - private readonly Relay_Session m_pSession; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Relay session what completed processing. - /// Exception what happened or null if relay completed successfully. - /// Is raised when session is null. - public Relay_SessionCompletedEventArgs(Relay_Session session, Exception exception) - { - if (session == null) - { - throw new ArgumentNullException("session"); - } - - m_pSession = session; - m_pException = exception; - } - - #endregion - - #region Properties - - /// - /// Gets Exception what happened or null if relay completed successfully. - /// - public Exception Exception - { - get { return m_pException; } - } - - /// - /// Gets relay session what completed processing. - /// - public Relay_Session Session - { - get { return m_pSession; } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Relay/Relay_SmartHost.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Relay/Relay_SmartHost.cs deleted file mode 100644 index 6545c449c..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Relay/Relay_SmartHost.cs +++ /dev/null @@ -1,184 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SMTP.Relay -{ - #region usings - - using System; - - #endregion - - /// - /// This class holds smart host settings. - /// - public class Relay_SmartHost - { - #region Members - - private readonly string m_Host = ""; - private readonly string m_Password; - private readonly int m_Port = 25; - private readonly SslMode m_SslMode = SslMode.None; - private readonly string m_UserName; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Smart host name or IP address. - /// Smart host port. - /// Is raised when host is null. - /// Is raised when any of the arguments has invalid value. - public Relay_SmartHost(string host, int port) : this(host, port, SslMode.None, null, null) {} - - /// - /// Default constructor. - /// - /// Smart host name or IP address. - /// Smart host port. - /// Smart host SSL mode. - /// Smart host user name. - /// Smart host password. - /// Is raised when host is null. - /// Is raised when any of the arguments has invalid value. - public Relay_SmartHost(string host, int port, SslMode sslMode, string userName, string password) - { - if (host == null) - { - throw new ArgumentNullException("host"); - } - if (host == "") - { - throw new ArgumentException("Argument 'host' value must be specified."); - } - if (port < 1) - { - throw new ArgumentException("Argument 'port' value is invalid."); - } - - m_Host = host; - m_Port = port; - m_SslMode = sslMode; - m_UserName = userName; - m_Password = password; - } - - #endregion - - #region Properties - - /// - /// Gets smart host name or IP address. - /// - public string Host - { - get { return m_Host; } - } - - /// - /// Gets smart host password. - /// - public string Password - { - get { return m_Password; } - } - - /// - /// Gets smart host port. - /// - public int Port - { - get { return m_Port; } - } - - /// - /// Gets smart host SSL mode. - /// - public SslMode SslMode - { - get { return m_SslMode; } - } - - /// - /// Gets smart host user name. Value null means no authentication used. - /// - public string UserName - { - get { return m_UserName; } - } - - #endregion - - #region Methods - - /// - /// Compares the current instance with another object of the same type. - /// - /// An object to compare with this instance. - /// Returns true if two objects are equal. - public override bool Equals(object obj) - { - if (obj == null) - { - return false; - } - if (!(obj is Relay_SmartHost)) - { - return false; - } - - Relay_SmartHost smartHost = (Relay_SmartHost) obj; - if (m_Host != smartHost.Host) - { - return false; - } - else if (m_Port != smartHost.Port) - { - return false; - } - else if (m_SslMode != smartHost.SslMode) - { - return false; - } - else if (m_UserName != smartHost.UserName) - { - return false; - } - else if (m_Password != smartHost.Password) - { - return false; - } - - return true; - } - - /// - /// Returns the hash code. - /// - /// Returns the hash code. - public override int GetHashCode() - { - return base.GetHashCode(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/SMTP_Notify.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/SMTP_Notify.cs deleted file mode 100644 index c5121d59c..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/SMTP_Notify.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SMTP -{ - /// - /// This value implements SMTP Notify value. Defined in RFC 1891. - /// - public enum SMTP_Notify - { - /// - /// Notify value not specified. - /// - /// - /// For compatibility with SMTP clients that do not use the NOTIFY - /// facility, the absence of a NOTIFY parameter in a RCPT command may be - /// interpreted as either NOTIFY=FAILURE or NOTIFY=FAILURE,DELAY. - /// - NotSpecified = 0, - - /// - /// DSN should not be returned to the sender under any conditions. - /// - Never = 0xFF, - - /// - /// DSN should be sent on successful delivery. - /// - Success = 2, - - /// - /// DSN should be sent on delivery failure. - /// - Failure = 4, - - /// - /// This value indicates the sender's willingness to receive "delayed" DSNs. - /// - Delay = 8, - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/SMTP_ServiceExtensions.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/SMTP_ServiceExtensions.cs deleted file mode 100644 index cf405c983..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/SMTP_ServiceExtensions.cs +++ /dev/null @@ -1,144 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SMTP -{ - /// - /// This class holds known SMTP service extensions. Defined in http://www.iana.org/assignments/mail-parameters. - /// - public class SMTP_ServiceExtensions - { - #region Members - - /// - /// Use 8-bit data. Defined in RFC 1652. - /// - public static readonly string _8BITMIME = "8BITMIME"; - - /// - /// Authenticated TURN. Defined in RFC 2645. - /// - public static readonly string ATRN = "ATRN"; - - /// - /// Authentication. Defined in RFC 4954. - /// - public static readonly string AUTH = "AUTH"; - - /// - /// Binary MIME. Defined in RFC 3030. - /// - public static readonly string BINARYMIME = "BINARYMIME"; - - /// - /// Remote Content. Defined in RFC 4468. - /// - public static readonly string BURL = "BURL"; - - /// - /// Checkpoint/Restart. Defined in RFC 1845. - /// - public static readonly string CHECKPOINT = "CHECKPOINT"; - - /// - /// Chunking. Defined in RFC 3030. - /// - public static readonly string CHUNKING = "CHUNKING"; - - /// - /// Delivery Status Notification. Defined in RFC 1891. - /// - public static readonly string DSN = "DSN"; - - /// - /// Enhanced Status Codes. Defined in RFC 2034. - /// - public static readonly string ENHANCEDSTATUSCODES = "ENHANCEDSTATUSCODES"; - - /// - /// Extended Turn. Defined in RFC 1985. - /// - public static readonly string ETRN = "ETRN"; - - /// - /// Expand the mailing list. Defined in RFC 821, - /// - public static readonly string EXPN = "EXPN"; - - /// - /// Future Message Release. Defined in RFC 4865. - /// - public static readonly string FUTURERELEASE = "FUTURERELEASE"; - - /// - /// Supply helpful information. Defined in RFC 821. - /// - public static readonly string HELP = "HELP"; - - /// - /// Message Tracking. Defined in RFC 3885. - /// - public static readonly string MTRK = "MTRK"; - - /// - /// Notification of no soliciting. Defined in RFC 3865. - /// - public static readonly string NO_SOLICITING = "NO-SOLICITING"; - - /// - /// Command Pipelining. Defined in RFC 2920. - /// - public static readonly string PIPELINING = "PIPELINING"; - - /// - /// Send as mail and terminal. Defined in RFC 821. - /// - public static readonly string SAML = "SAML"; - - /// - /// Send as mail. Defined in RFC RFC 821. - /// - public static readonly string SEND = "SEND"; - - /// - /// Message size declaration. Defined in RFC 1870. - /// - public static readonly string SIZE = "SIZE"; - - /// - /// Send as mail or terminal. Defined in RFC 821. - /// - public static readonly string SOML = "SOML"; - - /// - /// Start TLS. Defined in RFC 3207. - /// - public static readonly string STARTTLS = "STARTTLS"; - - /// - /// SMTP Responsible Submitter. Defined in RFC 4405. - /// - public static readonly string SUBMITTER = "SUBMITTER"; - - /// - /// Turn the operation around. Defined in RFC 821. - /// - public static readonly string TURN = "TURN"; - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/SMTP_Utils.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/SMTP_Utils.cs deleted file mode 100644 index ed27173a4..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/SMTP_Utils.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SMTP -{ - /// - /// This class provedes SMTP related utility methods. - /// - public class SMTP_Utils - { - #region Methods - - /// - /// Gets if specified smtp address has valid syntax. - /// - /// SMTP address, eg. ivar@lumisoft.ee. - /// Returns ture if address is valid, otherwise false. - public static bool IsValidAddress(string address) - { - // TODO: - - return true; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/SMTP_t_Mailbox.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/SMTP_t_Mailbox.cs deleted file mode 100644 index a77abd83a..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/SMTP_t_Mailbox.cs +++ /dev/null @@ -1,149 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SMTP -{ - #region usings - - using System; - using MIME; - - #endregion - - /// - /// This class represents SMTP "Mailbox" address. Defined in RFC 5321 4.1.2. - /// - /// - /// - /// RFC 5321 4.1.2. - /// Mailbox = Local-part "@" ( Domain / address-literal ) - /// Local-part = Dot-string / Quoted-string - /// ; MAY be case-sensitive - /// - /// - public class SMTP_t_Mailbox - { - #region Members - - private readonly string m_Domain; - private readonly string m_LocalPart; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Local part of mailbox. - /// Domain of mailbox. - /// Is raised when localPart or domain is null reference. - public SMTP_t_Mailbox(string localPart, string domain) - { - if (localPart == null) - { - throw new ArgumentNullException("localPart"); - } - if (domain == null) - { - throw new ArgumentNullException("domain"); - } - - m_LocalPart = localPart; - m_Domain = domain; - } - - #endregion - - #region Properties - - /// - /// Gets domain of mailbox. - /// - /// If domain is address-literal, surrounding bracets will be removed. - public string Domain - { - get { return m_Domain; } - } - - /// - /// Gets local-part of mailbox. - /// - /// If local-part is Quoted-string, quotes will not returned. - public string LocalPart - { - get { return m_LocalPart; } - } - - #endregion - - /* - /// - /// Parses SMTP mailbox from the specified string. - /// - /// Mailbox string. - /// Returns parsed SMTP mailbox. - /// Is raised when value is null reference. - public static SMTP_t_Mailbox Parse(string value) - { - if(value == null){ - throw new ArgumentNullException("value"); - } - - return Parse(new ABNF_Reader(value)); - } - - /// - /// Parses SMTP mailbox from the specified reader. - /// - /// Source reader. - /// Returns parsed SMTP mailbox. - /// Is raised when reader is null reference. - public static SMTP_t_Mailbox Parse(ABNF_Reader reader) - { - if(reader == null){ - throw new ArgumentNullException("reader"); - } - - // TODO: - - return null; - } - */ - - #region Methods - - /// - /// Returns mailbox as string. - /// - /// Returns mailbox as string. - public override string ToString() - { - if (MIME_Reader.IsDotAtom(m_LocalPart)) - { - return m_LocalPart + "@" + (Net_Utils.IsIPAddress(m_Domain) ? "[" + m_Domain + "]" : m_Domain); - } - else - { - return TextUtils.QuoteString(m_LocalPart) + "@" + - (Net_Utils.IsIPAddress(m_Domain) ? "[" + m_Domain + "]" : m_Domain); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_MailFrom.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_MailFrom.cs deleted file mode 100644 index e8ead4720..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_MailFrom.cs +++ /dev/null @@ -1,118 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SMTP.Server -{ - #region usings - - using System; - - #endregion - - /// - /// This class holds MAIL FROM: command value. - /// - public class SMTP_MailFrom - { - #region Members - - private readonly string m_Body; - private readonly string m_ENVID; - private readonly string m_Mailbox = ""; - private readonly string m_RET; - private readonly int m_Size = -1; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Mailbox value. - /// SIZE parameter value. - /// BODY parameter value. - /// DSN RET parameter value. - /// DSN ENVID parameter value. - /// Is raised when mailbox is null reference. - public SMTP_MailFrom(string mailbox, int size, string body, string ret, string envid) - { - if (mailbox == null) - { - throw new ArgumentNullException("mailbox"); - } - - m_Mailbox = mailbox; - m_Size = size; - m_Body = body; - m_RET = ret; - m_ENVID = envid; - } - - #endregion - - #region Properties - - /// - /// Gets MAIL FROM: BODY parameter value. Value null means not specified. - /// Defined in RFC 1652. - /// - public string Body - { - get { return m_Body; } - } - - /// - /// Gets DSN ENVID parameter value. Value null means not specified. - /// Defined in RFC 1891. - /// - public string ENVID - { - get { return m_ENVID; } - } - - /// - /// Gets SMTP "mailbox" value. Actually this is just email address. - /// This value can be "" if "null reverse-path". - /// - public string Mailbox - { - get { return m_Mailbox; } - } - - /// - /// Gets DSN RET parameter value. Value null means not specified. - /// RET specifies whether message or headers should be included in any failed DSN issued for message transmission. - /// Defined in RFC 1891. - /// - public string RET - { - get { return m_RET; } - } - - /// - /// Gets MAIL FROM: SIZE parameter value. Value -1 means not specified. - /// Defined in RFC 1870. - /// - public int Size - { - get { return m_Size; } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_RcptTo.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_RcptTo.cs deleted file mode 100644 index c095423cc..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_RcptTo.cs +++ /dev/null @@ -1,94 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SMTP.Server -{ - #region usings - - using System; - - #endregion - - /// - /// This class holds RCPT TO: command value. - /// - public class SMTP_RcptTo - { - #region Members - - private readonly string m_Mailbox = ""; - private readonly SMTP_Notify m_Notify = SMTP_Notify.NotSpecified; - private readonly string m_ORCPT = ""; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Mailbox value. - /// DSN NOTIFY parameter value. - /// DSN ORCPT parameter value. - /// Is raised when mailbox is null reference. - public SMTP_RcptTo(string mailbox, SMTP_Notify notify, string orcpt) - { - if (mailbox == null) - { - throw new ArgumentNullException("mailbox"); - } - - m_Mailbox = mailbox; - m_Notify = notify; - m_ORCPT = orcpt; - } - - #endregion - - #region Properties - - /// - /// Gets SMTP "mailbox" value. Actually this is just email address. - /// - public string Mailbox - { - get { return m_Mailbox; } - } - - /// - /// Gets DSN NOTIFY parameter value. - /// This value specified when SMTP server should send delivery status notification. - /// Defined in RFC 1891. - /// - public SMTP_Notify Notify - { - get { return m_Notify; } - } - - /// - /// Gets DSN ORCPT parameter value. Value null means not specified. - /// This value specifies "original" recipient address where message is sent (has point only when message forwarded). - /// Defined in RFC 1891. - /// - public string ORCPT - { - get { return m_ORCPT; } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_Reply.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_Reply.cs deleted file mode 100644 index 31f7131e6..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_Reply.cs +++ /dev/null @@ -1,133 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SMTP.Server -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// This class implements SMTP server reply. - /// - public class SMTP_Reply - { - #region Members - - private readonly string[] m_pReplyLines; - private readonly int m_ReplyCode; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// SMTP server reply code. - /// SMTP server reply line. - /// Is raised when any of the arguments has invalid value. - /// Is raised when replyLine is null reference. - public SMTP_Reply(int replyCode, string replyLine) : this(replyCode, new[] {replyLine}) - { - if (replyLine == null) - { - throw new ArgumentNullException("replyLine"); - } - } - - /// - /// Default constructor. - /// - /// SMTP server reply code. - /// SMTP server reply line(s). - /// Is raised when any of the arguments has invalid value. - /// Is raised when replyLines is null reference. - public SMTP_Reply(int replyCode, string[] replyLines) - { - if (replyCode < 200 || replyCode > 599) - { - throw new ArgumentException("Argument 'replyCode' value must be >= 200 and <= 599.", - "replyCode"); - } - if (replyLines == null) - { - throw new ArgumentNullException("replyLines"); - } - if (replyLines.Length == 0) - { - throw new ArgumentException("Argument 'replyLines' must conatin at least one line.", - "replyLines"); - } - - m_ReplyCode = replyCode; - m_pReplyLines = replyLines; - } - - #endregion - - #region Properties - - /// - /// Gets SMTP server reply code. - /// - public int ReplyCode - { - get { return m_ReplyCode; } - } - - /// - /// Gets SMTP server reply lines. - /// - public string[] ReplyLines - { - get { return m_pReplyLines; } - } - - #endregion - - #region Methods - - /// - /// Returns SMTP server reply as string. - /// - /// Returns SMTP server reply as string. - public override string ToString() - { - StringBuilder retVal = new StringBuilder(); - for (int i = 0; i < m_pReplyLines.Length; i++) - { - // Last line. - if (i == (m_pReplyLines.Length - 1)) - { - retVal.Append(m_ReplyCode + " " + m_pReplyLines[i] + "\r\n"); - } - else - { - retVal.Append(m_ReplyCode + "-" + m_pReplyLines[i] + "\r\n"); - } - } - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_Server.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_Server.cs deleted file mode 100644 index e6b06e6e5..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_Server.cs +++ /dev/null @@ -1,326 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SMTP.Server -{ - #region usings - - using System; - using System.Collections.Generic; - using TCP; - - #endregion - - /// - /// This class implements SMTP server. Defined RFC 5321. - /// - public class SMTP_Server : TCP_Server - { - #region Members - - private readonly List m_pServiceExtentions; - private string m_GreetingText = ""; - private int m_MaxBadCommands = 30; - private int m_MaxMessageSize = 10000000; - private int m_MaxRecipients = 100; - private int m_MaxTransactions = 10; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public SMTP_Server() - { - m_pServiceExtentions = new List(); - m_pServiceExtentions.Add(SMTP_ServiceExtensions.PIPELINING); - m_pServiceExtentions.Add(SMTP_ServiceExtensions.SIZE); - m_pServiceExtentions.Add(SMTP_ServiceExtensions.STARTTLS); - m_pServiceExtentions.Add(SMTP_ServiceExtensions._8BITMIME); - m_pServiceExtentions.Add(SMTP_ServiceExtensions.BINARYMIME); - m_pServiceExtentions.Add(SMTP_ServiceExtensions.CHUNKING); - } - - #endregion - - #region Properties - - /// - /// Gets or sets server greeting text. - /// - /// Is raised when this object is disposed and this property is accessed. - public string GreetingText - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_GreetingText; - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - m_GreetingText = value; - } - } - - /// - /// Gets or sets how many bad commands session can have before it's terminated. Value 0 means unlimited. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when invalid value is passed. - public int MaxBadCommands - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_MaxBadCommands; - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (value < 0) - { - throw new ArgumentException("Property 'MaxBadCommands' value must be >= 0."); - } - - m_MaxBadCommands = value; - } - } - - /// - /// Gets or sets maximum message size in bytes. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when invalid value is passed. - public int MaxMessageSize - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_MaxMessageSize; - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (value < 500) - { - throw new ArgumentException("Property 'MaxMessageSize' value must be >= 500."); - } - - m_MaxMessageSize = value; - } - } - - /// - /// Gets or sets maximum allowed recipients per SMTP transaction. - /// - /// According RFC 5321 this value SHOULD NOT be less than 100. - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when invalid value is passed. - public int MaxRecipients - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_MaxRecipients; - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (value < 1) - { - throw new ArgumentException("Property 'MaxRecipients' value must be >= 1."); - } - - m_MaxRecipients = value; - } - } - - /// - /// Gets or sets maximum mail transactions per session. Value 0 means unlimited. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when invalid value is passed. - public int MaxTransactions - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_MaxTransactions; - } - - set - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (value < 0) - { - throw new ArgumentException("Property 'MaxTransactions' value must be >= 0."); - } - - m_MaxTransactions = value; - } - } - - /// - /// Gets or sets SMTP server supported service extentions. - /// Supported values: PIPELINING,SIZE,STARTTLS,8BITMIME,BINARYMIME,CHUNKING,DSN. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when null reference passed. - public string[] ServiceExtentions - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pServiceExtentions.ToArray(); - } - - set - { - if (value == null) - { - throw new ArgumentNullException("ServiceExtentions"); - } - - m_pServiceExtentions.Clear(); - - foreach (string extention in value) - { - if (extention.ToUpper() == SMTP_ServiceExtensions.PIPELINING) - { - m_pServiceExtentions.Add(SMTP_ServiceExtensions.PIPELINING); - } - else if (extention.ToUpper() == SMTP_ServiceExtensions.SIZE) - { - m_pServiceExtentions.Add(SMTP_ServiceExtensions.SIZE); - } - else if (extention.ToUpper() == SMTP_ServiceExtensions.STARTTLS) - { - m_pServiceExtentions.Add(SMTP_ServiceExtensions.STARTTLS); - } - else if (extention.ToUpper() == SMTP_ServiceExtensions._8BITMIME) - { - m_pServiceExtentions.Add(SMTP_ServiceExtensions._8BITMIME); - } - else if (extention.ToUpper() == SMTP_ServiceExtensions.BINARYMIME) - { - m_pServiceExtentions.Add(SMTP_ServiceExtensions.BINARYMIME); - } - else if (extention.ToUpper() == SMTP_ServiceExtensions.CHUNKING) - { - m_pServiceExtentions.Add(SMTP_ServiceExtensions.CHUNKING); - } - else if (extention.ToUpper() == SMTP_ServiceExtensions.DSN) - { - m_pServiceExtentions.Add(SMTP_ServiceExtensions.DSN); - } - } - } - } - - /// - /// Gets SMTP service extentions list. - /// - internal List Extentions - { - get { return m_pServiceExtentions; } - } - - #endregion - - // TODO: - - //public override Dispose - - #region Overrides - - /// - /// Is called when new incoming session and server maximum allowed connections exceeded. - /// - /// Incoming session. - /// This method allows inhereted classes to report error message to connected client. - /// Session will be disconnected after this method completes. - /// - protected override void OnMaxConnectionsExceeded(SMTP_Session session) - { - session.TcpStream.WriteLine( - "421 Client host rejected: too many connections, please try again later."); - } - - /// - /// Is called when new incoming session and server maximum allowed connections per connected IP exceeded. - /// - /// Incoming session. - /// This method allows inhereted classes to report error message to connected client. - /// Session will be disconnected after this method completes. - /// - protected override void OnMaxConnectionsPerIPExceeded(SMTP_Session session) - { - session.TcpStream.WriteLine("421 Client host rejected: too many connections from your IP(" + - session.RemoteEndPoint.Address + "), please try again later."); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_Session.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_Session.cs deleted file mode 100644 index 76968ec1a..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_Session.cs +++ /dev/null @@ -1,1968 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SMTP.Server -{ - #region usings - - using System; - using System.Collections.Generic; - using System.IO; - using System.Net.Sockets; - using System.Security.Principal; - using System.Text; - using AUTH; - using IO; - using Mail; - using TCP; - - #endregion - - /// - /// This class implements SMTP session. Defined RFC 5321. - /// - public class SMTP_Session : TCP_ServerSession - { - #region Events - - /// - /// Is raised when EHLO command received. - /// - public event EventHandler Ehlo = null; - - /// - /// Is raised when SMTP server needs to get stream where to store incoming message. - /// - public event EventHandler GetMessageStream = null; - - /// - /// Is raised when MAIL FROM: command received. - /// - public event EventHandler MailFrom = null; - - /// - /// Is raised when SMTP server has canceled message storing. - /// - /// This can happen on 2 cases: on session timeout and if between BDAT chunks RSET issued. - public event EventHandler MessageStoringCanceled = null; - - /// - /// Is raised when SMTP server has completed message storing. - /// - public event EventHandler MessageStoringCompleted = null; - - /// - /// Is raised when RCPT TO: command received. - /// - public event EventHandler RcptTo = null; - - /// - /// Is raised when session has started processing and needs to send 220 greeting or 554 error resposne to the connected client. - /// - public event EventHandler Started = null; - - #endregion - - #region Members - - private int m_BadCommands; - private int m_BDatReadedCount; - private string m_EhloHost; - private Dictionary m_pAuthentications; - private SMTP_MailFrom m_pFrom; - private Stream m_pMessageStream; - private Dictionary m_pTo; - private GenericIdentity m_pUser; - private bool m_SessionRejected; - private int m_Transactions; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public SMTP_Session() - { - m_pAuthentications = - new Dictionary(StringComparer.CurrentCultureIgnoreCase); - m_pTo = new Dictionary(); - } - - #endregion - - #region Properties - - /// - /// Gets authenticated user identity or null if user has not authenticated. - /// - /// Is raised when this object is disposed and this property is accessed. - public override GenericIdentity AuthenticatedUserIdentity - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pUser; - } - } - - /// - /// Gets supported authentications collection. - /// - /// Is raised when this object is disposed and this property is accessed. - public Dictionary Authentications - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pAuthentications; - } - } - - /// - /// Gets number of bad commands happened on SMTP session. - /// - /// Is raised when this object is disposed and this property is accessed. - public int BadCommands - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_BadCommands; - } - } - - /// - /// Gets client reported EHLO host name. Returns null if EHLO/HELO is not issued yet. - /// - /// Is raised when this object is disposed and this property is accessed. - public string EhloHost - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_EhloHost; - } - } - - /// - /// Gets MAIL FROM: value. Returns null if MAIL FROM: is not issued yet. - /// - /// Is raised when this object is disposed and this property is accessed. - public SMTP_MailFrom From - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pFrom; - } - } - - /// - /// Gets session owner SMTP server. - /// - /// Is raised when this object is disposed and this property is accessed. - public new SMTP_Server Server - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return (SMTP_Server) base.Server; - } - } - - /// - /// Gets RCPT TO: values. Returns null if RCPT TO: is not issued yet. - /// - /// Is raised when this object is disposed and this property is accessed. - public SMTP_RcptTo[] To - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - lock (m_pTo) - { - SMTP_RcptTo[] retVal = new SMTP_RcptTo[m_pTo.Count]; - m_pTo.Values.CopyTo(retVal, 0); - - return retVal; - } - } - } - - /// - /// Gets number of mail transactions processed by this SMTP session. - /// - /// Is raised when this object is disposed and this property is accessed. - public int Transactions - { - get - { - if (IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_Transactions; - } - } - - #endregion - - #region Methods - - /// - /// Cleans up any resource being used. - /// - public override void Dispose() - { - if (IsDisposed) - { - return; - } - - base.Dispose(); - - m_pAuthentications = null; - m_EhloHost = null; - m_pUser = null; - m_pFrom = null; - m_pTo = null; - m_pMessageStream = null; - } - - #endregion - - #region Overrides - - /// - /// Starts session processing. - /// - protected internal override void Start() - { - base.Start(); - - /* RFC 5321 3.1. - The SMTP protocol allows a server to formally reject a mail session - while still allowing the initial connection as follows: a 554 - response MAY be given in the initial connection opening message - instead of the 220. A server taking this approach MUST still wait - for the client to send a QUIT (see Section 4.1.1.10) before closing - the connection and SHOULD respond to any intervening commands with - "503 bad sequence of commands". Since an attempt to make an SMTP - connection to such a system is probably in error, a server returning - a 554 response on connection opening SHOULD provide enough - information in the reply text to facilitate debugging of the sending - system. - */ - - try - { - SMTP_Reply reply = null; - if (string.IsNullOrEmpty(Server.GreetingText)) - { - reply = new SMTP_Reply(220, - "<" + Net_Utils.GetLocalHostName(LocalHostName) + - "> Simple Mail Transfer Service Ready."); - } - else - { - reply = new SMTP_Reply(220, Server.GreetingText); - } - - reply = OnStarted(reply); - - WriteLine(reply.ToString()); - - // Setup rejected flag, so we respond "503 bad sequence of commands" any command except QUIT. - if (reply.ReplyCode >= 300) - { - m_SessionRejected = true; - } - - BeginReadCmd(); - } - catch (Exception x) - { - OnError(x); - } - } - - /// - /// Is called when session has processing error. - /// - /// Exception happened. - protected override void OnError(Exception x) - { - if (IsDisposed) - { - return; - } - if (x == null) - { - return; - } - - /* Error handling: - IO and Socket exceptions are permanent, so we must end session. - */ - - try - { - LogAddText("Exception: " + x.Message); - - // Permanent error. - if (x is IOException || x is SocketException) - { - Dispose(); - } - // xx error, may be temporary. - else - { - // Raise SMTP_Server.Error event. - base.OnError(x); - - // Try to send "500 Internal server error." - try - { - WriteLine("500 Internal server error."); - } - catch - { - // Error is permanent. - Dispose(); - } - } - } - catch {} - } - - /// - /// This method is called when specified session times out. - /// - /// - /// This method allows inhereted classes to report error message to connected client. - /// Session will be disconnected after this method completes. - /// - protected override void OnTimeout() - { - try - { - if (m_pMessageStream != null) - { - OnMessageStoringCanceled(); - } - - WriteLine("421 Idle timeout, closing connection."); - } - catch - { - // Skip errors. - } - } - - #endregion - - #region Utility methods - - /// - /// Starts reading incoming command from the connected client. - /// - private void BeginReadCmd() - { - if (IsDisposed) - { - return; - } - - try - { - SmartStream.ReadLineAsyncOP readLineOP = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength], - SizeExceededAction. - JunkAndThrowException); - // This event is raised only if read period-terminated opeartion completes asynchronously. - readLineOP.Completed += delegate - { - if (ProcessCmd(readLineOP)) - { - BeginReadCmd(); - } - }; - // Process incoming commands while, command reading completes synchronously. - while (TcpStream.ReadLine(readLineOP, true)) - { - if (!ProcessCmd(readLineOP)) - { - break; - } - } - } - catch (Exception x) - { - OnError(x); - } - } - - /// - /// Completes command reading operation. - /// - /// Operation. - /// Returns true if server should start reading next command. - private bool ProcessCmd(SmartStream.ReadLineAsyncOP op) - { - bool readNextCommand = true; - - // Check errors. - if (op.Error != null) - { - OnError(op.Error); - } - - try - { - if (IsDisposed) - { - return false; - } - - // Log. - if (Server.Logger != null) - { - Server.Logger.AddRead(ID, - AuthenticatedUserIdentity, - op.BytesInBuffer, - op.LineUtf8, - LocalEndPoint, - RemoteEndPoint); - } - - string[] cmd_args = - Encoding.UTF8.GetString(op.Buffer, 0, op.LineBytesInBuffer).Split(new[] {' '}, 2); - string cmd = cmd_args[0].ToUpperInvariant(); - string args = cmd_args.Length == 2 ? cmd_args[1] : ""; - - if (cmd == "EHLO") - { - EHLO(args); - } - else if (cmd == "HELO") - { - HELO(args); - } - else if (cmd == "STARTTLS") - { - STARTTLS(args); - } - else if (cmd == "AUTH") - { - AUTH(args); - } - else if (cmd == "MAIL") - { - MAIL(args); - } - else if (cmd == "RCPT") - { - RCPT(args); - } - else if (cmd == "DATA") - { - readNextCommand = DATA(args); - } - else if (cmd == "BDAT") - { - readNextCommand = BDAT(args); - } - else if (cmd == "RSET") - { - RSET(args); - } - else if (cmd == "NOOP") - { - NOOP(args); - } - else if (cmd == "QUIT") - { - QUIT(args); - readNextCommand = false; - } - else - { - m_BadCommands++; - - // Maximum allowed bad commands exceeded. - if (Server.MaxBadCommands != 0 && m_BadCommands > Server.MaxBadCommands) - { - WriteLine("421 Too many bad commands, closing transmission channel."); - Disconnect(); - return false; - } - - WriteLine("502 Error: command '" + cmd + "' not recognized."); - } - } - catch (Exception x) - { - OnError(x); - } - - return readNextCommand; - } - - private void EHLO(string cmdText) - { - // RFC 5321 3.1. - if (m_SessionRejected) - { - WriteLine("503 bad sequence of commands: Session rejected."); - return; - } - - /* RFC 5321 4.1.1.1. - ehlo = "EHLO" SP ( Domain / address-literal ) CRLF - - ehlo-ok-rsp = ( "250" SP Domain [ SP ehlo-greet ] CRLF ) - / ( "250-" Domain [ SP ehlo-greet ] CRLF - *( "250-" ehlo-line CRLF ) - "250" SP ehlo-line CRLF ) - - ehlo-greet = 1*(%d0-9 / %d11-12 / %d14-127) - ; string of any characters other than CR or LF - - ehlo-line = ehlo-keyword *( SP ehlo-param ) - - ehlo-keyword = (ALPHA / DIGIT) *(ALPHA / DIGIT / "-") - ; additional syntax of ehlo-params depends on ehlo-keyword - - ehlo-param = 1*(%d33-126) - ; any CHAR excluding and all control characters (US-ASCII 0-31 and 127 inclusive) - */ - if (string.IsNullOrEmpty(cmdText) || cmdText.Split(' ').Length != 1) - { - WriteLine("501 Syntax error, syntax: \"EHLO\" SP hostname CRLF"); - return; - } - - List ehloLines = new List(); - ehloLines.Add(Net_Utils.GetLocalHostName(LocalHostName)); - if (Server.Extentions.Contains(SMTP_ServiceExtensions.PIPELINING)) - { - ehloLines.Add(SMTP_ServiceExtensions.PIPELINING); - } - if (Server.Extentions.Contains(SMTP_ServiceExtensions.SIZE)) - { - ehloLines.Add(SMTP_ServiceExtensions.SIZE + " " + Server.MaxMessageSize); - } - if (Server.Extentions.Contains(SMTP_ServiceExtensions.STARTTLS) && !IsSecureConnection && - Certificate != null) - { - ehloLines.Add(SMTP_ServiceExtensions.STARTTLS); - } - if (Server.Extentions.Contains(SMTP_ServiceExtensions._8BITMIME)) - { - ehloLines.Add(SMTP_ServiceExtensions._8BITMIME); - } - if (Server.Extentions.Contains(SMTP_ServiceExtensions.BINARYMIME)) - { - ehloLines.Add(SMTP_ServiceExtensions.BINARYMIME); - } - if (Server.Extentions.Contains(SMTP_ServiceExtensions.CHUNKING)) - { - ehloLines.Add(SMTP_ServiceExtensions.CHUNKING); - } - if (Server.Extentions.Contains(SMTP_ServiceExtensions.DSN)) - { - ehloLines.Add(SMTP_ServiceExtensions.DSN); - } - - StringBuilder sasl = new StringBuilder(); - foreach (AUTH_SASL_ServerMechanism authMechanism in Authentications.Values) - { - if (!authMechanism.RequireSSL || (authMechanism.RequireSSL && IsSecureConnection)) - { - sasl.Append(authMechanism.Name + " "); - } - } - if (sasl.Length > 0) - { - ehloLines.Add(SMTP_ServiceExtensions.AUTH + " " + sasl.ToString().Trim()); - } - - SMTP_Reply reply = new SMTP_Reply(250, ehloLines.ToArray()); - - reply = OnEhlo(cmdText, reply); - - // EHLO accepted. - if (reply.ReplyCode < 300) - { - m_EhloHost = cmdText; - - /* RFC 5321 4.1.4. - An EHLO command MAY be issued by a client later in the session. If - it is issued after the session begins and the EHLO command is - acceptable to the SMTP server, the SMTP server MUST clear all buffers - and reset the state exactly as if a RSET command had been issued. In - other words, the sequence of RSET followed immediately by EHLO is - redundant, but not harmful other than in the performance cost of - executing unnecessary commands. - */ - Reset(); - } - - WriteLine(reply.ToString()); - } - - private void HELO(string cmdText) - { - // RFC 5321 3.1. - if (m_SessionRejected) - { - WriteLine("503 bad sequence of commands: Session rejected."); - return; - } - - /* RFC 5321 4.1.1.1. - helo = "HELO" SP Domain CRLF - - response = "250" SP Domain [ SP ehlo-greet ] CRLF - */ - if (string.IsNullOrEmpty(cmdText) || cmdText.Split(' ').Length != 1) - { - WriteLine("501 Syntax error, syntax: \"HELO\" SP hostname CRLF"); - return; - } - - SMTP_Reply reply = new SMTP_Reply(250, Net_Utils.GetLocalHostName(LocalHostName)); - - reply = OnEhlo(cmdText, reply); - - // HELO accepted. - if (reply.ReplyCode < 300) - { - m_EhloHost = cmdText; - - /* RFC 5321 4.1.4. - An EHLO command MAY be issued by a client later in the session. If - it is issued after the session begins and the EHLO command is - acceptable to the SMTP server, the SMTP server MUST clear all buffers - and reset the state exactly as if a RSET command had been issued. In - other words, the sequence of RSET followed immediately by EHLO is - redundant, but not harmful other than in the performance cost of - executing unnecessary commands. - */ - Reset(); - } - - WriteLine(reply.ToString()); - } - - private void STARTTLS(string cmdText) - { - // RFC 5321 3.1. - if (m_SessionRejected) - { - WriteLine("503 Bad sequence of commands: Session rejected."); - return; - } - - /* RFC 3207 STARTTLS 4. - The format for the STARTTLS command is: - - STARTTLS - - with no parameters. - - After the client gives the STARTTLS command, the server responds with - one of the following reply codes: - - 220 Ready to start TLS - 501 Syntax error (no parameters allowed) - 454 TLS not available due to temporary reason - - 4.2 Result of the STARTTLS Command - Upon completion of the TLS handshake, the SMTP protocol is reset to - the initial state (the state in SMTP after a server issues a 220 - service ready greeting). The server MUST discard any knowledge - obtained from the client, such as the argument to the EHLO command, - which was not obtained from the TLS negotiation itself. The client - MUST discard any knowledge obtained from the server, such as the list - of SMTP service extensions, which was not obtained from the TLS - negotiation itself. The client SHOULD send an EHLO command as the - first command after a successful TLS negotiation. - - Both the client and the server MUST know if there is a TLS session - active. A client MUST NOT attempt to start a TLS session if a TLS - session is already active. A server MUST NOT return the STARTTLS - extension in response to an EHLO command received after a TLS - handshake has completed. - - - RFC 2246 7.2.2. Error alerts. - Error handling in the TLS Handshake protocol is very simple. When an - error is detected, the detecting party sends a message to the other - party. Upon transmission or receipt of an fatal alert message, both - parties immediately close the connection. <...> - */ - - if (!string.IsNullOrEmpty(cmdText)) - { - WriteLine("501 Syntax error: No parameters allowed."); - return; - } - if (IsSecureConnection) - { - WriteLine("503 Bad sequence of commands: Connection is already secure."); - return; - } - if (Certificate == null) - { - WriteLine("454 TLS not available: Server has no SSL certificate."); - return; - } - - WriteLine("220 Ready to start TLS."); - - try - { - SwitchToSecure(); - - // Log - LogAddText("TLS negotiation completed successfully."); - - m_EhloHost = null; - Reset(); - } - catch (Exception x) - { - // Log - LogAddText("TLS negotiation failed: " + x.Message + "."); - - Disconnect(); - } - } - - private void AUTH(string cmdText) - { - // RFC 5321 3.1. - if (m_SessionRejected) - { - WriteLine("503 Bad sequence of commands: Session rejected."); - return; - } - - /* RFC 4954 - AUTH mechanism [initial-response] - - Arguments: - mechanism: A string identifying a [SASL] authentication mechanism. - - initial-response: An optional initial client response. If - present, this response MUST be encoded as described in Section - 4 of [BASE64] or contain a single character "=". - - Restrictions: - After an AUTH command has been successfully completed, no more - AUTH commands may be issued in the same session. After a - successful AUTH command completes, a server MUST reject any - further AUTH commands with a 503 reply. - - The AUTH command is not permitted during a mail transaction. - An AUTH command issued during a mail transaction MUST be - rejected with a 503 reply. - - A server challenge is sent as a 334 reply with the text part - containing the [BASE64] encoded string supplied by the SASL - mechanism. This challenge MUST NOT contain any text other - than the BASE64 encoded challenge. - - In SMTP, a server challenge that contains no data is defined - as a 334 reply with no text part. Note that there is still a space - following the reply code, so the complete response line is "334 ". - - If the client wishes to cancel the authentication exchange, - it issues a line with a single "*". If the server receives - such a response, it MUST reject the AUTH command by sending a 501 reply. - */ - - if (IsAuthenticated) - { - WriteLine("503 Bad sequence of commands: you are already authenticated."); - return; - } - if (m_pFrom != null) - { - WriteLine( - "503 Bad sequence of commands: The AUTH command is not permitted during a mail transaction."); - return; - } - - #region Parse parameters - - string[] arguments = cmdText.Split(' '); - if (arguments.Length > 2) - { - WriteLine("501 Syntax error, syntax: AUTH SP mechanism [SP initial-response] CRLF"); - return; - } - string initialClientResponse = ""; - if (arguments.Length == 2) - { - if (arguments[1] == "=") - { - // Skip. - } - else - { - try - { - initialClientResponse = Encoding.UTF8.GetString(Convert.FromBase64String(arguments[1])); - } - catch - { - WriteLine( - "501 Syntax error: Parameter 'initial-response' value must be BASE64 or contain a single character '='."); - return; - } - } - } - string mechanism = arguments[0]; - - #endregion - - if (!Authentications.ContainsKey(mechanism)) - { - WriteLine("501 Not supported authentication mechanism."); - return; - } - - string clientResponse = initialClientResponse; - AUTH_SASL_ServerMechanism auth = Authentications[mechanism]; - while (true) - { - string serverResponse = auth.Continue(clientResponse); - // Authentication completed. - if (auth.IsCompleted) - { - if (auth.IsAuthenticated) - { - m_pUser = new GenericIdentity(auth.UserName, "SASL-" + auth.Name); - - WriteLine("235 2.7.0 Authentication succeeded."); - } - else - { - WriteLine("535 5.7.8 Authentication credentials invalid."); - } - break; - } - // Authentication continues. - else - { - // Send server challange. - if (string.IsNullOrEmpty(serverResponse)) - { - WriteLine("334 "); - } - else - { - WriteLine("334 " + Convert.ToBase64String(Encoding.UTF8.GetBytes(serverResponse))); - } - - // Read client response. - SmartStream.ReadLineAsyncOP readLineOP = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength], - SizeExceededAction - . - JunkAndThrowException); - TcpStream.ReadLine(readLineOP, false); - if (readLineOP.Error != null) - { - throw readLineOP.Error; - } - clientResponse = readLineOP.LineUtf8; - // Log - if (Server.Logger != null) - { - Server.Logger.AddRead(ID, - AuthenticatedUserIdentity, - readLineOP.BytesInBuffer, - "base64 auth-data", - LocalEndPoint, - RemoteEndPoint); - } - - // Client canceled authentication. - if (clientResponse == "*") - { - WriteLine("501 Authentication canceled."); - return; - } - // We have base64 client response, decode it. - else - { - try - { - clientResponse = Encoding.UTF8.GetString(Convert.FromBase64String(clientResponse)); - } - catch - { - WriteLine("501 Invalid client response '" + clientResponse + "'."); - return; - } - } - } - } - } - - private void MAIL(string cmdText) - { - // RFC 5321 3.1. - if (m_SessionRejected) - { - WriteLine("503 bad sequence of commands: Session rejected."); - return; - } - // RFC 5321 4.1.4. - if (string.IsNullOrEmpty(m_EhloHost)) - { - WriteLine("503 Bad sequence of commands: send EHLO/HELO first."); - return; - } - // RFC 5321 4.1.4. - if (m_pFrom != null) - { - WriteLine("503 Bad sequence of commands: nested MAIL command."); - return; - } - // RFC 3030 BDAT. - if (m_pMessageStream != null) - { - WriteLine("503 Bad sequence of commands: BDAT command is pending."); - return; - } - if (Server.MaxTransactions != 0 && m_Transactions >= Server.MaxTransactions) - { - WriteLine("503 Bad sequence of commands: Maximum allowed mail transactions exceeded."); - return; - } - - /* RFC 5321 4.1.1.2. - mail = "MAIL FROM:" Reverse-path [SP Mail-parameters] CRLF - - Mail-parameters = esmtp-param *(SP esmtp-param) - - esmtp-param = esmtp-keyword ["=" esmtp-value] - - esmtp-keyword = (ALPHA / DIGIT) *(ALPHA / DIGIT / "-") - - esmtp-value = 1*(%d33-60 / %d62-126) - ; any CHAR excluding "=", SP, and control - ; characters. If this string is an email address, - ; i.e., a Mailbox, then the "xtext" syntax [32] SHOULD be used. - - Reverse-path = Path / "<>" - Path = "<" [ A-d-l ":" ] Mailbox ">" - - 4.1.1.11. - If the server SMTP does not recognize or cannot implement one or more - of the parameters associated with a particular MAIL FROM or RCPT TO - command, it will return code 555. - */ - - if (cmdText.ToUpper().StartsWith("FROM:")) - { - // Remove FROM: from command text. - cmdText = cmdText.Substring(5).Trim(); - } - else - { - WriteLine( - "501 Syntax error, syntax: \"MAIL FROM:\" \"<\" address \">\" / \"<>\" [SP Mail-parameters] CRLF"); - return; - } - - string address = ""; - int size = -1; - string body = null; - string ret = null; - string envID = null; - - // Mailbox not between <>. - if (!cmdText.StartsWith("<") || cmdText.IndexOf('>') == -1) - { - WriteLine( - "501 Syntax error, syntax: \"MAIL FROM:\" \"<\" address \">\" / \"<>\" [SP Mail-parameters] CRLF"); - return; - } - // Parse mailbox. - else - { - address = cmdText.Substring(1, cmdText.IndexOf('>') - 1).Trim(); - cmdText = cmdText.Substring(cmdText.IndexOf('>') + 1).Trim(); - } - - #region Parse parameters - - string[] parameters = string.IsNullOrEmpty(cmdText) ? new string[0] : cmdText.Split(' '); - foreach (string parameter in parameters) - { - string[] name_value = parameter.Split(new[] {'='}, 2); - - // SIZE - if (Server.Extentions.Contains(SMTP_ServiceExtensions.SIZE) && - name_value[0].ToUpper() == "SIZE") - { - // RFC 1870. - // size-value ::= 1*20DIGIT - if (name_value.Length == 1) - { - WriteLine("501 Syntax error: SIZE parameter value must be specified."); - return; - } - if (!int.TryParse(name_value[1], out size)) - { - WriteLine("501 Syntax error: SIZE parameter value must be integer."); - return; - } - - // Message size exceeds maximum allowed message size. - if (size > Server.MaxMessageSize) - { - WriteLine("552 Message exceeds fixed maximum message size."); - return; - } - } - // BODY - else if (Server.Extentions.Contains(SMTP_ServiceExtensions._8BITMIME) && - name_value[0].ToUpper() == "BODY") - { - // RFC 1652. - // body-value ::= "7BIT" / "8BITMIME" / "BINARYMIME" - // - // BINARYMIME - defined in RFC 3030. - if (name_value.Length == 1) - { - WriteLine("501 Syntax error: BODY parameter value must be specified."); - return; - } - if (name_value[1].ToUpper() != "7BIT" && name_value[1].ToUpper() != "8BITMIME" && - name_value[1].ToUpper() != "BINARYMIME") - { - WriteLine( - "501 Syntax error: BODY parameter value must be \"7BIT\",\"8BITMIME\" or \"BINARYMIME\"."); - return; - } - body = name_value[1].ToUpper(); - } - // RET - else if (Server.Extentions.Contains(SMTP_ServiceExtensions.DSN) && - name_value[0].ToUpper() == "RET") - { - // RFC 1891 5.3. - // ret-value = "FULL" / "HDRS" - if (name_value.Length == 1) - { - WriteLine("501 Syntax error: RET parameter value must be specified."); - return; - } - if (name_value[1].ToUpper() != "FULL" && name_value[1].ToUpper() != "HDRS") - { - WriteLine( - "501 Syntax error: RET parameter value must be \"FULL\" or \"HDRS\"."); - return; - } - ret = name_value[1].ToUpper(); - } - // ENVID - else if (Server.Extentions.Contains(SMTP_ServiceExtensions.DSN) && - name_value[0].ToUpper() == "ENVID") - { - if (name_value.Length == 1) - { - WriteLine("501 Syntax error: ENVID parameter value must be specified."); - return; - } - - envID = name_value[1].ToUpper(); - } - // Unsupported parameter. - else - { - WriteLine("555 Unsupported parameter: " + parameter); - return; - } - } - - #endregion - - SMTP_MailFrom from = new SMTP_MailFrom(address, size, body, ret, envID); - SMTP_Reply reply = new SMTP_Reply(250, "OK."); - - reply = OnMailFrom(from, reply); - - // MAIL accepted. - if (reply.ReplyCode < 300) - { - m_pFrom = from; - m_Transactions++; - } - - WriteLine(reply.ToString()); - } - - private void RCPT(string cmdText) - { - // RFC 5321 3.1. - if (m_SessionRejected) - { - WriteLine("503 bad sequence of commands: Session rejected."); - return; - } - // RFC 5321 4.1.4. - if (string.IsNullOrEmpty(m_EhloHost)) - { - WriteLine("503 Bad sequence of commands: send EHLO/HELO first."); - return; - } - // RFC 5321 4.1.4. - if (m_pFrom == null) - { - WriteLine("503 Bad sequence of commands: send 'MAIL FROM:' first."); - return; - } - // RFC 3030 BDAT. - if (m_pMessageStream != null) - { - WriteLine("503 Bad sequence of commands: BDAT command is pending."); - return; - } - - /* RFC 5321 4.1.1.3. - rcpt = "RCPT TO:" ( "" / "" / Forward-path ) [SP Rcpt-parameters] CRLF - - Rcpt-parameters = esmtp-param *(SP esmtp-param) - - esmtp-param = esmtp-keyword ["=" esmtp-value] - - esmtp-keyword = (ALPHA / DIGIT) *(ALPHA / DIGIT / "-") - - esmtp-value = 1*(%d33-60 / %d62-126) - ; any CHAR excluding "=", SP, and control - ; characters. If this string is an email address, - ; i.e., a Mailbox, then the "xtext" syntax [32] SHOULD be used. - - Note that, in a departure from the usual rules for local-parts, the "Postmaster" string shown above is - treated as case-insensitive. - - Forward-path = Path - Path = "<" [ A-d-l ":" ] Mailbox ">" - - 4.1.1.11. - If the server SMTP does not recognize or cannot implement one or more - of the parameters associated with a particular MAIL FROM or RCPT TO - command, it will return code 555. - */ - - if (cmdText.ToUpper().StartsWith("TO:")) - { - // Remove TO: from command text. - cmdText = cmdText.Substring(3).Trim(); - } - else - { - WriteLine( - "501 Syntax error, syntax: \"RCPT TO:\" \"<\" address \">\" [SP Rcpt-parameters] CRLF"); - return; - } - - string address = ""; - SMTP_Notify notify = SMTP_Notify.NotSpecified; - string orcpt = null; - - // Mailbox not between <>. - if (!cmdText.StartsWith("<") || cmdText.IndexOf('>') == -1) - { - WriteLine( - "501 Syntax error, syntax: \"RCPT TO:\" \"<\" address \">\" [SP Rcpt-parameters] CRLF"); - return; - } - // Parse mailbox. - else - { - address = cmdText.Substring(1, cmdText.IndexOf('>') - 1).Trim(); - cmdText = cmdText.Substring(cmdText.IndexOf('>') + 1).Trim(); - } - if (address == string.Empty) - { - WriteLine( - "501 Syntax error('address' value must be specified), syntax: \"RCPT TO:\" \"<\" address \">\" [SP Rcpt-parameters] CRLF"); - return; - } - - #region Parse parameters - - string[] parameters = string.IsNullOrEmpty(cmdText) ? new string[0] : cmdText.Split(' '); - foreach (string parameter in parameters) - { - string[] name_value = parameter.Split(new[] {'='}, 2); - - // NOTIFY - if (Server.Extentions.Contains(SMTP_ServiceExtensions.DSN) && - name_value[0].ToUpper() == "NOTIFY") - { - /* RFC 1891 5.1. - notify-esmtp-value = "NEVER" / 1#notify-list-element - notify-list-element = "SUCCESS" / "FAILURE" / "DELAY" - - a. Multiple notify-list-elements, separated by commas, MAY appear in a - NOTIFY parameter; however, the NEVER keyword MUST appear by itself. - */ - if (name_value.Length == 1) - { - WriteLine("501 Syntax error: NOTIFY parameter value must be specified."); - return; - } - string[] notifyItems = name_value[1].ToUpper().Split(','); - foreach (string notifyItem in notifyItems) - { - if (notifyItem.Trim().ToUpper() == "NEVER") - { - notify |= SMTP_Notify.Never; - } - else if (notifyItem.Trim().ToUpper() == "SUCCESS") - { - notify |= SMTP_Notify.Success; - } - else if (notifyItem.Trim().ToUpper() == "FAILURE") - { - notify |= SMTP_Notify.Failure; - } - else if (notifyItem.Trim().ToUpper() == "DELAY") - { - notify |= SMTP_Notify.Delay; - } - // Invalid or not supported notify item. - else - { - WriteLine("501 Syntax error: Not supported NOTIFY parameter value '" + notifyItem + - "'."); - return; - } - } - } - // ORCPT - else if (Server.Extentions.Contains(SMTP_ServiceExtensions.DSN) && - name_value[0].ToUpper() == "ORCPT") - { - if (name_value.Length == 1) - { - WriteLine("501 Syntax error: ORCPT parameter value must be specified."); - return; - } - orcpt = name_value[1].ToUpper(); - } - // Unsupported parameter. - else - { - WriteLine("555 Unsupported parameter: " + parameter); - } - } - - #endregion - - // Maximum allowed recipients exceeded. - if (m_pTo.Count >= Server.MaxRecipients) - { - WriteLine("452 Too many recipients"); - return; - } - - SMTP_RcptTo to = new SMTP_RcptTo(address, notify, orcpt); - SMTP_Reply reply = new SMTP_Reply(250, "OK."); - - reply = OnRcptTo(to, reply); - - // RCPT accepted. - if (reply.ReplyCode < 300) - { - if (!m_pTo.ContainsKey(address.ToLower())) - { - m_pTo.Add(address.ToLower(), to); - } - } - - WriteLine(reply.ToString()); - } - - private bool DATA(string cmdText) - { - // RFC 5321 3.1. - if (m_SessionRejected) - { - WriteLine("503 bad sequence of commands: Session rejected."); - return true; - } - // RFC 5321 4.1.4. - if (string.IsNullOrEmpty(m_EhloHost)) - { - WriteLine("503 Bad sequence of commands: send EHLO/HELO first."); - return true; - } - // RFC 5321 4.1.4. - if (m_pFrom == null) - { - WriteLine("503 Bad sequence of commands: send 'MAIL FROM:' first."); - return true; - } - // RFC 5321 4.1.4. - if (m_pTo.Count == 0) - { - WriteLine("503 Bad sequence of commands: send 'RCPT TO:' first."); - return true; - } - // RFC 3030 BDAT. - if (m_pMessageStream != null) - { - WriteLine( - "503 Bad sequence of commands: DATA and BDAT commands cannot be used in the same transaction."); - return true; - } - - /* RFC 5321 4.1.1.4. - The receiver normally sends a 354 response to DATA, and then treats - the lines (strings ending in sequences, as described in - Section 2.3.7) following the command as mail data from the sender. - This command causes the mail data to be appended to the mail data - buffer. The mail data may contain any of the 128 ASCII character - codes, although experience has indicated that use of control - characters other than SP, HT, CR, and LF may cause problems and - SHOULD be avoided when possible. - - The custom of accepting lines ending only in , as a concession to - non-conforming behavior on the part of some UNIX systems, has proven - to cause more interoperability problems than it solves, and SMTP - server systems MUST NOT do this, even in the name of improved - robustness. In particular, the sequence "." (bare line - feeds, without carriage returns) MUST NOT be treated as equivalent to - . as the end of mail data indication. - - Receipt of the end of mail data indication requires the server to - process the stored mail transaction information. This processing - consumes the information in the reverse-path buffer, the forward-path - buffer, and the mail data buffer, and on the completion of this - command these buffers are cleared. If the processing is successful, - the receiver MUST send an OK reply. If the processing fails, the - receiver MUST send a failure reply. The SMTP model does not allow - for partial failures at this point: either the message is accepted by - the server for delivery and a positive response is returned or it is - not accepted and a failure reply is returned. In sending a positive - "250 OK" completion reply to the end of data indication, the receiver - takes full responsibility for the message (see Section 6.1). Errors - that are diagnosed subsequently MUST be reported in a mail message, - as discussed in Section 4.4. - - When the SMTP server accepts a message either for relaying or for - final delivery, it inserts a trace record (also referred to - interchangeably as a "time stamp line" or "Received" line) at the top - of the mail data. This trace record indicates the identity of the - host that sent the message, the identity of the host that received - the message (and is inserting this time stamp), and the date and time - the message was received. Relayed messages will have multiple time - stamp lines. Details for formation of these lines, including their - syntax, is specified in Section 4.4. - */ - - DateTime startTime = DateTime.Now; - - m_pMessageStream = OnGetMessageStream(); - if (m_pMessageStream == null) - { - m_pMessageStream = new MemoryStream(); - } - // RFC 5321.4.4 trace info. - byte[] recevived = CreateReceivedHeader(); - m_pMessageStream.Write(recevived, 0, recevived.Length); - - WriteLine("354 Start mail input; end with ."); - - // Create asynchronous read period-terminated opeartion. - SmartStream.ReadPeriodTerminatedAsyncOP readPeriodTermOP = - new SmartStream.ReadPeriodTerminatedAsyncOP(m_pMessageStream, - Server.MaxMessageSize, - SizeExceededAction.JunkAndThrowException); - // This event is raised only if read period-terminated opeartion completes asynchronously. - readPeriodTermOP.Completed += delegate { DATA_End(startTime, readPeriodTermOP); }; - // Read period-terminated completed synchronously. - if (TcpStream.ReadPeriodTerminated(readPeriodTermOP, true)) - { - DATA_End(startTime, readPeriodTermOP); - - return true; - } - // Read period-terminated completed asynchronously, Completed event will be raised once operation completes. - // else{ - - return false; - } - - /// - /// Completes DATA command. - /// - /// Time DATA command started. - /// Read period-terminated opeartion. - private void DATA_End(DateTime startTime, SmartStream.ReadPeriodTerminatedAsyncOP op) - { - try - { - if (op.Error != null) - { - if (op.Error is LineSizeExceededException) - { - WriteLine("500 Line too long."); - } - else if (op.Error is DataSizeExceededException) - { - WriteLine("552 Too much mail data."); - } - else - { - OnError(op.Error); - } - - OnMessageStoringCanceled(); - } - else - { - SMTP_Reply reply = new SMTP_Reply(250, - "DATA completed in " + - (DateTime.Now - startTime).TotalSeconds.ToString("f2") + - " seconds."); - - reply = OnMessageStoringCompleted(reply); - - WriteLine(reply.ToString()); - } - } - catch (Exception x) - { - OnError(x); - } - - Reset(); - BeginReadCmd(); - } - - private bool BDAT(string cmdText) - { - // RFC 5321 3.1. - if (m_SessionRejected) - { - WriteLine("503 bad sequence of commands: Session rejected."); - return true; - } - // RFC 5321 4.1.4. - if (string.IsNullOrEmpty(m_EhloHost)) - { - WriteLine("503 Bad sequence of commands: send EHLO/HELO first."); - return true; - } - // RFC 5321 4.1.4. - if (m_pFrom == null) - { - WriteLine("503 Bad sequence of commands: send 'MAIL FROM:' first."); - return true; - } - // RFC 5321 4.1.4. - if (m_pTo.Count == 0) - { - WriteLine("503 Bad sequence of commands: send 'RCPT TO:' first."); - return true; - } - - /* RFC 3030 2 - The BDAT verb takes two arguments.The first argument indicates the length, - in octets, of the binary data chunk. The second optional argument indicates - that the data chunk is the last. - - The message data is sent immediately after the trailing - of the BDAT command line. Once the receiver-SMTP receives the - specified number of octets, it will return a 250 reply code. - - The optional LAST parameter on the BDAT command indicates that this - is the last chunk of message data to be sent. The last BDAT command - MAY have a byte-count of zero indicating there is no additional data - to be sent. Any BDAT command sent after the BDAT LAST is illegal and - MUST be replied to with a 503 "Bad sequence of commands" reply code. - The state resulting from this error is indeterminate. A RSET command - MUST be sent to clear the transaction before continuing. - - A 250 response MUST be sent to each successful BDAT data block within - a mail transaction. - - bdat-cmd ::= "BDAT" SP chunk-size [ SP end-marker ] CR LF - chunk-size ::= 1*DIGIT - end-marker ::= "LAST" - */ - - DateTime startTime = DateTime.Now; - - int chunkSize = 0; - bool last = false; - string[] args = cmdText.Split(' '); - if (cmdText == string.Empty || args.Length > 2) - { - WriteLine("501 Syntax error, syntax: \"BDAT\" SP chunk-size [SP \"LAST\"] CRLF"); - return true; - } - if (!int.TryParse(args[0], out chunkSize)) - { - WriteLine( - "501 Syntax error(chunk-size must be integer), syntax: \"BDAT\" SP chunk-size [SP \"LAST\"] CRLF"); - return true; - } - if (args.Length == 2) - { - if (args[1].ToUpperInvariant() != "LAST") - { - WriteLine("501 Syntax error, syntax: \"BDAT\" SP chunk-size [SP \"LAST\"] CRLF"); - return true; - } - last = true; - } - - // First BDAT block in transaction. - if (m_pMessageStream == null) - { - m_pMessageStream = OnGetMessageStream(); - if (m_pMessageStream == null) - { - m_pMessageStream = new MemoryStream(); - } - // RFC 5321.4.4 trace info. - byte[] recevived = CreateReceivedHeader(); - m_pMessageStream.Write(recevived, 0, recevived.Length); - } - - Stream storeStream = m_pMessageStream; - // Maximum allowed message size exceeded. - if ((m_BDatReadedCount + chunkSize) > Server.MaxMessageSize) - { - storeStream = new JunkingStream(); - } - - // Read data block. - TcpStream.BeginReadFixedCount(storeStream, - chunkSize, - delegate(IAsyncResult ar) - { - try - { - TcpStream.EndReadFixedCount(ar); - - m_BDatReadedCount += chunkSize; - - // Maximum allowed message size exceeded. - if (m_BDatReadedCount > Server.MaxMessageSize) - { - WriteLine("552 Too much mail data."); - - OnMessageStoringCanceled(); - } - else - { - SMTP_Reply reply = new SMTP_Reply(250, - chunkSize + - " bytes received in " + - (DateTime.Now - - startTime). - TotalSeconds. - ToString("f2") + - " seconds."); - - if (last) - { - reply = OnMessageStoringCompleted(reply); - } - - WriteLine(reply.ToString()); - } - - if (last) - { - // Accoring RFC 3030, client should send RSET and we must wait it and reject transaction commands. - // If we reset internally, then all works as specified. - Reset(); - } - } - catch (Exception x) - { - OnError(x); - } - - BeginReadCmd(); - }, - null); - - return false; - } - - private void RSET(string cmdText) - { - // RFC 5321 3.1. - if (m_SessionRejected) - { - WriteLine("503 bad sequence of commands: Session rejected."); - return; - } - - /* RFC 5321 4.1.1.5. - This command specifies that the current mail transaction will be - aborted. Any stored sender, recipients, and mail data MUST be - discarded, and all buffers and state tables cleared. The receiver - MUST send a "250 OK" reply to a RSET command with no arguments. A - reset command may be issued by the client at any time. It is - effectively equivalent to a NOOP (i.e., it has no effect) if issued - immediately after EHLO, before EHLO is issued in the session, after - an end of data indicator has been sent and acknowledged, or - immediately before a QUIT. An SMTP server MUST NOT close the - connection as the result of receiving a RSET; that action is reserved - for QUIT (see Section 4.1.1.10). - */ - - if (m_pMessageStream != null) - { - OnMessageStoringCanceled(); - } - - Reset(); - - WriteLine("250 OK."); - } - - private void NOOP(string cmdText) - { - // RFC 5321 3.1. - if (m_SessionRejected) - { - WriteLine("503 bad sequence of commands: Session rejected."); - return; - } - - /* RFC 5321 4.1.1.9. - This command does not affect any parameters or previously entered - commands. It specifies no action other than that the receiver send a - "250 OK" reply. - - This command has no effect on the reverse-path buffer, the forward- - path buffer, or the mail data buffer, and it may be issued at any - time. If a parameter string is specified, servers SHOULD ignore it. - - Syntax: - noop = "NOOP" [ SP String ] CRLF - */ - - WriteLine("250 OK."); - } - - private void QUIT(string cmdText) - { - /* RFC 5321 4.1.1.10. - This command specifies that the receiver MUST send a "221 OK" reply, - and then close the transmission channel. - - The QUIT command may be issued at any time. Any current uncompleted - mail transaction will be aborted. - - quit = "QUIT" CRLF - */ - - try - { - WriteLine("221 <" + Net_Utils.GetLocalHostName(LocalHostName) + - "> Service closing transmission channel."); - } - catch {} - Disconnect(); - Dispose(); - } - - /// - /// Does reset as specified in RFC 5321. - /// - private void Reset() - { - if (IsDisposed) - { - return; - } - - m_pFrom = null; - m_pTo.Clear(); - m_pMessageStream = null; - m_BDatReadedCount = 0; - } - - /// - /// Creates "Received:" header field. For more info see RFC 5321.4.4. - /// - /// Returns "Received:" header field. - private byte[] CreateReceivedHeader() - { - /* 5321 4.4. Trace Information. - When an SMTP server receives a message for delivery or further - processing, it MUST insert trace ("time stamp" or "Received") - information at the beginning of the message content, as discussed in - Section 4.1.1.4. - - RFC 4954.7. Additional Requirements on Servers. - As described in Section 4.4 of [SMTP], an SMTP server that receives a - message for delivery or further processing MUST insert the - "Received:" header field at the beginning of the message content. - This document places additional requirements on the content of a - generated "Received:" header field. Upon successful authentication, - a server SHOULD use the "ESMTPA" or the "ESMTPSA" [SMTP-TT] (when - appropriate) keyword in the "with" clause of the Received header - field. - - http://www.iana.org/assignments/mail-parameters - ESMTP SMTP with Service Extensions [RFC5321] - ESMTPA ESMTP with SMTP AUTH [RFC3848] - ESMTPS ESMTP with STARTTLS [RFC3848] - ESMTPSA ESMTP with both STARTTLS and SMTP AUTH [RFC3848] - */ - - Mail_h_Received received = new Mail_h_Received(EhloHost, - Net_Utils.GetLocalHostName(LocalHostName), - DateTime.Now); - received.From_TcpInfo = new Mail_t_TcpInfo(RemoteEndPoint.Address, null); - received.Via = "TCP"; - if (!IsAuthenticated && !IsSecureConnection) - { - received.With = "ESMTP"; - } - else if (IsAuthenticated && !IsSecureConnection) - { - received.With = "ESMTPA"; - } - else if (!IsAuthenticated && IsSecureConnection) - { - received.With = "ESMTPS"; - } - else if (IsAuthenticated && IsSecureConnection) - { - received.With = "ESMTPSA"; - } - - return Encoding.UTF8.GetBytes(received.ToString()); - } - - /// - /// Sends and logs specified line to connected host. - /// - /// Line to send. - private void WriteLine(string line) - { - if (line == null) - { - throw new ArgumentNullException("line"); - } - - int countWritten = TcpStream.WriteLine(line); - - // Log. - if (Server.Logger != null) - { - Server.Logger.AddWrite(ID, - AuthenticatedUserIdentity, - countWritten, - line, - LocalEndPoint, - RemoteEndPoint); - } - } - - /// - /// Logs specified text. - /// - /// text to log. - /// Is raised when text is null reference. - private void LogAddText(string text) - { - if (text == null) - { - throw new ArgumentNullException("text"); - } - - // Log - if (Server.Logger != null) - { - Server.Logger.AddText(ID, text); - } - } - - /// - /// Raises Started event. - /// - /// Default SMTP server reply. - /// Returns SMTP server reply what must be sent to the connected client. - private SMTP_Reply OnStarted(SMTP_Reply reply) - { - if (Started != null) - { - SMTP_e_Started eArgs = new SMTP_e_Started(this, reply); - Started(this, eArgs); - - return eArgs.Reply; - } - - return reply; - } - - /// - /// Raises Ehlo event. - /// - /// Ehlo/Helo domain. - /// Default SMTP server reply. - /// Returns SMTP server reply what must be sent to the connected client. - private SMTP_Reply OnEhlo(string domain, SMTP_Reply reply) - { - if (Ehlo != null) - { - SMTP_e_Ehlo eArgs = new SMTP_e_Ehlo(this, domain, reply); - Ehlo(this, eArgs); - - return eArgs.Reply; - } - - return reply; - } - - /// - /// Raises MailFrom event. - /// - /// MAIL FROM: value. - /// Default SMTP server reply. - /// Returns SMTP server reply what must be sent to the connected client. - private SMTP_Reply OnMailFrom(SMTP_MailFrom from, SMTP_Reply reply) - { - if (MailFrom != null) - { - SMTP_e_MailFrom eArgs = new SMTP_e_MailFrom(this, from, reply); - MailFrom(this, eArgs); - - return eArgs.Reply; - } - - return reply; - } - - /// - /// Raises RcptTo event. - /// - /// RCPT TO: value. - /// Default SMTP server reply. - /// Returns SMTP server reply what must be sent to the connected client. - private SMTP_Reply OnRcptTo(SMTP_RcptTo to, SMTP_Reply reply) - { - if (RcptTo != null) - { - SMTP_e_RcptTo eArgs = new SMTP_e_RcptTo(this, to, reply); - RcptTo(this, eArgs); - - return eArgs.Reply; - } - - return reply; - } - - /// - /// Raises GetMessageStream event. - /// - /// Returns message store stream. - private Stream OnGetMessageStream() - { - if (GetMessageStream != null) - { - SMTP_e_Message eArgs = new SMTP_e_Message(this); - GetMessageStream(this, eArgs); - - return eArgs.Stream; - } - - return null; - } - - /// - /// Raises MessageStoringCanceled event. - /// - private void OnMessageStoringCanceled() - { - if (MessageStoringCanceled != null) - { - MessageStoringCanceled(this, new SMTP_e_MessageStored(this,m_pMessageStream, null)); - } - } - - /// - /// Raises MessageStoringCompleted event. - /// - /// Default SMTP server reply. - /// Returns SMTP server reply what must be sent to the connected client. - private SMTP_Reply OnMessageStoringCompleted(SMTP_Reply reply) - { - if (MessageStoringCompleted != null) - { - SMTP_e_MessageStored eArgs = new SMTP_e_MessageStored(this, m_pMessageStream, reply); - MessageStoringCompleted(this, eArgs); - - return eArgs.Reply; - } - - return reply; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_e_Ehlo.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_e_Ehlo.cs deleted file mode 100644 index 2c3b0cc01..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_e_Ehlo.cs +++ /dev/null @@ -1,114 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SMTP.Server -{ - #region usings - - using System; - - #endregion - - /// - /// This class provided data for SMTP_Session.Ehlo event. - /// - public class SMTP_e_Ehlo : EventArgs - { - #region Members - - private readonly string m_Domain = ""; - private readonly SMTP_Session m_pSession; - private SMTP_Reply m_pReply; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner SMTP server session. - /// Ehlo/Helo domain name. - /// SMTP server reply. - /// Is raised when session, domain or reply is null reference. - /// Is raised when any of the arguments has invalid value. - public SMTP_e_Ehlo(SMTP_Session session, string domain, SMTP_Reply reply) - { - if (session == null) - { - throw new ArgumentNullException("session"); - } - if (domain == null) - { - throw new ArgumentNullException("domain"); - } - if (domain == string.Empty) - { - throw new ArgumentException("Argument 'domain' value must be sepcified.", "domain"); - } - if (reply == null) - { - throw new ArgumentNullException("reply"); - } - - m_pSession = session; - m_Domain = domain; - m_pReply = reply; - } - - #endregion - - #region Properties - - /// - /// Gets connected client reported domain name. - /// - public string Domain - { - get { return m_Domain; } - } - - /// - /// Gets or sets SMTP server reply. - /// - /// Is raised when null reference passed. - public SMTP_Reply Reply - { - get { return m_pReply; } - - set - { - if (value == null) - { - throw new ArgumentNullException("Reply"); - } - - m_pReply = value; - } - } - - /// - /// Gets owner SMTP session. - /// - public SMTP_Session Session - { - get { return m_pSession; } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_e_MailFrom.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_e_MailFrom.cs deleted file mode 100644 index 5bedd92a6..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_e_MailFrom.cs +++ /dev/null @@ -1,109 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SMTP.Server -{ - #region usings - - using System; - - #endregion - - /// - /// This class provided data for SMTP_Session.MailFrom event. - /// - public class SMTP_e_MailFrom : EventArgs - { - #region Members - - private readonly SMTP_MailFrom m_pMailFrom; - private readonly SMTP_Session m_pSession; - private SMTP_Reply m_pReply; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner SMTP server session. - /// MAIL FROM: value. - /// SMTP server reply. - /// Is raised when session, from or reply is null reference. - public SMTP_e_MailFrom(SMTP_Session session, SMTP_MailFrom from, SMTP_Reply reply) - { - if (session == null) - { - throw new ArgumentNullException("session"); - } - if (from == null) - { - throw new ArgumentNullException("from"); - } - if (reply == null) - { - throw new ArgumentNullException("reply"); - } - - m_pSession = session; - m_pMailFrom = from; - m_pReply = reply; - } - - #endregion - - #region Properties - - /// - /// Gets MAIL FROM: value. - /// - public SMTP_MailFrom MailFrom - { - get { return m_pMailFrom; } - } - - /// - /// Gets or sets SMTP server reply. - /// - /// Is raised when null reference passed. - public SMTP_Reply Reply - { - get { return m_pReply; } - - set - { - if (value == null) - { - throw new ArgumentNullException("Reply"); - } - - m_pReply = value; - } - } - - /// - /// Gets owner SMTP session. - /// - public SMTP_Session Session - { - get { return m_pSession; } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_e_Message.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_e_Message.cs deleted file mode 100644 index 71a131d19..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_e_Message.cs +++ /dev/null @@ -1,89 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SMTP.Server -{ - #region usings - - using System; - using System.IO; - - #endregion - - /// - /// This class provided data for SMTP_Session.GetMessageStream event. - /// - public class SMTP_e_Message : EventArgs - { - #region Members - - private readonly SMTP_Session m_pSession; - private Stream m_pStream; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner SMTP server session. - /// Is raised when session is null reference. - public SMTP_e_Message(SMTP_Session session) - { - if (session == null) - { - throw new ArgumentNullException("session"); - } - - m_pSession = session; - } - - #endregion - - #region Properties - - /// - /// Gets owner SMTP session. - /// - public SMTP_Session Session - { - get { return m_pSession; } - } - - /// - /// Gets or stes stream where to store incoming message. - /// - /// Is raised when null reference is passed. - public Stream Stream - { - get { return m_pStream; } - - set - { - if (value == null) - { - throw new ArgumentNullException("Stream"); - } - - m_pStream = value; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_e_MessageStored.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_e_MessageStored.cs deleted file mode 100644 index 4d75f2980..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_e_MessageStored.cs +++ /dev/null @@ -1,110 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SMTP.Server -{ - #region usings - - using System; - using System.IO; - - #endregion - - /// - /// This class provided data for SMTP_Session.MessageStoringCompleted event. - /// - public class SMTP_e_MessageStored : EventArgs - { - #region Members - - private readonly SMTP_Session m_pSession; - private readonly Stream m_pStream; - private SMTP_Reply m_pReply; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner SMTP server session. - /// Message stream. - /// SMTP server reply. - /// Is raised when session, stream or reply is null reference. - public SMTP_e_MessageStored(SMTP_Session session, Stream stream, SMTP_Reply reply) - { - if (session == null) - { - throw new ArgumentNullException("session"); - } - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - if (reply == null) - { - throw new ArgumentNullException("reply"); - } - - m_pSession = session; - m_pStream = stream; - m_pReply = reply; - } - - #endregion - - #region Properties - - /// - /// Gets or sets SMTP server reply. - /// - /// Is raised when null reference passed. - public SMTP_Reply Reply - { - get { return m_pReply; } - - set - { - if (value == null) - { - throw new ArgumentNullException("Reply"); - } - - m_pReply = value; - } - } - - /// - /// Gets owner SMTP session. - /// - public SMTP_Session Session - { - get { return m_pSession; } - } - - /// - /// Gets message stream where message has stored. - /// - public Stream Stream - { - get { return m_pStream; } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_e_RcptTo.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_e_RcptTo.cs deleted file mode 100644 index afbf8ce31..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_e_RcptTo.cs +++ /dev/null @@ -1,109 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SMTP.Server -{ - #region usings - - using System; - - #endregion - - /// - /// This class provided data for SMTP_Session.RcptTo event. - /// - public class SMTP_e_RcptTo : EventArgs - { - #region Members - - private readonly SMTP_RcptTo m_pRcptTo; - private readonly SMTP_Session m_pSession; - private SMTP_Reply m_pReply; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner SMTP server session. - /// RCPT TO: value. - /// SMTP server reply. - /// Is raised when session, to or reply is null reference. - public SMTP_e_RcptTo(SMTP_Session session, SMTP_RcptTo to, SMTP_Reply reply) - { - if (session == null) - { - throw new ArgumentNullException("session"); - } - if (to == null) - { - throw new ArgumentNullException("from"); - } - if (reply == null) - { - throw new ArgumentNullException("reply"); - } - - m_pSession = session; - m_pRcptTo = to; - m_pReply = reply; - } - - #endregion - - #region Properties - - /// - /// Gets RCPT TO: value. - /// - public SMTP_RcptTo RcptTo - { - get { return m_pRcptTo; } - } - - /// - /// Gets or sets SMTP server reply. - /// - /// Is raised when null reference passed. - public SMTP_Reply Reply - { - get { return m_pReply; } - - set - { - if (value == null) - { - throw new ArgumentNullException("Reply"); - } - - m_pReply = value; - } - } - - /// - /// Gets owner SMTP session. - /// - public SMTP_Session Session - { - get { return m_pSession; } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_e_Started.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_e_Started.cs deleted file mode 100644 index 5220d1e26..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/SMTP_e_Started.cs +++ /dev/null @@ -1,94 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SMTP.Server -{ - #region usings - - using System; - - #endregion - - /// - /// This class provided data for SMTP_Session.Started event. - /// - public class SMTP_e_Started : EventArgs - { - #region Members - - private readonly SMTP_Session m_pSession; - private SMTP_Reply m_pReply; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner SMTP server session. - /// SMTP server reply. - /// Is raised when session or reply is null reference. - public SMTP_e_Started(SMTP_Session session, SMTP_Reply reply) - { - if (session == null) - { - throw new ArgumentNullException("session"); - } - if (reply == null) - { - throw new ArgumentNullException("reply"); - } - - m_pSession = session; - m_pReply = reply; - } - - #endregion - - #region Properties - - /// - /// Gets or sets SMTP server reply. - /// - /// Is raised when null reference passed. - public SMTP_Reply Reply - { - get { return m_pReply; } - - set - { - if (value == null) - { - throw new ArgumentNullException("Reply"); - } - - m_pReply = value; - } - } - - /// - /// Gets owner SMTP session. - /// - public SMTP_Session Session - { - get { return m_pSession; } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/AuthUser_EventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/AuthUser_EventArgs.cs deleted file mode 100644 index d04a84448..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/AuthUser_EventArgs.cs +++ /dev/null @@ -1,118 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using LumiSoft.Net; - -namespace LumiSoft.Net.SMTP.Server -{ - /// - /// Provides data for the AuthUser event for POP3_Server and SMTP_Server. - /// - public class AuthUser_EventArgs - { - private SMTP_Session m_pSession = null; - private string m_UserName = ""; - private string m_PasswData = ""; - private string m_Data = ""; - private AuthType m_AuthType; - private bool m_Validated = true; - private string m_ReturnData = ""; - - /// - /// Default constructor. - /// - /// Reference to pop3 session. - /// Username. - /// Password data. - /// Authentication specific data(as tag). - /// Authentication type. - public AuthUser_EventArgs(SMTP_Session session,string userName,string passwData,string data,AuthType authType) - { - m_pSession = session; - m_UserName = userName; - m_PasswData = passwData; - m_Data = data; - m_AuthType = authType; - } - - #region Properties Implementation - - /// - /// Gets reference to smtp session. - /// - public SMTP_Session Session - { - get{ return m_pSession; } - } - - /// - /// User name. - /// - public string UserName - { - get{ return m_UserName; } - } - - /// - /// Password data. eg. for AUTH=PLAIN it's password and for AUTH=APOP it's md5HexHash. - /// - public string PasswData - { - get{ return m_PasswData; } - } - - /// - /// Authentication specific data(as tag). - /// - public string AuthData - { - get{ return m_Data; } - } - - /// - /// Authentication type. - /// - public AuthType AuthType - { - get{ return m_AuthType; } - } - - /// - /// Gets or sets if user is valid. - /// - public bool Validated - { - get{ return m_Validated; } - - set{ m_Validated = value; } - } - - /// - /// Gets or sets authentication data what must be returned for connected client. - /// - public string ReturnData - { - get{ return m_ReturnData; } - - set{ m_ReturnData = value; } - } - - #endregion - - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/GetMessageStoreStream_eArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/GetMessageStoreStream_eArgs.cs deleted file mode 100644 index 85211bae6..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/GetMessageStoreStream_eArgs.cs +++ /dev/null @@ -1,73 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.Text; -using System.IO; - -namespace LumiSoft.Net.SMTP.Server -{ - /// - /// This class provides data for the GetMessageStoreStream event. - /// - public class GetMessageStoreStream_eArgs - { - private SMTP_Session m_pSession = null; - private Stream m_pStoreStream = null; - - /// - /// Default constructor. - /// - /// Reference to calling SMTP sesssion. - public GetMessageStoreStream_eArgs(SMTP_Session session) - { - m_pSession = session; - m_pStoreStream = new MemoryStream(); - } - - - #region Properties Implementation - - /// - /// Gets reference to smtp session. - /// - public SMTP_Session Session - { - get{ return m_pSession; } - } - - /// - /// Gets or sets Stream where to store incoming message. Storing starts from stream current position. - /// - public Stream StoreStream - { - get{ return m_pStoreStream; } - - set{ - if(value == null){ - throw new ArgumentNullException("Property StoreStream value can't be null !"); - } - - m_pStoreStream = value; - } - } - - #endregion - - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/MessageStoringCompleted_eArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/MessageStoringCompleted_eArgs.cs deleted file mode 100644 index fc5398725..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/MessageStoringCompleted_eArgs.cs +++ /dev/null @@ -1,96 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.IO; -using System.Collections.Generic; -using System.Text; - -namespace LumiSoft.Net.SMTP.Server -{ - /// - /// This class provides data for the MessageStoringCompleted event. - /// - public class MessageStoringCompleted_eArgs - { - private SMTP_Session m_pSession = null; - private string m_ErrorText = null; - //private long m_StoredCount = 0; - private Stream m_pMessageStream = null; - private SmtpServerReply m_pCustomReply = null; - - /// - /// Default constructor. - /// - /// Reference to calling SMTP session. - /// Gets errors what happened on storing message or null if no errors. - /// Gets message stream where messages was stored. Stream postions is End of Stream, where message storing ended. - public MessageStoringCompleted_eArgs(SMTP_Session session,string errorText,Stream messageStream) - { - m_pSession = session; - m_ErrorText = errorText; - m_pMessageStream = messageStream; - m_pCustomReply = new SmtpServerReply(); - } - - - #region Properties Implementation - - /// - /// Gets reference to smtp session. - /// - public SMTP_Session Session - { - get{ return m_pSession; } - } - - /// - /// Gets errors what happened on storing message or null if no errors. - /// - public string ErrorText - { - get{ return m_ErrorText; } - } - /* - /// - /// Gets count of bytes stored to MessageStream. This value as meaningfull value only if this.ErrorText == null (No errors). - /// - public long StoredCount - { - get{ return m_StoredCount; } - }*/ - - /// - /// Gets message stream where messages was stored. Stream postions is End of Stream, where message storing ended. - /// - public Stream MessageStream - { - get{ return m_pMessageStream; } - } - - /// - /// Gets SMTP server reply info. You can use this property for specifying custom reply text and optionally SMTP reply code. - /// - public SmtpServerReply ServerReply - { - get{ return m_pCustomReply; } - } - - #endregion - - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/SMTP_Server.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/SMTP_Server.cs deleted file mode 100644 index 8b5b82bc1..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/SMTP_Server.cs +++ /dev/null @@ -1,408 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.IO; -using System.ComponentModel; -using System.Collections; -using System.Diagnostics; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using System.Security.Cryptography.X509Certificates; - -using LumiSoft.Net; -using LumiSoft.Net.AUTH; - -namespace LumiSoft.Net.SMTP.Server -{ - #region Event delegates - - /// - /// Represents the method that will handle the AuthUser event for SMTP_Server. - /// - /// The source of the event. - /// A AuthUser_EventArgs that contains the event data. - public delegate void AuthUserEventHandler(object sender,AuthUser_EventArgs e); - - /// - /// Represents the method that will handle the ValidateMailFrom event for SMTP_Server. - /// - /// The source of the event. - /// A ValidateSender_EventArgs that contains the event data. - public delegate void ValidateMailFromHandler(object sender,ValidateSender_EventArgs e); - - /// - /// Represents the method that will handle the ValidateMailTo event for SMTP_Server. - /// - /// The source of the event. - /// A ValidateRecipient_EventArgs that contains the event data. - public delegate void ValidateMailToHandler(object sender,ValidateRecipient_EventArgs e); - - /// - /// Represents the method that will handle the ValidateMailboxSize event for SMTP_Server. - /// - /// The source of the event. - /// A ValidateMailboxSize_EventArgs that contains the event data. - public delegate void ValidateMailboxSize(object sender,ValidateMailboxSize_EventArgs e); - - /// - /// Represents the method that will handle the GetMessageStoreStream event for SMTP_Server. - /// - /// The source of the event. - /// A GetMessageStoreStream_eArgs that contains the event data. - public delegate void GetMessageStoreStreamHandler(object sender,GetMessageStoreStream_eArgs e); - - /// - /// Represents the method that will handle the MessageStoringCompleted event for SMTP_Server. - /// - /// The source of the event. - /// A MessageStoringCompleted_eArgs that contains the event data. - public delegate void MessageStoringCompletedHandler(object sender,MessageStoringCompleted_eArgs e); - - #endregion - - /// - /// SMTP server component. - /// - public class SMTP_Server : SocketServer - { - private int m_MaxConnectionsPerIP = 0; - private int m_MaxMessageSize = 1000000; - private int m_MaxRecipients = 100; - private SaslAuthTypes m_SupportedAuth = SaslAuthTypes.All; - private string m_GreetingText = ""; - - #region Event declarations - - /// - /// Occurs when new computer connected to SMTP server. - /// - public event ValidateIPHandler ValidateIPAddress = null; - - /// - /// Occurs when connected user tryes to authenticate. - /// - public event AuthUserEventHandler AuthUser = null; - - /// - /// Occurs when server needs to validate sender. - /// - public event ValidateMailFromHandler ValidateMailFrom = null; - - /// - /// Occurs when server needs to validate recipient. - /// - public event ValidateMailToHandler ValidateMailTo = null; - - /// - /// Occurs when server needs to validate recipient mailbox size. - /// - public event ValidateMailboxSize ValidateMailboxSize = null; - - /// - /// Occurs when server needs to get stream where to store incoming message. - /// - public event GetMessageStoreStreamHandler GetMessageStoreStream = null; - - /// - /// Occurs when server has finished message storing. - /// - public event MessageStoringCompletedHandler MessageStoringCompleted = null; - - /// - /// Occurs when SMTP session log is available. - /// - public event LogEventHandler SessionLog = null; - - #endregion - - - /// - /// Defalut constructor. - /// - public SMTP_Server() : base() - { - this.BindInfo = new IPBindInfo[]{new IPBindInfo("",IPAddress.Any,25,SslMode.None,null)}; - } - - - #region override InitNewSession - - /// - /// Initialize and start new session here. Session isn't added to session list automatically, - /// session must add itself to server session list by calling AddSession(). - /// - /// Connected client socket. - /// BindInfo what accepted socket. - protected override void InitNewSession(Socket socket,IPBindInfo bindInfo) - { - // Check maximum conncurent connections from 1 IP. - if(m_MaxConnectionsPerIP > 0){ - lock(this.Sessions){ - int nSessions = 0; - foreach(SocketServerSession s in this.Sessions){ - IPEndPoint ipEndpoint = s.RemoteEndPoint; - if(ipEndpoint != null){ - if(ipEndpoint.Address.Equals(((IPEndPoint)socket.RemoteEndPoint).Address)){ - nSessions++; - } - } - - // Maimum allowed exceeded - if(nSessions >= m_MaxConnectionsPerIP){ - socket.Send(System.Text.Encoding.ASCII.GetBytes("421 Maximum connections from your IP address is exceeded, try again later !\r\n")); - socket.Shutdown(SocketShutdown.Both); - socket.Close(); - return; - } - } - } - } - - string sessionID = Guid.NewGuid().ToString(); - SocketEx socketEx = new SocketEx(socket); - if(LogCommands){ - socketEx.Logger = new SocketLogger(socket,this.SessionLog); - socketEx.Logger.SessionID = sessionID; - } - SMTP_Session session = new SMTP_Session(sessionID,socketEx,bindInfo,this); - } - - #endregion - - - #region Properties Implementaion - - /// - /// Gets or sets server greeting text. - /// - public string GreetingText - { - get{ return m_GreetingText; } - - set{ m_GreetingText = value; } - } - - /// - /// Gets or sets maximum allowed conncurent connections from 1 IP address. Value 0 means unlimited connections. - /// - public int MaxConnectionsPerIP - { - get{ return m_MaxConnectionsPerIP; } - - set{ m_MaxConnectionsPerIP = value; } - } - - /// - /// Maximum message size in bytes. - /// - public int MaxMessageSize - { - get{ return m_MaxMessageSize; } - - set{ m_MaxMessageSize = value; } - } - - /// - /// Maximum recipients per message. - /// - public int MaxRecipients - { - get{ return m_MaxRecipients; } - - set{ m_MaxRecipients = value; } - } - - /// - /// Gets or sets server supported authentication types. - /// - public SaslAuthTypes SupportedAuthentications - { - get{ return m_SupportedAuth; } - - set{ m_SupportedAuth = value; } - } - - /// - /// Gets active sessions. - /// - public new SMTP_Session[] Sessions - { - get{ - SocketServerSession[] sessions = base.Sessions; - SMTP_Session[] smtpSessions = new SMTP_Session[sessions.Length]; - sessions.CopyTo(smtpSessions,0); - - return smtpSessions; - } - } - - #endregion - - #region Events Implementation - - #region method OnValidate_IpAddress - - /// - /// Raises event ValidateIP event. - /// - /// Reference to current smtp session. - internal ValidateIP_EventArgs OnValidate_IpAddress(SMTP_Session session) - { - ValidateIP_EventArgs oArg = new ValidateIP_EventArgs(session.LocalEndPoint,session.RemoteEndPoint); - if(this.ValidateIPAddress != null){ - this.ValidateIPAddress(this, oArg); - } - - session.Tag = oArg.SessionTag; - - return oArg; - } - - #endregion - - #region method OnAuthUser - - /// - /// Raises event AuthUser. - /// - /// Reference to current smtp session. - /// User name. - /// Password compare data,it depends of authentication type. - /// For md5 eg. md5 calculation hash.It depends of authentication type. - /// Authentication type. - /// - internal AuthUser_EventArgs OnAuthUser(SMTP_Session session,string userName,string passwordData,string data,AuthType authType) - { - AuthUser_EventArgs oArgs = new AuthUser_EventArgs(session,userName,passwordData,data,authType); - if(this.AuthUser != null){ - this.AuthUser(this,oArgs); - } - - return oArgs; - } - - #endregion - - #region method OnValidate_MailFrom - - /// - /// Raises event ValidateMailFrom. - /// - /// - /// - /// - /// - internal ValidateSender_EventArgs OnValidate_MailFrom(SMTP_Session session,string reverse_path,string email) - { - ValidateSender_EventArgs oArg = new ValidateSender_EventArgs(session,email); - if(this.ValidateMailFrom != null){ - this.ValidateMailFrom(this, oArg); - } - - return oArg; - } - - #endregion - - #region method OnValidate_MailTo - - /// - /// Raises event ValidateMailTo. - /// - /// - /// - /// - /// - /// - internal ValidateRecipient_EventArgs OnValidate_MailTo(SMTP_Session session,string forward_path,string email,bool authenticated) - { - ValidateRecipient_EventArgs oArg = new ValidateRecipient_EventArgs(session,email,authenticated); - if(this.ValidateMailTo != null){ - this.ValidateMailTo(this, oArg); - } - - return oArg; - } - - #endregion - - #region method Validate_MailBoxSize - - /// - /// Raises event ValidateMailboxSize. - /// - /// - /// - /// - /// - internal bool Validate_MailBoxSize(SMTP_Session session,string eAddress,long messageSize) - { - ValidateMailboxSize_EventArgs oArgs = new ValidateMailboxSize_EventArgs(session,eAddress,messageSize); - if(this.ValidateMailboxSize != null){ - this.ValidateMailboxSize(this,oArgs); - } - - return oArgs.IsValid; - } - - #endregion - - - #region method OnGetMessageStoreStream - - /// - /// Raises event GetMessageStoreStream. - /// - /// Reference to calling SMTP session. - /// - internal GetMessageStoreStream_eArgs OnGetMessageStoreStream(SMTP_Session session) - { - GetMessageStoreStream_eArgs eArgs = new GetMessageStoreStream_eArgs(session); - if(this.GetMessageStoreStream != null){ - this.GetMessageStoreStream(this,eArgs); - } - return eArgs; - } - - #endregion - - #region method OnMessageStoringCompleted - - /// - /// Raises event MessageStoringCompleted. - /// - /// Reference to calling SMTP session. - /// Null if no errors, otherwise conatians error text. If errors happened that means that messageStream is incomplete. - /// Stream where message was stored. - internal MessageStoringCompleted_eArgs OnMessageStoringCompleted(SMTP_Session session,string errorText,Stream messageStream) - { - MessageStoringCompleted_eArgs eArgs = new MessageStoringCompleted_eArgs(session,errorText,messageStream); - if(this.MessageStoringCompleted != null){ - this.MessageStoringCompleted(this,eArgs); - } - - return eArgs; - } - - #endregion - - #endregion - - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/SMTP_Session.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/SMTP_Session.cs deleted file mode 100644 index 6ba69f81b..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/SMTP_Session.cs +++ /dev/null @@ -1,1700 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.IO; -using System.Net; -using System.Net.Sockets; -using System.Collections; -using System.Text; - -using LumiSoft.Net; -using LumiSoft.Net.SMTP; -using LumiSoft.Net.AUTH; - -namespace LumiSoft.Net.SMTP.Server -{ - /// - /// SMTP Session. - /// - public class SMTP_Session : SocketServerSession - { - private SMTP_Server m_pServer = null; - private Stream m_pMsgStream = null; - private SMTP_Cmd_Validator m_CmdValidator = null; - private long m_BDAT_ReadedCount = 0; - private string m_EhloName = ""; - private string m_Reverse_path = ""; // Holds sender's reverse path. - private Hashtable m_Forward_path = null; // Holds Mail to. - private int m_BadCmdCount = 0; // Holds number of bad commands. - private BodyType m_BodyType; - private bool m_BDat = false; - - /// - /// Default constructor. - /// - /// Session ID. - /// Server connected socket. - /// BindInfo what accepted socket. - /// Reference to server. - internal SMTP_Session(string sessionID,SocketEx socket,IPBindInfo bindInfo,SMTP_Server server) : base(sessionID,socket,bindInfo,server) - { - m_pServer = server; - m_BodyType = BodyType.x7_bit; - m_Forward_path = new Hashtable(); - m_CmdValidator = new SMTP_Cmd_Validator(); - - // Start session proccessing - StartSession(); - } - - - #region method StartSession - - /// - /// Starts session. - /// - private void StartSession() - { - // Add session to session list - m_pServer.AddSession(this); - - try{ - // Check if ip is allowed to connect this computer - ValidateIP_EventArgs oArg = m_pServer.OnValidate_IpAddress(this); - if(oArg.Validated){ - //--- Dedicated SSL connection, switch to SSL -----------------------------------// - if(this.BindInfo.SslMode == SslMode.SSL){ - try{ - this.Socket.SwitchToSSL(this.BindInfo.Certificate); - - if(this.Socket.Logger != null){ - this.Socket.Logger.AddTextEntry("SSL negotiation completed successfully."); - } - } - catch(Exception x){ - if(this.Socket.Logger != null){ - this.Socket.Logger.AddTextEntry("SSL handshake failed ! " + x.Message); - - EndSession(); - return; - } - } - } - //-------------------------------------------------------------------------------// - - if(!string.IsNullOrEmpty(m_pServer.GreetingText)){ - this.Socket.WriteLine("220 " + m_pServer.GreetingText); - } - else{ - this.Socket.WriteLine("220 " + Net_Utils.GetLocalHostName(this.BindInfo.HostName) + " SMTP Server ready"); - } - - BeginRecieveCmd(); - } - else{ - // There is user specified error text, send it to connected socket - if(oArg.ErrorText.Length > 0){ - this.Socket.WriteLine(oArg.ErrorText); - } - - EndSession(); - } - } - catch(Exception x){ - OnError(x); - } - } - - #endregion - - #region method EndSession - - /// - /// Ends session, closes socket. - /// - private void EndSession() - { - try{ - try{ - // Message storing not completed successfully, otherwise it must be null here. - // This can happen if BDAT -> QUIT and LAST BDAT block wasn't sent or - // when session times out on DATA or BDAT command. - if(m_pMsgStream != null){ - // We must call that method to notify Message stream owner to close/dispose that stream. - m_pServer.OnMessageStoringCompleted(this,"Message storing not completed successfully",m_pMsgStream); - m_pMsgStream = null; - } - } - catch{ - } - - if(this.Socket != null){ - // Write logs to log file, if needed - if(m_pServer.LogCommands){ - this.Socket.Logger.Flush(); - } - - this.Socket.Shutdown(SocketShutdown.Both); - this.Socket.Disconnect(); - //this.Socket = null; - } - } - catch{ // We don't need to check errors here, because they only may be Socket closing errors. - } - finally{ - m_pServer.RemoveSession(this); - } - } - - #endregion - - - #region method Kill - - /// - /// Kill this session. - /// - public override void Kill() - { - EndSession(); - } - - #endregion - - #region method OnSessionTimeout - - /// - /// Is called by server when session has timed out. - /// - internal protected override void OnSessionTimeout() - { - try{ - this.Socket.WriteLine("421 Session timeout, closing transmission channel"); - } - catch{ - } - - EndSession(); - } - - #endregion - - #region method OnError - - /// - /// Is called when error occures. - /// - /// - private void OnError(Exception x) - { - try{ - // We must see InnerException too, SocketException may be as inner exception. - SocketException socketException = null; - if(x is SocketException){ - socketException = (SocketException)x; - } - else if(x.InnerException != null && x.InnerException is SocketException){ - socketException = (SocketException)x.InnerException; - } - - if(socketException != null){ - // Client disconnected without shutting down. - if(socketException.ErrorCode == 10054 || socketException.ErrorCode == 10053){ - if(m_pServer.LogCommands){ - this.Socket.Logger.AddTextEntry("Client aborted/disconnected"); - } - - EndSession(); - - // Exception handled, return - return; - } - // Connection timed out. - else if(socketException.ErrorCode == 10060){ - if(m_pServer.LogCommands){ - this.Socket.Logger.AddTextEntry("Connection timeout."); - } - - EndSession(); - - // Exception handled, return - return; - } - } - - m_pServer.OnSysError("",x); - } - catch(Exception ex){ - m_pServer.OnSysError("",ex); - } - } - - #endregion - - - #region method BeginRecieveCmd - - /// - /// Starts recieveing command. - /// - private void BeginRecieveCmd() - { - MemoryStream strm = new MemoryStream(); - this.Socket.BeginReadLine(strm,1024,strm,new SocketCallBack(this.EndRecieveCmd)); - } - - #endregion - - #region method EndRecieveCmd - - /// - /// Is called if command is recieved. - /// - /// - /// - /// - /// - private void EndRecieveCmd(SocketCallBackResult result,long count,Exception exception,object tag) - { - try{ - switch(result) - { - case SocketCallBackResult.Ok: - MemoryStream strm = (MemoryStream)tag; - - string cmdLine = System.Text.Encoding.Default.GetString(strm.ToArray()); - - // Exceute command - if(SwitchCommand(cmdLine)){ - // Session end, close session - EndSession(); - } - break; - - case SocketCallBackResult.LengthExceeded: - this.Socket.WriteLine("500 Line too long."); - - BeginRecieveCmd(); - break; - - case SocketCallBackResult.SocketClosed: - EndSession(); - break; - - case SocketCallBackResult.Exception: - OnError(exception); - break; - } - } - catch(ReadException x){ - if(x.ReadReplyCode == ReadReplyCode.LengthExceeded){ - this.Socket.WriteLine("500 Line too long."); - - BeginRecieveCmd(); - } - else if(x.ReadReplyCode == ReadReplyCode.SocketClosed){ - EndSession(); - } - else if(x.ReadReplyCode == ReadReplyCode.UnKnownError){ - OnError(x); - } - } - catch(Exception x){ - OnError(x); - } - } - - #endregion - - - #region method SwitchCommand - - /// - /// Executes SMTP command. - /// - /// Original command text. - /// Returns true if must end session(command loop). - private bool SwitchCommand(string SMTP_commandTxt) - { - //---- Parse command --------------------------------------------------// - string[] cmdParts = SMTP_commandTxt.TrimStart().Split(new char[]{' '}); - string SMTP_command = cmdParts[0].ToUpper().Trim(); - string argsText = Core.GetArgsText(SMTP_commandTxt,SMTP_command); - //---------------------------------------------------------------------// - - bool getNextCmd = true; - - switch(SMTP_command) - { - case "HELO": - HELO(argsText); - getNextCmd = false; - break; - - case "EHLO": - EHLO(argsText); - getNextCmd = false; - break; - - case "STARTTLS": - STARTTLS(argsText); - getNextCmd = false; - break; - - case "AUTH": - AUTH(argsText); - break; - - case "MAIL": - MAIL(argsText); - getNextCmd = false; - break; - - case "RCPT": - RCPT(argsText); - getNextCmd = false; - break; - - case "DATA": - BeginDataCmd(argsText); - getNextCmd = false; - break; - - case "BDAT": - BeginBDATCmd(argsText); - getNextCmd = false; - break; - - case "RSET": - RSET(argsText); - getNextCmd = false; - break; - - // case "VRFY": - // VRFY(); - // break; - - // case "EXPN": - // EXPN(); - // break; - - case "HELP": - HELP(); - break; - - case "NOOP": - NOOP(); - getNextCmd = false; - break; - - case "QUIT": - QUIT(argsText); - getNextCmd = false; - return true; - - default: - this.Socket.WriteLine("500 command unrecognized"); - - //---- Check that maximum bad commands count isn't exceeded ---------------// - if(m_BadCmdCount > m_pServer.MaxBadCommands-1){ - this.Socket.WriteLine("421 Too many bad commands, closing transmission channel"); - return true; - } - m_BadCmdCount++; - //-------------------------------------------------------------------------// - - break; - } - - if(getNextCmd){ - BeginRecieveCmd(); - } - - return false; - } - - #endregion - - - #region method HELO - - private void HELO(string argsText) - { - /* Rfc 2821 4.1.1.1 - These commands, and a "250 OK" reply to one of them, confirm that - both the SMTP client and the SMTP server are in the initial state, - that is, there is no transaction in progress and all state tables and - buffers are cleared. - - Arguments: - Host name. - - Syntax: - "HELO" SP Domain CRLF - */ - - m_EhloName = argsText; - - ResetState(); - - this.Socket.BeginWriteLine("250 " + Net_Utils.GetLocalHostName(this.BindInfo.HostName) + " Hello [" + this.RemoteEndPoint.Address.ToString() + "]",new SocketCallBack(this.EndSend)); - m_CmdValidator.Helo_ok = true; - } - - #endregion - - #region method EHLO - - private void EHLO(string argsText) - { - /* Rfc 2821 4.1.1.1 - These commands, and a "250 OK" reply to one of them, confirm that - both the SMTP client and the SMTP server are in the initial state, - that is, there is no transaction in progress and all state tables and - buffers are cleared. - */ - - m_EhloName = argsText; - - ResetState(); - - //--- Construct supported AUTH types value ----------------------------// - string authTypesString = ""; - if((m_pServer.SupportedAuthentications & SaslAuthTypes.Login) != 0){ - authTypesString += "LOGIN "; - } - if((m_pServer.SupportedAuthentications & SaslAuthTypes.Cram_md5) != 0){ - authTypesString += "CRAM-MD5 "; - } - if((m_pServer.SupportedAuthentications & SaslAuthTypes.Digest_md5) != 0){ - authTypesString += "DIGEST-MD5 "; - } - authTypesString = authTypesString.Trim(); - //-----------------------------------------------------------------------// - - string reply = ""; - reply += "250-" + Net_Utils.GetLocalHostName(this.BindInfo.HostName) + " Hello [" + this.RemoteEndPoint.Address.ToString() + "]\r\n"; - reply += "250-PIPELINING\r\n"; - reply += "250-SIZE " + m_pServer.MaxMessageSize + "\r\n"; - // reply += "250-DSN\r\n"; - // reply += "250-HELP\r\n"; - reply += "250-8BITMIME\r\n"; - reply += "250-BINARYMIME\r\n"; - reply += "250-CHUNKING\r\n"; - if(authTypesString.Length > 0){ - reply += "250-AUTH " + authTypesString + "\r\n"; - } - if(!this.Socket.SSL && this.BindInfo.Certificate != null){ - reply += "250-STARTTLS\r\n"; - } - reply += "250 Ok\r\n"; - - this.Socket.BeginWriteLine(reply,null,new SocketCallBack(this.EndSend)); - - m_CmdValidator.Helo_ok = true; - } - - #endregion - - #region method STARTTLS - - private void STARTTLS(string argsText) - { - /* RFC 2487 STARTTLS 5. STARTTLS Command. - The format for the STARTTLS command is: - - STARTTLS - - with no parameters. - - After the client gives the STARTTLS command, the server responds with - one of the following reply codes: - - 220 Ready to start TLS - 501 Syntax error (no parameters allowed) - 454 TLS not available due to temporary reason - - 5.2 Result of the STARTTLS Command - Upon completion of the TLS handshake, the SMTP protocol is reset to - the initial state (the state in SMTP after a server issues a 220 - service ready greeting). - */ - - if(this.Socket.SSL){ - this.Socket.WriteLine("500 TLS already started !"); - return; - } - - if(this.BindInfo.Certificate == null){ - this.Socket.WriteLine("454 TLS not available, SSL certificate isn't specified !"); - return; - } - - this.Socket.WriteLine("220 Ready to start TLS"); - - try{ - this.Socket.SwitchToSSL(this.BindInfo.Certificate); - - if(m_pServer.LogCommands){ - this.Socket.Logger.AddTextEntry("TLS negotiation completed successfully."); - } - } - catch(Exception x){ - this.Socket.WriteLine("500 TLS handshake failed ! " + x.Message); - } - - ResetState(); - - BeginRecieveCmd(); - } - - #endregion - - #region method AUTH - - private void AUTH(string argsText) - { - /* Rfc 2554 AUTH --------------------------------------------------// - Restrictions: - After an AUTH command has successfully completed, no more AUTH - commands may be issued in the same session. After a successful - AUTH command completes, a server MUST reject any further AUTH - commands with a 503 reply. - - Remarks: - If an AUTH command fails, the server MUST behave the same as if - the client had not issued the AUTH command. - */ - if(this.Authenticated){ - this.Socket.WriteLine("503 already authenticated"); - return; - } - - try{ - //------ Parse parameters -------------------------------------// - string userName = ""; - string password = ""; - AuthUser_EventArgs aArgs = null; - - string[] param = argsText.Split(new char[]{' '}); - switch(param[0].ToUpper()) - { - case "PLAIN": - this.Socket.WriteLine("504 Unrecognized authentication type."); - break; - - case "LOGIN": - - #region LOGIN authentication - - //---- AUTH = LOGIN ------------------------------ - /* Login - C: AUTH LOGIN - S: 334 VXNlcm5hbWU6 - C: username_in_base64 - S: 334 UGFzc3dvcmQ6 - C: password_in_base64 - - or (initial-response argument included to avoid one 334 server response) - - C: AUTH LOGIN username_in_base64 - S: 334 UGFzc3dvcmQ6 - C: password_in_base64 - - - VXNlcm5hbWU6 base64_decoded= USERNAME - UGFzc3dvcmQ6 base64_decoded= PASSWORD - */ - // Note: all strings are base64 strings eg. VXNlcm5hbWU6 = UserName. - - // No user name included (initial-response argument) - if(param.Length == 1){ - // Query UserName - this.Socket.WriteLine("334 VXNlcm5hbWU6"); - - string userNameLine = this.Socket.ReadLine(); - // Encode username from base64 - if(userNameLine.Length > 0){ - userName = System.Text.Encoding.Default.GetString(Convert.FromBase64String(userNameLine)); - } - } - // User name included, use it - else{ - userName = System.Text.Encoding.Default.GetString(Convert.FromBase64String(param[1])); - } - - // Query Password - this.Socket.WriteLine("334 UGFzc3dvcmQ6"); - - string passwordLine = this.Socket.ReadLine(); - // Encode password from base64 - if(passwordLine.Length > 0){ - password = System.Text.Encoding.Default.GetString(Convert.FromBase64String(passwordLine)); - } - - aArgs = m_pServer.OnAuthUser(this,userName,password,"",AuthType.Plain); - if(aArgs.Validated){ - this.Socket.WriteLine("235 Authentication successful."); - - this.SetUserName(userName); - } - else{ - this.Socket.WriteLine("535 Authentication failed"); - } - - #endregion - - break; - - case "CRAM-MD5": - - #region CRAM-MD5 authentication - - /* Cram-M5 - C: AUTH CRAM-MD5 - S: 334 - C: base64(username password_hash) - */ - - string md5Hash = "<" + Guid.NewGuid().ToString().ToLower() + ">"; - this.Socket.WriteLine("334 " + Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(md5Hash))); - - string reply = this.Socket.ReadLine(); - - reply = System.Text.Encoding.Default.GetString(Convert.FromBase64String(reply)); - string[] replyArgs = reply.Split(' '); - userName = replyArgs[0]; - - aArgs = m_pServer.OnAuthUser(this,userName,replyArgs[1],md5Hash,AuthType.CRAM_MD5); - if(aArgs.Validated){ - this.Socket.WriteLine("235 Authentication successful."); - - this.SetUserName(userName); - } - else{ - this.Socket.WriteLine("535 Authentication failed"); - } - - #endregion - - break; - - case "DIGEST-MD5": - - #region DIGEST-MD5 authentication - - /* RFC 2831 AUTH DIGEST-MD5 - * - * Example: - * - * C: AUTH DIGEST-MD5 - * S: 334 base64(realm="elwood.innosoft.com",nonce="OA6MG9tEQGm2hh",qop="auth",algorithm=md5-sess) - * C: base64(username="chris",realm="elwood.innosoft.com",nonce="OA6MG9tEQGm2hh", - * nc=00000001,cnonce="OA6MHXh6VqTrRk",digest-uri="smtp/elwood.innosoft.com", - * response=d388dad90d4bbd760a152321f2143af7,qop=auth) - * S: 334 base64(rspauth=ea40f60335c427b5527b84dbabcdfffd) - * C: - * S: 235 Authentication successful. - */ - - string nonce = Auth_HttpDigest.CreateNonce(); - string opaque = Auth_HttpDigest.CreateOpaque(); - Auth_HttpDigest digest = new Auth_HttpDigest(this.BindInfo.HostName,nonce,opaque); - digest.Algorithm = "md5-sess"; - - this.Socket.WriteLine("334 " + AuthHelper.Base64en(digest.ToChallange(false))); - - string clientResponse = AuthHelper.Base64de(this.Socket.ReadLine()); - digest = new Auth_HttpDigest(clientResponse,"AUTHENTICATE"); - - // Check that realm,nonce and opaque in client response are same as we specified. - if(this.BindInfo.HostName != digest.Realm){ - this.Socket.WriteLine("535 Authentication failed, 'realm' won't match."); - return; - } - if(nonce != digest.Nonce){ - this.Socket.WriteLine("535 Authentication failed, 'nonce' won't match."); - return; - } - if(opaque != digest.Opaque){ - this.Socket.WriteLine("535 Authentication failed, 'opaque' won't match."); - return; - } - - userName = digest.UserName; - - aArgs = m_pServer.OnAuthUser(this,userName,digest.Response,clientResponse,AuthType.DIGEST_MD5); - if(aArgs.Validated){ - // Send server computed password hash - this.Socket.WriteLine("334 " + AuthHelper.Base64en("rspauth=" + aArgs.ReturnData)); - - // We must got empty line here - clientResponse = this.Socket.ReadLine(); - if(clientResponse == ""){ - this.Socket.WriteLine("235 Authentication successful."); - - this.SetUserName(userName); - } - else{ - this.Socket.WriteLine("535 Authentication failed, unexpected client response."); - } - } - else{ - this.Socket.WriteLine("535 Authentication failed."); - } - - #endregion - - break; - - default: - this.Socket.WriteLine("504 Unrecognized authentication type."); - break; - } - //-----------------------------------------------------------------// - } - catch{ - this.Socket.WriteLine("535 Authentication failed."); - } - } - - #endregion - - #region method MAIL - - private void MAIL(string argsText) - { - /* RFC 2821 3.3 - NOTE: - This command tells the SMTP-receiver that a new mail transaction is - starting and to reset all its state tables and buffers, including any - recipients or mail data. The portion of the first or - only argument contains the source mailbox (between "<" and ">" - brackets), which can be used to report errors (see section 4.2 for a - discussion of error reporting). If accepted, the SMTP server returns - a 250 OK reply. - - MAIL FROM: [SP ] - reverse-path = "<" [ A-d-l ":" ] Mailbox ">" - Mailbox = Local-part "@" Domain - - body-value ::= "7BIT" / "8BITMIME" / "BINARYMIME" - - Examples: - C: MAIL FROM: - C: MAIL FROM: SIZE=500000 BODY=8BITMIME AUTH=xxxx - */ - - if(!m_CmdValidator.MayHandle_MAIL){ - if(m_CmdValidator.MailFrom_ok){ - this.Socket.BeginWriteLine("503 Sender already specified",new SocketCallBack(this.EndSend)); - } - else{ - this.Socket.BeginWriteLine("503 Bad sequence of commands",new SocketCallBack(this.EndSend)); - } - return; - } - - //------ Parse parameters -------------------------------------------------------------------// - string senderEmail = ""; - long messageSize = 0; - BodyType bodyType = BodyType.x7_bit; - bool isFromParam = false; - - // Parse while all params parsed or while is breaked - while(argsText.Length > 0){ - if(argsText.ToLower().StartsWith("from:")){ - // Remove from: - argsText = argsText.Substring(5).Trim(); - - // If there is more parameters - if(argsText.IndexOf(" ") > -1){ - senderEmail = argsText.Substring(0,argsText.IndexOf(" ")); - argsText = argsText.Substring(argsText.IndexOf(" ")).Trim(); - } - else{ - senderEmail = argsText; - argsText = ""; - } - - // If address between <>, remove <> - if(senderEmail.StartsWith("<") && senderEmail.EndsWith(">")){ - senderEmail = senderEmail.Substring(1,senderEmail.Length - 2); - } - - isFromParam = true; - } - else if(argsText.ToLower().StartsWith("size=")){ - // Remove size= - argsText = argsText.Substring(5).Trim(); - - string sizeS = ""; - // If there is more parameters - if(argsText.IndexOf(" ") > -1){ - sizeS = argsText.Substring(0,argsText.IndexOf(" ")); - argsText = argsText.Substring(argsText.IndexOf(" ")).Trim(); - } - else{ - sizeS = argsText; - argsText = ""; - } - - // See if value ok - if(Core.IsNumber(sizeS)){ - messageSize = Convert.ToInt64(sizeS); - } - else{ - this.Socket.BeginWriteLine("501 SIZE parameter value is invalid. Syntax:{MAIL FROM:
    [SIZE=msgSize] [BODY=8BITMIME]}",new SocketCallBack(this.EndSend)); - return; - } - } - else if(argsText.ToLower().StartsWith("body=")){ - // Remove body= - argsText = argsText.Substring(5).Trim(); - - string bodyTypeS = ""; - // If there is more parameters - if(argsText.IndexOf(" ") > -1){ - bodyTypeS = argsText.Substring(0,argsText.IndexOf(" ")); - argsText = argsText.Substring(argsText.IndexOf(" ")).Trim(); - } - else{ - bodyTypeS = argsText; - argsText = ""; - } - - // See if value ok - switch(bodyTypeS.ToUpper()) - { - case "7BIT": - bodyType = BodyType.x7_bit; - break; - case "8BITMIME": - bodyType = BodyType.x8_bit; - break; - case "BINARYMIME": - bodyType = BodyType.binary; - break; - default: - this.Socket.BeginWriteLine("501 BODY parameter value is invalid. Syntax:{MAIL FROM:
    [BODY=(7BIT/8BITMIME)]}",new SocketCallBack(this.EndSend)); - return; - } - } - else if(argsText.ToLower().StartsWith("auth=")){ - // Currently just eat AUTH keyword - - // Remove auth= - argsText = argsText.Substring(5).Trim(); - - string authS = ""; - // If there is more parameters - if(argsText.IndexOf(" ") > -1){ - authS = argsText.Substring(0,argsText.IndexOf(" ")); - argsText = argsText.Substring(argsText.IndexOf(" ")).Trim(); - } - else{ - authS = argsText; - argsText = ""; - } - } - else{ - this.Socket.BeginWriteLine("501 Error in parameters. Syntax:{MAIL FROM:
    [SIZE=msgSize] [BODY=8BITMIME]}",new SocketCallBack(this.EndSend)); - return; - } - } - - // If required parameter 'FROM:' is missing - if(!isFromParam){ - this.Socket.BeginWriteLine("501 Required param FROM: is missing. Syntax:{MAIL FROM:
    [SIZE=msgSize] [BODY=8BITMIME]}",new SocketCallBack(this.EndSend)); - return; - } - //---------------------------------------------------------------------------------------------// - - //--- Check message size - if(m_pServer.MaxMessageSize > messageSize){ - // Check if sender is ok - ValidateSender_EventArgs eArgs = m_pServer.OnValidate_MailFrom(this,senderEmail,senderEmail); - if(eArgs.Validated){ - // See note above - ResetState(); - - // Store reverse path - m_Reverse_path = senderEmail; - m_CmdValidator.MailFrom_ok = true; - - //-- Store params - m_BodyType = bodyType; - - this.Socket.BeginWriteLine("250 OK <" + senderEmail + "> Sender ok",new SocketCallBack(this.EndSend)); - } - else{ - if(eArgs.ErrorText != null && eArgs.ErrorText.Length > 0){ - this.Socket.BeginWriteLine("550 " + eArgs.ErrorText,new SocketCallBack(this.EndSend)); - } - else{ - this.Socket.BeginWriteLine("550 You are refused to send mail here",new SocketCallBack(this.EndSend)); - } - } - } - else{ - this.Socket.BeginWriteLine("552 Message exceeds allowed size",new SocketCallBack(this.EndSend)); - } - } - - #endregion - - #region method RCPT - - private void RCPT(string argsText) - { - /* RFC 2821 4.1.1.3 RCPT - NOTE: - This command is used to identify an individual recipient of the mail - data; multiple recipients are specified by multiple use of this - command. The argument field contains a forward-path and may contain - optional parameters. - - Relay hosts SHOULD strip or ignore source routes, and - names MUST NOT be copied into the reverse-path. - - Example: - RCPT TO:<@hosta.int,@jkl.org:userc@d.bar.org> - - will normally be sent directly on to host d.bar.org with envelope - commands - - RCPT TO: - RCPT TO: SIZE=40000 - - RCPT TO: [ SP ] - */ - - /* RFC 2821 3.3 - If a RCPT command appears without a previous MAIL command, - the server MUST return a 503 "Bad sequence of commands" response. - */ - if(!m_CmdValidator.MayHandle_RCPT || m_BDat){ - this.Socket.BeginWriteLine("503 Bad sequence of commands",new SocketCallBack(this.EndSend)); - return; - } - - // Check that recipient count isn't exceeded - if(m_Forward_path.Count > m_pServer.MaxRecipients){ - this.Socket.BeginWriteLine("452 Too many recipients",new SocketCallBack(this.EndSend)); - return; - } - - //------ Parse parameters -------------------------------------------------------------------// - string recipientEmail = ""; - long messageSize = 0; - bool isToParam = false; - - // Parse while all params parsed or while is breaked - while(argsText.Length > 0){ - if(argsText.ToLower().StartsWith("to:")){ - // Remove to: - argsText = argsText.Substring(3).Trim(); - - // If there is more parameters - if(argsText.IndexOf(" ") > -1){ - recipientEmail = argsText.Substring(0,argsText.IndexOf(" ")); - argsText = argsText.Substring(argsText.IndexOf(" ")).Trim(); - } - else{ - recipientEmail = argsText; - argsText = ""; - } - - // If address between <>, remove <> - if(recipientEmail.StartsWith("<") && recipientEmail.EndsWith(">")){ - recipientEmail = recipientEmail.Substring(1,recipientEmail.Length - 2); - } - - // See if value ok - if(recipientEmail.Length == 0){ - this.Socket.BeginWriteLine("501 Recipient address isn't specified. Syntax:{RCPT TO:
    [SIZE=msgSize]}",new SocketCallBack(this.EndSend)); - return; - } - - isToParam = true; - } - else if(argsText.ToLower().StartsWith("size=")){ - // Remove size= - argsText = argsText.Substring(5).Trim(); - - string sizeS = ""; - // If there is more parameters - if(argsText.IndexOf(" ") > -1){ - sizeS = argsText.Substring(0,argsText.IndexOf(" ")); - argsText = argsText.Substring(argsText.IndexOf(" ")).Trim(); - } - else{ - sizeS = argsText; - argsText = ""; - } - - // See if value ok - if(Core.IsNumber(sizeS)){ - messageSize = Convert.ToInt64(sizeS); - } - else{ - this.Socket.BeginWriteLine("501 SIZE parameter value is invalid. Syntax:{RCPT TO:
    [SIZE=msgSize]}",new SocketCallBack(this.EndSend)); - return; - } - } - else{ - this.Socket.BeginWriteLine("501 Error in parameters. Syntax:{RCPT TO:
    [SIZE=msgSize]}",new SocketCallBack(this.EndSend)); - return; - } - } - - // If required parameter 'TO:' is missing - if(!isToParam){ - this.Socket.BeginWriteLine("501 Required param TO: is missing. Syntax: [SIZE=msgSize]}",new SocketCallBack(this.EndSend)); - return; - } - //---------------------------------------------------------------------------------------------// - - // Check message size - if(m_pServer.MaxMessageSize > messageSize){ - // Check if email address is ok - ValidateRecipient_EventArgs rcpt_args = m_pServer.OnValidate_MailTo(this,recipientEmail,recipientEmail,this.Authenticated); - if(rcpt_args.Validated){ - // Check if mailbox size isn't exceeded - if(m_pServer.Validate_MailBoxSize(this,recipientEmail,messageSize)){ - // Store reciptient - if(!m_Forward_path.Contains(recipientEmail)){ - m_Forward_path.Add(recipientEmail,recipientEmail); - } - m_CmdValidator.RcptTo_ok = true; - - this.Socket.BeginWriteLine("250 OK <" + recipientEmail + "> Recipient ok",new SocketCallBack(this.EndSend)); - } - else{ - this.Socket.BeginWriteLine("552 Mailbox size limit exceeded",new SocketCallBack(this.EndSend)); - } - } - // Recipient rejected - else{ - if(rcpt_args.LocalRecipient){ - this.Socket.BeginWriteLine("550 <" + recipientEmail + "> No such user here",new SocketCallBack(this.EndSend)); - } - else{ - this.Socket.BeginWriteLine("550 <" + recipientEmail + "> Relay not allowed",new SocketCallBack(this.EndSend)); - } - } - } - else{ - this.Socket.BeginWriteLine("552 Message exceeds allowed size",new SocketCallBack(this.EndSend)); - } - } - - #endregion - - #region method DATA - - #region method BeginDataCmd - - private void BeginDataCmd(string argsText) - { - /* RFC 2821 4.1.1 - NOTE: - Several commands (RSET, DATA, QUIT) are specified as not permitting - parameters. In the absence of specific extensions offered by the - server and accepted by the client, clients MUST NOT send such - parameters and servers SHOULD reject commands containing them as - having invalid syntax. - */ - - if(argsText.Length > 0){ - this.Socket.BeginWriteLine("500 Syntax error. Syntax:{DATA}",new SocketCallBack(this.EndSend)); - return; - } - - - /* RFC 2821 4.1.1.4 DATA - NOTE: - If accepted, the SMTP server returns a 354 Intermediate reply and - considers all succeeding lines up to but not including the end of - mail data indicator to be the message text. When the end of text is - successfully received and stored the SMTP-receiver sends a 250 OK - reply. - - The mail data is terminated by a line containing only a period, that - is, the character sequence "." (see section 4.5.2). This - is the end of mail data indication. - - - When the SMTP server accepts a message either for relaying or for - final delivery, it inserts a trace record (also referred to - interchangeably as a "time stamp line" or "Received" line) at the top - of the mail data. This trace record indicates the identity of the - host that sent the message, the identity of the host that received - the message (and is inserting this time stamp), and the date and time - the message was received. Relayed messages will have multiple time - stamp lines. Details for formation of these lines, including their - syntax, is specified in section 4.4. - - */ - - - /* RFC 2821 DATA - NOTE: - If there was no MAIL, or no RCPT, command, or all such commands - were rejected, the server MAY return a "command out of sequence" - (503) or "no valid recipients" (554) reply in response to the DATA - command. - */ - if(!m_CmdValidator.MayHandle_DATA || m_BDat){ - this.Socket.BeginWriteLine("503 Bad sequence of commands",new SocketCallBack(this.EndSend)); - return; - } - - if(m_Forward_path.Count == 0){ - this.Socket.BeginWriteLine("554 no valid recipients given",new SocketCallBack(this.EndSend)); - return; - } - - // Get message store stream - GetMessageStoreStream_eArgs eArgs = m_pServer.OnGetMessageStoreStream(this); - m_pMsgStream = eArgs.StoreStream; - - // reply: 354 Start mail input - this.Socket.WriteLine("354 Start mail input; end with ."); - - //---- Construct server headers for message----------------------------------------------------------------// - string header = "Received: from " + Core.GetHostName(this.RemoteEndPoint.Address) + " (" + this.RemoteEndPoint.Address.ToString() + ")\r\n"; - header += "\tby " + this.BindInfo.HostName + " with SMTP; " + DateTime.Now.ToUniversalTime().ToString("r",System.Globalization.DateTimeFormatInfo.InvariantInfo) + "\r\n"; - - byte[] headers = System.Text.Encoding.ASCII.GetBytes(header); - m_pMsgStream.Write(headers,0,headers.Length); - //---------------------------------------------------------------------------------------------------------// - - // Begin recieving data - this.Socket.BeginReadPeriodTerminated(m_pMsgStream,m_pServer.MaxMessageSize,null,new SocketCallBack(this.EndDataCmd)); - } - - #endregion - - #region method EndDataCmd - - /// - /// Is called when DATA command is finnished. - /// - /// - /// - /// - /// - private void EndDataCmd(SocketCallBackResult result,long count,Exception exception,object tag) - { - try{ - switch(result) - { - case SocketCallBackResult.Ok: - // Notify Message stream owner that message storing completed ok. - MessageStoringCompleted_eArgs oArg = m_pServer.OnMessageStoringCompleted(this,null,m_pMsgStream); - if(oArg.ServerReply.ErrorReply){ - this.Socket.BeginWriteLine(oArg.ServerReply.ToSmtpReply("500","Error storing message"),new SocketCallBack(this.EndSend)); - } - else{ - this.Socket.BeginWriteLine(oArg.ServerReply.ToSmtpReply("250","OK"),new SocketCallBack(this.EndSend)); - } - break; - - case SocketCallBackResult.LengthExceeded: - // We must call that method to notify Message stream owner to close/dispose that stream. - m_pServer.OnMessageStoringCompleted(this,"Requested mail action aborted: exceeded storage allocation",m_pMsgStream); - - this.Socket.BeginWriteLine("552 Requested mail action aborted: exceeded storage allocation",new SocketCallBack(this.EndSend)); - break; - - case SocketCallBackResult.SocketClosed: - if(m_pMsgStream != null){ - // We must call that method to notify Message stream owner to close/dispose that stream. - m_pServer.OnMessageStoringCompleted(this,"SocketClosed",m_pMsgStream); - m_pMsgStream = null; - } - // Stream is already closed, probably by the EndSession method, do nothing. - //else{ - //} - - EndSession(); - break; - - case SocketCallBackResult.Exception: - if(m_pMsgStream != null){ - // We must call that method to notify Message stream owner to close/dispose that stream. - m_pServer.OnMessageStoringCompleted(this,"Exception: " + exception.Message,m_pMsgStream); - m_pMsgStream = null; - } - // Stream is already closed, probably by the EndSession method, do nothing. - //else{ - //} - - OnError(exception); - break; - } - - /* RFC 2821 4.1.1.4 DATA - NOTE: - Receipt of the end of mail data indication requires the server to - process the stored mail transaction information. This processing - consumes the information in the reverse-path buffer, the forward-path - buffer, and the mail data buffer, and on the completion of this - command these buffers are cleared. - */ - ResetState(); - } - catch(Exception x){ - OnError(x); - } - } - - #endregion - - #endregion - - #region function BDAT - - #region method BeginBDATCmd - - private void BeginBDATCmd(string argsText) - { - /*RFC 3030 2 - The BDAT verb takes two arguments.The first argument indicates the length, - in octets, of the binary data chunk. The second optional argument indicates - that the data chunk is the last. - - The message data is sent immediately after the trailing - of the BDAT command line. Once the receiver-SMTP receives the - specified number of octets, it will return a 250 reply code. - - The optional LAST parameter on the BDAT command indicates that this - is the last chunk of message data to be sent. The last BDAT command - MAY have a byte-count of zero indicating there is no additional data - to be sent. Any BDAT command sent after the BDAT LAST is illegal and - MUST be replied to with a 503 "Bad sequence of commands" reply code. - The state resulting from this error is indeterminate. A RSET command - MUST be sent to clear the transaction before continuing. - - A 250 response MUST be sent to each successful BDAT data block within - a mail transaction. - - bdat-cmd ::= "BDAT" SP chunk-size [ SP end-marker ] CR LF - chunk-size ::= 1*DIGIT - end-marker ::= "LAST" - */ - - if(!m_CmdValidator.MayHandle_BDAT){ - this.Socket.BeginWriteLine("503 Bad sequence of commands",new SocketCallBack(this.EndSend)); - return; - } - - string[] param = argsText.Split(new char[]{' '}); - if(param.Length > 0 && param.Length < 3){ - if(Core.IsNumber(param[0])){ - int countToRead = Convert.ToInt32(param[0]); - - // LAST specified - bool lastChunk = false; - if(param.Length == 2){ - lastChunk = true; - } - - // First BDAT command call. - if(!m_BDat){ - // Get message store stream - GetMessageStoreStream_eArgs eArgs = m_pServer.OnGetMessageStoreStream(this); - m_pMsgStream = eArgs.StoreStream; - - // Add header to first bdat block only - //---- Construct server headers for message----------------------------------------------------------------// - string header = "Received: from " + Core.GetHostName(this.RemoteEndPoint.Address) + " (" + this.RemoteEndPoint.Address.ToString() + ")\r\n"; - header += "\tby " + this.BindInfo.HostName + " with SMTP; " + DateTime.Now.ToUniversalTime().ToString("r",System.Globalization.DateTimeFormatInfo.InvariantInfo) + "\r\n"; - - byte[] headers = System.Text.Encoding.ASCII.GetBytes(header); - m_pMsgStream.Write(headers,0,headers.Length); - //---------------------------------------------------------------------------------------------------------// - } - - // Begin junking data, maximum allowed message size exceeded. - // BDAT comman is dummy, after commandline binary data is at once follwed, - // so server server must junk all data and then report error. - if((m_BDAT_ReadedCount + countToRead) > m_pServer.MaxMessageSize){ - this.Socket.BeginReadSpecifiedLength(new JunkingStream(),countToRead,lastChunk,new SocketCallBack(this.EndBDatCmd)); - } - // Begin reading data - else{ - this.Socket.BeginReadSpecifiedLength(m_pMsgStream,countToRead,lastChunk,new SocketCallBack(this.EndBDatCmd)); - } - - m_BDat = true; - } - else{ - this.Socket.BeginWriteLine("500 Syntax error. Syntax:{BDAT chunk-size [LAST]}",new SocketCallBack(this.EndSend)); - } - } - else{ - this.Socket.BeginWriteLine("500 Syntax error. Syntax:{BDAT chunk-size [LAST]}",new SocketCallBack(this.EndSend)); - } - } - - #endregion - - #region method EndBDatCmd - - private void EndBDatCmd(SocketCallBackResult result,long count,Exception exception,object tag) - { - try{ - switch(result) - { - case SocketCallBackResult.Ok: - m_BDAT_ReadedCount += count; - - // BDAT command completed, got all data junks - if((bool)tag){ - // Maximum allowed message size exceeded. - if((m_BDAT_ReadedCount) > m_pServer.MaxMessageSize){ - m_pServer.OnMessageStoringCompleted(this,"Requested mail action aborted: exceeded storage allocation",m_pMsgStream); - - this.Socket.BeginWriteLine("552 Requested mail action aborted: exceeded storage allocation",new SocketCallBack(this.EndSend)); - } - else{ - // Notify Message stream owner that message storing completed ok. - MessageStoringCompleted_eArgs oArg = m_pServer.OnMessageStoringCompleted(this,null,m_pMsgStream); - if(oArg.ServerReply.ErrorReply){ - this.Socket.BeginWriteLine(oArg.ServerReply.ToSmtpReply("500","Error storing message"),new SocketCallBack(this.EndSend)); - } - else{ - this.Socket.BeginWriteLine(oArg.ServerReply.ToSmtpReply("250","Message(" + m_BDAT_ReadedCount + " bytes) stored ok."),new SocketCallBack(this.EndSend)); - } - } - - /* RFC 2821 4.1.1.4 DATA - NOTE: - Receipt of the end of mail data indication requires the server to - process the stored mail transaction information. This processing - consumes the information in the reverse-path buffer, the forward-path - buffer, and the mail data buffer, and on the completion of this - command these buffers are cleared. - */ - ResetState(); - } - // Got BDAT data block, BDAT must continue, that wasn't last data block. - else{ - // Maximum allowed message size exceeded. - if((m_BDAT_ReadedCount) > m_pServer.MaxMessageSize){ - this.Socket.BeginWriteLine("552 Requested mail action aborted: exceeded storage allocation",new SocketCallBack(this.EndSend)); - } - else{ - this.Socket.BeginWriteLine("250 Data block of " + count + " bytes recieved OK.",new SocketCallBack(this.EndSend)); - } - } - break; - - case SocketCallBackResult.SocketClosed: - if(m_pMsgStream != null){ - // We must call that method to notify Message stream owner to close/dispose that stream. - m_pServer.OnMessageStoringCompleted(this,"SocketClosed",m_pMsgStream); - m_pMsgStream = null; - } - // Stream is already closed, probably by the EndSession method, do nothing. - //else{ - //} - - EndSession(); - return; - - case SocketCallBackResult.Exception: - if(m_pMsgStream != null){ - // We must call that method to notify Message stream owner to close/dispose that stream. - m_pServer.OnMessageStoringCompleted(this,"Exception: " + exception.Message,m_pMsgStream); - m_pMsgStream = null; - } - // Stream is already closed, probably by the EndSession method, do nothing. - //else{ - //} - - OnError(exception); - return; - } - } - catch(Exception x){ - OnError(x); - } - } - - #endregion - - #endregion - - #region method RSET - - private void RSET(string argsText) - { - /* RFC 2821 4.1.1 - NOTE: - Several commands (RSET, DATA, QUIT) are specified as not permitting - parameters. In the absence of specific extensions offered by the - server and accepted by the client, clients MUST NOT send such - parameters and servers SHOULD reject commands containing them as - having invalid syntax. - */ - - if(argsText.Length > 0){ - this.Socket.BeginWriteLine("500 Syntax error. Syntax:{RSET}",new SocketCallBack(this.EndSend)); - return; - } - - /* RFC 2821 4.1.1.5 RESET (RSET) - NOTE: - This command specifies that the current mail transaction will be - aborted. Any stored sender, recipients, and mail data MUST be - discarded, and all buffers and state tables cleared. The receiver - MUST send a "250 OK" reply to a RSET command with no arguments. - */ - - try{ - // Message storing aborted by RSET. - // This can happen if BDAT -> RSET and LAST BDAT block wasn't sent. - if(m_pMsgStream != null){ - // We must call that method to notify Message stream owner to close/dispose that stream. - m_pServer.OnMessageStoringCompleted(this,"Message storing aborted by RSET",m_pMsgStream); - m_pMsgStream = null; - } - } - catch{ - } - - ResetState(); - - this.Socket.BeginWriteLine("250 OK",new SocketCallBack(this.EndSend)); - } - - #endregion - - #region method VRFY - - private void VRFY() - { - /* RFC 821 VRFY - Example: - S: VRFY Lumi - R: 250 Ivar Lumi - - S: VRFY lum - R: 550 String does not match anything. - */ - - // ToDo: Parse user, add new event for cheking user - - this.Socket.BeginWriteLine("502 Command not implemented",new SocketCallBack(this.EndSend)); - } - - #endregion - - #region mehtod NOOP - - private void NOOP() - { - /* RFC 2821 4.1.1.9 NOOP (NOOP) - NOTE: - This command does not affect any parameters or previously entered - commands. It specifies no action other than that the receiver send - an OK reply. - */ - - this.Socket.BeginWriteLine("250 OK",new SocketCallBack(this.EndSend)); - } - - #endregion - - #region method QUIT - - private void QUIT(string argsText) - { - /* RFC 2821 4.1.1 - NOTE: - Several commands (RSET, DATA, QUIT) are specified as not permitting - parameters. In the absence of specific extensions offered by the - server and accepted by the client, clients MUST NOT send such - parameters and servers SHOULD reject commands containing them as - having invalid syntax. - */ - - if(argsText.Length > 0){ - this.Socket.BeginWriteLine("500 Syntax error. Syntax:",new SocketCallBack(this.EndSend)); - return; - } - - /* RFC 2821 4.1.1.10 QUIT (QUIT) - NOTE: - This command specifies that the receiver MUST send an OK reply, and - then close the transmission channel. - */ - - // reply: 221 - Close transmission cannel - this.Socket.WriteLine("221 Service closing transmission channel"); - // this.Socket.BeginSendLine("221 Service closing transmission channel",null); - } - - #endregion - - - //---- Optional commands - - #region function EXPN - - private void EXPN() - { - /* RFC 821 EXPN - NOTE: - This command asks the receiver to confirm that the argument - identifies a mailing list, and if so, to return the - membership of that list. The full name of the users (if - known) and the fully specified mailboxes are returned in a - multiline reply. - - Example: - S: EXPN lsAll - R: 250-ivar lumi - R: 250- - R: 250 - */ - - this.Socket.WriteLine("502 Command not implemented"); - } - - #endregion - - #region function HELP - - private void HELP() - { - /* RFC 821 HELP - NOTE: - This command causes the receiver to send helpful information - to the sender of the HELP command. The command may take an - argument (e.g., any command name) and return more specific - information as a response. - */ - - this.Socket.WriteLine("502 Command not implemented"); - } - - #endregion - - - #region method ResetState - - private void ResetState() - { - //--- Reset variables - m_BodyType = BodyType.x7_bit; - m_Forward_path.Clear(); - m_Reverse_path = ""; - // m_Authenticated = false; // Keep AUTH - m_CmdValidator.Reset(); - m_CmdValidator.Helo_ok = true; - - m_pMsgStream = null; - m_BDat = false; - m_BDAT_ReadedCount = 0; - } - - #endregion - - - #region function EndSend - - /// - /// Is called when asynchronous send completes. - /// - /// If true, then send was successfull. - /// Count sended. - /// Exception happend on send. NOTE: available only is result=false. - /// User data. - private void EndSend(SocketCallBackResult result,long count,Exception exception,object tag) - { - try{ - switch(result) - { - case SocketCallBackResult.Ok: - BeginRecieveCmd(); - break; - - case SocketCallBackResult.SocketClosed: - EndSession(); - break; - - case SocketCallBackResult.Exception: - OnError(exception); - break; - } - } - catch(Exception x){ - OnError(x); - } - } - - #endregion - - - #region Properties Implementation - - /// - /// Gets client reported EHLO/HELO name. - /// - public string EhloName - { - get{ return m_EhloName; } - } - - /// - /// Gets body type. - /// - public BodyType BodyType - { - get{ return m_BodyType; } - } - - /// - /// Gets sender. - /// - public string MailFrom - { - get{ return m_Reverse_path; } - } - - /// - /// Gets recipients. - /// - public string[] MailTo - { - get{ - string[] to = new string[m_Forward_path.Count]; - m_Forward_path.Values.CopyTo(to,0); - - return to; - } - } - - #endregion - - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/SmtpServerReply.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/SmtpServerReply.cs deleted file mode 100644 index 62033c0c8..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/SmtpServerReply.cs +++ /dev/null @@ -1,111 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; - -namespace LumiSoft.Net.SMTP.Server -{ - /// - /// SMTP server reply info. This class specifies smtp reply what is returned to connected client. - /// - public class SmtpServerReply - { - private bool m_ErrorReply = false; - private int m_SmtpReplyCode = -1; - private string m_ReplyText = ""; - - /// - /// Default consttuctor. - /// - public SmtpServerReply() - { - } - - - #region method ToSmtpReply - - internal string ToSmtpReply(string defaultSmtpRelyCode,string defaultReplyText) - { - string replyText = ""; - if(this.SmtpReplyCode == -1){ - replyText = defaultSmtpRelyCode + " "; - } - else{ - replyText = this.SmtpReplyCode.ToString() + " "; - } - - if(this.ReplyText == ""){ - replyText += defaultReplyText; - } - else{ - replyText += this.ReplyText; - } - - return replyText; - } - - #endregion - - - #region Properties Implementation - - /// - /// Gets or sets if CustomReply is error or ok reply. - /// - public bool ErrorReply - { - get{ return m_ErrorReply; } - - set{ m_ErrorReply = value; } - } - - /// - /// Gets or sets SMTP reply code (250,500,500, ...). Value -1 means that SMTP reply code isn't specified and server uses it's defult error code. - /// - public int SmtpReplyCode - { - get{ return m_SmtpReplyCode; } - - set{ m_SmtpReplyCode = value; } - } - - /// - /// Gets or sets reply text what is shown to connected client. - /// Note: Maximum lenth of reply text is 500 chars and text can contain only ASCII chars - /// without CR or LF. - /// - public string ReplyText - { - get{ return m_ReplyText; } - - set{ - if(!Core.IsAscii(value)){ - throw new Exception("Reply text can contian only ASCII chars !"); - } - if(value.Length > 500){ - value = value.Substring(0,500); - } - value = value.Replace("\r","").Replace("\n",""); - - m_ReplyText = value; - } - } - - #endregion - - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/ValidateMailboxSize_EventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/ValidateMailboxSize_EventArgs.cs deleted file mode 100644 index d8fa9edb8..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/ValidateMailboxSize_EventArgs.cs +++ /dev/null @@ -1,90 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; - -namespace LumiSoft.Net.SMTP.Server -{ - /// - /// Provides data for the ValidateMailboxSize event. - /// - public class ValidateMailboxSize_EventArgs - { - private SMTP_Session m_pSession = null; - private string m_eAddress = ""; - private long m_MsgSize = 0; - private bool m_IsValid = true; - - /// - /// Default constructor. - /// - /// Reference to smtp session. - /// Email address of recipient. - /// Message size. - public ValidateMailboxSize_EventArgs(SMTP_Session session,string eAddress,long messageSize) - { - m_pSession = session; - m_eAddress = eAddress; - m_MsgSize = messageSize; - } - - - #region Properties Implementation - - /// - /// Gets reference to smtp session. - /// - public SMTP_Session Session - { - get{ return m_pSession; } - } - - /// - /// Email address which mailbox size to check. - /// - public string eAddress - { - get{ return m_eAddress; } - } - - /// - /// Message size.NOTE: value 0 means that size is unknown. - /// - public long MessageSize - { - get{ return m_MsgSize; } - } - - /// - /// Gets or sets if mailbox size is valid. - /// - public bool IsValid - { - get{ return m_IsValid; } - - set{ m_IsValid = value; } - } - - // public SMTP_Session aa - // { - // get{ return null; } - // } - - #endregion - - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/ValidateRecipient_EventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/ValidateRecipient_EventArgs.cs deleted file mode 100644 index a18df1c6c..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/ValidateRecipient_EventArgs.cs +++ /dev/null @@ -1,103 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; - -namespace LumiSoft.Net.SMTP.Server -{ - /// - /// Provides data for the ValidateMailTo event. - /// - public class ValidateRecipient_EventArgs - { - private SMTP_Session m_pSession = null; - private string m_MailTo = ""; - private bool m_Validated = true; - private bool m_Authenticated = false; - private bool m_LocalRecipient = true; - - /// - /// Default constructor. - /// - /// Reference to smtp session. - /// Recipient email address. - /// Specifies if connected user is authenticated. - public ValidateRecipient_EventArgs(SMTP_Session session,string mailTo,bool authenticated) - { - m_pSession = session; - m_MailTo = mailTo; - m_Authenticated = authenticated; - } - - #region Properties Implementation - - /// - /// Gets reference to smtp session. - /// - public SMTP_Session Session - { - get{ return m_pSession; } - } - - /// - /// Recipient's email address. - /// - public string MailTo - { - get{ return m_MailTo; } - } - - /// - /// Gets if connected user is authenticated. - /// - public bool Authenticated - { - get{ return m_Authenticated; } - } - - /// - /// IP address of computer, which is sending mail to here. - /// - public string ConnectedIP - { - get{ return m_pSession.RemoteEndPoint.Address.ToString(); } - } - - /// - /// Gets or sets if reciptient is allowed to send mail here. - /// - public bool Validated - { - get{ return m_Validated; } - - set{ m_Validated = value; } - } - - /// - /// Gets or sets if recipient is local or needs relay. - /// - public bool LocalRecipient - { - get{ return m_LocalRecipient; } - - set{ m_LocalRecipient = value; } - } - - #endregion - - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/ValidateSender_EventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/ValidateSender_EventArgs.cs deleted file mode 100644 index f0f3576ab..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SMTP/Server/old/ValidateSender_EventArgs.cs +++ /dev/null @@ -1,93 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; - -namespace LumiSoft.Net.SMTP.Server -{ - /// - /// Provides data for the ValidateMailFrom event. - /// - public class ValidateSender_EventArgs - { - private SMTP_Session m_pSession = null; - private string m_MailFrom = ""; - private string m_ErrorText = ""; - private bool m_Validated = true; - - /// - /// Default constructor. - /// - /// Reference to smtp session. - /// Sender email address. - public ValidateSender_EventArgs(SMTP_Session session,string mailFrom) - { - m_pSession = session; - m_MailFrom = mailFrom; - } - - - #region Properties Implementation - - /// - /// Gets reference to smtp session. - /// - public SMTP_Session Session - { - get{ return m_pSession; } - } - - /// - /// Sender's email address. - /// - public string MailFrom - { - get{ return m_MailFrom; } - } - - /// - /// Gets or sets error text reported to connected client. - /// - public string ErrorText - { - get{ return m_ErrorText; } - - set{ - value = value.Replace("\r\n"," "); - if(value.Length > 200){ - m_ErrorText = value.Substring(0,200); - } - else{ - m_ErrorText = value; - } - } - } - - /// - /// Gets or sets if sender is ok. - /// - public bool Validated - { - get{ return m_Validated; } - - set{ m_Validated = value; } - } - - #endregion - - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SNTP/Client/SNTP_Client.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SNTP/Client/SNTP_Client.cs deleted file mode 100644 index a6507ade8..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SNTP/Client/SNTP_Client.cs +++ /dev/null @@ -1,84 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.Text; - -namespace LumiSoft.Net.SNTP.Client -{ - /// - /// This class implments Simple Network Time Protocol (SNTP) protocol. Defined in RFC 2030. - /// - public class SNTP_Client - { - /// - /// Gets UTC time from NTP server. - /// - /// NTP server. - /// NTP port. Default NTP port is 123. - /// Returns UTC time. - public static DateTime GetTime(string server,int port) - { - /* RFC 2030 4. - Below is a description of the NTP/SNTP Version 4 message format, - which follows the IP and UDP headers. This format is identical to - that described in RFC-1305, with the exception of the contents of the - reference identifier field. The header fields are defined as follows: - - 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |LI | VN |Mode | Stratum | Poll | Precision | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Root Delay | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Root Dispersion | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Reference Identifier | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | | - | Reference Timestamp (64) | - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | | - | Originate Timestamp (64) | - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | | - | Receive Timestamp (64) | - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | | - | Transmit Timestamp (64) | - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Key Identifier (optional) (32) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | | - | | - | Message Digest (optional) (128) | - | | - | | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - 2030 5. For unicast request we need to fill version and mode only. - - */ - } - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/STUN/Client/STUN_Client.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/STUN/Client/STUN_Client.cs deleted file mode 100644 index 8289d49c4..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/STUN/Client/STUN_Client.cs +++ /dev/null @@ -1,370 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.STUN.Client -{ - #region usings - - using System; - using System.IO; - using System.Net; - using System.Net.Sockets; - using Message; - - #endregion - - /// - /// This class implements STUN client. Defined in RFC 3489. - /// - /// - /// - /// // Create new socket for STUN client. - /// Socket socket = new Socket(AddressFamily.InterNetwork,SocketType.Dgram,ProtocolType.Udp); - /// socket.Bind(new IPEndPoint(IPAddress.Any,0)); - /// - /// // Query STUN server - /// STUN_Result result = STUN_Client.Query("stunserver.org",3478,socket); - /// if(result.NetType != STUN_NetType.UdpBlocked){ - /// // UDP blocked or !!!! bad STUN server - /// } - /// else{ - /// IPEndPoint publicEP = result.PublicEndPoint; - /// // Do your stuff - /// } - /// - /// - public class STUN_Client - { - #region Methods - - /// - /// Gets NAT info from STUN server. - /// - /// STUN server name or IP. - /// STUN server port. Default port is 3478. - /// UDP socket to use. - /// Returns UDP netwrok info. - /// Throws exception if unexpected error happens. - public static STUN_Result Query(string host, int port, Socket socket) - { - if (host == null) - { - throw new ArgumentNullException("host"); - } - if (socket == null) - { - throw new ArgumentNullException("socket"); - } - if (port < 1) - { - throw new ArgumentException("Port value must be >= 1 !"); - } - if (socket.ProtocolType != ProtocolType.Udp) - { - throw new ArgumentException("Socket must be UDP socket !"); - } - - IPEndPoint remoteEndPoint = new IPEndPoint(Dns.GetHostAddresses(host)[0], port); - - socket.ReceiveTimeout = 3000; - socket.SendTimeout = 3000; - - /* - In test I, the client sends a STUN Binding Request to a server, without any flags set in the - CHANGE-REQUEST attribute, and without the RESPONSE-ADDRESS attribute. This causes the server - to send the response back to the address and port that the request came from. - - In test II, the client sends a Binding Request with both the "change IP" and "change port" flags - from the CHANGE-REQUEST attribute set. - - In test III, the client sends a Binding Request with only the "change port" flag set. - - +--------+ - | Test | - | I | - +--------+ - | - | - V - /\ /\ - N / \ Y / \ Y +--------+ - UDP <-------/Resp\--------->/ IP \------------->| Test | - Blocked \ ? / \Same/ | II | - \ / \? / +--------+ - \/ \/ | - | N | - | V - V /\ - +--------+ Sym. N / \ - | Test | UDP <---/Resp\ - | II | Firewall \ ? / - +--------+ \ / - | \/ - V |Y - /\ /\ | - Symmetric N / \ +--------+ N / \ V - NAT <--- / IP \<-----| Test |<--- /Resp\ Open - \Same/ | I | \ ? / Internet - \? / +--------+ \ / - \/ \/ - | |Y - | | - | V - | Full - | Cone - V /\ - +--------+ / \ Y - | Test |------>/Resp\---->Restricted - | III | \ ? / - +--------+ \ / - \/ - |N - | Port - +------>Restricted - - */ - - // Test I - STUN_Message test1 = new STUN_Message(); - test1.Type = STUN_MessageType.BindingRequest; - STUN_Message test1response = DoTransaction(test1, socket, remoteEndPoint); - - // UDP blocked. - if (test1response == null) - { - return new STUN_Result(STUN_NetType.UdpBlocked, null); - } - else - { - // Test II - STUN_Message test2 = new STUN_Message(); - test2.Type = STUN_MessageType.BindingRequest; - test2.ChangeRequest = new STUN_t_ChangeRequest(true, true); - - // No NAT. - if (socket.LocalEndPoint.Equals(test1response.MappedAddress)) - { - STUN_Message test2Response = DoTransaction(test2, socket, remoteEndPoint); - // Open Internet. - if (test2Response != null) - { - return new STUN_Result(STUN_NetType.OpenInternet, test1response.MappedAddress); - } - // Symmetric UDP firewall. - else - { - return new STUN_Result(STUN_NetType.SymmetricUdpFirewall, test1response.MappedAddress); - } - } - // NAT - else - { - STUN_Message test2Response = DoTransaction(test2, socket, remoteEndPoint); - // Full cone NAT. - if (test2Response != null) - { - return new STUN_Result(STUN_NetType.FullCone, test1response.MappedAddress); - } - else - { - /* - If no response is received, it performs test I again, but this time, does so to - the address and port from the CHANGED-ADDRESS attribute from the response to test I. - */ - - // Test I(II) - STUN_Message test12 = new STUN_Message(); - test12.Type = STUN_MessageType.BindingRequest; - - STUN_Message test12Response = DoTransaction(test12, - socket, - test1response.ChangedAddress); - if (test12Response == null) - { - throw new Exception("STUN Test I(II) dind't get resonse !"); - } - else - { - // Symmetric NAT - if (!test12Response.MappedAddress.Equals(test1response.MappedAddress)) - { - return new STUN_Result(STUN_NetType.Symmetric, test1response.MappedAddress); - } - else - { - // Test III - STUN_Message test3 = new STUN_Message(); - test3.Type = STUN_MessageType.BindingRequest; - test3.ChangeRequest = new STUN_t_ChangeRequest(false, true); - - STUN_Message test3Response = DoTransaction(test3, - socket, - test1response.ChangedAddress); - // Restricted - if (test3Response != null) - { - return new STUN_Result(STUN_NetType.RestrictedCone, - test1response.MappedAddress); - } - // Port restricted - else - { - return new STUN_Result(STUN_NetType.PortRestrictedCone, - test1response.MappedAddress); - } - } - } - } - } - } - } - - /// - /// Resolves local IP to public IP using STUN. - /// - /// STUN server. - /// STUN server port. Default port is 3478. - /// Local IP address. - /// Returns public IP address. - /// Is raised when stunServer or localIP is null reference. - /// Is raised when any of the arguments has invalid value. - /// Is raised when no connection to STUN server. - public static IPAddress GetPublicIP(string stunServer, int port, IPAddress localIP) - { - if (stunServer == null) - { - throw new ArgumentNullException("stunServer"); - } - if (stunServer == "") - { - throw new ArgumentException("Argument 'stunServer' value must be specified."); - } - if (port < 1) - { - throw new ArgumentException("Invalid argument 'port' value."); - } - if (localIP == null) - { - throw new ArgumentNullException("localIP"); - } - - if (!Core.IsPrivateIP(localIP)) - { - return localIP; - } - - STUN_Result result = Query(stunServer, - port, - Core.CreateSocket(new IPEndPoint(localIP, 0), ProtocolType.Udp)); - if (result.PublicEndPoint != null) - { - return result.PublicEndPoint.Address; - } - else - { - throw new IOException( - "Failed to STUN public IP address. STUN server name is invalid or firewall blocks STUN."); - } - } - - #endregion - - #region Utility methods - - /// - /// Does STUN transaction. Returns transaction response or null if transaction failed. - /// - /// STUN message. - /// Socket to use for send/receive. - /// Remote end point. - /// Returns transaction response or null if transaction failed. - private static STUN_Message DoTransaction(STUN_Message request, - Socket socket, - IPEndPoint remoteEndPoint) - { - byte[] requestBytes = request.ToByteData(); - DateTime startTime = DateTime.Now; - // We do it only 2 sec and retransmit with 100 ms. - while (startTime.AddSeconds(2) > DateTime.Now) - { - try - { - socket.SendTo(requestBytes, remoteEndPoint); - - // We got response. - if (socket.Poll(100, SelectMode.SelectRead)) - { - byte[] receiveBuffer = new byte[512]; - socket.Receive(receiveBuffer); - - // Parse message - STUN_Message response = new STUN_Message(); - response.Parse(receiveBuffer); - - // Check that transaction ID matches or not response what we want. - if (request.TransactionID.Equals(response.TransactionID)) - { - return response; - } - } - } - catch {} - } - - return null; - } - - private void GetSharedSecret() - { - /* - *) Open TLS connection to STUN server. - *) Send Shared Secret request. - */ - - /* - using(SocketEx socket = new SocketEx()){ - socket.RawSocket.ReceiveTimeout = 5000; - socket.RawSocket.SendTimeout = 5000; - - socket.Connect(host,port); - socket.SwitchToSSL_AsClient(); - - // Send Shared Secret request. - STUN_Message sharedSecretRequest = new STUN_Message(); - sharedSecretRequest.Type = STUN_MessageType.SharedSecretRequest; - socket.Write(sharedSecretRequest.ToByteData()); - - // TODO: Parse message - - // We must get "Shared Secret" or "Shared Secret Error" response. - - byte[] receiveBuffer = new byte[256]; - socket.RawSocket.Receive(receiveBuffer); - - STUN_Message sharedSecretRequestResponse = new STUN_Message(); - if(sharedSecretRequestResponse.Type == STUN_MessageType.SharedSecretResponse){ - } - // Shared Secret Error or Unknown response, just try again. - else{ - // TODO: Unknown response - } - }*/ - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/STUN/Client/STUN_NetType.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/STUN/Client/STUN_NetType.cs deleted file mode 100644 index 3e4c8597d..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/STUN/Client/STUN_NetType.cs +++ /dev/null @@ -1,72 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.STUN.Client -{ - /// - /// Specifies UDP network type. - /// - public enum STUN_NetType - { - /// - /// UDP is always blocked. - /// - UdpBlocked, - - /// - /// No NAT, public IP, no firewall. - /// - OpenInternet, - - /// - /// No NAT, public IP, but symmetric UDP firewall. - /// - SymmetricUdpFirewall, - - /// - /// A full cone NAT is one where all requests from the same internal IP address and port are - /// mapped to the same external IP address and port. Furthermore, any external host can send - /// a packet to the internal host, by sending a packet to the mapped external address. - /// - FullCone, - - /// - /// A restricted cone NAT is one where all requests from the same internal IP address and - /// port are mapped to the same external IP address and port. Unlike a full cone NAT, an external - /// host (with IP address X) can send a packet to the internal host only if the internal host - /// had previously sent a packet to IP address X. - /// - RestrictedCone, - - /// - /// A port restricted cone NAT is like a restricted cone NAT, but the restriction - /// includes port numbers. Specifically, an external host can send a packet, with source IP - /// address X and source port P, to the internal host only if the internal host had previously - /// sent a packet to IP address X and port P. - /// - PortRestrictedCone, - - /// - /// A symmetric NAT is one where all requests from the same internal IP address and port, - /// to a specific destination IP address and port, are mapped to the same external IP address and - /// port. If the same host sends a packet with the same source address and port, but to - /// a different destination, a different mapping is used. Furthermore, only the external host that - /// receives a packet can send a UDP packet back to the internal host. - /// - Symmetric - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/STUN/Client/STUN_Result.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/STUN/Client/STUN_Result.cs deleted file mode 100644 index f74232b4b..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/STUN/Client/STUN_Result.cs +++ /dev/null @@ -1,73 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.STUN.Client -{ - #region usings - - using System.Net; - - #endregion - - /// - /// This class holds STUN_Client.Query method return data. - /// - public class STUN_Result - { - #region Members - - private readonly STUN_NetType m_NetType = STUN_NetType.OpenInternet; - private readonly IPEndPoint m_pPublicEndPoint; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Specifies UDP network type. - /// Public IP end point. - public STUN_Result(STUN_NetType netType, IPEndPoint publicEndPoint) - { - m_NetType = netType; - m_pPublicEndPoint = publicEndPoint; - } - - #endregion - - #region Properties - - /// - /// Gets UDP network type. - /// - public STUN_NetType NetType - { - get { return m_NetType; } - } - - /// - /// Gets public IP end point. This value is null if failed to get network type. - /// - public IPEndPoint PublicEndPoint - { - get { return m_pPublicEndPoint; } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/STUN/Message/STUN_Message.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/STUN/Message/STUN_Message.cs deleted file mode 100644 index 8a475b1f4..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/STUN/Message/STUN_Message.cs +++ /dev/null @@ -1,729 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.STUN.Message -{ - #region usings - - using System; - using System.Net; - using System.Text; - - #endregion - - /// - /// Implements STUN message. Defined in RFC 3489. - /// - public class STUN_Message - { - #region Members - - private string m_Password; - private IPEndPoint m_pChangedAddress; - private STUN_t_ChangeRequest m_pChangeRequest; - private STUN_t_ErrorCode m_pErrorCode; - private IPEndPoint m_pMappedAddress; - private IPEndPoint m_pReflectedFrom; - private IPEndPoint m_pResponseAddress; - private IPEndPoint m_pSourceAddress; - private Guid m_pTransactionID = Guid.Empty; - private string m_ServerName; - private STUN_MessageType m_Type = STUN_MessageType.BindingRequest; - private string m_UserName; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public STUN_Message() - { - m_pTransactionID = Guid.NewGuid(); - } - - #endregion - - #region Properties - - /// - /// Gets or sets IP end point where STUN server will send response back to STUN client - /// if the "change IP" and "change port" flags had been set in the ChangeRequest. - /// - public IPEndPoint ChangedAddress - { - get { return m_pChangedAddress; } - - set { m_pChangedAddress = value; } - } - - /// - /// Gets or sets how and where STUN server must send response back to STUN client. - /// Value null means not specified. - /// - public STUN_t_ChangeRequest ChangeRequest - { - get { return m_pChangeRequest; } - - set { m_pChangeRequest = value; } - } - - //public MessageIntegrity - - /// - /// Gets or sets error info. Returns null if not specified. - /// - public STUN_t_ErrorCode ErrorCode - { - get { return m_pErrorCode; } - - set { m_pErrorCode = value; } - } - - /// - /// Gets or sets IP end point what was actually connected to STUN server. Returns null if not specified. - /// - public IPEndPoint MappedAddress - { - get { return m_pMappedAddress; } - - set { m_pMappedAddress = value; } - } - - /// - /// Gets or sets password. Value null means not specified. - /// - public string Password - { - get { return m_Password; } - - set { m_Password = value; } - } - - /// - /// Gets or sets IP endpoint from which IP end point STUN server got STUN client request. - /// Value null means not specified. - /// - public IPEndPoint ReflectedFrom - { - get { return m_pReflectedFrom; } - - set { m_pReflectedFrom = value; } - } - - /// - /// Gets or sets IP end point where to STUN client likes to receive response. - /// Value null means not specified. - /// - public IPEndPoint ResponseAddress - { - get { return m_pResponseAddress; } - - set { m_pResponseAddress = value; } - } - - /// - /// Gets or sets server name. - /// - public string ServerName - { - get { return m_ServerName; } - - set { m_ServerName = value; } - } - - /// - /// Gets or sets STUN server IP end point what sent response to STUN client. Value null - /// means not specified. - /// - public IPEndPoint SourceAddress - { - get { return m_pSourceAddress; } - - set { m_pSourceAddress = value; } - } - - /// - /// Gets transaction ID. - /// - public Guid TransactionID - { - get { return m_pTransactionID; } - } - - /// - /// Gets STUN message type. - /// - public STUN_MessageType Type - { - get { return m_Type; } - - set { m_Type = value; } - } - - /// - /// Gets or sets user name. Value null means not specified. - /// - public string UserName - { - get { return m_UserName; } - - set { m_UserName = value; } - } - - #endregion - - #region Methods - - /// - /// Parses STUN message from raw data packet. - /// - /// Raw STUN message. - public void Parse(byte[] data) - { - /* RFC 3489 11.1. - All STUN messages consist of a 20 byte header: - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | STUN Message Type | Message Length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - Transaction ID - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - The message length is the count, in bytes, of the size of the - message, not including the 20 byte header. - */ - - if (data.Length < 20) - { - throw new ArgumentException("Invalid STUN message value !"); - } - - int offset = 0; - - //--- message header -------------------------------------------------- - - // STUN Message Type - int messageType = (data[offset++] << 8 | data[offset++]); - if (messageType == (int) STUN_MessageType.BindingErrorResponse) - { - m_Type = STUN_MessageType.BindingErrorResponse; - } - else if (messageType == (int) STUN_MessageType.BindingRequest) - { - m_Type = STUN_MessageType.BindingRequest; - } - else if (messageType == (int) STUN_MessageType.BindingResponse) - { - m_Type = STUN_MessageType.BindingResponse; - } - else if (messageType == (int) STUN_MessageType.SharedSecretErrorResponse) - { - m_Type = STUN_MessageType.SharedSecretErrorResponse; - } - else if (messageType == (int) STUN_MessageType.SharedSecretRequest) - { - m_Type = STUN_MessageType.SharedSecretRequest; - } - else if (messageType == (int) STUN_MessageType.SharedSecretResponse) - { - m_Type = STUN_MessageType.SharedSecretResponse; - } - else - { - throw new ArgumentException("Invalid STUN message type value !"); - } - - // Message Length - int messageLength = (data[offset++] << 8 | data[offset++]); - - // Transaction ID - byte[] guid = new byte[16]; - Array.Copy(data, offset, guid, 0, 16); - m_pTransactionID = new Guid(guid); - offset += 16; - - //--- Message attributes --------------------------------------------- - while ((offset - 20) < messageLength) - { - ParseAttribute(data, ref offset); - } - } - - /// - /// Converts this to raw STUN packet. - /// - /// Returns raw STUN packet. - public byte[] ToByteData() - { - /* RFC 3489 11.1. - All STUN messages consist of a 20 byte header: - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | STUN Message Type | Message Length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - Transaction ID - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - The message length is the count, in bytes, of the size of the - message, not including the 20 byte header. - - */ - - // We allocate 512 for header, that should be more than enough. - byte[] msg = new byte[512]; - - int offset = 0; - - //--- message header ------------------------------------- - - // STUN Message Type (2 bytes) - msg[offset++] = (byte) ((int) Type >> 8); - msg[offset++] = (byte) ((int) Type & 0xFF); - - // Message Length (2 bytes) will be assigned at last. - msg[offset++] = 0; - msg[offset++] = 0; - - // Transaction ID (16 bytes) - Array.Copy(m_pTransactionID.ToByteArray(), 0, msg, offset, 16); - offset += 16; - - //--- Message attributes ------------------------------------ - - /* RFC 3489 11.2. - After the header are 0 or more attributes. Each attribute is TLV - encoded, with a 16 bit type, 16 bit length, and variable value: - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Value .... - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - if (MappedAddress != null) - { - StoreEndPoint(AttributeType.MappedAddress, MappedAddress, msg, ref offset); - } - else if (ResponseAddress != null) - { - StoreEndPoint(AttributeType.ResponseAddress, ResponseAddress, msg, ref offset); - } - else if (ChangeRequest != null) - { - /* - The CHANGE-REQUEST attribute is used by the client to request that - the server use a different address and/or port when sending the - response. The attribute is 32 bits long, although only two bits (A - and B) are used: - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 A B 0| - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - The meaning of the flags is: - - A: This is the "change IP" flag. If true, it requests the server - to send the Binding Response with a different IP address than the - one the Binding Request was received on. - - B: This is the "change port" flag. If true, it requests the - server to send the Binding Response with a different port than the - one the Binding Request was received on. - */ - - // Attribute header - msg[offset++] = (int) AttributeType.ChangeRequest >> 8; - msg[offset++] = (int) AttributeType.ChangeRequest & 0xFF; - msg[offset++] = 0; - msg[offset++] = 4; - - msg[offset++] = 0; - msg[offset++] = 0; - msg[offset++] = 0; - msg[offset++] = - (byte) - (Convert.ToInt32(ChangeRequest.ChangeIP) << 2 | - Convert.ToInt32(ChangeRequest.ChangePort) << 1); - } - else if (SourceAddress != null) - { - StoreEndPoint(AttributeType.SourceAddress, SourceAddress, msg, ref offset); - } - else if (ChangedAddress != null) - { - StoreEndPoint(AttributeType.ChangedAddress, ChangedAddress, msg, ref offset); - } - else if (UserName != null) - { - byte[] userBytes = Encoding.ASCII.GetBytes(UserName); - - // Attribute header - msg[offset++] = (int) AttributeType.Username >> 8; - msg[offset++] = (int) AttributeType.Username & 0xFF; - msg[offset++] = (byte) (userBytes.Length >> 8); - msg[offset++] = (byte) (userBytes.Length & 0xFF); - - Array.Copy(userBytes, 0, msg, offset, userBytes.Length); - offset += userBytes.Length; - } - else if (Password != null) - { - byte[] userBytes = Encoding.ASCII.GetBytes(UserName); - - // Attribute header - msg[offset++] = (int) AttributeType.Password >> 8; - msg[offset++] = (int) AttributeType.Password & 0xFF; - msg[offset++] = (byte) (userBytes.Length >> 8); - msg[offset++] = (byte) (userBytes.Length & 0xFF); - - Array.Copy(userBytes, 0, msg, offset, userBytes.Length); - offset += userBytes.Length; - } - else if (ErrorCode != null) - { - /* 3489 11.2.9. - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | 0 |Class| Number | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Reason Phrase (variable) .. - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - byte[] reasonBytes = Encoding.ASCII.GetBytes(ErrorCode.ReasonText); - - // Header - msg[offset++] = 0; - msg[offset++] = (int) AttributeType.ErrorCode; - msg[offset++] = 0; - msg[offset++] = (byte) (4 + reasonBytes.Length); - - // Empty - msg[offset++] = 0; - msg[offset++] = 0; - // Class - msg[offset++] = (byte) Math.Floor((double) (ErrorCode.Code/100)); - // Number - msg[offset++] = (byte) (ErrorCode.Code & 0xFF); - // ReasonPhrase - Array.Copy(reasonBytes, msg, reasonBytes.Length); - offset += reasonBytes.Length; - } - else if (ReflectedFrom != null) - { - StoreEndPoint(AttributeType.ReflectedFrom, ReflectedFrom, msg, ref offset); - } - - // Update Message Length. NOTE: 20 bytes header not included. - msg[2] = (byte) ((offset - 20) >> 8); - msg[3] = (byte) ((offset - 20) & 0xFF); - - // Make reatval with actual size. - byte[] retVal = new byte[offset]; - Array.Copy(msg, retVal, retVal.Length); - - return retVal; - } - - #endregion - - #region Utility methods - - /// - /// Parses attribute from data. - /// - /// SIP message data. - /// Offset in data. - private void ParseAttribute(byte[] data, ref int offset) - { - /* RFC 3489 11.2. - Each attribute is TLV encoded, with a 16 bit type, 16 bit length, and variable value: - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Type | Length | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Value .... - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - // Type - AttributeType type = (AttributeType) (data[offset++] << 8 | data[offset++]); - - // Length - int length = (data[offset++] << 8 | data[offset++]); - - // MAPPED-ADDRESS - if (type == AttributeType.MappedAddress) - { - m_pMappedAddress = ParseEndPoint(data, ref offset); - } - // RESPONSE-ADDRESS - else if (type == AttributeType.ResponseAddress) - { - m_pResponseAddress = ParseEndPoint(data, ref offset); - } - // CHANGE-REQUEST - else if (type == AttributeType.ChangeRequest) - { - /* - The CHANGE-REQUEST attribute is used by the client to request that - the server use a different address and/or port when sending the - response. The attribute is 32 bits long, although only two bits (A - and B) are used: - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 A B 0| - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - The meaning of the flags is: - - A: This is the "change IP" flag. If true, it requests the server - to send the Binding Response with a different IP address than the - one the Binding Request was received on. - - B: This is the "change port" flag. If true, it requests the - server to send the Binding Response with a different port than the - one the Binding Request was received on. - */ - - // Skip 3 bytes - offset += 3; - - m_pChangeRequest = new STUN_t_ChangeRequest((data[offset] & 4) != 0, (data[offset] & 2) != 0); - offset++; - } - // SOURCE-ADDRESS - else if (type == AttributeType.SourceAddress) - { - m_pSourceAddress = ParseEndPoint(data, ref offset); - } - // CHANGED-ADDRESS - else if (type == AttributeType.ChangedAddress) - { - m_pChangedAddress = ParseEndPoint(data, ref offset); - } - // USERNAME - else if (type == AttributeType.Username) - { - m_UserName = Encoding.Default.GetString(data, offset, length); - offset += length; - } - // PASSWORD - else if (type == AttributeType.Password) - { - m_Password = Encoding.Default.GetString(data, offset, length); - offset += length; - } - // MESSAGE-INTEGRITY - else if (type == AttributeType.MessageIntegrity) - { - offset += length; - } - // ERROR-CODE - else if (type == AttributeType.ErrorCode) - { - /* 3489 11.2.9. - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | 0 |Class| Number | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Reason Phrase (variable) .. - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - int errorCode = (data[offset + 2] & 0x7)*100 + (data[offset + 3] & 0xFF); - - m_pErrorCode = new STUN_t_ErrorCode(errorCode, - Encoding.Default.GetString(data, offset + 4, length - 4)); - offset += length; - } - // UNKNOWN-ATTRIBUTES - else if (type == AttributeType.UnknownAttribute) - { - offset += length; - } - // REFLECTED-FROM - else if (type == AttributeType.ReflectedFrom) - { - m_pReflectedFrom = ParseEndPoint(data, ref offset); - } - // XorMappedAddress - // XorOnly - // ServerName - else if (type == AttributeType.ServerName) - { - m_ServerName = Encoding.Default.GetString(data, offset, length); - offset += length; - } - // Unknown - else - { - offset += length; - } - } - - /// - /// Pasrses IP endpoint attribute. - /// - /// STUN message data. - /// Offset in data. - /// Returns parsed IP end point. - private IPEndPoint ParseEndPoint(byte[] data, ref int offset) - { - /* - It consists of an eight bit address family, and a sixteen bit - port, followed by a fixed length value representing the IP address. - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |x x x x x x x x| Family | Port | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Address | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - // Skip family - offset++; - offset++; - - // Port - int port = (data[offset++] << 8 | data[offset++]); - - // Address - byte[] ip = new byte[4]; - ip[0] = data[offset++]; - ip[1] = data[offset++]; - ip[2] = data[offset++]; - ip[3] = data[offset++]; - - return new IPEndPoint(new IPAddress(ip), port); - } - - /// - /// Stores ip end point attribute to buffer. - /// - /// Attribute type. - /// IP end point. - /// Buffer where to store. - /// Offset in buffer. - private void StoreEndPoint(AttributeType type, IPEndPoint endPoint, byte[] message, ref int offset) - { - /* - It consists of an eight bit address family, and a sixteen bit - port, followed by a fixed length value representing the IP address. - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - |x x x x x x x x| Family | Port | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Address | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - - // Header - message[offset++] = (byte) ((int) type >> 8); - message[offset++] = (byte) ((int) type & 0xFF); - message[offset++] = 0; - message[offset++] = 8; - - // Unused - message[offset++] = 0; - // Family - message[offset++] = (byte) IPFamily.IPv4; - // Port - message[offset++] = (byte) (endPoint.Port >> 8); - message[offset++] = (byte) (endPoint.Port & 0xFF); - // Address - byte[] ipBytes = endPoint.Address.GetAddressBytes(); - message[offset++] = ipBytes[0]; - message[offset++] = ipBytes[1]; - message[offset++] = ipBytes[2]; - message[offset++] = ipBytes[3]; - } - - #endregion - - #region Nested type: AttributeType - - /// - /// Specifies STUN attribute type. - /// - private enum AttributeType - { - MappedAddress = 0x0001, - ResponseAddress = 0x0002, - ChangeRequest = 0x0003, - SourceAddress = 0x0004, - ChangedAddress = 0x0005, - Username = 0x0006, - Password = 0x0007, - MessageIntegrity = 0x0008, - ErrorCode = 0x0009, - UnknownAttribute = 0x000A, - ReflectedFrom = 0x000B, - XorMappedAddress = 0x8020, - XorOnly = 0x0021, - ServerName = 0x8022, - } - - #endregion - - #region Nested type: IPFamily - - /// - /// Specifies IP address family. - /// - private enum IPFamily - { - IPv4 = 0x01, - IPv6 = 0x02, - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/STUN/Message/STUN_MessageType.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/STUN/Message/STUN_MessageType.cs deleted file mode 100644 index 016ffc3ea..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/STUN/Message/STUN_MessageType.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.STUN.Message -{ - /// - /// This enum specifies STUN message type. - /// - public enum STUN_MessageType - { - /// - /// STUN message is binding request. - /// - BindingRequest = 0x0001, - - /// - /// STUN message is binding request response. - /// - BindingResponse = 0x0101, - - /// - /// STUN message is binding requesr error response. - /// - BindingErrorResponse = 0x0111, - - /// - /// STUN message is "shared secret" request. - /// - SharedSecretRequest = 0x0002, - - /// - /// STUN message is "shared secret" request response. - /// - SharedSecretResponse = 0x0102, - - /// - /// STUN message is "shared secret" request error response. - /// - SharedSecretErrorResponse = 0x0112, - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/STUN/Message/STUN_t_ChangeRequest.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/STUN/Message/STUN_t_ChangeRequest.cs deleted file mode 100644 index 6d0e1ef5f..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/STUN/Message/STUN_t_ChangeRequest.cs +++ /dev/null @@ -1,76 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.STUN.Message -{ - /// - /// This class implements STUN CHANGE-REQUEST attribute. Defined in RFC 3489 11.2.4. - /// - public class STUN_t_ChangeRequest - { - #region Members - - private bool m_ChangeIP = true; - private bool m_ChangePort = true; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public STUN_t_ChangeRequest() {} - - /// - /// Default constructor. - /// - /// Specifies if STUN server must send response to different IP than request was received. - /// Specifies if STUN server must send response to different port than request was received. - public STUN_t_ChangeRequest(bool changeIP, bool changePort) - { - m_ChangeIP = changeIP; - m_ChangePort = changePort; - } - - #endregion - - #region Properties - - /// - /// Gets or sets if STUN server must send response to different IP than request was received. - /// - public bool ChangeIP - { - get { return m_ChangeIP; } - - set { m_ChangeIP = value; } - } - - /// - /// Gets or sets if STUN server must send response to different port than request was received. - /// - public bool ChangePort - { - get { return m_ChangePort; } - - set { m_ChangePort = value; } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/STUN/Message/STUN_t_ErrorCode.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/STUN/Message/STUN_t_ErrorCode.cs deleted file mode 100644 index 75f115abf..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/STUN/Message/STUN_t_ErrorCode.cs +++ /dev/null @@ -1,65 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.STUN.Message -{ - /// - /// This class implements STUN ERROR-CODE. Defined in RFC 3489 11.2.9. - /// - public class STUN_t_ErrorCode - { - #region Members - - private string m_ReasonText = ""; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Error code. - /// Reason text. - public STUN_t_ErrorCode(int code, string reasonText) - { - Code = code; - m_ReasonText = reasonText; - } - - #endregion - - #region Properties - - /// - /// Gets or sets error code. - /// - public int Code { get; set; } - - /// - /// Gets reason text. - /// - public string ReasonText - { - get { return m_ReasonText; } - - set { m_ReasonText = value; } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ServersCore/Error_EventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ServersCore/Error_EventArgs.cs deleted file mode 100644 index d5634dc68..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ServersCore/Error_EventArgs.cs +++ /dev/null @@ -1,83 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - #region usings - - using System; - using System.Diagnostics; - - #endregion - - /// - /// Provides data for the SysError event for servers. - /// - public class Error_EventArgs - { - #region Members - - private readonly Exception m_pException; - private readonly StackTrace m_pStackTrace; - private string m_Text = ""; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// - /// - public Error_EventArgs(Exception x, StackTrace stackTrace) - { - m_pException = x; - m_pStackTrace = stackTrace; - } - - #endregion - - #region Properties - - /// - /// Occured error's exception. - /// - public Exception Exception - { - get { return m_pException; } - } - - /// - /// Occured error's stacktrace. - /// - public StackTrace StackTrace - { - get { return m_pStackTrace; } - } - - /// - /// Gets comment text. - /// - public string Text - { - get { return m_Text; } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ServersCore/SocketBufferedWriter.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ServersCore/SocketBufferedWriter.cs deleted file mode 100644 index d8445d54e..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ServersCore/SocketBufferedWriter.cs +++ /dev/null @@ -1,114 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// Implements buffered writer for socket. - /// - public class SocketBufferedWriter - { - #region Members - - private readonly byte[] m_Buffer; - private readonly SocketEx m_pSocket; - private int m_AvailableInBuffer; - private int m_BufferSize = 8000; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Socket where to write data. - public SocketBufferedWriter(SocketEx socket) - { - m_pSocket = socket; - - m_Buffer = new byte[m_BufferSize]; - } - - #endregion - - #region Methods - - /// - /// Forces to send all data in buffer to destination host. - /// - public void Flush() - { - if (m_AvailableInBuffer > 0) - { - m_pSocket.Write(m_Buffer, 0, m_AvailableInBuffer); - m_AvailableInBuffer = 0; - } - } - - /// - /// Queues specified data to write buffer. If write buffer is full, buffered data will be sent to detination host. - /// - /// Data to queue. - public void Write(string data) - { - Write(Encoding.UTF8.GetBytes(data)); - } - - /// - /// Queues specified data to write buffer. If write buffer is full, buffered data will be sent to detination host. - /// - /// Data to queue. - public void Write(byte[] data) - { - // There is no room to accomodate data to buffer - if ((m_AvailableInBuffer + data.Length) > m_BufferSize) - { - // Send buffer data - m_pSocket.Write(m_Buffer, 0, m_AvailableInBuffer); - m_AvailableInBuffer = 0; - - // Store new data to buffer - if (data.Length < m_BufferSize) - { - Array.Copy(data, m_Buffer, data.Length); - m_AvailableInBuffer = data.Length; - } - // Buffer is smaller than data, send it directly - else - { - m_pSocket.Write(data); - } - } - // Store data to buffer - else - { - Array.Copy(data, 0, m_Buffer, m_AvailableInBuffer, data.Length); - m_AvailableInBuffer += data.Length; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ServersCore/ValidateIP_EventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ServersCore/ValidateIP_EventArgs.cs deleted file mode 100644 index 7e24e6478..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ServersCore/ValidateIP_EventArgs.cs +++ /dev/null @@ -1,108 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - #region usings - - using System.Net; - - #endregion - - /// - /// Provides data for the ValidateIPAddress event for servers. - /// - public class ValidateIP_EventArgs - { - #region Members - - private readonly IPEndPoint m_pLocalEndPoint; - private readonly IPEndPoint m_pRemoteEndPoint; - private string m_ErrorText = ""; - private bool m_Validated = true; - - #endregion - - #region Properties - - /// - /// IP address of computer, which is sending mail to here. - /// - public string ConnectedIP - { - get { return m_pRemoteEndPoint.Address.ToString(); } - } - - /// - /// Gets local endpoint. - /// - public IPEndPoint LocalEndPoint - { - get { return m_pLocalEndPoint; } - } - - /// - /// Gets remote endpoint. - /// - public IPEndPoint RemoteEndPoint - { - get { return m_pRemoteEndPoint; } - } - - /// - /// Gets or sets if IP is allowed access. - /// - public bool Validated - { - get { return m_Validated; } - - set { m_Validated = value; } - } - - /// - /// Gets or sets user data what is stored to session.Tag property. - /// - public object SessionTag { get; set; } - - /// - /// Gets or sets error text what is sent to connected socket. NOTE: This is only used if Validated = false. - /// - public string ErrorText - { - get { return m_ErrorText; } - - set { m_ErrorText = value; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Server IP. - /// Connected client IP. - public ValidateIP_EventArgs(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint) - { - m_pLocalEndPoint = localEndPoint; - m_pRemoteEndPoint = remoteEndPoint; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ServersCore/commonDelegates.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ServersCore/commonDelegates.cs deleted file mode 100644 index 799fe0794..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/ServersCore/commonDelegates.cs +++ /dev/null @@ -1,38 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - /// - /// Represent the method what will handle Error event. - /// - /// Delegate caller. - /// Event data. - public delegate void ErrorEventHandler(object sender, Error_EventArgs e); - - /// - /// To be supplied. - /// - public delegate void LogEventHandler(object sender, Log_EventArgs e); - - /// - /// Represents the method that will handle the SMTP_Server.ValidateIPAddress and POP3_Server.ValidateIPAddressevent. - /// - /// The source of the event. - /// A ValidateIP_EventArgs that contains the event data. - public delegate void ValidateIPHandler(object sender, ValidateIP_EventArgs e); -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SslMode.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SslMode.cs deleted file mode 100644 index aeaafc1f6..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/SslMode.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - /// - /// This enum holds SSL modes. - /// - public enum SslMode - { - /// - /// No SSL is used. - /// - None, - - /// - /// Connection is SSL. - /// - SSL, - - /// - /// Connection will be switched to SSL with start TLS. - /// - TLS - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/StringReader.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/StringReader.cs deleted file mode 100644 index 69c86696a..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/StringReader.cs +++ /dev/null @@ -1,513 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// String reader. - /// - public class StringReader - { - #region Members - - private readonly string m_OriginalString = ""; - private string m_SourceString = ""; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Source string. - /// Is raised when source is null. - public StringReader(string source) - { - if (source == null) - { - throw new ArgumentNullException("source"); - } - - m_OriginalString = source; - m_SourceString = source; - } - - #endregion - - #region Properties - - /// - /// Gets how many chars are available for reading. - /// - public long Available - { - get { return m_SourceString.Length; } - } - - /// - /// Gets original string passed to class constructor. - /// - public string OriginalString - { - get { return m_OriginalString; } - } - - /// - /// Gets position in original string. - /// - public int Position - { - get { return m_OriginalString.Length - m_SourceString.Length; } - } - - /// - /// Gets currently remaining string. - /// - public string SourceString - { - get { return m_SourceString; } - } - - #endregion - - #region Methods - - /// - /// Appends specified string to SourceString. - /// - /// String value to append. - public void AppenString(string str) - { - m_SourceString += str; - } - - /// - /// Reads to first char, skips white-space(SP,VTAB,HTAB,CR,LF) from the beginning of source string. - /// - /// Returns white-space chars which was readed. - public string ReadToFirstChar() - { - int whiteSpaces = 0; - for (int i = 0; i < m_SourceString.Length; i++) - { - if (char.IsWhiteSpace(m_SourceString[i])) - { - whiteSpaces++; - } - else - { - break; - } - } - - string whiteSpaceChars = m_SourceString.Substring(0, whiteSpaces); - m_SourceString = m_SourceString.Substring(whiteSpaces); - - return whiteSpaceChars; - } - - // public string ReadToDelimiter(char delimiter) - // { - // } - - /// - /// Reads string with specified length. Throws exception if read length is bigger than source string length. - /// - /// Number of chars to read. - /// - public string ReadSpecifiedLength(int length) - { - if (m_SourceString.Length >= length) - { - string retVal = m_SourceString.Substring(0, length); - m_SourceString = m_SourceString.Substring(length); - - return retVal; - } - else - { - throw new Exception("Read length can't be bigger than source string !"); - } - } - - /// - /// Reads string to specified delimiter or to end of underlying string. Notes: Delimiter in quoted string is skipped. - /// Delimiter is removed by default. - /// For example: delimiter = ',', text = '"aaaa,eee",qqqq' - then result is '"aaaa,eee"'. - /// - /// Data delimiter. - /// - public string QuotedReadToDelimiter(char delimiter) - { - return QuotedReadToDelimiter(new[] {delimiter}); - } - - /// - /// Reads string to specified delimiter or to end of underlying string. Notes: Delimiters in quoted string is skipped. - /// Delimiter is removed by default. - /// For example: delimiter = ',', text = '"aaaa,eee",qqqq' - then result is '"aaaa,eee"'. - /// - /// Data delimiters. - /// - public string QuotedReadToDelimiter(char[] delimiters) - { - return QuotedReadToDelimiter(delimiters, true); - } - - /// - /// Reads string to specified delimiter or to end of underlying string. Notes: Delimiters in quoted string is skipped. - /// For example: delimiter = ',', text = '"aaaa,eee",qqqq' - then result is '"aaaa,eee"'. - /// - /// Data delimiters. - /// Specifies if delimiter is removed from underlying string. - /// - public string QuotedReadToDelimiter(char[] delimiters, bool removeDelimiter) - { - StringBuilder currentSplitBuffer = new StringBuilder(); // Holds active - bool inQuotedString = false; // Holds flag if position is quoted string or not - char lastChar = (char) 0; - - for (int i = 0; i < m_SourceString.Length; i++) - { - char c = m_SourceString[i]; - - // Skip escaped(\) " - if (lastChar != '\\' && c == '\"') - { - // Start/end quoted string area - inQuotedString = !inQuotedString; - } - - // See if char is delimiter - bool isDelimiter = false; - foreach (char delimiter in delimiters) - { - if (c == delimiter) - { - isDelimiter = true; - break; - } - } - - // Current char is split char and it isn't in quoted string, do split - if (!inQuotedString && isDelimiter) - { - string retVal = currentSplitBuffer.ToString(); - - // Remove readed string + delimiter from source string - if (removeDelimiter) - { - m_SourceString = m_SourceString.Substring(retVal.Length + 1); - } - // Remove readed string - else - { - m_SourceString = m_SourceString.Substring(retVal.Length); - } - - return retVal; - } - else - { - currentSplitBuffer.Append(c); - } - - lastChar = c; - } - - // If we reached so far then we are end of string, return it - m_SourceString = ""; - return currentSplitBuffer.ToString(); - } - - /// - /// Reads word from string. Returns null if no word is available. - /// Word reading begins from first char, for example if SP"text", then space is trimmed. - /// - /// - public string ReadWord() - { - return ReadWord(true); - } - - /// - /// Reads word from string. Returns null if no word is available. - /// Word reading begins from first char, for example if SP"text", then space is trimmed. - /// - /// Specifies if quoted string word is unquoted. - /// - public string ReadWord(bool unQuote) - { - return ReadWord(unQuote, - new[] {' ', ',', ';', '{', '}', '(', ')', '[', ']', '<', '>', '\r', '\n'}, - false); - } - - /// - /// Reads word from string. Returns null if no word is available. - /// Word reading begins from first char, for example if SP"text", then space is trimmed. - /// - /// Specifies if quoted string word is unquoted. - /// Specifies chars what terminate word. - /// Specifies if work terminator is removed. - /// - public string ReadWord(bool unQuote, char[] wordTerminatorChars, bool removeWordTerminator) - { - // Always start word reading from first char. - ReadToFirstChar(); - - if (Available == 0) - { - return null; - } - - // quoted word can contain any char, " must be escaped with \ - // unqouted word can conatin any char except: SP VTAB HTAB,{}()[]<> - - if (m_SourceString.StartsWith("\"")) - { - if (unQuote) - { - return - TextUtils.UnQuoteString(QuotedReadToDelimiter(wordTerminatorChars, - removeWordTerminator)); - } - else - { - return QuotedReadToDelimiter(wordTerminatorChars, removeWordTerminator); - } - } - else - { - int wordLength = 0; - for (int i = 0; i < m_SourceString.Length; i++) - { - char c = m_SourceString[i]; - - bool isTerminator = false; - foreach (char terminator in wordTerminatorChars) - { - if (c == terminator) - { - isTerminator = true; - break; - } - } - if (isTerminator) - { - break; - } - - wordLength++; - } - - string retVal = m_SourceString.Substring(0, wordLength); - if (removeWordTerminator) - { - if (m_SourceString.Length >= wordLength + 1) - { - m_SourceString = m_SourceString.Substring(wordLength + 1); - } - } - else - { - m_SourceString = m_SourceString.Substring(wordLength); - } - - return retVal; - } - } - - /// - /// Reads parenthesized value. Supports {},(),[],<> parenthesis. - /// Throws exception if there isn't parenthesized value or closing parenthesize is missing. - /// - /// - public string ReadParenthesized() - { - ReadToFirstChar(); - - char startingChar = ' '; - char closingChar = ' '; - - if (m_SourceString.StartsWith("{")) - { - startingChar = '{'; - closingChar = '}'; - } - else if (m_SourceString.StartsWith("(")) - { - startingChar = '('; - closingChar = ')'; - } - else if (m_SourceString.StartsWith("[")) - { - startingChar = '['; - closingChar = ']'; - } - else if (m_SourceString.StartsWith("<")) - { - startingChar = '<'; - closingChar = '>'; - } - else - { - throw new Exception("No parenthesized value '" + m_SourceString + "' !"); - } - - bool inQuotedString = false; // Holds flag if position is quoted string or not - char lastChar = (char) 0; - - int closingCharIndex = -1; - int nestedStartingCharCounter = 0; - for (int i = 1; i < m_SourceString.Length; i++) - { - // Skip escaped(\) " - if (lastChar != '\\' && m_SourceString[i] == '\"') - { - // Start/end quoted string area - inQuotedString = !inQuotedString; - } - // We need to skip parenthesis in quoted string - else if (!inQuotedString) - { - // There is nested parenthesis - if (m_SourceString[i] == startingChar) - { - nestedStartingCharCounter++; - } - // Closing char - else if (m_SourceString[i] == closingChar) - { - // There isn't nested parenthesis closing chars left, this is closing char what we want - if (nestedStartingCharCounter == 0) - { - closingCharIndex = i; - break; - } - // This is nested parenthesis closing char - else - { - nestedStartingCharCounter--; - } - } - } - - lastChar = m_SourceString[i]; - } - - if (closingCharIndex == -1) - { - throw new Exception("There is no closing parenthesize for '" + m_SourceString + "' !"); - } - else - { - string retVal = m_SourceString.Substring(1, closingCharIndex - 1); - m_SourceString = m_SourceString.Substring(closingCharIndex + 1); - - return retVal; - } - } - - /// - /// Reads all remaining string, returns null if no chars left to read. - /// - /// - public string ReadToEnd() - { - if (Available == 0) - { - return null; - } - - string retVal = m_SourceString; - m_SourceString = ""; - - return retVal; - } - - /// - /// Gets if source string starts with specified value. Compare is case-sensitive. - /// - /// Start string value. - /// - public bool StartsWith(string value) - { - return m_SourceString.StartsWith(value); - } - - /// - /// Gets if source string starts with specified value. - /// - /// Start string value. - /// Specifies if compare is case-sensitive. - /// - public bool StartsWith(string value, bool case_sensitive) - { - if (case_sensitive) - { - return m_SourceString.StartsWith(value); - } - else - { - return m_SourceString.ToLower().StartsWith(value.ToLower()); - } - } - - /// - /// Gets if current source string starts with word. For example if source string starts with - /// whiter space or parenthesize, this method returns false. - /// - /// - public bool StartsWithWord() - { - if (m_SourceString.Length == 0) - { - return false; - } - - if (char.IsWhiteSpace(m_SourceString[0])) - { - return false; - } - if (char.IsSeparator(m_SourceString[0])) - { - return false; - } - char[] wordTerminators = new[] {' ', ',', ';', '{', '}', '(', ')', '[', ']', '<', '>', '\r', '\n'}; - foreach (char c in wordTerminators) - { - if (c == m_SourceString[0]) - { - return false; - } - } - - return true; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TCP/TCP_Client.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TCP/TCP_Client.cs deleted file mode 100644 index 11151ab7c..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TCP/TCP_Client.cs +++ /dev/null @@ -1,1041 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.TCP -{ - #region usings - - using System; - using System.Diagnostics; - using System.Net; - using System.Net.Security; - using System.Net.Sockets; - using System.Security.Cryptography.X509Certificates; - using System.Text.RegularExpressions; - using IO; - using Log; - - #endregion - - /// - /// This class implements generic TCP client. - /// - public class TCP_Client : TCP_Session - { - #region Delegates - - /// - /// Internal helper method for asynchronous Connect method. - /// - /// Local IP end point to use for connect. - /// Remote IP end point where to connect. - /// Specifies if connects to SSL end point. - private delegate void BeginConnectEPDelegate(IPEndPoint localEP, IPEndPoint remoteEP, bool ssl); - - /// - /// Internal helper method for asynchronous Connect method. - /// - /// Host name or IP address. - /// Port to connect. - /// Specifies if connects to SSL end point. - private delegate void BeginConnectHostDelegate(string host, int port, bool ssl); - - /// - /// Internal helper method for asynchronous Disconnect method. - /// - private delegate void DisconnectDelegate(); - - #endregion - - #region Members - - private static readonly Regex IpRegexp = - new Regex(@"^(([01]?\d\d?|2[0-4]\d|25[0-5])\.){3}([01]?\d\d?|25[0-5]|2[0-4]\d)$", - RegexOptions.Compiled | RegexOptions.IgnoreCase); - - private DateTime m_ConnectTime; - private string m_ID = ""; - private bool m_IsConnected; - private bool m_IsDisposed; - private bool m_IsSecure; - private RemoteCertificateValidationCallback m_pCertificateCallback; - private IPEndPoint m_pLocalEP; - private Logger m_pLogger; - private IPEndPoint m_pRemoteEP; - private SmartStream m_pTcpStream; - private Socket socket; - - #endregion - - #region Properties - - /// - /// Gets the time when session was connected. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when TCP client is not connected. - public override DateTime ConnectTime - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_Client"); - } - if (!IsConnected) - { - throw new InvalidOperationException("TCP client is not connected."); - } - - return m_ConnectTime; - } - } - - /// - /// Gets session ID. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when TCP client is not connected. - public override string ID - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_Client"); - } - if (!IsConnected) - { - throw new InvalidOperationException("TCP client is not connected."); - } - - return m_ID; - } - } - - /// - /// Gets if TCP client is connected. - /// - public override bool IsConnected - { - get - { - if (Socket != null) - { - return Socket.Connected; - } - return m_IsConnected; - } - } - - /// - /// Gets if this object is disposed. - /// - public bool IsDisposed - { - get { return m_IsDisposed; } - } - - /// - /// Gets if this session TCP connection is secure connection. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when TCP client is not connected. - public override bool IsSecureConnection - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_Client"); - } - if (!IsConnected) - { - throw new InvalidOperationException("TCP client is not connected."); - } - - return m_IsSecure; - } - } - - /// - /// Gets the last time when data was sent or received. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when TCP client is not connected. - public override DateTime LastActivity - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_Client"); - } - if (!IsConnected) - { - throw new InvalidOperationException("TCP client is not connected."); - } - - return m_pTcpStream.LastActivity; - } - } - - /// - /// Gets session local IP end point. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when TCP client is not connected. - public override IPEndPoint LocalEndPoint - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_Client"); - } - if (!IsConnected) - { - throw new InvalidOperationException("TCP client is not connected."); - } - - return m_pLocalEP; - } - } - - /// - /// Gets or sets TCP client logger. Value null means no logging. - /// - public Logger Logger - { - get { return m_pLogger; } - - set { m_pLogger = value; } - } - - /// - /// Gets session remote IP end point. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when TCP client is not connected. - public override IPEndPoint RemoteEndPoint - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_Client"); - } - if (!IsConnected) - { - throw new InvalidOperationException("TCP client is not connected."); - } - - return m_pRemoteEP; - } - } - - /// - /// Gets TCP stream which must be used to send/receive data through this session. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when TCP client is not connected. - public override SmartStream TcpStream - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_Client"); - } - if (!IsConnected) - { - throw new InvalidOperationException("TCP client is not connected."); - } - - return m_pTcpStream; - } - } - - /// - /// Gets or stes remote callback which is called when remote server certificate needs to be validated. - /// Value null means not sepcified. - /// - public RemoteCertificateValidationCallback ValidateCertificateCallback - { - get { return m_pCertificateCallback; } - - set { m_pCertificateCallback = value; } - } - - public Socket Socket - { - get { return socket; } - set { socket = value; } - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. This method is thread-safe. - /// - public override void Dispose() - { - lock (this) - { - Debug.Print("TCP client disposed"); - if (m_IsDisposed) - { - return; - } - try - { - if (IsConnected) - { - Disconnect(); - } - } - catch {} - m_IsDisposed = true; - } - } - - /// - /// Starts connection to the specified host. - /// - /// Host name or IP address. - /// Port to connect. - /// Callback to call when the connect operation is complete. - /// User data. - /// An IAsyncResult that references the asynchronous connection. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when TCP client is already connected. - /// Is raised when any of the arguments has invalid value. - public IAsyncResult BeginConnect(string host, int port, AsyncCallback callback, object state) - { - return BeginConnect(host, port, false, callback, state); - } - - /// - /// Starts connection to the specified host. - /// - /// Host name or IP address. - /// Port to connect. - /// Specifies if connects to SSL end point. - /// Callback to call when the connect operation is complete. - /// User data. - /// An IAsyncResult that references the asynchronous connection. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when TCP client is already connected. - /// Is raised when any of the arguments has invalid value. - public IAsyncResult BeginConnect(string host, int port, bool ssl, AsyncCallback callback, object state) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (IsConnected) - { - throw new InvalidOperationException("TCP client is already connected."); - } - if (string.IsNullOrEmpty(host)) - { - throw new ArgumentException("Argument 'host' value may not be null or empty."); - } - if (port < 1) - { - throw new ArgumentException("Argument 'port' value must be >= 1."); - } - - BeginConnectHostDelegate asyncMethod = Connect; - AsyncResultState asyncState = new AsyncResultState(this, asyncMethod, callback, state); - asyncState.SetAsyncResult(asyncMethod.BeginInvoke(host, - port, - ssl, - asyncState.CompletedCallback, - null)); - - return asyncState; - } - - /// - /// Starts connection to the specified remote end point. - /// - /// Remote IP end point where to connect. - /// Specifies if connects to SSL end point. - /// Callback to call when the connect operation is complete. - /// User data. - /// An IAsyncResult that references the asynchronous connection. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when TCP client is already connected. - /// Is raised when remoteEP is null. - /// Is raised when any of the arguments has invalid value. - public IAsyncResult BeginConnect(IPEndPoint remoteEP, bool ssl, AsyncCallback callback, object state) - { - return BeginConnect(null, remoteEP, ssl, callback, state); - } - - /// - /// Starts connection to the specified remote end point. - /// - /// Local IP end point to use for connect. - /// Remote IP end point where to connect. - /// Specifies if connects to SSL end point. - /// Callback to call when the connect operation is complete. - /// User data. - /// An IAsyncResult that references the asynchronous connection. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when TCP client is already connected. - /// Is raised when remoteEP is null. - /// Is raised when any of the arguments has invalid value. - public IAsyncResult BeginConnect(IPEndPoint localEP, - IPEndPoint remoteEP, - bool ssl, - AsyncCallback callback, - object state) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (IsConnected) - { - throw new InvalidOperationException("TCP client is already connected."); - } - if (remoteEP == null) - { - throw new ArgumentNullException("remoteEP"); - } - - BeginConnectEPDelegate asyncMethod = Connect; - AsyncResultState asyncState = new AsyncResultState(this, asyncMethod, callback, state); - asyncState.SetAsyncResult(asyncMethod.BeginInvoke(localEP, - remoteEP, - ssl, - asyncState.CompletedCallback, - null)); - - return asyncState; - } - - /// - /// Ends a pending asynchronous connection request. - /// - /// An IAsyncResult that stores state information and any user defined data for this asynchronous operation. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when asyncResult is null. - /// Is raised when argument asyncResult was not returned by a call to the BeginConnect method. - /// Is raised when EndConnect was previously called for the asynchronous connection. - public void EndConnect(IAsyncResult asyncResult) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (asyncResult == null) - { - throw new ArgumentNullException("asyncResult"); - } - - AsyncResultState castedAsyncResult = asyncResult as AsyncResultState; - if (castedAsyncResult == null || castedAsyncResult.AsyncObject != this) - { - throw new ArgumentException( - "Argument asyncResult was not returned by a call to the BeginConnect method."); - } - if (castedAsyncResult.IsEndCalled) - { - throw new InvalidOperationException( - "EndConnect was previously called for the asynchronous operation."); - } - - castedAsyncResult.IsEndCalled = true; - if (castedAsyncResult.AsyncDelegate is BeginConnectHostDelegate) - { - ((BeginConnectHostDelegate) castedAsyncResult.AsyncDelegate).EndInvoke( - castedAsyncResult.AsyncResult); - } - else if (castedAsyncResult.AsyncDelegate is BeginConnectEPDelegate) - { - ((BeginConnectEPDelegate) castedAsyncResult.AsyncDelegate).EndInvoke( - castedAsyncResult.AsyncResult); - } - else - { - throw new ArgumentException( - "Argument asyncResult was not returned by a call to the BeginConnect method."); - } - } - - /// - /// Connects to the specified host. If the hostname resolves to more than one IP address, - /// all IP addresses will be tried for connection, until one of them connects. - /// - /// Host name or IP address. - /// Port to connect. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when TCP client is already connected. - /// Is raised when any of the arguments has invalid value. - public void Connect(string host, int port) - { - Connect(host, port, false); - } - - /// - /// Connects to the specified host. If the hostname resolves to more than one IP address, - /// all IP addresses will be tried for connection, until one of them connects. - /// - /// Host name or IP address. - /// Port to connect. - /// Specifies if connects to SSL end point. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when TCP client is already connected. - /// Is raised when any of the arguments has invalid value. - public void Connect(string host, int port, bool ssl) - { - Connect(host, port, ssl, 30000); - } - - /// - /// Connects to the specified host. If the hostname resolves to more than one IP address, - /// all IP addresses will be tried for connection, until one of them connects. - /// - /// Host name or IP address. - /// Port to connect. - /// Specifies if connects to SSL end point. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when TCP client is already connected. - /// Is raised when any of the arguments has invalid value. - public void Connect(string host, int port, bool ssl, int timeout) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_Client"); - } - if (IsConnected) - { - throw new InvalidOperationException("TCP client is already connected."); - } - if (string.IsNullOrEmpty(host)) - { - throw new ArgumentException("Argument 'host' value may not be null or empty."); - } - if (port < 1) - { - throw new ArgumentException("Argument 'port' value must be >= 1."); - } - //Check ip - - IPAddress[] ips = Dns.GetHostAddresses(host); - - for (int i = 0; i < ips.Length; i++) - { - try - { - Connect(null, new IPEndPoint(ips[i], port), ssl, timeout); - break; - } - catch (Exception x) - { - if (IsConnected) - { - throw x; - } - // Connect failed for specified IP address, if there are some more IPs left, try next, otherwise forward exception. - else if (i == (ips.Length - 1)) - { - throw x; - } - } - } - } - - /// - /// Connects to the specified remote end point. - /// - /// Remote IP end point where to connect. - /// Specifies if connects to SSL end point. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when TCP client is already connected. - /// Is raised when remoteEP is null. - /// Is raised when any of the arguments has invalid value. - public void Connect(IPEndPoint remoteEP, bool ssl) - { - Connect(null, remoteEP, ssl); - } - - /// - /// Connects to the specified remote end point. - /// - /// Local IP end point to use for connet. - /// Remote IP end point where to connect. - /// Specifies if connects to SSL end point. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when TCP client is already connected. - /// Is raised when remoteEP is null. - /// Is raised when any of the arguments has invalid value. - public void Connect(IPEndPoint localEP, IPEndPoint remoteEP, bool ssl) - { - Connect(localEP, remoteEP, ssl, 30000); - } - - /// - /// Connects to the specified remote end point. - /// - /// Local IP end point to use for connet. - /// Remote IP end point where to connect. - /// Specifies if connects to SSL end point. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when TCP client is already connected. - /// Is raised when remoteEP is null. - /// Is raised when any of the arguments has invalid value. - public void Connect(IPEndPoint localEP, IPEndPoint remoteEP, bool ssl, int timeout) - { - Debug.Print("TCP client connect"); - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_Client"); - } - if (IsConnected) - { - throw new InvalidOperationException("TCP client is already connected."); - } - if (remoteEP == null) - { - throw new ArgumentNullException("remoteEP"); - } - - if (Socket != null && Socket.Connected) - { - Socket.Disconnect(false); - Socket.Shutdown(SocketShutdown.Both); - } - - Socket = null; - if (remoteEP.AddressFamily == AddressFamily.InterNetwork) - { - Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - } - else if (remoteEP.AddressFamily == AddressFamily.InterNetworkV6) - { - Socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp); - } - else - { - throw new ArgumentException("Remote end point has invalid AddressFamily."); - } - - try - { - Socket.SendTimeout = timeout; - Socket.ReceiveTimeout = timeout; - - if (localEP != null) - { - Socket.Bind(localEP); - } - - LogAddText("Connecting to " + remoteEP + "."); - Socket.Connect(remoteEP); - - m_IsConnected = true; - m_ID = Guid.NewGuid().ToString(); - m_ConnectTime = DateTime.Now; - m_pLocalEP = (IPEndPoint) Socket.LocalEndPoint; - m_pRemoteEP = (IPEndPoint) Socket.RemoteEndPoint; - m_pTcpStream = new SmartStream(new NetworkStream(Socket, true), true); - - LogAddText("Connected, localEP='" + m_pLocalEP + "'; remoteEP='" + remoteEP + "'."); - - if (ssl) - { - SwitchToSecure(); - } - } - catch (Exception x) - { - LogAddException("Exception: " + x.Message, x); - - // Switching to secure failed. - if (IsConnected) - { - Disconnect(); - } - // Bind or connect failed. - else - { - Socket.Close(); - } - - OnError(x); - - throw x; - } - - OnConnected(); - } - - /// - /// Disconnects connection. - /// - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when TCP client is not connected. - public override void Disconnect() - { - Debug.Print("TCP client disconect"); - - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_Client"); - } - if (!IsConnected) - { - throw new InvalidOperationException("TCP client is not connected."); - } - - try - { - m_pLocalEP = null; - m_pRemoteEP = null; - m_pTcpStream.Dispose(); - LogAddText("Socket disconnecting"); - m_IsSecure = false; - - } - catch (Exception) - { - - } - } - - private void DisconnectCallback(IAsyncResult ar) - { - try - { - - Socket.EndDisconnect(ar); - Socket.Close(); - m_IsConnected = false; - LogAddText("Disconnected."); - - } - catch (Exception) - { - - } - } - - /// - /// Starts disconnecting connection. - /// - /// Callback to call when the asynchronous operation is complete. - /// User data. - /// An IAsyncResult that references the asynchronous disconnect. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when TCP client is not connected. - public IAsyncResult BeginDisconnect(AsyncCallback callback, object state) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (!IsConnected) - { - throw new InvalidOperationException("TCP client is not connected."); - } - - DisconnectDelegate asyncMethod = Disconnect; - AsyncResultState asyncState = new AsyncResultState(this, asyncMethod, callback, state); - asyncState.SetAsyncResult(asyncMethod.BeginInvoke(asyncState.CompletedCallback, null)); - - return asyncState; - } - - /// - /// Ends a pending asynchronous disconnect request. - /// - /// An IAsyncResult that stores state information and any user defined data for this asynchronous operation. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when asyncResult is null. - /// Is raised when argument asyncResult was not returned by a call to the BeginDisconnect method. - /// Is raised when EndDisconnect was previously called for the asynchronous connection. - public void EndDisconnect(IAsyncResult asyncResult) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (asyncResult == null) - { - throw new ArgumentNullException("asyncResult"); - } - - AsyncResultState castedAsyncResult = asyncResult as AsyncResultState; - if (castedAsyncResult == null || castedAsyncResult.AsyncObject != this) - { - throw new ArgumentException( - "Argument asyncResult was not returned by a call to the BeginDisconnect method."); - } - if (castedAsyncResult.IsEndCalled) - { - throw new InvalidOperationException( - "EndDisconnect was previously called for the asynchronous connection."); - } - - castedAsyncResult.IsEndCalled = true; - if (castedAsyncResult.AsyncDelegate is DisconnectDelegate) - { - ((DisconnectDelegate) castedAsyncResult.AsyncDelegate).EndInvoke(castedAsyncResult.AsyncResult); - } - else - { - throw new ArgumentException( - "Argument asyncResult was not returned by a call to the BeginDisconnect method."); - } - } - - #endregion - - #region Virtual methods - - /// - /// This method is called after TCP client has sucessfully connected. - /// - protected virtual void OnConnected() {} - - #endregion - - #region Utility methods - - private bool RemoteCertificateValidationCallback(object sender, - X509Certificate certificate, - X509Chain chain, - SslPolicyErrors sslPolicyErrors) - { - // User will handle it. - if (m_pCertificateCallback != null) - { - return m_pCertificateCallback(sender, certificate, chain, sslPolicyErrors); - } - else - { - if (sslPolicyErrors == SslPolicyErrors.None || - sslPolicyErrors == SslPolicyErrors.RemoteCertificateNameMismatch) - { - return true; - } - - // Do not allow this client to communicate with unauthenticated servers. - return false; - } - } - - #endregion - - /// - /// Switches session to secure connection. - /// - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when TCP client is not connected or is already secure. - protected void SwitchToSecure() - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_Client"); - } - if (!IsConnected) - { - throw new InvalidOperationException("TCP client is not connected."); - } - if (m_IsSecure) - { - throw new InvalidOperationException("TCP client is already secure."); - } - - LogAddText("Switching to SSL."); - - // FIX ME: if ssl switching fails, it closes source stream or otherwise if ssl successful, source stream leaks. - - SslStream sslStream = new SslStream(m_pTcpStream.SourceStream, - true, - RemoteCertificateValidationCallback); - sslStream.AuthenticateAsClient(""); - - // Close old stream, but leave source stream open. - m_pTcpStream.IsOwner = false; - m_pTcpStream.Dispose(); - - m_IsSecure = true; - m_pTcpStream = new SmartStream(sslStream, true); - } - - // Do we need it ? - - /// - /// This must be called when unexpected error happens. When inheriting TCP_Client class, be sure that you call OnError - /// method for each unexpected error. - /// - /// Exception happened. - protected void OnError(Exception x) - { - try - { - if (m_pLogger != null) - { - //m_pLogger.AddException(x); - } - } - catch {} - } - - /// - /// Reads and logs specified line from connected host. - /// - /// Returns readed line. - protected string ReadLine() - { - SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength], - SizeExceededAction. - JunkAndThrowException); - TcpStream.ReadLine(args, false); - if (args.Error != null) - { - throw args.Error; - } - string line = args.LineUtf8; - if (args.BytesInBuffer > 0) - { - LogAddRead(args.BytesInBuffer, line); - } - else - { - LogAddText("Remote host closed connection."); - } - - return line; - } - - /// - /// Sends and logs specified line to connected host. - /// - /// Line to send. - protected void WriteLine(string line) - { - if (line == null) - { - throw new ArgumentNullException("line"); - } - - int countWritten = TcpStream.WriteLine(line); - LogAddWrite(countWritten, line); - } - - /// - /// Logs read operation. - /// - /// Number of bytes readed. - /// Log text. - protected void LogAddRead(long size, string text) - { - try - { - if (m_pLogger != null) - { - m_pLogger.AddRead(ID, AuthenticatedUserIdentity, size, text, LocalEndPoint, RemoteEndPoint); - } - } - catch - { - // We skip all logging errors, normally there shouldn't be any. - } - } - - /// - /// Logs write operation. - /// - /// Number of bytes written. - /// Log text. - protected void LogAddWrite(long size, string text) - { - try - { - if (m_pLogger != null) - { - m_pLogger.AddWrite(ID, - AuthenticatedUserIdentity, - size, - text, - LocalEndPoint, - RemoteEndPoint); - } - } - catch - { - // We skip all logging errors, normally there shouldn't be any. - } - } - - /// - /// Logs free text entry. - /// - /// Log text. - protected void LogAddText(string text) - { - try - { - if (m_pLogger != null) - { - m_pLogger.AddText(IsConnected ? ID : "", - IsConnected ? AuthenticatedUserIdentity : null, - text, - IsConnected ? LocalEndPoint : null, - IsConnected ? RemoteEndPoint : null); - } - } - catch - { - // We skip all logging errors, normally there shouldn't be any. - } - } - - /// - /// Logs exception. - /// - /// Log text. - /// Exception happened. - protected void LogAddException(string text, Exception x) - { - try - { - if (m_pLogger != null) - { - m_pLogger.AddException(IsConnected ? ID : "", - IsConnected ? AuthenticatedUserIdentity : null, - text, - IsConnected ? LocalEndPoint : null, - IsConnected ? RemoteEndPoint : null, - x); - } - } - catch - { - // We skip all logging errors, normally there shouldn't be any. - } - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TCP/TCP_Server.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TCP/TCP_Server.cs deleted file mode 100644 index 531798842..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TCP/TCP_Server.cs +++ /dev/null @@ -1,925 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.TCP -{ - #region usings - - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.Net; - using System.Net.Sockets; - using System.Threading; - using System.Timers; - using Log; - - #endregion - - /// - /// This class implements generic TCP session based server. - /// - public class TCP_Server : IDisposable where T : TCP_ServerSession, new() - { - #region Events - - /// - /// This event is raised when TCP server has disposed. - /// - public event EventHandler Disposed = null; - - /// - /// This event is raised when TCP server has unknown unhandled error. - /// - public event ErrorEventHandler Error = null; - - /// - /// This event is raised when TCP server creates new session. - /// - public event EventHandler> SessionCreated = null; - - /// - /// This event is raised when TCP server has started. - /// - public event EventHandler Started = null; - - /// - /// This event is raised when TCP server has stopped. - /// - public event EventHandler Stopped = null; - - #endregion - - #region Members - - private readonly List m_pListeningPoints; - private long m_ConnectionsProcessed; - - private bool m_IsDisposed; - private bool m_IsRunning; - private long m_MaxConnections; - private long m_MaxConnectionsPerIP; - private IPBindInfo[] m_pBindings = new IPBindInfo[0]; - private Logger m_pLogger; - private TCP_SessionCollection m_pSessions; - private TimerEx m_pTimer_IdleTimeout; - private int m_SessionIdleTimeout = 100; - private DateTime m_StartTime; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public TCP_Server() - { - m_pListeningPoints = new List(); - m_pSessions = new TCP_SessionCollection(); - } - - #endregion - - #region Properties - - /// - /// Gets or sets TCP server IP bindings. - /// - /// Is raised when this object is disposed and this property is accessed. - public IPBindInfo[] Bindings - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pBindings; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - if (value == null) - { - value = new IPBindInfo[0]; - } - - //--- See binds has changed -------------- - bool changed = false; - if (m_pBindings.Length != value.Length) - { - changed = true; - } - else - { - for (int i = 0; i < m_pBindings.Length; i++) - { - if (!m_pBindings[i].Equals(value[i])) - { - changed = true; - break; - } - } - } - - if (changed) - { - m_pBindings = value; - - if (m_IsRunning) - { - StartListen(); - } - } - } - } - - /// - /// Gets how many connections this TCP server has processed. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when TCP server is not running and this property is accesed. - public long ConnectionsProcessed - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_Server"); - } - if (!m_IsRunning) - { - throw new InvalidOperationException("TCP server is not running."); - } - - return m_ConnectionsProcessed; - } - } - - /// - /// Gets if server is disposed. - /// - public bool IsDisposed - { - get { return m_IsDisposed; } - } - - /// - /// Gets if server is running. - /// - public bool IsRunning - { - get { return m_IsRunning; } - } - - /// - /// Gets local listening IP end points. - /// - /// Is raised when this object is disposed and this property is accessed. - public IPEndPoint[] LocalEndPoints - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - List retVal = new List(); - foreach (IPBindInfo bind in Bindings) - { - if (bind.IP.Equals(IPAddress.Any)) - { - foreach (IPAddress ip in Dns.GetHostAddresses("")) - { - if (ip.AddressFamily == AddressFamily.InterNetwork && - !retVal.Contains(new IPEndPoint(ip, bind.Port))) - { - retVal.Add(new IPEndPoint(ip, bind.Port)); - } - } - } - else if (bind.IP.Equals(IPAddress.IPv6Any)) - { - foreach (IPAddress ip in Dns.GetHostAddresses("")) - { - if (ip.AddressFamily == AddressFamily.InterNetworkV6 && - !retVal.Contains(new IPEndPoint(ip, bind.Port))) - { - retVal.Add(new IPEndPoint(ip, bind.Port)); - } - } - } - else - { - if (!retVal.Contains(bind.EndPoint)) - { - retVal.Add(bind.EndPoint); - } - } - } - - return retVal.ToArray(); - } - } - - /// - /// Gets or sets logger. Value null means no logging. - /// - public Logger Logger - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - return m_pLogger; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - m_pLogger = value; - } - } - - /// - /// Gets or sets maximum allowed concurent connections. Value 0 means unlimited. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when negative value is passed. - public long MaxConnections - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_Server"); - } - - return m_MaxConnections; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_Server"); - } - if (value < 0) - { - throw new ArgumentException("Property 'MaxConnections' value must be >= 0."); - } - - m_MaxConnections = value; - } - } - - /// - /// Gets or sets maximum allowed connections for 1 IP address. Value 0 means unlimited. - /// - public long MaxConnectionsPerIP - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_Server"); - } - - return m_MaxConnectionsPerIP; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_Server"); - } - if (m_MaxConnectionsPerIP < 0) - { - throw new ArgumentException("Property 'MaxConnectionsPerIP' value must be >= 0."); - } - - m_MaxConnectionsPerIP = value; - } - } - - /// - /// Gets or sets maximum allowed session idle time in seconds, after what session will be terminated. Value 0 means unlimited, - /// but this is strongly not recommened. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when negative value is passed. - public int SessionIdleTimeout - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_Server"); - } - - return m_SessionIdleTimeout; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_Server"); - } - if (m_SessionIdleTimeout < 0) - { - throw new ArgumentException("Property 'SessionIdleTimeout' value must be >= 0."); - } - - m_SessionIdleTimeout = value; - } - } - - /// - /// Gets TCP server active sessions. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when TCP server is not running and this property is accesed. - public TCP_SessionCollection Sessions - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_Server"); - } - if (!m_IsRunning) - { - throw new InvalidOperationException("TCP server is not running."); - } - - return m_pSessions; - } - } - - /// - /// Gets the time when server was started. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when TCP server is not running and this property is accesed. - public DateTime StartTime - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_Server"); - } - if (!m_IsRunning) - { - throw new InvalidOperationException("TCP server is not running."); - } - - return m_StartTime; - } - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public void Dispose() - { - if (m_IsDisposed) - { - return; - } - if (m_IsRunning) - { - try - { - Stop(); - } - catch {} - } - m_IsDisposed = true; - - // We must call disposed event before we release events. - try - { - OnDisposed(); - } - catch - { - // We never should get exception here, user should handle it, just skip it. - } - - m_pSessions = null; - - // Release all events. - Started = null; - Stopped = null; - Disposed = null; - Error = null; - } - - /// - /// Starts TCP server. - /// - /// Is raised when this object is disposed and this property is accessed. - public void Start() - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_Server"); - } - if (m_IsRunning) - { - return; - } - m_IsRunning = true; - - m_StartTime = DateTime.Now; - m_ConnectionsProcessed = 0; - - ThreadPool.QueueUserWorkItem(delegate { StartListen(); }); - - m_pTimer_IdleTimeout = new TimerEx(30000, true); - m_pTimer_IdleTimeout.Elapsed += m_pTimer_IdleTimeout_Elapsed; - m_pTimer_IdleTimeout.Enabled = true; - - OnStarted(); - } - - /// - /// Stops TCP server, all active connections will be terminated. - /// - public void Stop() - { - if (!m_IsRunning) - { - return; - } - m_IsRunning = false; - - // Dispose all old binds. - foreach (ListeningPoint listeningPoint in m_pListeningPoints.ToArray()) - { - try - { - listeningPoint.Socket.Close(); - } - catch (Exception x) - { - OnError(x); - } - } - m_pListeningPoints.Clear(); - - m_pTimer_IdleTimeout.Dispose(); - m_pTimer_IdleTimeout = null; - - OnStopped(); - } - - /// - /// Restarts TCP server. - /// - public void Restart() - { - Stop(); - Start(); - } - - #endregion - - #region Virtual methods - - /// - /// Is called when new incoming session and server maximum allowed connections exceeded. - /// - /// Incoming session. - /// This method allows inhereted classes to report error message to connected client. - /// Session will be disconnected after this method completes. - /// - protected virtual void OnMaxConnectionsExceeded(T session) {} - - /// - /// Is called when new incoming session and server maximum allowed connections per connected IP exceeded. - /// - /// Incoming session. - /// This method allows inhereted classes to report error message to connected client. - /// Session will be disconnected after this method completes. - /// - protected virtual void OnMaxConnectionsPerIPExceeded(T session) {} - - #endregion - - #region Utility methods - - /// - /// Is called when session idle check timer triggered. - /// - /// Sender. - /// Event data. - private void m_pTimer_IdleTimeout_Elapsed(object sender, ElapsedEventArgs e) - { - try - { - foreach (T session in Sessions.ToArray()) - { - try - { - if (DateTime.Now > session.TcpStream.LastActivity.AddSeconds(m_SessionIdleTimeout)) - { - - session.OnTimeoutI(); - // Session didn't dispose itself, so dispose it. - if (!session.IsDisposed) - { - session.Disconnect(); - session.Dispose(); - } - } - } - catch {} - } - } - catch (Exception x) - { - OnError(x); - } - } - - /// - /// Starts listening incoming connections. NOTE: All active listening points will be disposed. - /// - private void StartListen() - { - try - { - // Dispose all old binds. - foreach (ListeningPoint listeningPoint in m_pListeningPoints.ToArray()) - { - try - { - listeningPoint.Socket.Close(); - } - catch (Exception x) - { - OnError(x); - } - } - m_pListeningPoints.Clear(); - - // Create new listening points and start accepting connections. - bool ioCompletion_asyncSockets = Net_Utils.IsIoCompletionPortsSupported(); - foreach (IPBindInfo bind in m_pBindings) - { - try - { - Socket socket = null; - if (bind.IP.AddressFamily == AddressFamily.InterNetwork) - { - socket = new Socket(AddressFamily.InterNetwork, - SocketType.Stream, - ProtocolType.Tcp); - } - else if (bind.IP.AddressFamily == AddressFamily.InterNetworkV6) - { - socket = new Socket(AddressFamily.InterNetworkV6, - SocketType.Stream, - ProtocolType.Tcp); - } - else - { - // Invalid address family, just skip it. - continue; - } - socket.Bind(new IPEndPoint(bind.IP, bind.Port)); - socket.Listen(100); - - ListeningPoint listeningPoint = new ListeningPoint(socket, bind); - m_pListeningPoints.Add(listeningPoint); - - // Begin accept. - // We MUST use socket.AcceptAsync method, this consume all threading power in Windows paltform(IO completion ports). - // For other platforms we need to use BeginAccept. - - #region IO completion ports - - if (ioCompletion_asyncSockets) - { - SocketAsyncEventArgs eArgs = new SocketAsyncEventArgs(); - eArgs.Completed += delegate(object s, SocketAsyncEventArgs e) - { - if (e.SocketError == SocketError.Success) - { - ProcessConnection(e.AcceptSocket, bind); - } - - // Start accepting new connection. - IOCompletionBeginAccept(e, socket, bind); - }; - - // Move processing to thread-pool, because IOCompletionBeginAccept keeps using calling thread as loang as there is work todo. - ThreadPool.QueueUserWorkItem(delegate - { - // Start accepting new connection. - IOCompletionBeginAccept(eArgs, socket, bind); - }); - } - - #endregion - - #region Async sockets - - else - { - // Begin accepting connection. - socket.BeginAccept(new AsyncCallback(AsynSocketsAcceptCompleted), listeningPoint); - } - - #endregion - } - catch (Exception x) - { - // The only exception what we should get there is if socket is in use. - OnError(x); - } - } - } - catch (Exception x) - { - OnError(x); - } - } - - /// - /// Starts accepting connection(s). - /// - /// AcceptAsync method data. - /// Local listening socket. - /// Local listening socket bind info. - /// Is raised when socketArgs,listeningSocket or bindInfo is null reference. - private void IOCompletionBeginAccept(SocketAsyncEventArgs socketArgs, - Socket listeningSocket, - IPBindInfo bindInfo) - { - if (socketArgs == null) - { - throw new ArgumentNullException("socketArgs"); - } - if (listeningSocket == null) - { - throw new ArgumentNullException("listeningSocket"); - } - if (bindInfo == null) - { - throw new ArgumentNullException("bindInfo"); - } - - try - { - // We need to clear it, before reuse. - socketArgs.AcceptSocket = null; - - // Use active worker thread as long as AcceptAsync completes synchronously. - // (With this approeach we don't have thread context switches while AcceptAsync completes synchronously) - while (m_IsRunning && !listeningSocket.AcceptAsync(socketArgs)) - { - // Operation completed synchronously. - - if (socketArgs.SocketError == SocketError.Success) - { - ProcessConnection(socketArgs.AcceptSocket, bindInfo); - } - - // We need to clear it, before reuse. - socketArgs.AcceptSocket = null; - } - } - catch (ObjectDisposedException x) - { - string dummy = x.Message; - // Listening socket closed, so skip that error. - } - } - - /// - /// This method is called when BeginAccept ha completed. - /// - /// The result of the asynchronous operation. - private void AsynSocketsAcceptCompleted(IAsyncResult ar) - { - ListeningPoint lPoint = (ListeningPoint) ar.AsyncState; - try - { - ProcessConnection(lPoint.Socket.EndAccept(ar), lPoint.BindInfo); - } - catch - { - // Skip accept errors. - } - - // Begin accepting connection. - lPoint.Socket.BeginAccept(new AsyncCallback(AsynSocketsAcceptCompleted), lPoint); - } - - /// - /// Processes specified connection. - /// - /// Accpeted socket. - /// Local bind info what accpeted connection. - /// Is raised when socket or bindInfo is null reference. - private void ProcessConnection(Socket socket, IPBindInfo bindInfo) - { - if (socket == null) - { - throw new ArgumentNullException("socket"); - } - if (bindInfo == null) - { - throw new ArgumentNullException("bindInfo"); - } - - m_ConnectionsProcessed++; - - try - { - T session = new T(); - session.Init(this, - socket, - bindInfo.HostName, - bindInfo.SslMode == SslMode.SSL, - bindInfo.Certificate); - - // Maximum allowed connections exceeded, reject connection. - if (m_MaxConnections != 0 && m_pSessions.Count > m_MaxConnections) - { - OnMaxConnectionsExceeded(session); - session.Dispose(); - } - // Maximum allowed connections per IP exceeded, reject connection. - else if (m_MaxConnectionsPerIP != 0) - { - OnMaxConnectionsPerIPExceeded(session); - session.Dispose(); - } - // Start processing new session. - else - { - session.Disonnected += - delegate(object sender, EventArgs e) { m_pSessions.Remove((T)sender); }; - m_pSessions.Add(session); - - OnSessionCreated(session); - - session.Start(); - } - } - catch (Exception x) - { - OnError(x); - } - } - - /// - /// Raises SessionCreated event. - /// - /// TCP server session that was created. - private void OnSessionCreated(T session) - { - if (SessionCreated != null) - { - SessionCreated(this, new TCP_ServerSessionEventArgs(this, session)); - } - } - - /// - /// Raises Error event. - /// - /// Exception happened. - protected void OnError(Exception x) - { - if (Error != null) - { - Error(this, new Error_EventArgs(x, new StackTrace())); - } - } - - #endregion - - /// - /// Raises Started event. - /// - protected void OnStarted() - { - if (Started != null) - { - Started(this, new EventArgs()); - } - } - - /// - /// Raises Stopped event. - /// - protected void OnStopped() - { - if (Stopped != null) - { - Stopped(this, new EventArgs()); - } - } - - /// - /// Raises Disposed event. - /// - protected void OnDisposed() - { - if (Disposed != null) - { - Disposed(this, new EventArgs()); - } - } - - #region Nested type: ListeningPoint - - /// - /// This class holds listening point info. - /// - private class ListeningPoint - { - #region Members - - private readonly IPBindInfo m_pBindInfo; - private readonly Socket m_pSocket; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Listening socket. - /// Bind info what acceped socket. - public ListeningPoint(Socket socket, IPBindInfo bind) - { - m_pSocket = socket; - m_pBindInfo = bind; - } - - #endregion - - #region Properties - - /// - /// Gets bind info. - /// - public IPBindInfo BindInfo - { - get { return m_pBindInfo; } - } - - /// - /// Gets socket. - /// - public Socket Socket - { - get { return m_pSocket; } - } - - #endregion - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TCP/TCP_ServerSession.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TCP/TCP_ServerSession.cs deleted file mode 100644 index 0a0e32d6a..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TCP/TCP_ServerSession.cs +++ /dev/null @@ -1,567 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.TCP -{ - #region usings - - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.Net; - using System.Net.Security; - using System.Net.Sockets; - using System.Security.Cryptography.X509Certificates; - using IO; - - #endregion - - /// - /// This class implements generic TCP server session. - /// - public class TCP_ServerSession : TCP_Session - { - #region Events - - /// - /// This event is raised when session has disconnected and will be disposed soon. - /// - public event EventHandler Disonnected = null; - - /// - /// This event is raised when session has disposed. - /// - public event EventHandler Disposed = null; - - /// - /// This event is raised when TCP server session has unknown unhandled error. - /// - public event ErrorEventHandler Error = null; - - /// - /// This event is raised when session idle(no activity) timeout reached. - /// - public event EventHandler IdleTimeout = null; - - #endregion - - #region Members - - private DateTime m_ConnectTime; - private string m_ID = ""; - - private bool m_IsDisposed; - private bool m_IsSecure; - private bool m_IsTerminated; - private string m_LocalHostName = ""; - private X509Certificate m_pCertificate; - private IPEndPoint m_pLocalEP; - private IPEndPoint m_pRemoteEP; - private object m_pServer; - private Dictionary m_pTags; - private SmartStream m_pTcpStream; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public TCP_ServerSession() - { - m_pTags = new Dictionary(); - } - - #endregion - - #region Properties - - /// - /// Gets session certificate. - /// - /// Is raised when this object is disposed and this property is accessed. - public X509Certificate Certificate - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_ServerSession"); - } - - return m_pCertificate; - } - } - - /// - /// Gets the time when session was connected. - /// - /// Is raised when this object is disposed and this property is accessed. - public override DateTime ConnectTime - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_ServerSession"); - } - - return m_ConnectTime; - } - } - - /// - /// Gets session ID. - /// - /// Is raised when this object is disposed and this property is accessed. - public override string ID - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_ServerSession"); - } - - return m_ID; - } - } - - /// - /// Gets if session is connected. - /// - public override bool IsConnected - { - get { return true; } - } - - /// - /// Gets if TCP server session is disposed. - /// - public bool IsDisposed - { - get { return m_IsDisposed; } - } - - /// - /// Gets if this session TCP connection is secure connection. - /// - /// Is raised when this object is disposed and this property is accessed. - public override bool IsSecureConnection - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_ServerSession"); - } - - return m_IsSecure; - } - } - - /// - /// Gets the last time when data was sent or received. - /// - /// Is raised when this object is disposed and this property is accessed. - public override DateTime LastActivity - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_ServerSession"); - } - - return m_pTcpStream.LastActivity; - } - } - - /// - /// Gets session local IP end point. - /// - /// Is raised when this object is disposed and this property is accessed. - public override IPEndPoint LocalEndPoint - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_ServerSession"); - } - - return m_pLocalEP; - } - } - - /// - /// Gets local host name. - /// - /// Is raised when this object is disposed and this property is accessed. - public string LocalHostName - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_ServerSession"); - } - - return m_LocalHostName; - } - } - - /// - /// Gets session remote IP end point. - /// - /// Is raised when this object is disposed and this property is accessed. - public override IPEndPoint RemoteEndPoint - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_ServerSession"); - } - - return m_pRemoteEP; - } - } - - /// - /// Gets owner TCP server. - /// - /// Is raised when this object is disposed and this property is accessed. - public object Server - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_ServerSession"); - } - - return m_pServer; - } - } - - /// - /// Gets or sets user data. - /// - public object Tag { get; set; } - - /// - /// Gets user data items collection. - /// - /// Is raised when this object is disposed and this property is accessed. - public Dictionary Tags - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_ServerSession"); - } - - return m_pTags; - } - } - - /// - /// Gets TCP stream which must be used to send/receive data through this session. - /// - /// Is raised when this object is disposed and this property is accessed. - public override SmartStream TcpStream - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_ServerSession"); - } - - return m_pTcpStream; - } - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public override void Dispose() - { - if (m_IsDisposed) - { - return; - } - if (!m_IsTerminated) - { - try - { - Disconnect(); - } - catch - { - // Skip disconnect errors. - } - } - m_IsDisposed = true; - - // We must call disposed event before we release events. - try - { - OnDisposed(); - } - catch - { - // We never should get exception here, user should handle it, just skip it. - } - - m_pLocalEP = null; - m_pRemoteEP = null; - m_pCertificate = null; - - try - { - //_socket.Shutdown(SocketShutdown.Both);//Stop coms - m_pTcpStream.Dispose(); - } - catch (Exception) - { - - } - - m_pTcpStream = null; - m_pTags = null; - - // Release events. - IdleTimeout = null; - Disonnected = null; - Disposed = null; - } - - /// - /// Switches session to secure connection. - /// - /// Is raised when this object is disposed and this method is accessed. - /// Is raised when connection is already secure or when SSL certificate is not specified. - public void SwitchToSecure() - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_ServerSession"); - } - if (m_IsSecure) - { - throw new InvalidOperationException("Session is already SSL/TLS."); - } - if (m_pCertificate == null) - { - throw new InvalidOperationException("There is no certificate specified."); - } - - // FIX ME: if ssl switching fails, it closes source stream or otherwise if ssl successful, source stream leaks. - - SslStream sslStream = new SslStream(m_pTcpStream.SourceStream); - sslStream.AuthenticateAsServer(m_pCertificate); - - // Close old stream, but leave source stream open. - m_pTcpStream.IsOwner = false; - m_pTcpStream.Dispose(); - - m_IsSecure = true; - m_pTcpStream = new SmartStream(sslStream, true); - } - - /// - /// Disconnects session. - /// - /// Is raised when this object is disposed and this property is accessed. - public override void Disconnect() - { - Disconnect(null); - } - - /// - /// Disconnects session. - /// - /// Text what is sent to connected host before disconnecting. - /// Is raised when this object is disposed and this method is accessed. - public void Disconnect(string text) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("TCP_ServerSession"); - } - if (m_IsTerminated) - { - return; - } - m_IsTerminated = true; - - if (!string.IsNullOrEmpty(text)) - { - try - { - m_pTcpStream.Write(text); - } - catch (Exception x) - { - OnError(x); - } - } - - try - { - OnDisonnected(); - } - catch (Exception x) - { - // We never should get exception here, user should handle it. - OnError(x); - } - - Dispose(); - } - - #endregion - - #region Virtual methods - - /// - /// This method is called from TCP server when session should start processing incoming connection. - /// - protected internal virtual void Start() {} - - /// - /// This method is called when specified session times out. - /// - /// - /// This method allows inhereted classes to report error message to connected client. - /// Session will be disconnected after this method completes. - /// - protected virtual void OnTimeout() {} - - /// - /// Just calls OnTimeout method. - /// - internal virtual void OnTimeoutI() - { - OnTimeout(); - } - - /// - /// Raises Error event. - /// - /// Exception happened. - protected virtual void OnError(Exception x) - { - if (Error != null) - { - Error(this, new Error_EventArgs(x, new StackTrace())); - } - } - - #endregion - - #region Internal methods - - private Socket _socket; - /// - /// Initializes session. This method is called from TCP_Server when new session created. - /// - /// Owner TCP server. - /// Connected socket. - /// Local host name. - /// Specifies if session should switch to SSL. - /// SSL certificate. - internal void Init(object server, - Socket socket, - string hostName, - bool ssl, - X509Certificate certificate) - { - // NOTE: We may not raise any event here ! - _socket = socket; - m_pServer = server; - m_LocalHostName = hostName; - m_ID = Guid.NewGuid().ToString(); - m_ConnectTime = DateTime.Now; - m_pLocalEP = (IPEndPoint) socket.LocalEndPoint; - m_pRemoteEP = (IPEndPoint) socket.RemoteEndPoint; - m_pCertificate = certificate; - - socket.ReceiveBufferSize = Workaround.Definitions.MaxStreamLineLength; - socket.SendBufferSize = Workaround.Definitions.MaxStreamLineLength; - socket.ReceiveTimeout = 30000; - socket.SendTimeout = 10000;//10 sec - - if (ssl) - { - SwitchToSecure(); - } - else - { - m_pTcpStream = new SmartStream(new NetworkStream(socket, true), true); - } - } - - #endregion - - #region Utility methods - - /// - /// Raises IdleTimeout event. - /// - private void OnIdleTimeout() - { - if (IdleTimeout != null) - { - IdleTimeout(this, new EventArgs()); - } - } - - - /// - /// Raises Disonnected event. - /// - private void OnDisonnected() - { - if (Disonnected != null) - { - Disonnected(this, new EventArgs()); - } - } - - /// - /// Raises Disposed event. - /// - private void OnDisposed() - { - if (Disposed != null) - { - Disposed(this, new EventArgs()); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TCP/TCP_ServerSessionEventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TCP/TCP_ServerSessionEventArgs.cs deleted file mode 100644 index a4c373ab9..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TCP/TCP_ServerSessionEventArgs.cs +++ /dev/null @@ -1,73 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.TCP -{ - #region usings - - using System; - - #endregion - - /// - /// This class provides data to .... . - /// - public class TCP_ServerSessionEventArgs : EventArgs where T : TCP_ServerSession, new() - { - #region Members - - private readonly TCP_Server m_pServer; - private readonly T m_pSession; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// TCP server. - /// TCP server session. - internal TCP_ServerSessionEventArgs(TCP_Server server, T session) - { - m_pServer = server; - m_pSession = session; - } - - #endregion - - #region Properties - - /// - /// Gets TCP server. - /// - public TCP_Server Server - { - get { return m_pServer; } - } - - /// - /// Gets TCP server session. - /// - public T Session - { - get { return m_pSession; } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TCP/TCP_Session.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TCP/TCP_Session.cs deleted file mode 100644 index 57e9f4b9c..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TCP/TCP_Session.cs +++ /dev/null @@ -1,111 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.TCP -{ - #region usings - - using System; - using System.Net; - using System.Security.Principal; - using IO; - - #endregion - - /// - /// This is base class for TCP_Client and TCP_ServerSession. - /// - public abstract class TCP_Session : IDisposable - { - #region Properties - - /// - /// Gets session authenticated user identity , returns null if not authenticated. - /// - public virtual GenericIdentity AuthenticatedUserIdentity - { - get { return null; } - } - - /// - /// Gets the time when session was connected. - /// - public abstract DateTime ConnectTime { get; } - - /// - /// Gets session ID. - /// - public abstract string ID { get; } - - /// - /// Gets if this session is authenticated. - /// - public bool IsAuthenticated - { - get { return AuthenticatedUserIdentity != null; } - } - - /// - /// Gets if session is connected. - /// - public abstract bool IsConnected { get; } - - /// - /// Gets if this session TCP connection is secure connection. - /// - public virtual bool IsSecureConnection - { - get { return false; } - } - - /// - /// Gets the last time when data was sent or received. - /// - public abstract DateTime LastActivity { get; } - - /// - /// Gets session local IP end point. - /// - public abstract IPEndPoint LocalEndPoint { get; } - - /// - /// Gets session remote IP end point. - /// - public abstract IPEndPoint RemoteEndPoint { get; } - - /// - /// Gets TCP stream which must be used to send/receive data through this session. - /// - public abstract SmartStream TcpStream { get; } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public abstract void Dispose(); - - /// - /// Disconnects session. - /// - public abstract void Disconnect(); - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TCP/TCP_SessionCollection.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TCP/TCP_SessionCollection.cs deleted file mode 100644 index ad6613874..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TCP/TCP_SessionCollection.cs +++ /dev/null @@ -1,154 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System.Collections; - -namespace ASC.Mail.Net.TCP -{ - #region usings - - using System; - using System.Collections.Generic; - - #endregion - - /// - /// This class implements TCP session collection. - /// - public class TCP_SessionCollection:IEnumerable where T : TCP_Session - { - #region Members - - private readonly Dictionary m_pItems; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - internal TCP_SessionCollection() - { - m_pItems = new Dictionary(); - } - - #endregion - - #region Properties - - /// - /// Gets number of items in the collection. - /// - public int Count - { - get { return m_pItems.Count; } - } - - /// - /// Gets TCP session with the specified ID. - /// - /// Session ID. - /// Returns TCP session with the specified ID. - public T this[string id] - { - get { return m_pItems[id]; } - } - - #endregion - - #region Methods - - /// - /// Copies all TCP server session to new array. This method is thread-safe. - /// - /// Returns TCP sessions array. - public T[] ToArray() - { - lock (m_pItems) - { - T[] retVal = new T[m_pItems.Count]; - m_pItems.Values.CopyTo(retVal, 0); - - return retVal; - } - } - - #endregion - - #region Internal methods - - /// - /// Adds specified TCP session to the colletion. - /// - /// TCP server session to add. - /// Is raised when session is null. - internal void Add(T session) - { - if (session == null) - { - throw new ArgumentNullException("session"); - } - - lock (m_pItems) - { - m_pItems.Add(session.ID, session); - } - } - - /// - /// Removes specified TCP server session from the collection. - /// - /// TCP server session to remove. - /// Is raised when session is null. - internal void Remove(T session) - { - if (session == null) - { - throw new ArgumentNullException("session"); - } - - lock (m_pItems) - { - m_pItems.Remove(session.ID); - } - } - - /// - /// Removes all items from the collection. - /// - internal void Clear() - { - lock (m_pItems) - { - m_pItems.Clear(); - } - } - - #endregion - - public IEnumerator GetEnumerator() - { - return m_pItems.Values.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TextUtils.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TextUtils.cs deleted file mode 100644 index b018b7cbd..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TextUtils.cs +++ /dev/null @@ -1,395 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - #region usings - - using System; - using System.Collections; - using System.Collections.Generic; - using System.Text; - - #endregion - - /// - /// This class provides usefull text methods. - /// - public class TextUtils - { - #region Methods - - /// - /// Qoutes and escapes fishy(\") chars. - /// - /// Text to quote. - /// - public static string QuoteString(string text) - { - StringBuilder retVal = new StringBuilder(); - - for (int i = 0; i < text.Length; i++) - { - char c = text[i]; - - if (c == '\\') - { - retVal.Append("\\\\"); - } - else if (c == '\"') - { - retVal.Append("\\\""); - } - else - { - retVal.Append(c); - } - } - - return "\"" + retVal + "\""; - } - - /// - /// Unquotes and unescapes escaped chars specified text. For example "xxx" will become to 'xxx', "escaped quote \"", will become to escaped 'quote "'. - /// - /// Text to unquote. - /// - public static string UnQuoteString(string text) - { - int startPosInText = 0; - int endPosInText = text.Length; - - //--- Trim. We can't use standard string.Trim(), it's slow. ----// - for (int i = 0; i < endPosInText; i++) - { - char c = text[i]; - if (c == ' ' || c == '\t') - { - startPosInText++; - } - else - { - break; - } - } - for (int i = endPosInText - 1; i > 0; i--) - { - char c = text[i]; - if (c == ' ' || c == '\t') - { - endPosInText--; - } - else - { - break; - } - } - //--------------------------------------------------------------// - - // All text trimmed - if ((endPosInText - startPosInText) <= 0) - { - return ""; - } - - // Remove starting and ending quotes. - if (text[startPosInText] == '\"') - { - startPosInText++; - } - if (text[endPosInText - 1] == '\"') - { - endPosInText--; - } - - // Just '"' - if (endPosInText == startPosInText - 1) - { - return ""; - } - - char[] chars = new char[endPosInText - startPosInText]; - - int posInChars = 0; - bool charIsEscaped = false; - for (int i = startPosInText; i < endPosInText; i++) - { - char c = text[i]; - - // Escaping char - if (!charIsEscaped && c == '\\') - { - charIsEscaped = true; - } - // Escaped char - else if (charIsEscaped) - { - // TODO: replace \n,\r,\t,\v ??? - chars[posInChars] = c; - posInChars++; - charIsEscaped = false; - } - // Normal char - else - { - chars[posInChars] = c; - posInChars++; - charIsEscaped = false; - } - } - - return new string(chars, 0, posInChars); - } - - /// - /// Escapes specified chars in the specified string. - /// - /// Text to escape. - /// Chars to escape. - public static string EscapeString(string text, char[] charsToEscape) - { - // Create worst scenario buffer, assume all chars must be escaped - char[] buffer = new char[text.Length*2]; - int nChars = 0; - foreach (char c in text) - { - foreach (char escapeChar in charsToEscape) - { - if (c == escapeChar) - { - buffer[nChars] = '\\'; - nChars++; - break; - } - } - - buffer[nChars] = c; - nChars++; - } - - return new string(buffer, 0, nChars); - } - - /// - /// Unescapes all escaped chars. - /// - /// Text to unescape. - /// - public static string UnEscapeString(string text) - { - // Create worst scenarion buffer, non of the chars escaped. - char[] buffer = new char[text.Length]; - int nChars = 0; - bool escapedCahr = false; - foreach (char c in text) - { - if (!escapedCahr && c == '\\') - { - escapedCahr = true; - } - else - { - buffer[nChars] = c; - nChars++; - escapedCahr = false; - } - } - - return new string(buffer, 0, nChars); - } - - /// - /// Splits string into string arrays. This split method won't split qouted strings, but only text outside of qouted string. - /// For example: '"text1, text2",text3' will be 2 parts: "text1, text2" and text3. - /// - /// Text to split. - /// Char that splits text. - /// - public static string[] SplitQuotedString(string text, char splitChar) - { - return SplitQuotedString(text, splitChar, false); - } - - /// - /// Splits string into string arrays. This split method won't split qouted strings, but only text outside of qouted string. - /// For example: '"text1, text2",text3' will be 2 parts: "text1, text2" and text3. - /// - /// Text to split. - /// Char that splits text. - /// If true, splitted parst will be unqouted if they are qouted. - /// - public static string[] SplitQuotedString(string text, char splitChar, bool unquote) - { - List splitParts = new List(); // Holds splitted parts - int startPos = 0; - bool inQuotedString = false; // Holds flag if position is quoted string or not - char lastChar = '0'; - - for (int i = 0; i < text.Length; i++) - { - char c = text[i]; - - // Start/end quoted string area. Ingonre escaped \". - if (lastChar != '\\' && c == '\"') - { - inQuotedString = !inQuotedString; - } - - // Current char is split char and it isn't in quoted string, do split - if (!inQuotedString && c == splitChar) - { - // Add current currentSplitBuffer value to splitted parts list - if (unquote) - { - splitParts.Add(UnQuoteString(text.Substring(startPos, i - startPos))); - } - else - { - splitParts.Add(text.Substring(startPos, i - startPos)); - } - - // Store new split part start position. - startPos = i + 1; - } - lastChar = c; - } - // Add last split part to splitted parts list - if (unquote) - { - splitParts.Add(UnQuoteString(text.Substring(startPos, text.Length - startPos))); - } - else - { - splitParts.Add(text.Substring(startPos, text.Length - startPos)); - } - - return splitParts.ToArray(); - } - - /// - /// Gets first index of specified char. The specified char in quoted string is skipped. - /// Returns -1 if specified char doesn't exist. - /// - /// Text in what to check. - /// Char what index to get. - /// - public static int QuotedIndexOf(string text, char indexChar) - { - int retVal = -1; - bool inQuotedString = false; // Holds flag if position is quoted string or not - for (int i = 0; i < text.Length; i++) - { - char c = text[i]; - - if (c == '\"') - { - // Start/end quoted string area - inQuotedString = !inQuotedString; - } - - // Current char is what index we want and it isn't in quoted string, return it's index - if (!inQuotedString && c == indexChar) - { - return i; - } - } - - return retVal; - } - - /// - /// Splits string into string arrays. - /// - /// Text to split. - /// Char Char that splits text. - /// - public static string[] SplitString(string text, char splitChar) - { - ArrayList splitParts = new ArrayList(); // Holds splitted parts - - int lastSplitPoint = 0; - int textLength = text.Length; - for (int i = 0; i < textLength; i++) - { - if (text[i] == splitChar) - { - // Add current currentSplitBuffer value to splitted parts list - splitParts.Add(text.Substring(lastSplitPoint, i - lastSplitPoint)); - - lastSplitPoint = i + 1; - } - } - // Add last split part to splitted parts list - if (lastSplitPoint <= textLength) - { - splitParts.Add(text.Substring(lastSplitPoint)); - } - - string[] retVal = new string[splitParts.Count]; - splitParts.CopyTo(retVal, 0); - - return retVal; - } - - /// - /// Gets if specified string is valid "token" value. - /// - /// String value to check. - /// Returns true if specified string value is valid "token" value. - /// Is raised if value is null. - public static bool IsToken(string value) - { - if (value == null) - { - throw new ArgumentNullException(value); - } - - /* This syntax is taken from rfc 3261, but token must be universal so ... . - token = 1*(alphanum / "-" / "." / "!" / "%" / "*" / "_" / "+" / "`" / "'" / "~" ) - alphanum = ALPHA / DIGIT - ALPHA = %x41-5A / %x61-7A ; A-Z / a-z - DIGIT = %x30-39 ; 0-9 - */ - - char[] tokenChars = new[] {'-', '.', '!', '%', '*', '_', '+', '`', '\'', '~'}; - foreach (char c in value) - { - // We don't have letter or digit, so we only may have token char. - if (!((c >= 0x41 && c <= 0x5A) || (c >= 0x61 && c <= 0x7A) || (c >= 0x30 && c <= 0x39))) - { - bool validTokenChar = false; - foreach (char tokenChar in tokenChars) - { - if (c == tokenChar) - { - validTokenChar = true; - break; - } - } - if (!validTokenChar) - { - return false; - } - } - } - - return true; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TimerEx.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TimerEx.cs deleted file mode 100644 index a2573cfc3..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/TimerEx.cs +++ /dev/null @@ -1,58 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - #region usings - - using System.Timers; - - #endregion - - /// - /// Simple timer implementation. - /// - public class TimerEx : Timer - { - #region Constructor - - /// - /// Default contructor. - /// - public TimerEx() {} - - /// - /// Default contructor. - /// - /// The time in milliseconds between events. - public TimerEx(double interval) : base(interval) {} - - /// - /// Default contructor. - /// - /// The time in milliseconds between events. - /// Specifies if timer is auto reseted. - public TimerEx(double interval, bool autoReset) : base(interval) - { - AutoReset = autoReset; - } - - #endregion - - // TODO: We need to do this class CF compatible. - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/UDP/UDP_PacketEventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/UDP/UDP_PacketEventArgs.cs deleted file mode 100644 index 8b704dbbf..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/UDP/UDP_PacketEventArgs.cs +++ /dev/null @@ -1,128 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.UDP -{ - #region usings - - using System; - using System.Net; - using System.Net.Sockets; - - #endregion - - /// - /// This class provides data for UdpServer.PacketReceived event. - /// - public class UDP_PacketEventArgs : EventArgs - { - #region Members - - private readonly byte[] m_pData; - private readonly IPEndPoint m_pRemoteEP; - private readonly Socket m_pSocket; - private readonly UDP_Server m_pUdpServer; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// UDP server which received packet. - /// Socket which received packet. - /// Remote end point which sent data. - /// UDP data. - internal UDP_PacketEventArgs(UDP_Server server, Socket socket, IPEndPoint remoteEP, byte[] data) - { - m_pUdpServer = server; - m_pSocket = socket; - m_pRemoteEP = remoteEP; - m_pData = data; - } - - #endregion - - #region Properties - - /// - /// Gets UDP packet data. - /// - public byte[] Data - { - get { return m_pData; } - } - - /// - /// Gets local end point what recieved packet. - /// - public IPEndPoint LocalEndPoint - { - get { return (IPEndPoint) m_pSocket.LocalEndPoint; } - } - - /// - /// Gets remote end point what sent data. - /// - public IPEndPoint RemoteEndPoint - { - get { return m_pRemoteEP; } - } - - /// - /// Gets UDP server which received packet. - /// - public UDP_Server UdpServer - { - get { return m_pUdpServer; } - } - - /// - /// Gets socket which received packet. - /// - internal Socket Socket - { - get { return m_pSocket; } - } - - #endregion - - #region Methods - - /// - /// Sends reply to received packet. This method uses same local end point to send packet which - /// received packet, this ensures right NAT traversal. - /// - /// Data buffer. - /// Offset in the buffer. - /// Number of bytes to send. - /// Is raised when data is null. - public void SendReply(byte[] data, int offset, int count) - { - if (data == null) - { - throw new ArgumentNullException("data"); - } - - IPEndPoint localEP = null; - m_pUdpServer.SendPacket(m_pSocket, data, offset, count, m_pRemoteEP, out localEP); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/UDP/UDP_ProcessMode.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/UDP/UDP_ProcessMode.cs deleted file mode 100644 index 9f270bbb4..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/UDP/UDP_ProcessMode.cs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.UDP -{ - /// - /// This enum specified UDP server packets process mode. - /// - public enum UDP_ProcessMode - { - /// - /// UDP packets processed one by one in their receive order. - /// - Sequential = 1, - - /// - /// UDP packets proecesses parallel. - /// - Parallel = 2, - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/UDP/UDP_Server.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/UDP/UDP_Server.cs deleted file mode 100644 index 34bf51a5f..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/UDP/UDP_Server.cs +++ /dev/null @@ -1,958 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.UDP -{ - #region usings - - using System; - using System.Collections.Generic; - using System.Diagnostics; - using System.Net; - using System.Net.Sockets; - using System.Threading; - - #endregion - - #region Delegates Declarations - - /// - /// Represents the method that will handle the UdpServer.PacketReceived event. - /// - /// Event data. - public delegate void PacketReceivedHandler(UDP_PacketEventArgs e); - - #endregion - - /// - /// This class implements generic UDP server. - /// - public class UDP_Server - { - #region Events - - /// - /// This event is raised when unexpected error happens. - /// - public event ErrorEventHandler Error = null; - - /// - /// This event is raised when new UDP packet received. - /// - public event PacketReceivedHandler PacketReceived = null; - - #endregion - - #region Members - - private long m_BytesReceived; - private long m_BytesSent; - private bool m_IsDisposed; - private bool m_IsRunning; - - private int m_MaxQueueSize = 200; - private int m_MTU = 1400; - private long m_PacketsReceived; - private long m_PacketsSent; - private IPEndPoint[] m_pBindings; - private Queue m_pQueuedPackets; - private UDP_ProcessMode m_ProcessMode = UDP_ProcessMode.Sequential; - private CircleCollection m_pSendSocketsIPv4; - private CircleCollection m_pSendSocketsIPv6; - private List m_pSockets; - private DateTime m_StartTime; - - #endregion - - #region Properties - - /// - /// Gets or sets IP end point where UDP server is binded. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when null value is passed. - public IPEndPoint[] Bindings - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("UdpServer"); - } - - return m_pBindings; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("UdpServer"); - } - if (value == null) - { - throw new ArgumentNullException(); - } - - // See if changed. Also if server running we must restart it. - bool changed = false; - if (m_pBindings == null) - { - changed = true; - } - else if (m_pBindings.Length != value.Length) - { - changed = true; - } - else - { - for (int i = 0; i < m_pBindings.Length; i++) - { - if (!m_pBindings[i].Equals(value[i])) - { - changed = true; - break; - } - } - } - if (changed) - { - m_pBindings = value; - Restart(); - } - } - } - - /// - /// Gets how many bytes this UDP server has received since start. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised whan UDP server is not running and this property is accessed. - public long BytesReceived - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("UdpServer"); - } - if (!m_IsRunning) - { - throw new InvalidOperationException("UDP server is not running."); - } - - return m_BytesReceived; - } - } - - /// - /// Gets how many bytes this UDP server has sent since start. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised whan UDP server is not running and this property is accessed. - public long BytesSent - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("UdpServer"); - } - if (!m_IsRunning) - { - throw new InvalidOperationException("UDP server is not running."); - } - - return m_BytesSent; - } - } - - /// - /// Gets if this object is disposed. - /// - public bool IsDisposed - { - get { return m_IsDisposed; } - } - - /// - /// Gets if UDP server is running. - /// - /// Is raised when this object is disposed and this property is accessed. - public bool IsRunning - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("UdpServer"); - } - - return m_IsRunning; - } - } - - /// - /// Gets maximum UDP packets to queue. - /// - /// Is raised when this object is disposed and this property is accessed. - public int MaxQueueSize - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("UdpServer"); - } - - return m_MaxQueueSize; - } - } - - /// - /// Gets or sets maximum network transmission unit. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when server is running and this property value is tried to set. - public int MTU - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("UdpServer"); - } - - return m_MTU; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("UdpServer"); - } - if (m_IsRunning) - { - throw new InvalidOperationException( - "MTU value can be changed only if UDP server is not running."); - } - - m_MTU = value; - } - } - - /// - /// Gets how many UDP packets this UDP server has received since start. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised whan UDP server is not running and this property is accessed. - public long PacketsReceived - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("UdpServer"); - } - if (!m_IsRunning) - { - throw new InvalidOperationException("UDP server is not running."); - } - - return m_PacketsReceived; - } - } - - /// - /// Gets how many UDP packets this UDP server has sent since start. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised whan UDP server is not running and this property is accessed. - public long PacketsSent - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("UdpServer"); - } - if (!m_IsRunning) - { - throw new InvalidOperationException("UDP server is not running."); - } - - return m_PacketsSent; - } - } - - /// - /// Gets or sets UDP packets process mode. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised when server is running and this property value is tried to set. - public UDP_ProcessMode ProcessMode - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("UdpServer"); - } - - return m_ProcessMode; - } - - set - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("UdpServer"); - } - if (m_IsRunning) - { - throw new InvalidOperationException( - "ProcessMode value can be changed only if UDP server is not running."); - } - - m_ProcessMode = value; - } - } - - /// - /// Gets time when server was started. - /// - /// Is raised when this object is disposed and this property is accessed. - /// Is raised whan UDP server is not running and this property is accessed. - public DateTime StartTime - { - get - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("UdpServer"); - } - if (!m_IsRunning) - { - throw new InvalidOperationException("UDP server is not running."); - } - - return m_StartTime; - } - } - - #endregion - - #region Methods - - /// - /// Cleans up any resources being used. - /// - public void Dispose() - { - if (m_IsDisposed) - { - return; - } - m_IsDisposed = false; - Stop(); - // Release all events. - Error = null; - PacketReceived = null; - } - - /// - /// Starts UDP server. - /// - public void Start() - { - if (m_IsRunning) - { - return; - } - m_IsRunning = true; - - m_StartTime = DateTime.Now; - m_pQueuedPackets = new Queue(); - - // Run only if we have some listening point. - if (m_pBindings != null) - { - // We must replace IPAddress.Any to all available IPs, otherwise it's impossible to send - // reply back to UDP packet sender on same local EP where packet received. This is very - // important when clients are behind NAT. - List listeningEPs = new List(); - foreach (IPEndPoint ep in m_pBindings) - { - if (ep.Address.Equals(IPAddress.Any)) - { - // Add localhost. - IPEndPoint epLocalhost = new IPEndPoint(IPAddress.Loopback, ep.Port); - if (!listeningEPs.Contains(epLocalhost)) - { - listeningEPs.Add(epLocalhost); - } - // Add all host IPs. - foreach (IPAddress ip in Dns.GetHostAddresses("")) - { - IPEndPoint epNew = new IPEndPoint(ip, ep.Port); - if (!listeningEPs.Contains(epNew)) - { - listeningEPs.Add(epNew); - } - } - } - else - { - if (!listeningEPs.Contains(ep)) - { - listeningEPs.Add(ep); - } - } - } - - // Create sockets. - m_pSockets = new List(); - foreach (IPEndPoint ep in listeningEPs) - { - try - { - m_pSockets.Add(Core.CreateSocket(ep, ProtocolType.Udp)); - } - catch (Exception x) - { - OnError(x); - } - } - - // Create round-robin send sockets. NOTE: We must skip localhost, it can't be used - // for sending out of server. - m_pSendSocketsIPv4 = new CircleCollection(); - m_pSendSocketsIPv6 = new CircleCollection(); - foreach (Socket socket in m_pSockets) - { - if ((socket.LocalEndPoint).AddressFamily == AddressFamily.InterNetwork) - { - if (!((IPEndPoint) socket.LocalEndPoint).Address.Equals(IPAddress.Loopback)) - { - m_pSendSocketsIPv4.Add(socket); - } - } - else if ((socket.LocalEndPoint).AddressFamily == AddressFamily.InterNetworkV6) - { - m_pSendSocketsIPv6.Add(socket); - } - } - - Thread tr = new Thread(ProcessIncomingUdp); - tr.Start(); - Thread tr2 = new Thread(ProcessQueuedPackets); - tr2.Start(); - } - } - - /// - /// Stops UDP server. - /// - public void Stop() - { - if (!m_IsRunning) - { - return; - } - m_IsRunning = false; - - m_pQueuedPackets = null; - // Close sockets. - foreach (Socket socket in m_pSockets) - { - socket.Close(); - } - m_pSockets = null; - m_pSendSocketsIPv4 = null; - m_pSendSocketsIPv6 = null; - } - - /// - /// Restarts running server. If server is not running, this methods has no efffect. - /// - public void Restart() - { - if (m_IsRunning) - { - Stop(); - Start(); - } - } - - /// - /// Sends specified UDP packet to the specified remote end point. - /// - /// UDP packet to send. - /// Offset in the buffer. - /// Number of bytes to send. - /// Remote end point. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised whan UDP server is not running and this method is accessed. - /// Is raised when any of the arumnets is null. - /// Is raised when any of the arguments has invalid value. - public void SendPacket(byte[] packet, int offset, int count, IPEndPoint remoteEP) - { - IPEndPoint localEP = null; - SendPacket(packet, offset, count, remoteEP, out localEP); - } - - /// - /// Sends specified UDP packet to the specified remote end point. - /// - /// UDP packet to send. - /// Offset in the buffer. - /// Number of bytes to send. - /// Remote end point. - /// Returns local IP end point which was used to send UDP packet. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised whan UDP server is not running and this method is accessed. - /// Is raised when any of the arumnets is null. - /// Is raised when any of the arguments has invalid value. - public void SendPacket(byte[] packet, - int offset, - int count, - IPEndPoint remoteEP, - out IPEndPoint localEP) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("UdpServer"); - } - if (!m_IsRunning) - { - throw new InvalidOperationException("UDP server is not running."); - } - if (packet == null) - { - throw new ArgumentNullException("packet"); - } - if (remoteEP == null) - { - throw new ArgumentNullException("remoteEP"); - } - - localEP = null; - SendPacket(null, packet, offset, count, remoteEP, out localEP); - } - - /// - /// Sends specified UDP packet to the specified remote end point. - /// - /// Local end point to use for sending. - /// UDP packet to send. - /// Offset in the buffer. - /// Number of bytes to send. - /// Remote end point. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised whan UDP server is not running and this method is accessed. - /// Is raised when any of the arumnets is null. - /// Is raised when any of the arguments has invalid value. - public void SendPacket(IPEndPoint localEP, byte[] packet, int offset, int count, IPEndPoint remoteEP) - { - if (m_IsDisposed) - { - throw new ObjectDisposedException("UdpServer"); - } - if (!m_IsRunning) - { - throw new InvalidOperationException("UDP server is not running."); - } - if (packet == null) - { - throw new ArgumentNullException("packet"); - } - if (localEP == null) - { - throw new ArgumentNullException("localEP"); - } - if (remoteEP == null) - { - throw new ArgumentNullException("remoteEP"); - } - if (localEP.AddressFamily != remoteEP.AddressFamily) - { - throw new ArgumentException("Argumnet localEP and remoteEP AddressFamily won't match."); - } - - // Search specified local end point socket. - Socket socket = null; - if (localEP.AddressFamily == AddressFamily.InterNetwork) - { - foreach (Socket s in m_pSendSocketsIPv4.ToArray()) - { - if (localEP.Equals(s.LocalEndPoint)) - { - socket = s; - break; - } - } - } - else if (localEP.AddressFamily == AddressFamily.InterNetworkV6) - { - foreach (Socket s in m_pSendSocketsIPv6.ToArray()) - { - if (localEP.Equals(s.LocalEndPoint)) - { - socket = s; - break; - } - } - } - else - { - throw new ArgumentException("Argument 'localEP' has unknown AddressFamily."); - } - - // We don't have specified local end point. - if (socket == null) - { - throw new ArgumentException("Specified local end point '" + localEP + "' doesn't exist."); - } - - IPEndPoint lEP = null; - SendPacket(socket, packet, offset, count, remoteEP, out lEP); - } - - /// - /// Gets suitable local IP end point for the specified remote endpoint. - /// If there are multiple sending local end points, they will be load-balanched with round-robin. - /// - /// Remote end point. - /// Returns local IP end point. - /// Is raised when argument remoteEP is null. - /// Is raised when argument remoteEP has invalid value. - /// Is raised when no suitable IPv4 or IPv6 socket for remoteEP. - public IPEndPoint GetLocalEndPoint(IPEndPoint remoteEP) - { - if (remoteEP == null) - { - throw new ArgumentNullException("remoteEP"); - } - - if (remoteEP.AddressFamily == AddressFamily.InterNetwork) - { - // We don't have any IPv4 local end point. - if (m_pSendSocketsIPv4.Count == 0) - { - throw new InvalidOperationException( - "There is no suitable IPv4 local end point in this.Bindings."); - } - - return (IPEndPoint) m_pSendSocketsIPv4.Next().LocalEndPoint; - } - else if (remoteEP.AddressFamily == AddressFamily.InterNetworkV6) - { - // We don't have any IPv6 local end point. - if (m_pSendSocketsIPv6.Count == 0) - { - throw new InvalidOperationException( - "There is no suitable IPv6 local end point in this.Bindings."); - } - - return (IPEndPoint) m_pSendSocketsIPv6.Next().LocalEndPoint; - } - else - { - throw new ArgumentException("Argument 'remoteEP' has unknown AddressFamily."); - } - } - - #endregion - - #region Internal methods - - /// - /// Sends specified UDP packet to the specified remote end point. - /// - /// UDP socket to use for data sending. - /// UDP packet to send. - /// Offset in the buffer. - /// Number of bytes to send. - /// Remote end point. - /// Returns local IP end point which was used to send UDP packet. - /// Is raised when this object is disposed and this method is accessed. - /// Is raised whan UDP server is not running and this method is accessed. - /// Is raised when any of the arumnets is null. - /// Is raised when any of the arguments has invalid value. - internal void SendPacket(Socket socket, - byte[] packet, - int offset, - int count, - IPEndPoint remoteEP, - out IPEndPoint localEP) - { - // Round-Robin all local end points, if no end point specified. - if (socket == null) - { - // Get right IP address family socket which matches remote end point. - if (remoteEP.AddressFamily == AddressFamily.InterNetwork) - { - if (m_pSendSocketsIPv4.Count == 0) - { - throw new ArgumentException( - "There is no suitable IPv4 local end point in this.Bindings."); - } - - socket = m_pSendSocketsIPv4.Next(); - } - else if (remoteEP.AddressFamily == AddressFamily.InterNetworkV6) - { - if (m_pSendSocketsIPv6.Count == 0) - { - throw new ArgumentException( - "There is no suitable IPv6 local end point in this.Bindings."); - } - - socket = m_pSendSocketsIPv6.Next(); - } - else - { - throw new ArgumentException("Invalid remote end point address family."); - } - } - - // Send packet. - socket.SendTo(packet, 0, count, SocketFlags.None, remoteEP); - - localEP = (IPEndPoint) socket.LocalEndPoint; - - m_BytesSent += count; - m_PacketsSent++; - } - - #endregion - - #region Utility methods - - /// - /// Processes incoming UDP data and queues it for processing. - /// - private void ProcessIncomingUdp() - { - // Create Round-Robin for listening points. - CircleCollection listeningEPs = new CircleCollection(); - foreach (Socket socket in m_pSockets) - { - listeningEPs.Add(socket); - } - - byte[] buffer = new byte[m_MTU]; - while (m_IsRunning) - { - try - { - // Maximum allowed UDP queued packts exceeded. - if (m_pQueuedPackets.Count >= m_MaxQueueSize) - { - Thread.Sleep(1); - } - else - { - // Roun-Robin sockets. - bool receivedData = false; - for (int i = 0; i < listeningEPs.Count; i++) - { - Socket socket = listeningEPs.Next(); - // Read data only when there is some, otherwise we block thread. - if (socket.Poll(0, SelectMode.SelectRead)) - { - // Receive data. - EndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0); - int received = socket.ReceiveFrom(buffer, ref remoteEP); - m_BytesReceived += received; - m_PacketsReceived++; - - // Queue received packet. - byte[] data = new byte[received]; - Array.Copy(buffer, data, received); - lock (m_pQueuedPackets) - { - m_pQueuedPackets.Enqueue(new UdpPacket(socket, (IPEndPoint) remoteEP, data)); - } - - // We received data, so exit round-robin loop. - receivedData = true; - break; - } - } - // We didn't get any data from any listening point, we must sleep or we use 100% CPU. - if (!receivedData) - { - Thread.Sleep(1); - } - } - } - catch (Exception x) - { - OnError(x); - } - } - } - - /// - /// This method processes queued UDP packets. - /// - private void ProcessQueuedPackets() - { - while (m_IsRunning) - { - try - { - // There are no packets to process. - if (m_pQueuedPackets.Count == 0) - { - Thread.Sleep(1); - } - else - { - UdpPacket udpPacket = null; - lock (m_pQueuedPackets) - { - udpPacket = m_pQueuedPackets.Dequeue(); - } - - // Sequential, call PacketReceived event on same thread. - if (m_ProcessMode == UDP_ProcessMode.Sequential) - { - OnUdpPacketReceived(udpPacket); - } - // Parallel, call PacketReceived event on Thread pool thread. - else - { - ThreadPool.QueueUserWorkItem(ProcessPacketOnTrPool, udpPacket); - } - } - } - catch (Exception x) - { - OnError(x); - } - } - } - - /// - /// Processes UDP packet on thread pool thread. - /// - /// User data. - private void ProcessPacketOnTrPool(object state) - { - try - { - OnUdpPacketReceived((UdpPacket) state); - } - catch (Exception x) - { - OnError(x); - } - } - - /// - /// Raises PacketReceived event. - /// - /// UDP packet. - private void OnUdpPacketReceived(UdpPacket packet) - { - if (PacketReceived != null) - { - PacketReceived(new UDP_PacketEventArgs(this, packet.Socket, packet.RemoteEndPoint, packet.Data)); - } - } - - /// - /// Raises Error event. - /// - /// Exception occured. - private void OnError(Exception x) - { - if (Error != null) - { - Error(this, new Error_EventArgs(x, new StackTrace())); - } - } - - #endregion - - #region Nested type: UdpPacket - - /// - /// This class represents UDP packet. - /// - private class UdpPacket - { - #region Members - - private readonly byte[] m_pData; - private readonly IPEndPoint m_pRemoteEP; - private readonly Socket m_pSocket; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Socket which received packet. - /// Remote end point from where packet was received. - /// UDP packet data. - public UdpPacket(Socket socket, IPEndPoint remoteEP, byte[] data) - { - m_pSocket = socket; - m_pRemoteEP = remoteEP; - m_pData = data; - } - - #endregion - - #region Properties - - /// - /// Gets UDP packet data. - /// - public byte[] Data - { - get { return m_pData; } - } - - /// - /// Gets remote end point from where packet was received. - /// - public IPEndPoint RemoteEndPoint - { - get { return m_pRemoteEP; } - } - - /// - /// Gets socket which received packet. - /// - public Socket Socket - { - get { return m_pSocket; } - } - - #endregion - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/URI/AbsoluteUri.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/URI/AbsoluteUri.cs deleted file mode 100644 index e6ef654f8..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/URI/AbsoluteUri.cs +++ /dev/null @@ -1,138 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - #region usings - - using System; - - #endregion - - /// - /// Implements absolute-URI. Defined in RFC 3986.4.3. - /// - public class AbsoluteUri - { - #region Members - - private string m_Scheme = ""; - private string m_Value = ""; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - internal AbsoluteUri() {} - - #endregion - - #region Properties - - /// - /// Gets URI scheme. - /// - public virtual string Scheme - { - get { return m_Scheme; } - } - - /// - /// Gets URI value after scheme. - /// - public string Value - { - get { return ToString().Split(new[] {':'}, 2)[1]; } - } - - #endregion - - #region Methods - - /// - /// Parse URI from string value. - /// - /// String URI value. - /// Is raised when value is null reference. - /// Is raised when value has invalid URI value. - public static AbsoluteUri Parse(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - if (value == "") - { - throw new ArgumentException("Argument 'value' value must be specified."); - } - - string[] scheme_value = value.Split(new[] {':'}, 2); - if (scheme_value[0].ToLower() == UriSchemes.sip || scheme_value[0].ToLower() == UriSchemes.sips) - { - SIP_Uri uri = new SIP_Uri(); - uri.ParseInternal(value); - - return uri; - } - else - { - AbsoluteUri uri = new AbsoluteUri(); - uri.ParseInternal(value); - - return uri; - } - } - - /// - /// Converts URI to string. - /// - /// Returns URI as string. - public override string ToString() - { - return m_Scheme + ":" + m_Value; - } - - #endregion - - #region Virtual methods - - /// - /// Parses URI from the specified string. - /// - /// URI string. - /// Is raised when value is null reference. - protected virtual void ParseInternal(string value) - { - if (value == null) - { - throw new ArgumentNullException("value"); - } - - string[] scheme_value = value.Split(new[] {':'}, 1); - m_Scheme = scheme_value[0].ToLower(); - if (scheme_value.Length == 2) - { - m_Value = scheme_value[1]; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/URI/SIP_Uri.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/URI/SIP_Uri.cs deleted file mode 100644 index 15aba205d..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/URI/SIP_Uri.cs +++ /dev/null @@ -1,976 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - #region usings - - using System; - using System.Text; - using MIME; - using SIP.Message; - - #endregion - - /// - /// Implements SIP-URI. Defined in 3261. - /// - /// - /// - /// RFC 3261 Syntax: - /// SIP-URI = "sip:" [ userinfo ] hostport uri-parameters [ headers ] - /// SIPS-URI = "sips:" [ userinfo ] hostport uri-parameters [ headers ] - /// userinfo = ( user / telephone-subscriber ) [ ":" password ] "@") - /// hostport = host [ ":" port ] - /// host = hostname / IPv4address / IPv6reference - /// - /// - public class SIP_Uri : AbsoluteUri - { - #region Members - - private readonly SIP_ParameterCollection m_pParameters; - private string m_Header; - private string m_Host = ""; - private int m_Port = -1; - private string m_User; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public SIP_Uri() - { - m_pParameters = new SIP_ParameterCollection(); - } - - #endregion - - #region Properties - - /// - /// Gets address from SIP URI. Examples: ivar@lumisoft.ee,ivar@195.222.10.1. - /// - public string Address - { - get { return m_User + "@" + m_Host; } - } - - /// - /// Gets or sets header. - /// - public string Header - { - get { return m_Header; } - - set { m_Header = value; } - } - - /// - /// Gets or sets host name or IP. - /// - public string Host - { - get { return m_Host; } - - set - { - if (string.IsNullOrEmpty(value)) - { - throw new ArgumentException("Property Host value can't be null or '' !"); - } - - m_Host = value; - } - } - - /// - /// Gets host with optional port. If port specified returns Host:Port, otherwise Host. - /// - public string HostPort - { - get - { - if (m_Port == -1) - { - return m_Host; - } - else - { - return m_Host + ":" + m_Port; - } - } - } - - /// - /// Gets or sets if secure SIP. If true then sips: uri, otherwise sip: uri. - /// - public bool IsSecure { get; set; } - - /// - /// Gets or sets 'cause' parameter value. Value -1 means not specified. - /// Cause is a URI parameter that is used to indicate the service that - /// the User Agent Server (UAS) receiving the message should perform. - /// Defined in RFC 4458. - /// - public int Param_Cause - { - get - { - SIP_Parameter parameter = Parameters["cause"]; - if (parameter != null) - { - return Convert.ToInt32(parameter.Value); - } - else - { - return -1; - } - } - - set - { - if (value == -1) - { - Parameters.Remove("cause"); - } - else - { - Parameters.Set("cause", value.ToString()); - } - } - } - - /// - /// Gets or sets 'comp' parameter value. Value null means not specified. Defined in RFC 3486. - /// - public string Param_Comp - { - get - { - SIP_Parameter parameter = Parameters["comp"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - Parameters.Remove("comp"); - } - else - { - Parameters.Set("comp", value); - } - } - } - - /// - /// Gets or sets 'content-type' parameter value. Value null means not specified. Defined in RFC 4240. - /// - public string Param_ContentType - { - get - { - SIP_Parameter parameter = Parameters["content-type"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - Parameters.Remove("content-type"); - } - else - { - Parameters.Set("content-type", value); - } - } - } - - /// - /// Gets or sets 'delay' prameter value. Value -1 means not specified. - /// Specifies a delay interval between announcement repetitions. The delay is measured in milliseconds. - /// Defined in RFC 4240. - /// - public int Param_Delay - { - get - { - SIP_Parameter parameter = Parameters["delay"]; - if (parameter != null) - { - return Convert.ToInt32(parameter.Value); - } - else - { - return -1; - } - } - - set - { - if (value == -1) - { - Parameters.Remove("delay"); - } - else - { - Parameters.Set("delay", value.ToString()); - } - } - } - - /// - /// Gets or sets 'duration' prameter value. Value -1 means not specified. - /// Specifies the maximum duration of the announcement. The media server will discontinue - /// the announcement and end the call if the maximum duration has been reached. The duration - /// is measured in milliseconds. Defined in RFC 4240. - /// - public int Param_Duration - { - get - { - SIP_Parameter parameter = Parameters["duration"]; - if (parameter != null) - { - return Convert.ToInt32(parameter.Value); - } - else - { - return -1; - } - } - - set - { - if (value == -1) - { - Parameters.Remove("duration"); - } - else - { - Parameters.Set("duration", value.ToString()); - } - } - } - - /// - /// Gets or sets 'locale' prameter value. Specifies the language and optionally country - /// variant of the announcement sequence named in the "play=" parameter. Defined in RFC 4240. - /// - public string Param_Locale - { - get - { - SIP_Parameter parameter = Parameters["locale"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - Parameters.Remove("locale"); - } - else - { - Parameters.Set("locale", value); - } - } - } - - /// - /// Gets or sets 'lr' parameter. The lr parameter, when present, indicates that the element - /// responsible for this resource implements the routing mechanisms - /// specified in this document. Defined in RFC 3261. - /// - public bool Param_Lr - { - get - { - SIP_Parameter parameter = Parameters["lr"]; - if (parameter != null) - { - return true; - } - else - { - return false; - } - } - - set - { - if (!value) - { - Parameters.Remove("lr"); - } - else - { - Parameters.Set("lr", null); - } - } - } - - /// - /// Gets or sets 'maddr' parameter value. Value null means not specified. - /// NOTE: This value is deprecated in since SIP 2.0. - /// The maddr parameter indicates the server address to be contacted for this user, - /// overriding any address derived from the host field. Defined in RFC 3261. - /// - public string Param_Maddr - { - get - { - SIP_Parameter parameter = Parameters["maddr"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - Parameters.Remove("maddr"); - } - else - { - Parameters.Set("maddr", value); - } - } - } - - /// - /// Gets or sets 'method' prameter value. Value null means not specified. Defined in RFC 3261. - /// - public string Param_Method - { - get - { - SIP_Parameter parameter = Parameters["method"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - Parameters.Remove("method"); - } - else - { - Parameters.Set("method", value); - } - } - } - - // param[n] No [RFC4240] - - /// - /// Gets or sets 'play' parameter value. Value null means not specified. - /// Specifies the resource or announcement sequence to be played. Defined in RFC 4240. - /// - public string Param_Play - { - get - { - SIP_Parameter parameter = Parameters["play"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - Parameters.Remove("play"); - } - else - { - Parameters.Set("play", value); - } - } - } - - /// - /// Gets or sets 'repeat' parameter value. Value -1 means not specified, value int.MaxValue means 'forever'. - /// Specifies how many times the media server should repeat the announcement or sequence named by - /// the "play=" parameter. Defined in RFC 4240. - /// - public int Param_Repeat - { - get - { - SIP_Parameter parameter = Parameters["ttl"]; - if (parameter != null) - { - if (parameter.Value.ToLower() == "forever") - { - return int.MaxValue; - } - else - { - return Convert.ToInt32(parameter.Value); - } - } - else - { - return -1; - } - } - - set - { - if (value == -1) - { - Parameters.Remove("ttl"); - } - else if (value == int.MaxValue) - { - Parameters.Set("ttl", "forever"); - } - else - { - Parameters.Set("ttl", value.ToString()); - } - } - } - - /// - /// Gets or sets 'target' parameter value. Value null means not specified. Defined in RFC 4240. - /// - public string Param_Target - { - get - { - SIP_Parameter parameter = Parameters["target"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - Parameters.Remove("target"); - } - else - { - Parameters.Set("target", value); - } - } - } - - /// - /// Gets or sets 'transport' parameter value. Value null means not specified. - /// The transport parameter determines the transport mechanism to - /// be used for sending SIP messages. Defined in RFC 3261. - /// - public string Param_Transport - { - get - { - SIP_Parameter parameter = Parameters["transport"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - Parameters.Remove("transport"); - } - else - { - Parameters.Set("transport", value); - } - } - } - - /// - /// Gets or sets 'ttl' parameter value. Value -1 means not specified. - /// NOTE: This value is deprecated in since SIP 2.0. - /// The ttl parameter determines the time-to-live value of the UDP - /// multicast packet and MUST only be used if maddr is a multicast - /// address and the transport protocol is UDP. Defined in RFC 3261. - /// - public int Param_Ttl - { - get - { - SIP_Parameter parameter = Parameters["ttl"]; - if (parameter != null) - { - return Convert.ToInt32(parameter.Value); - } - else - { - return -1; - } - } - - set - { - if (value == -1) - { - Parameters.Remove("ttl"); - } - else - { - Parameters.Set("ttl", value.ToString()); - } - } - } - - /// - /// Gets or sets 'user' parameter value. Value null means not specified. Defined in RFC 3261. - /// - public string Param_User - { - get - { - SIP_Parameter parameter = Parameters["user"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - Parameters.Remove("user"); - } - else - { - Parameters.Set("user", value); - } - } - } - - /// - /// Gets or sets 'voicexml' parameter value. Value null means not specified. Defined in RFC 4240. - /// - public string Param_Voicexml - { - get - { - SIP_Parameter parameter = Parameters["voicexml"]; - if (parameter != null) - { - return parameter.Value; - } - else - { - return null; - } - } - - set - { - if (value == null) - { - Parameters.Remove("voicexml"); - } - else - { - Parameters.Set("voicexml", value); - } - } - } - - /// - /// Gets URI parameters. - /// - public SIP_ParameterCollection Parameters - { - get { return m_pParameters; } - } - - /// - /// Gets or sets host port. Value -1 means not specified. - /// - public int Port - { - get { return m_Port; } - - set { m_Port = value; } - } - - /// - /// Gets URI scheme. - /// - public override string Scheme - { - get - { - if (IsSecure) - { - return "sips"; - } - else - { - return "sip"; - } - } - } - - /// - /// Gets or sets user name. Value null means not specified. - /// - public string User - { - get { return m_User; } - - set { m_User = value; } - } - - #endregion - - #region Methods - - /// - /// Parse SIP or SIPS URI from string value. - /// - /// String URI value. - /// Is raised when value is null reference. - /// Is raised when value is not valid SIP or SIPS URI. - public new static SIP_Uri Parse(string value) - { - AbsoluteUri uri = AbsoluteUri.Parse(value); - if (uri is SIP_Uri) - { - return (SIP_Uri) uri; - } - else - { - throw new ArgumentException("Argument 'value' is not valid SIP or SIPS URI."); - } - } - - /// - /// Compares the current instance with another object of the same type. - /// - /// An object to compare with this instance. - /// Returns true if two objects are equal. - public override bool Equals(object obj) - { - /* RFC 3261 19.1.4 URI Comparison. - SIP and SIPS URIs are compared for equality according to the following rules: - o A SIP and SIPS URI are never equivalent. - - o Comparison of the userinfo of SIP and SIPS URIs is case- - sensitive. This includes userinfo containing passwords or - formatted as telephone-subscribers. Comparison of all other - components of the URI is case-insensitive unless explicitly - defined otherwise. - - o The ordering of parameters and header fields is not significant - in comparing SIP and SIPS URIs. - - o Characters other than those in the "reserved" set (see RFC 2396 - [5]) are equivalent to their ""%" HEX HEX" encoding. - - o An IP address that is the result of a DNS lookup of a host name - does not match that host name. - - o For two URIs to be equal, the user, password, host, and port - components must match. - - A URI omitting the user component will not match a URI that - includes one. A URI omitting the password component will not - match a URI that includes one. - - A URI omitting any component with a default value will not - match a URI explicitly containing that component with its - default value. For instance, a URI omitting the optional port - component will not match a URI explicitly declaring port 5060. - The same is true for the transport-parameter, ttl-parameter, - user-parameter, and method components. - - o URI uri-parameter components are compared as follows: - - Any uri-parameter appearing in both URIs must match. - - - A user, ttl, or method uri-parameter appearing in only one - URI never matches, even if it contains the default value. - - - A URI that includes an maddr parameter will not match a URI - that contains no maddr parameter. - - - All other uri-parameters appearing in only one URI are - ignored when comparing the URIs. - - o URI header components are never ignored. Any present header - component MUST be present in both URIs and match for the URIs - to match. - - */ - - if (obj == null) - { - return false; - } - if (!(obj is SIP_Uri)) - { - return false; - } - - SIP_Uri sipUri = (SIP_Uri) obj; - - if (IsSecure && !sipUri.IsSecure) - { - return false; - } - - if (User != sipUri.User) - { - return false; - } - - /* - if(this.Password != sipUri.Password){ - return false; - }*/ - - if (Host.ToLower() != sipUri.Host.ToLower()) - { - return false; - } - - if (Port != sipUri.Port) - { - return false; - } - - // TODO: prameters compare - // TODO: header fields compare - - return true; - } - - /// - /// Returns the hash code. - /// - /// Returns the hash code. - public override int GetHashCode() - { - return base.GetHashCode(); - } - - /// - /// Converts SIP_Uri to valid SIP-URI string. - /// - /// Returns SIP-URI string. - public override string ToString() - { - // Syntax: sip:/sips: username@host *[;parameter] [?header *[&header]] - - StringBuilder retVal = new StringBuilder(); - if (IsSecure) - { - retVal.Append("sips:"); - } - else - { - retVal.Append("sip:"); - } - if (User != null) - { - retVal.Append(User + "@"); - } - - retVal.Append(Host); - if (Port > -1) - { - retVal.Append(":" + Port); - } - - // Add URI parameters. - foreach (SIP_Parameter parameter in m_pParameters) - { - /* - * If value is token value is not quoted(quoted-string). - * If value contains `tspecials', value should be represented as quoted-string. - * If value is empty string, only parameter name is added. - */ - if (parameter.Value != null) - { - if (MIME_Reader.IsToken(parameter.Value)) - { - retVal.Append(";" + parameter.Name + "=" + parameter.Value); - } - else - { - retVal.Append(";" + parameter.Name + "=" + TextUtils.QuoteString(parameter.Value)); - } - } - else - { - retVal.Append(";" + parameter.Name); - } - } - - if (Header != null) - { - retVal.Append("?" + Header); - } - - return retVal.ToString(); - } - - #endregion - - #region Overrides - - /// - /// Parses SIP_Uri from SIP-URI string. - /// - /// SIP-URI string. - /// Returns parsed SIP_Uri object. - /// Raised when reader is null. - /// Raised when invalid SIP message. - protected override void ParseInternal(string value) - { - // Syntax: sip:/sips: username@host:port *[;parameter] [?header *[&header]] - - if (value == null) - { - throw new ArgumentNullException("value"); - } - - value = Uri.UnescapeDataString(value); - - if (!(value.ToLower().StartsWith("sip:") || value.ToLower().StartsWith("sips:"))) - { - throw new SIP_ParseException("Specified value is invalid SIP-URI !"); - } - - StringReader r = new StringReader(value); - - // IsSecure - IsSecure = r.QuotedReadToDelimiter(':').ToLower() == "sips"; - - // Get username - if (r.SourceString.IndexOf('@') > -1) - { - User = r.QuotedReadToDelimiter('@'); - } - - // Gets host[:port] - string[] host_port = r.QuotedReadToDelimiter(new[] {';', '?'}, false).Split(':'); - Host = host_port[0]; - // Optional port specified - if (host_port.Length == 2) - { - Port = Convert.ToInt32(host_port[1]); - } - - // We have parameters and/or header - if (r.Available > 0) - { - // Get parameters - string[] parameters = TextUtils.SplitQuotedString(r.QuotedReadToDelimiter('?'), ';'); - foreach (string parameter in parameters) - { - if (parameter.Trim() != "") - { - string[] name_value = parameter.Trim().Split(new[] {'='}, 2); - if (name_value.Length == 2) - { - Parameters.Add(name_value[0], TextUtils.UnQuoteString(name_value[1])); - } - else - { - Parameters.Add(name_value[0], null); - } - } - } - - // We have header - if (r.Available > 0) - { - m_Header = r.ReadToEnd(); - } - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/URI/TEL_Uri.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/URI/TEL_Uri.cs deleted file mode 100644 index 02f570a2b..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/URI/TEL_Uri.cs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.Text; - -namespace LumiSoft.Net -{ - /// - /// Implements TEL URI. Defined in RFC 2806. - /// - public class TEL_Uri : AbsoluteUri - { - /// - /// Default constructor. - /// - internal TEL_Uri() - { - } - - - #region Properties implementation - - public bool IsGlobal - { - get{ return false; } - } - - public string PhoneNmber - { - get{ return ""; } - } - - - #endregion - - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/URI/UriSchemes.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/URI/UriSchemes.cs deleted file mode 100644 index 533706683..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/URI/UriSchemes.cs +++ /dev/null @@ -1,74 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - /// - /// This class represents well known URI schemes. - /// - public class UriSchemes - { - #region Constants - - /// - /// HTTP Extensions for Distributed Authoring (WebDAV). - /// - public const string dav = "dav"; - - /// - /// Addressing files on local or network file systems. - /// - public const string file = "file"; - - /// - /// FTP resources. - /// - public const string ftp = "ftp"; - - /// - /// HTTP resources. - /// - public const string http = "http"; - - /// - /// HTTP connections secured using SSL/TLS. - /// - public const string https = "https"; - - /// - /// SMTP e-mail addresses and default content. - /// - public const string mailto = "mailto"; - - /// - /// Session Initiation Protocol (SIP). - /// - public const string sip = "sip"; - - /// - /// Session Initiation Protocol (SIP) using TLS. - /// - public const string sips = "sips"; - - /// - /// Telephone numbers. - /// - public const string tel = "tel"; - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/WellKnownPorts.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/WellKnownPorts.cs deleted file mode 100644 index a69505997..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/WellKnownPorts.cs +++ /dev/null @@ -1,99 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - /// - /// This class provides well known TCP/UDP service ports. - /// - public class WellKnownPorts - { - #region Members - - /// - /// DNS protocol. - /// - public static readonly int DNS = 53; - - /// - /// FTP - control (command) port. - /// - public static readonly int FTP_Control = 21; - - /// - /// FTP over SSL protocol. - /// - public static readonly int FTP_Control_SSL = 990; - - /// - /// FTP - data port. - /// - public static readonly int FTP_Data = 20; - - /// - /// HTTP protocol. - /// - public static readonly int HTTP = 80; - - /// - /// HTTPS protocol. - /// - public static readonly int HTTPS = 443; - - /// - /// IMAP4 protocol. - /// - public static readonly int IMAP4 = 143; - - /// - /// IMAP4 over SSL protocol. - /// - public static readonly int IMAP4_SSL = 993; - - /// - /// NNTP (Network News Transfer Protocol) protocol. - /// - public static readonly int NNTP = 119; - - /// - /// NTP (Network Time Protocol) protocol. - /// - public static readonly int NTP = 123; - - /// - /// POP3 protocol. - /// - public static readonly int POP3 = 110; - - /// - /// POP3 over SSL protocol. - /// - public static readonly int POP3_SSL = 995; - - /// - /// SMTP protocol. - /// - public static readonly int SMTP = 25; - - /// - /// SMTP over SSL protocol. - /// - public static readonly int SMTP_SSL = 465; - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Workaround/Definitions.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Workaround/Definitions.cs deleted file mode 100644 index 23d85cb3c..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/Workaround/Definitions.cs +++ /dev/null @@ -1,29 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace ASC.Mail.Net.Workaround -{ - public static class Definitions - { - public const int MaxStreamLineLength = 1024 * 1024; - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_AsyncResultState.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_AsyncResultState.cs deleted file mode 100644 index 69dbdcbba..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_AsyncResultState.cs +++ /dev/null @@ -1,163 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - #region usings - - using System; - using System.Threading; - - #endregion - - /// - /// (For internal use only). This class provides holder for IAsyncResult interface and extends it's features. - /// - internal class AsyncResultState : IAsyncResult - { - #region Members - - private readonly Delegate m_pAsyncDelegate; - private readonly object m_pAsyncObject; - private readonly AsyncCallback m_pCallback; - private readonly object m_pState; - private IAsyncResult m_pAsyncResult; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Caller's async object. - /// Delegate which is called asynchronously. - /// Callback to call when the connect operation is complete. - /// User data. - public AsyncResultState(object asyncObject, - Delegate asyncDelegate, - AsyncCallback callback, - object state) - { - m_pAsyncObject = asyncObject; - m_pAsyncDelegate = asyncDelegate; - m_pCallback = callback; - m_pState = state; - } - - #endregion - - #region Properties - - /// - /// Gets delegate which is called asynchronously. - /// - public Delegate AsyncDelegate - { - get { return m_pAsyncDelegate; } - } - - /// - /// Gets or sets caller's async object. - /// - public object AsyncObject - { - get { return m_pAsyncObject; } - } - - /// - /// Gets source asynchronous result what we wrap. - /// - public IAsyncResult AsyncResult - { - get { return m_pAsyncResult; } - } - - /// - /// Gets if the user called the End*() method. - /// - public bool IsEndCalled { get; set; } - - #endregion - - #region Methods - - /// - /// Sets AsyncResult value. - /// - /// Asycnhronous result to wrap. - public void SetAsyncResult(IAsyncResult asyncResult) - { - if (asyncResult == null) - { - throw new ArgumentNullException("asyncResult"); - } - - m_pAsyncResult = asyncResult; - } - - /// - /// This method is called by AsyncDelegate when asynchronous operation completes. - /// - /// An IAsyncResult that stores state information and any user defined data for this asynchronous operation. - public void CompletedCallback(IAsyncResult ar) - { - if (m_pCallback != null) - { - m_pCallback(this); - } - } - - #endregion - - #region IAsyncResult Members - - /// - /// Gets a user-defined object that qualifies or contains information about an asynchronous operation. - /// - public object AsyncState - { - get { return m_pState; } - } - - /// - /// Gets a WaitHandle that is used to wait for an asynchronous operation to complete. - /// - public WaitHandle AsyncWaitHandle - { - get { return m_pAsyncResult.AsyncWaitHandle; } - } - - /// - /// Gets an indication of whether the asynchronous operation completed synchronously. - /// - public bool CompletedSynchronously - { - get { return m_pAsyncResult.CompletedSynchronously; } - } - - /// - /// Gets an indication whether the asynchronous operation has completed. - /// - public bool IsCompleted - { - get { return m_pAsyncResult.IsCompleted; } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Debug/BitDebuger.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Debug/BitDebuger.cs deleted file mode 100644 index fff1afcc6..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Debug/BitDebuger.cs +++ /dev/null @@ -1,80 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// This class provides bit debugging methods. - /// - internal class BitDebuger - { - #region Methods - - /// - /// Converts byte array to bit(1 byte = 8 bit) representation. - /// - /// Data buffer. - /// Numer of bytes to convert. - /// Number of bytes per line. - /// Returns byte array as bit(1 byte = 8 bit) representation. - public static string ToBit(byte[] buffer, int count, int bytesPerLine) - { - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - - StringBuilder retVal = new StringBuilder(); - - int offset = 0; - int bytesInCurrentLine = 1; - while (offset < count) - { - byte currentByte = buffer[offset]; - char[] bits = new char[8]; - for (int i = 7; i >= 0; i--) - { - bits[i] = ((currentByte >> (7 - i)) & 0x1).ToString()[0]; - } - retVal.Append(bits); - - if (bytesInCurrentLine == bytesPerLine) - { - retVal.AppendLine(); - bytesInCurrentLine = 0; - } - else - { - retVal.Append(" "); - } - bytesInCurrentLine++; - offset++; - } - - return retVal.ToString(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/Log_EventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/Log_EventArgs.cs deleted file mode 100644 index 3295a5997..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/Log_EventArgs.cs +++ /dev/null @@ -1,70 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - /// - /// Provides data for the SessionLog event. - /// - public class Log_EventArgs - { - #region Members - - private readonly bool m_FirstLogPart = true; - private readonly bool m_LastLogPart; - private readonly SocketLogger m_pLoggger; - - #endregion - - #region Properties - - /// - /// Gets log text. - /// - public string LogText - { - get { return SocketLogger.LogEntriesToString(m_pLoggger, m_FirstLogPart, m_LastLogPart); } - } - - /// - /// Gets logger. - /// - public SocketLogger Logger - { - get { return m_pLoggger; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Socket logger. - /// Specifies if first log part of multipart log. - /// Specifies if last log part (logging ended). - public Log_EventArgs(SocketLogger logger, bool firstLogPart, bool lastLogPart) - { - m_pLoggger = logger; - m_FirstLogPart = firstLogPart; - m_LastLogPart = lastLogPart; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/Address.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/Address.cs deleted file mode 100644 index 813d22886..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/Address.cs +++ /dev/null @@ -1,68 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime -{ - #region usings - - using System; - - #endregion - - /// - /// Rfc 2822 3.4 Address class. This class is base class for MailboxAddress and GroupAddress. - /// - [Obsolete("See LumiSoft.Net.MIME or LumiSoft.Net.Mail namepaces for replacement.")] - public abstract class Address - { - #region Members - - private readonly bool m_GroupAddress; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Spcified is address is group or mailbox address. - public Address(bool groupAddress) - { - m_GroupAddress = groupAddress; - } - - #endregion - - #region Properties - - /// - /// Gets if address is group address or mailbox address. - /// - public bool IsGroupAddress - { - get { return m_GroupAddress; } - } - - /// - /// Gets or sets owner of this address. - /// - internal object Owner { get; set; } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/AddressList.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/AddressList.cs deleted file mode 100644 index 5e995f9c6..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/AddressList.cs +++ /dev/null @@ -1,293 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime -{ - #region usings - - using System; - using System.Collections; - using System.Collections.Generic; - using System.Text; - - #endregion - - /// - /// Rfc 2822 3.4 address-list. Rfc defines two types of addresses mailbox and group. - ///

    - ///

    address-list syntax: address *("," address). - ///

    address syntax: mailbox / group. - ///

    mailbox syntax: ['"'dispaly-name'"' ]<localpart@domain>. - ///

    group syntax: '"'dispaly-name'":' [mailbox *(',' mailbox)]';'. - ///

    - [Obsolete("See LumiSoft.Net.MIME or LumiSoft.Net.Mail namepaces for replacement.")] - public class AddressList : IEnumerable - { - #region Members - - private readonly List
    m_pAddresses; - private HeaderField m_HeaderField; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public AddressList() - { - m_pAddresses = new List
    (); - } - - #endregion - - #region Properties - - /// - /// Gets address count in the collection. - /// - public int Count - { - get { return m_pAddresses.Count; } - } - - /// - /// Gets address from specified index. - /// - public Address this[int index] - { - get { return m_pAddresses[index]; } - } - - /// - /// Gets all mailbox addresses. Note: group address mailbox addresses are also included. - /// - public MailboxAddress[] Mailboxes - { - get - { - ArrayList adressesAll = new ArrayList(); - foreach (Address adress in this) - { - if (!adress.IsGroupAddress) - { - adressesAll.Add(adress); - } - else - { - foreach (MailboxAddress groupChildAddress in ((GroupAddress) adress).GroupMembers) - { - adressesAll.Add(groupChildAddress); - } - } - } - - MailboxAddress[] retVal = new MailboxAddress[adressesAll.Count]; - adressesAll.CopyTo(retVal); - - return retVal; - } - } - - /// - /// Bound address-list to specified header field. - /// - internal HeaderField BoundedHeaderField - { - get { return m_HeaderField; } - - set { m_HeaderField = value; } - } - - #endregion - - #region Methods - - /// - /// Adds a new address to the end of the collection. - /// - /// Address to add. - public void Add(Address address) - { - address.Owner = this; - m_pAddresses.Add(address); - - OnCollectionChanged(); - } - - /// - /// Inserts a new address into the collection at the specified location. - /// - /// The location in the collection where you want to add the address. - /// Address to add. - public void Insert(int index, Address address) - { - address.Owner = this; - m_pAddresses.Insert(index, address); - - OnCollectionChanged(); - } - - /// - /// Removes address at the specified index from the collection. - /// - /// Index of the address which to remove. - public void Remove(int index) - { - Remove(m_pAddresses[index]); - } - - /// - /// Removes specified address from the collection. - /// - /// Address to remove. - public void Remove(Address address) - { - address.Owner = null; - m_pAddresses.Remove(address); - - OnCollectionChanged(); - } - - /// - /// Clears the collection of all addresses. - /// - public void Clear() - { - foreach (Address address in m_pAddresses) - { - address.Owner = null; - } - m_pAddresses.Clear(); - - OnCollectionChanged(); - } - - /// - /// Parses address-list from string. - /// - /// Address list string. - /// - public void Parse(string addressList) - { - addressList = addressList.Trim(); - - StringReader reader = new StringReader(addressList); - while (reader.SourceString.Length > 0) - { - // See if mailbox or group. If ',' is before ':', then mailbox - // Example: xxx@domain.com, group:xxxgroup@domain.com; - int commaIndex = TextUtils.QuotedIndexOf(reader.SourceString, ','); - int colonIndex = TextUtils.QuotedIndexOf(reader.SourceString, ':'); - - // Mailbox - if (colonIndex == -1 || (commaIndex < colonIndex && commaIndex != -1)) - { - // FIX: why quotes missing - //System.Windows.Forms.MessageBox.Show(reader.SourceString + "#" + reader.OriginalString); - - // Read to ',' or to end if last element - MailboxAddress address = MailboxAddress.Parse(reader.QuotedReadToDelimiter(',')); - m_pAddresses.Add(address); - address.Owner = this; - } - // Group - else - { - // Read to ';', this is end of group - GroupAddress address = GroupAddress.Parse(reader.QuotedReadToDelimiter(';')); - m_pAddresses.Add(address); - address.Owner = this; - - // If there are next items, remove first comma because it's part of group address - if (reader.SourceString.Length > 0) - { - reader.QuotedReadToDelimiter(','); - } - } - } - - OnCollectionChanged(); - } - - /// - /// Convert addresses to Rfc 2822 address-list string. - /// - /// - public string ToAddressListString() - { - StringBuilder retVal = new StringBuilder(); - for (int i = 0; i < m_pAddresses.Count; i++) - { - if (m_pAddresses[i] is MailboxAddress) - { - // For last address don't add , and - if (i == (m_pAddresses.Count - 1)) - { - retVal.Append(((MailboxAddress) m_pAddresses[i]).ToMailboxAddressString()); - } - else - { - retVal.Append(((MailboxAddress) m_pAddresses[i]).ToMailboxAddressString() + ",\t"); - } - } - else if (m_pAddresses[i] is GroupAddress) - { - // For last address don't add , and - if (i == (m_pAddresses.Count - 1)) - { - retVal.Append(((GroupAddress) m_pAddresses[i]).GroupString); - } - else - { - retVal.Append(((GroupAddress) m_pAddresses[i]).GroupString + ",\t"); - } - } - } - - return retVal.ToString(); - } - - /// - /// Gets enumerator. - /// - /// - public IEnumerator GetEnumerator() - { - return m_pAddresses.GetEnumerator(); - } - - #endregion - - #region Internal methods - - /// - /// This called when collection has changed. Item is added,deleted,changed or collection cleared. - /// - internal void OnCollectionChanged() - { - // AddressList is bounded to HeaderField, update header field value - if (m_HeaderField != null) - { - m_HeaderField.Value = ToAddressListString(); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/ContentDisposition_enum.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/ContentDisposition_enum.cs deleted file mode 100644 index b22fd8556..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/ContentDisposition_enum.cs +++ /dev/null @@ -1,52 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime -{ - #region usings - - using System; - - #endregion - - /// - /// Rfc 2183 Content-Disposition. - /// - [Obsolete("See LumiSoft.Net.MIME or LumiSoft.Net.Mail namepaces for replacement.")] - public enum ContentDisposition_enum - { - /// - /// Content is attachment. - /// - Attachment = 0, - - /// - /// Content is embbed resource. - /// - Inline = 1, - - /// - /// Content-Disposition header field isn't available or isn't written to mime message. - /// - NotSpecified = 30, - - /// - /// Content is unknown. - /// - Unknown = 40 - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/ContentTransferEncoding_enum.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/ContentTransferEncoding_enum.cs deleted file mode 100644 index befe7902b..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/ContentTransferEncoding_enum.cs +++ /dev/null @@ -1,79 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime -{ - #region usings - - using System; - - #endregion - - /// - /// Rfc 2045 6. Content-Transfer-Encoding. Specified how entity data is encoded. - /// - [Obsolete("See LumiSoft.Net.MIME or LumiSoft.Net.Mail namepaces for replacement.")] - public enum ContentTransferEncoding_enum - { - /// - /// Rfc 2045 2.7. 7bit data. - /// "7bit data" refers to data that is all represented as relatively - /// short lines with 998 octets or less between CRLF line separation - /// sequences [RFC-821]. No octets with decimal values greater than 127 - /// are allowed and neither are NULs (octets with decimal value 0). CR - /// (decimal value 13) and LF (decimal value 10) octets only occur as - /// part of CRLF line separation sequences. - /// - _7bit = 1, - - /// - /// Rfc 2045 2.8. 8bit data. - /// "8bit data" refers to data that is all represented as relatively - /// short lines with 998 octets or less between CRLF line separation - /// sequences [RFC-821]), but octets with decimal values greater than 127 - /// may be used. As with "7bit data" CR and LF octets only occur as part - /// of CRLF line separation sequences and no NULs are allowed. - /// - _8bit = 2, - - /// - /// Rfc 2045 2.9. Binary data. - /// "Binary data" refers to data where any sequence of octets whatsoever is allowed. - /// - Binary = 3, - - /// - /// Rfc 2045 6.7 Quoted-Printable Content-Transfer-Encoding. - /// - QuotedPrintable = 4, - - /// - /// Rfc 2045 6.8 Base64 Content-Transfer-Encoding. - /// - Base64 = 5, - - /// - /// Content-Transfer-Encoding field isn't available(doesn't exist). - /// - NotSpecified = 30, - - /// - /// Content transfer encoding is unknown. - /// - Unknown = 40, - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/GroupAddress.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/GroupAddress.cs deleted file mode 100644 index dfccebbc9..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/GroupAddress.cs +++ /dev/null @@ -1,134 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime -{ - #region usings - - using System; - - #endregion - - /// - /// RFC 2822 3.4. (Address Specification) Group address. - ///

    - /// Syntax: display-name':'[mailbox *(',' mailbox)]';' - ///

    - [Obsolete("See LumiSoft.Net.MIME or LumiSoft.Net.Mail namepaces for replacement.")] - public class GroupAddress : Address - { - #region Members - - private readonly MailboxAddressCollection m_pGroupMembers; - private string m_DisplayName = ""; - - #endregion - - #region Properties - - /// - /// Gets Group as RFC 2822(3.4. Address Specification) string. Syntax: display-name':'[mailbox *(',' mailbox)]';' - /// - public string GroupString - { - get { return TextUtils.QuoteString(DisplayName) + ":" + GroupMembers.ToMailboxListString() + ";"; } - } - - /// - /// Gets or sets display name. - /// - public string DisplayName - { - get { return m_DisplayName; } - - set - { - m_DisplayName = value; - - OnChanged(); - } - } - - /// - /// Gets group members collection. - /// - public MailboxAddressCollection GroupMembers - { - get { return m_pGroupMembers; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public GroupAddress() : base(true) - { - m_pGroupMembers = new MailboxAddressCollection(); - m_pGroupMembers.Owner = this; - } - - #endregion - - #region Methods - - /// - /// Parses Rfc 2822 3.4 group address from group address string. Syntax: display-name':'[mailbox *(',' mailbox)]';' - /// - /// Group address string. - /// - public static GroupAddress Parse(string group) - { - GroupAddress g = new GroupAddress(); - - // Syntax: display-name':'[mailbox *(',' mailbox)]';' - string[] parts = TextUtils.SplitQuotedString(group, ':'); - if (parts.Length > -1) - { - g.DisplayName = TextUtils.UnQuoteString(parts[0]); - } - if (parts.Length > 1) - { - g.GroupMembers.Parse(parts[1]); - } - - return g; - } - - #endregion - - #region Internal methods - - /// - /// This called when group address has changed. - /// - internal void OnChanged() - { - if (Owner != null) - { - if (Owner is AddressList) - { - ((AddressList) Owner).OnCollectionChanged(); - } - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/HeaderField.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/HeaderField.cs deleted file mode 100644 index c0f5397fb..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/HeaderField.cs +++ /dev/null @@ -1,127 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime -{ - #region usings - - using System; - - #endregion - - /// - /// Mime entity header field. - /// - [Obsolete("See LumiSoft.Net.MIME or LumiSoft.Net.Mail namepaces for replacement.")] - public class HeaderField - { - #region Members - - private string m_Name = ""; - private string m_Value = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets header field name. Header field name must end with colon(:) and may contain US-ASCII character values between 33 and 126. - /// - public string Name - { - get { return m_Name; } - - set - { - /* RFC 2822 2.2 Header Fields - Header fields are lines composed of a field name, followed by a colon - (":"), followed by a field body, and terminated by CRLF. A field - name MUST be composed of printable US-ASCII characters (i.e., - characters that have values between 33 and 126, inclusive), except - colon. A field body may be composed of any US-ASCII characters, - except for CR and LF. However, a field body may contain CRLF when - used in header "folding" and "unfolding" as described in section - 2.2.3. All field bodies MUST conform to the syntax described in - sections 3 and 4 of this standard. - */ - - if (value == "") - { - throw new Exception("Header Field name can't be empty !"); - } - - // Colon is missing from name, just add it - if (!value.EndsWith(":")) - { - value += ":"; - } - - // Check if name is between ASCII 33 - 126 characters - foreach (char c in value.Substring(0, value.Length - 1)) - { - if (c < 33 || c > 126) - { - throw new Exception("Invalid field name '" + value + - "'. A field name MUST be composed of printable US-ASCII characters (i.e.,characters that have values between 33 and 126, inclusive),except colon."); - } - } - - m_Name = value; - } - } - - /// - /// Gets or sets header field value. - /// - public string Value - { - get { return MimeUtils.DecodeWords(m_Value); } - - set { m_Value = Core.CanonicalEncode(value, "utf-8"); } - } - - /// - /// Gets header field encoded value. - /// - internal string EncodedValue - { - get { return m_Value; } - } - - #endregion - - #region Constructor - - /// - /// Default construtor. - /// - public HeaderField() {} - - /// - /// Creates new header field with specified name and value. - /// - /// Header field name. Header field name must end with colon(:) and may contain US-ASCII character values between 33 and 126. - /// Header field value. - public HeaderField(string name, string value) - { - Name = name; - Value = value; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/HeaderFieldCollection.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/HeaderFieldCollection.cs deleted file mode 100644 index 434381c64..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/HeaderFieldCollection.cs +++ /dev/null @@ -1,351 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime -{ - #region usings - - using System; - using System.Collections; - using System.Collections.Generic; - using System.IO; - using System.Text; - using IO; - - #endregion - - /// - /// Mime entity header fields collection. - /// - [Obsolete("See LumiSoft.Net.MIME or LumiSoft.Net.Mail namepaces for replacement.")] - public class HeaderFieldCollection : IEnumerable - { - #region Members - - private readonly List m_pHeaderFields; - - #endregion - - #region Properties - - /// - /// Gets header field from specified index. - /// - public HeaderField this[int index] - { - get { return m_pHeaderFields[index]; } - } - - /// - /// Gets header fields count in the collection. - /// - public int Count - { - get { return m_pHeaderFields.Count; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public HeaderFieldCollection() - { - m_pHeaderFields = new List(); - } - - #endregion - - #region Methods - - /// - /// Adds a new header field with specified name and value to the end of the collection. - /// - /// Header field name. - /// Header field value. - public void Add(string fieldName, string value) - { - m_pHeaderFields.Add(new HeaderField(fieldName, value)); - } - - /// - /// Adds specified header field to the end of the collection. - /// - /// Header field. - public void Add(HeaderField headerField) - { - m_pHeaderFields.Add(headerField); - } - - /// - /// Inserts a new header field into the collection at the specified location. - /// - /// The location in the collection where you want to add the header field. - /// Header field name. - /// Header field value. - public void Insert(int index, string fieldName, string value) - { - m_pHeaderFields.Insert(index, new HeaderField(fieldName, value)); - } - - /// - /// Removes header field at the specified index from the collection. - /// - /// The index of the header field to remove. - public void Remove(int index) - { - m_pHeaderFields.RemoveAt(index); - } - - /// - /// Removes specified header field from the collection. - /// - /// Header field to remove. - public void Remove(HeaderField field) - { - m_pHeaderFields.Remove(field); - } - - /// - /// Removes all header fields with specified name from the collection. - /// - /// Header field name. - public void RemoveAll(string fieldName) - { - for (int i = 0; i < m_pHeaderFields.Count; i++) - { - HeaderField h = m_pHeaderFields[i]; - if (h.Name.ToLower() == fieldName.ToLower()) - { - m_pHeaderFields.Remove(h); - i--; - } - } - } - - /// - /// Clears the collection of all header fields. - /// - public void Clear() - { - m_pHeaderFields.Clear(); - } - - /// - /// Gets if collection contains specified header field. - /// - /// Header field name. - /// - public bool Contains(string fieldName) - { - foreach (HeaderField h in m_pHeaderFields) - { - if (h.Name.ToLower() == fieldName.ToLower()) - { - return true; - } - } - - return false; - } - - /// - /// Gets if collection contains specified header field. - /// - /// Header field. - /// - public bool Contains(HeaderField headerField) - { - return m_pHeaderFields.Contains(headerField); - } - - /// - /// Gets first header field with specified name, returns null if specified field doesn't exist. - /// - /// Header field name. - /// - public HeaderField GetFirst(string fieldName) - { - foreach (HeaderField h in m_pHeaderFields) - { - if (h.Name.ToLower() == fieldName.ToLower()) - { - return h; - } - } - - return null; - } - - /// - /// Gets header fields with specified name, returns null if specified field doesn't exist. - /// - /// Header field name. - /// - public HeaderField[] Get(string fieldName) - { - ArrayList fields = new ArrayList(); - foreach (HeaderField h in m_pHeaderFields) - { - if (h.Name.ToLower() == fieldName.ToLower()) - { - fields.Add(h); - } - } - - if (fields.Count > 0) - { - HeaderField[] retVal = new HeaderField[fields.Count]; - fields.CopyTo(retVal); - - return retVal; - } - else - { - return null; - } - } - - /// - /// Parses header fields from string. - /// - /// Header string. - public void Parse(string headerString) - { - Parse(new MemoryStream(Encoding.Default.GetBytes(headerString))); - } - - /// - /// Parses header fields from stream. Stream position stays where header reading ends. - /// - /// Stream from where to parse. - public void Parse(Stream stream) - { - Parse(new SmartStream(stream, false)); - } - - /// - /// Parses header fields from stream. Stream position stays where header reading ends. - /// - /// Stream from where to parse. - public void Parse(SmartStream stream) - { - /* Rfc 2822 2.2 Header Fields - Header fields are lines composed of a field name, followed by a colon - (":"), followed by a field body, and terminated by CRLF. A field - name MUST be composed of printable US-ASCII characters (i.e., - characters that have values between 33 and 126, inclusive), except - colon. A field body may be composed of any US-ASCII characters, - except for CR and LF. However, a field body may contain CRLF when - used in header "folding" and "unfolding" as described in section - 2.2.3. All field bodies MUST conform to the syntax described in - sections 3 and 4 of this standard. - - Rfc 2822 2.2.3 Long Header Fields - The process of moving from this folded multiple-line representation - of a header field to its single line representation is called - "unfolding". Unfolding is accomplished by simply removing any CRLF - that is immediately followed by WSP. Each header field should be - treated in its unfolded form for further syntactic and semantic - evaluation. - - Example: - Subject: aaaaa - aaaaa - */ - - m_pHeaderFields.Clear(); - - SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength], - SizeExceededAction. - JunkAndThrowException); - stream.ReadLine(args, false); - if (args.Error != null) - { - throw args.Error; - } - string line = args.LineUtf8; - - while (line != null) - { - // End of header reached - if (line == "") - { - break; - } - - // Store current header line and read next. We need to read 1 header line to ahead, - // because of multiline header fields. - string headerField = line; - stream.ReadLine(args, false); - if (args.Error != null) - { - throw args.Error; - } - line = args.LineUtf8; - - // See if header field is multiline. See comment above. - while (line != null && (line.StartsWith("\t") || line.StartsWith(" "))) - { - headerField += line; - stream.ReadLine(args, false); - if (args.Error != null) - { - throw args.Error; - } - line = args.LineUtf8; - } - - string[] name_value = headerField.Split(new[] {':'}, 2); - // There must be header field name and value, otherwise invalid header field - if (name_value.Length == 2) - { - Add(name_value[0] + ":", name_value[1].Trim()); - } - } - } - - /// - /// Converts header fields to rfc 2822 message header string. - /// - /// CharSet to use for non ASCII header field values. Utf-8 is recommended value, if you explicity don't need other. - /// - public string ToHeaderString(string encodingCharSet) - { - StringBuilder headerString = new StringBuilder(); - foreach (HeaderField f in this) - { - headerString.Append(f.Name + " " + f.EncodedValue + "\r\n"); - } - - return headerString.ToString(); - } - - /// - /// Gets enumerator. - /// - /// - public IEnumerator GetEnumerator() - { - return m_pHeaderFields.GetEnumerator(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/HeaderFieldParameter.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/HeaderFieldParameter.cs deleted file mode 100644 index 52e734ea0..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/HeaderFieldParameter.cs +++ /dev/null @@ -1,74 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime -{ - #region usings - - using System; - - #endregion - - /// - /// Header field parameter. - /// - [Obsolete("See LumiSoft.Net.MIME or LumiSoft.Net.Mail namepaces for replacement.")] - public class HeaderFieldParameter - { - #region Members - - private readonly string m_Name = ""; - private readonly string m_Value = ""; - - #endregion - - #region Properties - - /// - /// Gets header field parameter name. - /// - public string Name - { - get { return m_Name; } - } - - /// - /// Gets header field parameter name. - /// - public string Value - { - get { return m_Value; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Header field parameter name. - /// Header field parameter value. - public HeaderFieldParameter(string parameterName, string parameterValue) - { - m_Name = parameterName; - m_Value = parameterValue; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/HeaderFieldParameterCollection.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/HeaderFieldParameterCollection.cs deleted file mode 100644 index 2d484eb60..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/HeaderFieldParameterCollection.cs +++ /dev/null @@ -1,183 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime -{ - #region usings - - using System; - using System.Collections; - - #endregion - - /// - /// Header field parameters collection. - /// - [Obsolete("See LumiSoft.Net.MIME or LumiSoft.Net.Mail namepaces for replacement.")] - public class HeaderFieldParameterCollection : IEnumerable - { - #region Members - - private readonly ParametizedHeaderField m_pHeaderField; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Header field. - internal HeaderFieldParameterCollection(ParametizedHeaderField headerField) - { - m_pHeaderField = headerField; - } - - #endregion - - #region Properties - - /// - /// Gets header field parameters count in the collection. - /// - public int Count - { - get { return m_pHeaderField.ParseParameters().Count; } - } - - /// - /// Gets or sets specified parameter value. - /// - public string this[string parameterName] - { - get - { - parameterName = parameterName.ToLower(); - - Hashtable parameters = m_pHeaderField.ParseParameters(); - if (!parameters.ContainsKey(parameterName)) - { - throw new Exception("Specified parameter '" + parameterName + "' doesn't exist !"); - } - else - { - return (string) parameters[parameterName]; - } - } - - set - { - parameterName = parameterName.ToLower(); - - Hashtable parameters = m_pHeaderField.ParseParameters(); - if (parameters.ContainsKey(parameterName)) - { - parameters[parameterName] = value; - - m_pHeaderField.StoreParameters(m_pHeaderField.Value, parameters); - } - } - } - - #endregion - - #region Methods - - /// - /// Adds a new header field parameter with specified name and value to the end of the collection. - /// - /// Parameter name. - /// Parameter value. - public void Add(string parameterName, string parameterValue) - { - parameterName = parameterName.ToLower(); - - Hashtable parameters = m_pHeaderField.ParseParameters(); - if (!parameters.ContainsKey(parameterName)) - { - parameters.Add(parameterName, parameterValue); - - m_pHeaderField.StoreParameters(m_pHeaderField.Value, parameters); - } - else - { - throw new Exception("Header field '" + m_pHeaderField.Name + "' parameter '" + parameterName + - "' already exists !"); - } - } - - /// - /// Removes specified header field parameter from the collection. - /// - /// The name of the header field parameter to remove. - public void Remove(string parameterName) - { - parameterName = parameterName.ToLower(); - - Hashtable parameters = m_pHeaderField.ParseParameters(); - if (!parameters.ContainsKey(parameterName)) - { - parameters.Remove(parameterName); - - m_pHeaderField.StoreParameters(m_pHeaderField.Value, parameters); - } - } - - /// - /// Clears the collection of all header field parameters. - /// - public void Clear() - { - Hashtable parameters = m_pHeaderField.ParseParameters(); - parameters.Clear(); - m_pHeaderField.StoreParameters(m_pHeaderField.Value, parameters); - } - - /// - /// Gets if collection contains specified parameter. - /// - /// Parameter name. - /// - public bool Contains(string parameterName) - { - parameterName = parameterName.ToLower(); - - Hashtable parameters = m_pHeaderField.ParseParameters(); - return parameters.ContainsKey(parameterName); - } - - /// - /// Gets enumerator. - /// - /// - public IEnumerator GetEnumerator() - { - Hashtable parameters = m_pHeaderField.ParseParameters(); - HeaderFieldParameter[] retVal = new HeaderFieldParameter[parameters.Count]; - int i = 0; - foreach (DictionaryEntry entry in parameters) - { - retVal[i] = new HeaderFieldParameter(entry.Key.ToString(), entry.Value.ToString()); - i++; - } - - return retVal.GetEnumerator(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/MailboxAddress.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/MailboxAddress.cs deleted file mode 100644 index 0d67d8073..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/MailboxAddress.cs +++ /dev/null @@ -1,274 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime -{ - #region usings - - using System; - using MIME; - - #endregion - - /// - /// RFC 2822 3.4. (Address Specification) Mailbox address. - ///

    - /// Syntax: ["display-name"<SP>]<local-part@domain>. - ///

    - [Obsolete("See LumiSoft.Net.MIME or LumiSoft.Net.Mail namepaces for replacement.")] - public class MailboxAddress : Address - { - #region Members - - private string m_DisplayName = ""; - private string m_EmailAddress = ""; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public MailboxAddress() : base(false) {} - - /// - /// Creates new mailbox from specified email address. - /// - /// Email address. - public MailboxAddress(string emailAddress) : base(false) - { - m_EmailAddress = emailAddress; - } - - /// - /// Creates new mailbox from specified name and email address. - /// - /// Display name. - /// Email address. - public MailboxAddress(string displayName, string emailAddress) : base(false) - { - m_DisplayName = displayName; - m_EmailAddress = emailAddress; - } - - #endregion - - #region Properties - - /// - /// Gets or sets display name. - /// - public string DisplayName - { - get { return m_DisplayName; } - - set - { - m_DisplayName = value; - - OnChanged(); - } - } - - /// - /// Gets domain from email address. For example domain is "lumisoft.ee" from "ivar@lumisoft.ee". - /// - public string Domain - { - get - { - if (EmailAddress.IndexOf("@") != -1) - { - return EmailAddress.Substring(EmailAddress.IndexOf("@") + 1); - } - else - { - return ""; - } - } - } - - /// - /// Gets or sets email address. For example ivar@lumisoft.ee. - /// - public string EmailAddress - { - get { return m_EmailAddress; } - - set - { - // Email address can contain only ASCII chars. - if (!Core.IsAscii(value)) - { - throw new Exception("Email address can contain ASCII chars only !"); - } - - m_EmailAddress = value; - - OnChanged(); - } - } - - /// - /// Gets local-part from email address. For example mailbox is "ivar" from "ivar@lumisoft.ee". - /// - public string LocalPart - { - get - { - if (EmailAddress.IndexOf("@") > -1) - { - return EmailAddress.Substring(0, EmailAddress.IndexOf("@")); - } - else - { - return EmailAddress; - } - } - } - - /// - /// Gets Mailbox as RFC 2822(3.4. Address Specification) string. Format: ["display-name"<SP>]<local-part@domain>. - /// For example, "Ivar Lumi" <ivar@lumisoft.ee>. - /// - [Obsolete("Use ToMailboxAddressString instead !")] - public string MailboxString - { - get - { - string retVal = ""; - if (DisplayName != "") - { - retVal += TextUtils.QuoteString(DisplayName) + " "; - } - retVal += "<" + EmailAddress + ">"; - - return retVal; - } - } - - #endregion - - #region Methods - - /// - /// Parses mailbox from mailbox address string. - /// - /// Mailbox string. Format: ["diplay-name"<SP>]<local-part@domain>. - /// - public static MailboxAddress Parse(string mailbox) - { - mailbox = mailbox.Trim(); - - /* We must parse following situations: - "Ivar Lumi" - "Ivar Lumi" ivar@lumisoft.ee - - ivar@lumisoft.ee - Ivar Lumi - */ - - string name = ""; - string emailAddress = mailbox; - - // Email address is between <> and remaining left part is display name - if (mailbox.IndexOf("<") > -1 && mailbox.IndexOf(">") > -1) - { - name = - MIME_Encoding_EncodedWord.DecodeS( - TextUtils.UnQuoteString(mailbox.Substring(0, mailbox.LastIndexOf("<")))); - emailAddress = - mailbox.Substring(mailbox.LastIndexOf("<") + 1, - mailbox.Length - mailbox.LastIndexOf("<") - 2).Trim(); - } - else - { - // There is name included, parse it - if (mailbox.StartsWith("\"")) - { - int startIndex = mailbox.IndexOf("\""); - if (startIndex > -1 && mailbox.LastIndexOf("\"") > startIndex) - { - name = - MIME_Encoding_EncodedWord.DecodeS( - mailbox.Substring(startIndex + 1, mailbox.LastIndexOf("\"") - startIndex - 1). - Trim()); - } - - emailAddress = mailbox.Substring(mailbox.LastIndexOf("\"") + 1).Trim(); - } - - // Right part must be email address - emailAddress = emailAddress.Replace("<", "").Replace(">", "").Trim(); - } - - return new MailboxAddress(name, emailAddress); - } - - /// - /// Converts this to valid mailbox address string. - /// Defined in RFC 2822(3.4. Address Specification) string. Format: ["display-name"<SP>]<local-part@domain>. - /// For example, "Ivar Lumi" <ivar@lumisoft.ee>. - /// If display name contains unicode chrs, display name will be encoded with canonical encoding in utf-8 charset. - /// - /// - public string ToMailboxAddressString() - { - string retVal = ""; - if (m_DisplayName.Length > 0) - { - if (Core.IsAscii(m_DisplayName)) - { - retVal = TextUtils.QuoteString(m_DisplayName) + " "; - } - else - { - // Encoded word must be treated as unquoted and unescaped word. - retVal = MimeUtils.EncodeWord(m_DisplayName) + " "; - } - } - retVal += "<" + EmailAddress + ">"; - - return retVal; - } - - #endregion - - #region Internal methods - - /// - /// This called when mailox address has changed. - /// - internal void OnChanged() - { - if (Owner != null) - { - if (Owner is AddressList) - { - ((AddressList) Owner).OnCollectionChanged(); - } - else if (Owner is MailboxAddressCollection) - { - ((MailboxAddressCollection) Owner).OnCollectionChanged(); - } - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/MailboxAddressCollection.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/MailboxAddressCollection.cs deleted file mode 100644 index 6fd285e51..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/MailboxAddressCollection.cs +++ /dev/null @@ -1,209 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime -{ - #region usings - - using System; - using System.Collections; - using System.Collections.Generic; - - #endregion - - /// - /// Rfc 2822 3.4 mailbox-list. Syntax: mailbox *(',' mailbox). - /// - [Obsolete("See LumiSoft.Net.MIME or LumiSoft.Net.Mail namepaces for replacement.")] - public class MailboxAddressCollection : IEnumerable - { - #region Members - - private readonly List m_pMailboxes; - private Address m_pOwner; - - #endregion - - #region Properties - - /// - /// Gets mailbox from specified index. - /// - public MailboxAddress this[int index] - { - get { return m_pMailboxes[index]; } - } - - /// - /// Gets mailboxes count in the collection. - /// - public int Count - { - get { return m_pMailboxes.Count; } - } - - /// - /// Gets or sets owner of this collection. - /// - internal Address Owner - { - get { return m_pOwner; } - - set { m_pOwner = value; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public MailboxAddressCollection() - { - m_pMailboxes = new List(); - } - - #endregion - - #region Methods - - /// - /// Adds a new mailbox to the end of the collection. - /// - /// Mailbox to add. - public void Add(MailboxAddress mailbox) - { - m_pMailboxes.Add(mailbox); - - OnCollectionChanged(); - } - - /// - /// Inserts a new mailbox into the collection at the specified location. - /// - /// The location in the collection where you want to add the mailbox. - /// Mailbox to add. - public void Insert(int index, MailboxAddress mailbox) - { - m_pMailboxes.Insert(index, mailbox); - - OnCollectionChanged(); - } - - /// - /// Removes header field at the specified index from the collection. - /// - /// Index of the mailbox which to remove. - public void Remove(int index) - { - m_pMailboxes.RemoveAt(index); - - OnCollectionChanged(); - } - - /// - /// Removes specified mailbox from the collection. - /// - /// Mailbox to remove. - public void Remove(MailboxAddress mailbox) - { - m_pMailboxes.Remove(mailbox); - - OnCollectionChanged(); - } - - /// - /// Clears the collection of all mailboxes. - /// - public void Clear() - { - m_pMailboxes.Clear(); - - OnCollectionChanged(); - } - - /// - /// Parses mailboxes from Rfc 2822 3.4 mailbox-list string. Syntax: mailbox *(',' mailbox). - /// - /// Mailbox list string. - public void Parse(string mailboxList) - { - // We need to parse right !!! - // Can't use standard String.Split() because commas in quoted strings must be skiped. - // Example: "ivar, test" ,"xxx" - - string[] mailboxes = TextUtils.SplitQuotedString(mailboxList, ','); - foreach (string mailbox in mailboxes) - { - m_pMailboxes.Add(MailboxAddress.Parse(mailbox)); - } - } - - /// - /// Convert addresses to Rfc 2822 mailbox-list string. - /// - /// - public string ToMailboxListString() - { - string retVal = ""; - for (int i = 0; i < m_pMailboxes.Count; i++) - { - // For last address don't add , and - if (i == (m_pMailboxes.Count - 1)) - { - retVal += (m_pMailboxes[i]).ToMailboxAddressString(); - } - else - { - retVal += (m_pMailboxes[i]).ToMailboxAddressString() + ",\t"; - } - } - - return retVal; - } - - /// - /// Gets enumerator. - /// - /// - public IEnumerator GetEnumerator() - { - return m_pMailboxes.GetEnumerator(); - } - - #endregion - - #region Internal methods - - /// - /// This called when collection has changed. Item is added,deleted,changed or collection cleared. - /// - internal void OnCollectionChanged() - { - if (m_pOwner != null) - { - if (m_pOwner is GroupAddress) - { - ((GroupAddress) m_pOwner).OnChanged(); - } - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/MediaType_enum.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/MediaType_enum.cs deleted file mode 100644 index 19b09b093..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/MediaType_enum.cs +++ /dev/null @@ -1,147 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime -{ - #region usings - - using System; - - #endregion - - /// - /// Rfc 2046,2387 Media Types. - /// - [Obsolete("See LumiSoft.Net.MIME or LumiSoft.Net.Mail namepaces for replacement.")] - public enum MediaType_enum - { - /// - /// Text data. - /// - Text = 1, - - /// - /// Simple text data. Defined in Rfc 1521. - /// - Text_plain = Text | (1 << 1), - - /// - /// Html data. Defined in Rfc 2854. - /// - Text_html = Text | (1 << 2), - - /// - /// Xml data. Defined in Rfc 3023 3.1. - /// - Text_xml = Text | (1 << 3), - - /// - /// Rich text (RTF) data. - /// - Text_rtf = Text | (1 << 4), - - /// - /// Image data. - /// - Image = 1 << 5, - - /// - /// Gif image. Defined in Rfc 1521. - /// - Image_gif = Image | (1 << 6), - - /// - /// Tiff image. - /// - Image_tiff = Image | (1 << 7), - - /// - /// Jpeg image. Defined in Rfc 1521. - /// - Image_jpeg = Image | (1 << 8), - - /// - /// Audio data. - /// - Audio = 1 << 8, - - /// - /// Video data. - /// - Video = 1 << 10, - - /// - /// Some other kind of data, typically either uninterpreted binary data or information to be processed by an application. - /// - Application = 1 << 11, - - /// - /// The "octet-stream" subtype is used to indicate that a body contains arbitrary binary data. Defined in Rfc 4046 4.5.1. - /// - Application_octet_stream = Application | (1 << 12), - - /// - /// Data consisting of multiple entities of independent data types. - /// - Multipart = 1 << 13, - - /// - /// Data consisting of multiple entities of independent data types. - /// - Multipart_mixed = Multipart | (1 << 14), - - /// - /// Data consisting of multiple entities of independent data types. - /// - Multipart_alternative = Multipart | (1 << 15), - - /// - /// Data consisting of multiple entities of independent data types. - /// - Multipart_parallel = Multipart | (1 << 16), - - /// - /// Data consisting of multiple entities of independent data types. (Rfc 2387) - /// - Multipart_related = Multipart | (1 << 17), - - /// - /// Multipart signed. Defined in Rfc 1847. - /// - Multipart_signed = Multipart | (1 << 18), - - /// - /// Message -- an encapsulated message. - /// - Message = 1 << 19, - - /// - /// Rfc 822 encapsulated message. - /// - Message_rfc822 = Message | (1 << 20), - - /// - /// Media type isn't specified. - /// - NotSpecified = 1 << 21, - - /// - /// Media type is unknown. - /// - Unknown = 1 << 22, - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/Mime.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/Mime.cs deleted file mode 100644 index 3e2857dec..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/Mime.cs +++ /dev/null @@ -1,544 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime -{ - #region usings - - using System; - using System.Collections.Generic; - using System.IO; - using System.Text; - using IO; - - #endregion - - /// - /// Class for creating,parsing,modifing rfc 2822 mime messages. - /// - /// - /// - /// - /// Message examples: - /// - /// Simple message: - /// - /// //--- Beginning of message - /// From: sender@domain.com - /// To: recipient@domain.com - /// Subject: Message subject. - /// Content-Type: text/plain - /// - /// Message body text. Bla blaa - /// blaa,blaa. - /// //--- End of message - /// - /// - /// In simple message MainEntity is whole message. - /// - /// Message with attachments: - /// - /// //--- Beginning of message - /// From: sender@domain.com - /// To: recipient@domain.com - /// Subject: Message subject. - /// Content-Type: multipart/mixed; boundary="multipart_mixed" - /// - /// --multipart_mixed /* text entity */ - /// Content-Type: text/plain - /// - /// Message body text. Bla blaa - /// blaa,blaa. - /// --multipart_mixed /* attachment entity */ - /// Content-Type: application/octet-stream - /// - /// attachment_data - /// --multipart_mixed-- - /// //--- End of message - /// - /// MainEntity is multipart_mixed entity and text and attachment entities are child entities of MainEntity. - /// - /// - /// - /// - /// // Parsing example: - /// Mime m = Mime.Parse("message.eml"); - /// // Do your stuff with mime - /// - /// - /// // Create simple message with simple way: - /// AddressList from = new AddressList(); - /// from.Add(new MailboxAddress("dispaly name","user@domain.com")); - /// AddressList to = new AddressList(); - /// to.Add(new MailboxAddress("dispaly name","user@domain.com")); - /// - /// Mime m = Mime.CreateSimple(from,to,"test subject","test body text",""); - /// - /// - /// // Creating a new simple message - /// Mime m = new Mime(); - /// MimeEntity mainEntity = m.MainEntity; - /// // Force to create From: header field - /// mainEntity.From = new AddressList(); - /// mainEntity.From.Add(new MailboxAddress("dispaly name","user@domain.com")); - /// // Force to create To: header field - /// mainEntity.To = new AddressList(); - /// mainEntity.To.Add(new MailboxAddress("dispaly name","user@domain.com")); - /// mainEntity.Subject = "subject"; - /// mainEntity.ContentType = MediaType_enum.Text_plain; - /// mainEntity.ContentTransferEncoding = ContentTransferEncoding_enum.QuotedPrintable; - /// mainEntity.DataText = "Message body text."; - /// - /// m.ToFile("message.eml"); - /// - /// - /// // Creating message with text and attachments - /// Mime m = new Mime(); - /// MimeEntity mainEntity = m.MainEntity; - /// // Force to create From: header field - /// mainEntity.From = new AddressList(); - /// mainEntity.From.Add(new MailboxAddress("dispaly name","user@domain.com")); - /// // Force to create To: header field - /// mainEntity.To = new AddressList(); - /// mainEntity.To.Add(new MailboxAddress("dispaly name","user@domain.com")); - /// mainEntity.Subject = "subject"; - /// mainEntity.ContentType = MediaType_enum.Multipart_mixed; - /// - /// MimeEntity textEntity = mainEntity.ChildEntities.Add(); - /// textEntity.ContentType = MediaType_enum.Text_plain; - /// textEntity.ContentTransferEncoding = ContentTransferEncoding_enum.QuotedPrintable; - /// textEntity.DataText = "Message body text."; - /// - /// MimeEntity attachmentEntity = mainEntity.ChildEntities.Add(); - /// attachmentEntity.ContentType = MediaType_enum.Application_octet_stream; - /// attachmentEntity.ContentDisposition = ContentDisposition_enum.Attachment; - /// attachmentEntity.ContentTransferEncoding = ContentTransferEncoding_enum.Base64; - /// attachmentEntity.ContentDisposition_FileName = "yourfile.xxx"; - /// attachmentEntity.DataFromFile("yourfile.xxx"); - /// // or - /// attachmentEntity.Data = your_attachment_data; - /// - /// - [Obsolete("See LumiSoft.Net.MIME or LumiSoft.Net.Mail namepaces for replacement.")] - public class Mime - { - #region Members - - private readonly MimeEntity m_pMainEntity; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public Mime() - { - m_pMainEntity = new MimeEntity(); - - // Add default header fields - m_pMainEntity.MessageID = MimeUtils.CreateMessageID(); - m_pMainEntity.Date = DateTime.Now; - m_pMainEntity.MimeVersion = "1.0"; - } - - #endregion - - #region Properties - - /// - /// Gets attachment entities. Entity is considered as attachmnet if:

    - /// *) Content-Disposition: attachment (RFC 2822 message)

    - /// *) Content-Disposition: filename = "" is specified (RFC 2822 message)

    - /// *) Content-Type: name = "" is specified (old RFC 822 message)

    - ///

    - public MimeEntity[] Attachments - { - get - { - List attachments = new List(); - MimeEntity[] entities = MimeEntities; - foreach (MimeEntity entity in entities) - { - if (entity.ContentDisposition == ContentDisposition_enum.Attachment) - { - attachments.Add(entity); - } - else if (entity.ContentType_Name != null) - { - attachments.Add(entity); - } - else if (entity.ContentDisposition_FileName != null) - { - attachments.Add(entity); - } - } - - return attachments.ToArray(); - } - } - - /* - /// - /// Gets body text mime entity. Returns null if no body body text entity. - /// - public MimeEntity BodyTextEntity - { - get{ - if(this.MainEntity.ContentType == MediaType_enum.NotSpecified){ - if(this.MainEntity.DataEncoded != null){ - return this.MainEntity; - } - } - else{ - MimeEntity[] entities = this.MimeEntities; - foreach(MimeEntity entity in entities){ - if(entity.ContentType == MediaType_enum.Text_plain){ - return entity; - } - } - } - - return null; - } - } -*/ - - /// - /// Gets message body html. Returns null if no body html text specified. - /// - public string BodyHtml - { - get - { - MimeEntity[] entities = MimeEntities; - foreach (MimeEntity entity in entities) - { - if (entity.ContentType == MediaType_enum.Text_html) - { - return entity.DataText; - } - } - - return null; - } - } - - /// - /// Gets message body text. Returns null if no body text specified. - /// - public string BodyText - { - get - { - /* RFC 2045 5.2 - If content Content-Type: header field is missing, then it defaults to: - Content-type: text/plain; charset=us-ascii - */ - - if (MainEntity.ContentType == MediaType_enum.NotSpecified) - { - if (MainEntity.DataEncoded != null) - { - return Encoding.ASCII.GetString(MainEntity.Data); - } - } - else - { - MimeEntity[] entities = MimeEntities; - foreach (MimeEntity entity in entities) - { - if (entity.ContentType == MediaType_enum.Text_plain) - { - return entity.DataText; - } - } - } - - return null; - } - } - - /// - /// Message main(top-level) entity. - /// - public MimeEntity MainEntity - { - get { return m_pMainEntity; } - } - - /// - /// Gets all mime entities contained in message, including child entities. - /// - public MimeEntity[] MimeEntities - { - get - { - List allEntities = new List(); - allEntities.Add(m_pMainEntity); - GetEntities(m_pMainEntity.ChildEntities, allEntities); - - return allEntities.ToArray(); - } - } - - #endregion - - #region Methods - - /// - /// Parses mime message from byte[] data. - /// - /// Mime message data. - /// - public static Mime Parse(byte[] data) - { - using (MemoryStream ms = new MemoryStream(data)) - { - return Parse(ms); - } - } - - /// - /// Parses mime message from file. - /// - /// Mime message file. - /// - public static Mime Parse(string fileName) - { - using (FileStream fs = File.OpenRead(fileName)) - { - return Parse(fs); - } - } - - /// - /// Parses mime message from stream. - /// - /// Mime message stream. - /// - public static Mime Parse(Stream stream) - { - Mime mime = new Mime(); - mime.MainEntity.Parse(new SmartStream(stream, false), null); - - return mime; - } - - /// - /// Creates simple mime message. - /// - /// Header field From: value. - /// Header field To: value. - /// Header field Subject: value. - /// Body text of message. NOTE: Pass null is body text isn't wanted. - /// Body HTML text of message. NOTE: Pass null is body HTML text isn't wanted. - /// - public static Mime CreateSimple(AddressList from, - AddressList to, - string subject, - string bodyText, - string bodyHtml) - { - return CreateSimple(from, to, subject, bodyText, bodyHtml, null); - } - - /// - /// Creates simple mime message with attachments. - /// - /// Header field From: value. - /// Header field To: value. - /// Header field Subject: value. - /// Body text of message. NOTE: Pass null is body text isn't wanted. - /// Body HTML text of message. NOTE: Pass null is body HTML text isn't wanted. - /// Attachment file names. Pass null if no attachments. NOTE: File name must contain full path to file, for example: c:\test.pdf. - /// - public static Mime CreateSimple(AddressList from, - AddressList to, - string subject, - string bodyText, - string bodyHtml, - string[] attachmentFileNames) - { - Mime m = new Mime(); - - MimeEntity mainEntity = m.MainEntity; - mainEntity.From = from; - mainEntity.To = to; - mainEntity.Subject = subject; - - // There are no atachments - if (attachmentFileNames == null || attachmentFileNames.Length == 0) - { - // If bodyText and bodyHtml both specified - if (bodyText != null && bodyHtml != null) - { - mainEntity.ContentType = MediaType_enum.Multipart_alternative; - - MimeEntity textEntity = mainEntity.ChildEntities.Add(); - textEntity.ContentType = MediaType_enum.Text_plain; - textEntity.ContentTransferEncoding = ContentTransferEncoding_enum.QuotedPrintable; - textEntity.DataText = bodyText; - - MimeEntity textHtmlEntity = mainEntity.ChildEntities.Add(); - textHtmlEntity.ContentType = MediaType_enum.Text_html; - textHtmlEntity.ContentTransferEncoding = ContentTransferEncoding_enum.QuotedPrintable; - textHtmlEntity.DataText = bodyHtml; - } - // There is only body text - else if (bodyText != null) - { - MimeEntity textEntity = mainEntity; - textEntity.ContentType = MediaType_enum.Text_plain; - textEntity.ContentTransferEncoding = ContentTransferEncoding_enum.QuotedPrintable; - textEntity.DataText = bodyText; - } - // There is only body html text - else if (bodyHtml != null) - { - MimeEntity textHtmlEntity = mainEntity; - textHtmlEntity.ContentType = MediaType_enum.Text_html; - textHtmlEntity.ContentTransferEncoding = ContentTransferEncoding_enum.QuotedPrintable; - textHtmlEntity.DataText = bodyHtml; - } - } - // There are attachments - else - { - mainEntity.ContentType = MediaType_enum.Multipart_mixed; - - // If bodyText and bodyHtml both specified - if (bodyText != null && bodyHtml != null) - { - MimeEntity multiPartAlternativeEntity = mainEntity.ChildEntities.Add(); - multiPartAlternativeEntity.ContentType = MediaType_enum.Multipart_alternative; - - MimeEntity textEntity = multiPartAlternativeEntity.ChildEntities.Add(); - textEntity.ContentType = MediaType_enum.Text_plain; - textEntity.ContentTransferEncoding = ContentTransferEncoding_enum.QuotedPrintable; - textEntity.DataText = bodyText; - - MimeEntity textHtmlEntity = multiPartAlternativeEntity.ChildEntities.Add(); - textHtmlEntity.ContentType = MediaType_enum.Text_html; - textHtmlEntity.ContentTransferEncoding = ContentTransferEncoding_enum.QuotedPrintable; - textHtmlEntity.DataText = bodyHtml; - } - // There is only body text - else if (bodyText != null) - { - MimeEntity textEntity = mainEntity.ChildEntities.Add(); - textEntity.ContentType = MediaType_enum.Text_plain; - textEntity.ContentTransferEncoding = ContentTransferEncoding_enum.QuotedPrintable; - textEntity.DataText = bodyText; - } - // There is only body html text - else if (bodyHtml != null) - { - MimeEntity textHtmlEntity = mainEntity.ChildEntities.Add(); - textHtmlEntity.ContentType = MediaType_enum.Text_html; - textHtmlEntity.ContentTransferEncoding = ContentTransferEncoding_enum.QuotedPrintable; - textHtmlEntity.DataText = bodyHtml; - } - - foreach (string fileName in attachmentFileNames) - { - MimeEntity attachmentEntity = mainEntity.ChildEntities.Add(); - attachmentEntity.ContentType = MediaType_enum.Application_octet_stream; - attachmentEntity.ContentDisposition = ContentDisposition_enum.Attachment; - attachmentEntity.ContentTransferEncoding = ContentTransferEncoding_enum.Base64; - attachmentEntity.ContentDisposition_FileName = Core.GetFileNameFromPath(fileName); - attachmentEntity.DataFromFile(fileName); - } - } - - return m; - } - - /// - /// Stores mime message to string. - /// - /// - public string ToStringData() - { - return Encoding.Default.GetString(ToByteData()); - } - - /// - /// Stores mime message to byte[]. - /// - /// - public byte[] ToByteData() - { - using (MemoryStream ms = new MemoryStream()) - { - ToStream(ms); - - return ms.ToArray(); - } - } - - /// - /// Stores mime message to specified file. - /// - /// File name. - public void ToFile(string fileName) - { - using (FileStream fs = File.Create(fileName)) - { - ToStream(fs); - } - } - - /// - /// Stores mime message to specified stream. Stream position stays where mime writing ends. - /// - /// Stream where to store mime message. - public void ToStream(Stream storeStream) - { - m_pMainEntity.ToStream(storeStream); - } - - #endregion - - #region Utility methods - - /// - /// Gets mime entities, including nested entries. - /// - /// - /// - private void GetEntities(MimeEntityCollection entities, List allEntries) - { - if (entities != null) - { - foreach (MimeEntity ent in entities) - { - allEntries.Add(ent); - - // Add child entities, if any - if (ent.ChildEntities.Count > 0) - { - GetEntities(ent.ChildEntities, allEntries); - } - } - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/MimeEntity.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/MimeEntity.cs deleted file mode 100644 index 3106c2a5d..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/MimeEntity.cs +++ /dev/null @@ -1,1769 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime -{ - #region usings - - using System; - using System.Collections; - using System.IO; - using System.Text; - using IO; - using MIME; - - #endregion - - /// - /// Rfc 2822 Mime Entity. - /// - [Obsolete("See LumiSoft.Net.MIME or LumiSoft.Net.Mail namepaces for replacement.")] - public class MimeEntity - { - #region Members - - private readonly MimeEntityCollection m_pChildEntities; - private readonly HeaderFieldCollection m_pHeader; - private readonly Hashtable m_pHeaderFieldCache; - private byte[] m_EncodedData; - private MimeEntity m_pParentEntity; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public MimeEntity() - { - m_pHeader = new HeaderFieldCollection(); - m_pChildEntities = new MimeEntityCollection(this); - m_pHeaderFieldCache = new Hashtable(); - } - - #endregion - - #region Properties - - /// - /// Gets or sets header field "Bcc:" value. Returns null if value isn't set. - /// - public AddressList Bcc - { - get - { - if (m_pHeader.Contains("Bcc:")) - { - // There is already cached version, return it - if (m_pHeaderFieldCache.Contains("Bcc:")) - { - return (AddressList) m_pHeaderFieldCache["Bcc:"]; - } - // These isn't cached version, we need to create it - else - { - // Create and bound address-list to existing header field - HeaderField field = m_pHeader.GetFirst("Bcc:"); - AddressList list = new AddressList(); - list.Parse(field.EncodedValue); - list.BoundedHeaderField = field; - - // Cache header field - m_pHeaderFieldCache["Bcc:"] = list; - - return list; - } - } - else - { - return null; - } - } - - set - { - // Just remove header field - if (value == null) - { - m_pHeader.Remove(m_pHeader.GetFirst("Bcc:")); - return; - } - - // Release old address collection - if (m_pHeaderFieldCache["Bcc:"] != null) - { - ((AddressList) m_pHeaderFieldCache["Bcc:"]).BoundedHeaderField = null; - } - - // Bound address-list to To: header field. If header field doesn't exist, add it. - HeaderField bcc = m_pHeader.GetFirst("Bcc:"); - if (bcc == null) - { - bcc = new HeaderField("Bcc:", value.ToAddressListString()); - m_pHeader.Add(bcc); - } - else - { - bcc.Value = value.ToAddressListString(); - } - value.BoundedHeaderField = bcc; - - m_pHeaderFieldCache["Bcc:"] = value; - } - } - - /// - /// Gets or sets header field "Cc:" value. Returns null if value isn't set. - /// - public AddressList Cc - { - get - { - if (m_pHeader.Contains("Cc:")) - { - // There is already cached version, return it - if (m_pHeaderFieldCache.Contains("Cc:")) - { - return (AddressList) m_pHeaderFieldCache["Cc:"]; - } - // These isn't cached version, we need to create it - else - { - // Create and bound address-list to existing header field - HeaderField field = m_pHeader.GetFirst("Cc:"); - AddressList list = new AddressList(); - list.Parse(field.EncodedValue); - list.BoundedHeaderField = field; - - // Cache header field - m_pHeaderFieldCache["Cc:"] = list; - - return list; - } - } - else - { - return null; - } - } - - set - { - // Just remove header field - if (value == null) - { - m_pHeader.Remove(m_pHeader.GetFirst("Cc:")); - return; - } - - // Release old address collection - if (m_pHeaderFieldCache["Cc:"] != null) - { - ((AddressList) m_pHeaderFieldCache["Cc:"]).BoundedHeaderField = null; - } - - // Bound address-list to To: header field. If header field doesn't exist, add it. - HeaderField cc = m_pHeader.GetFirst("Cc:"); - if (cc == null) - { - cc = new HeaderField("Cc:", value.ToAddressListString()); - m_pHeader.Add(cc); - } - else - { - cc.Value = value.ToAddressListString(); - } - value.BoundedHeaderField = cc; - - m_pHeaderFieldCache["Cc:"] = value; - } - } - - /// - /// Gets child entities. This property is available only if ContentType = multipart/... . - /// - public MimeEntityCollection ChildEntities - { - get { return m_pChildEntities; } - } - - /// - /// Gets or sets header field "Content-class:" value. Returns null if value isn't set.
    - /// Additional property to support messages of CalendarItem type which have iCal/vCal entries. - ///
    - public string ContentClass - { - get - { - if (m_pHeader.Contains("Content-Class:")) - { - return m_pHeader.GetFirst("Content-Class:").Value; - } - else - { - return null; - } - } - - set - { - if (m_pHeader.Contains("Content-Class:")) - { - m_pHeader.GetFirst("Content-Class:").Value = value; - } - else - { - m_pHeader.Add("Content-Class:", value); - } - } - } - - /// - /// Gets or sets header field "Content-Description:" value. Returns null if value isn't set. - /// - public string ContentDescription - { - get - { - if (m_pHeader.Contains("Content-Description:")) - { - return m_pHeader.GetFirst("Content-Description:").Value; - } - else - { - return null; - } - } - - set - { - if (m_pHeader.Contains("Content-Description:")) - { - m_pHeader.GetFirst("Content-Description:").Value = value; - } - else - { - m_pHeader.Add("Content-Description:", value); - } - } - } - - /// - /// Gets or sets header field "Content-Disposition:" value. - /// - public ContentDisposition_enum ContentDisposition - { - get - { - if (m_pHeader.Contains("Content-Disposition:")) - { - return MimeUtils.ParseContentDisposition(m_pHeader.GetFirst("Content-Disposition:").Value); - } - else - { - return ContentDisposition_enum.NotSpecified; - } - } - - set - { - if (value == ContentDisposition_enum.Unknown) - { - throw new Exception("ContentDisposition_enum.Unknown isn't allowed to set !"); - } - - // Just remove Content-Disposition: header field if exists - if (value == ContentDisposition_enum.NotSpecified) - { - HeaderField disposition = m_pHeader.GetFirst("Content-Disposition:"); - if (disposition != null) - { - m_pHeader.Remove(disposition); - } - } - else - { - string disposition = MimeUtils.ContentDispositionToString(value); - if (m_pHeader.Contains("Content-Disposition:")) - { - m_pHeader.GetFirst("Content-Disposition:").Value = disposition; - } - else - { - m_pHeader.Add("Content-Disposition:", disposition); - } - } - } - } - - /// - /// Gets or sets "Content-Disposition:" header field "filename" parameter. - /// Returns null if Content-Disposition: header field value isn't set or Content-Disposition: header field "filename" parameter isn't set. - /// Note: Content-Disposition must be attachment or inline. - /// - public string ContentDisposition_FileName - { - get - { - if (m_pHeader.Contains("Content-Disposition:")) - { - ParametizedHeaderField contentDisposition = - new ParametizedHeaderField(m_pHeader.GetFirst("Content-Disposition:")); - if (contentDisposition.Parameters.Contains("filename")) - { - return MimeUtils.DecodeWords(contentDisposition.Parameters["filename"]); - } - else - { - return null; - } - } - else - { - return null; - } - } - - set - { - if (!m_pHeader.Contains("Content-Disposition:")) - { - throw new Exception("Please specify Content-Disposition first !"); - } - - ParametizedHeaderField contentType = - new ParametizedHeaderField(m_pHeader.GetFirst("Content-Disposition:")); - if (contentType.Parameters.Contains("filename")) - { - contentType.Parameters["filename"] = MimeUtils.EncodeWord(value); - } - else - { - contentType.Parameters.Add("filename", MimeUtils.EncodeWord(value)); - } - } - } - - /// - /// Gets or sets header field "Content-ID:" value. Returns null if value isn't set. - /// - public string ContentID - { - get - { - if (m_pHeader.Contains("Content-ID:")) - { - return m_pHeader.GetFirst("Content-ID:").Value; - } - else - { - return null; - } - } - - set - { - if (m_pHeader.Contains("Content-ID:")) - { - m_pHeader.GetFirst("Content-ID:").Value = value; - } - else - { - m_pHeader.Add("Content-ID:", value); - } - } - } - - /// - /// Gets or sets header field "Content-Transfer-Encoding:" value. This property specifies how data is encoded/decoded. - /// If you set this value, it's recommended that you use QuotedPrintable for text and Base64 for binary data. - /// 7bit,_8bit,Binary are today obsolete (used for parsing). - /// - public ContentTransferEncoding_enum ContentTransferEncoding - { - get - { - if (m_pHeader.Contains("Content-Transfer-Encoding:")) - { - return - MimeUtils.ParseContentTransferEncoding( - m_pHeader.GetFirst("Content-Transfer-Encoding:").Value); - } - else - { - return ContentTransferEncoding_enum.NotSpecified; - } - } - - set - { - if (value == ContentTransferEncoding_enum.Unknown) - { - throw new Exception("ContentTransferEncoding_enum.Unknown isn't allowed to set !"); - } - if (value == ContentTransferEncoding_enum.NotSpecified) - { - throw new Exception("ContentTransferEncoding_enum.NotSpecified isn't allowed to set !"); - } - - string encoding = MimeUtils.ContentTransferEncodingToString(value); - - // There is entity data specified and encoding changed, we need to convert existing data - if (DataEncoded != null) - { - ContentTransferEncoding_enum oldEncoding = ContentTransferEncoding; - if (oldEncoding == ContentTransferEncoding_enum.Unknown || - oldEncoding == ContentTransferEncoding_enum.NotSpecified) - { - throw new Exception("Data can't be converted because old encoding '" + - MimeUtils.ContentTransferEncodingToString(oldEncoding) + - "' is unknown !"); - } - - DataEncoded = EncodeData(Data, value); - } - - if (m_pHeader.Contains("Content-Transfer-Encoding:")) - { - m_pHeader.GetFirst("Content-Transfer-Encoding:").Value = encoding; - } - else - { - m_pHeader.Add("Content-Transfer-Encoding:", encoding); - } - } - } - - /// - /// Gets or sets header field "Content-Type:" value. This property specifies what entity data is. - /// NOTE: ContentType can't be changed while there is data specified(Exception is thrown) in this mime entity, because it isn't - /// possible todo data conversion between different types. For example text/xx has charset parameter and other types don't, - /// changing loses it and text data becomes useless. - /// - public MediaType_enum ContentType - { - get - { - if (m_pHeader.Contains("Content-Type:")) - { - string contentType = new ParametizedHeaderField(m_pHeader.GetFirst("Content-Type:")).Value; - return MimeUtils.ParseMediaType(contentType); - } - else - { - return MediaType_enum.NotSpecified; - } - } - - set - { - if (DataEncoded != null) - { - throw new Exception( - "ContentType can't be changed while there is data specified, set data to null before !"); - } - if (value == MediaType_enum.Unknown) - { - throw new Exception("MediaType_enum.Unkown isn't allowed to set !"); - } - if (value == MediaType_enum.NotSpecified) - { - throw new Exception("MediaType_enum.NotSpecified isn't allowed to set !"); - } - - string contentType = ""; - //--- Text/xxx --------------------------------// - if (value == MediaType_enum.Text_plain) - { - contentType = "text/plain; charset=\"utf-8\""; - } - else if (value == MediaType_enum.Text_html) - { - contentType = "text/html; charset=\"utf-8\""; - } - else if (value == MediaType_enum.Text_xml) - { - contentType = "text/xml; charset=\"utf-8\""; - } - else if (value == MediaType_enum.Text_rtf) - { - contentType = "text/rtf; charset=\"utf-8\""; - } - else if (value == MediaType_enum.Text) - { - contentType = "text; charset=\"utf-8\""; - } - //---------------------------------------------// - - //--- Image/xxx -------------------------------// - else if (value == MediaType_enum.Image_gif) - { - contentType = "image/gif"; - } - else if (value == MediaType_enum.Image_tiff) - { - contentType = "image/tiff"; - } - else if (value == MediaType_enum.Image_jpeg) - { - contentType = "image/jpeg"; - } - else if (value == MediaType_enum.Image) - { - contentType = "image"; - } - //---------------------------------------------// - - //--- Audio/xxx -------------------------------// - else if (value == MediaType_enum.Audio) - { - contentType = "audio"; - } - //---------------------------------------------// - - //--- Video/xxx -------------------------------// - else if (value == MediaType_enum.Video) - { - contentType = "video"; - } - //---------------------------------------------// - - //--- Application/xxx -------------------------// - else if (value == MediaType_enum.Application_octet_stream) - { - contentType = "application/octet-stream"; - } - else if (value == MediaType_enum.Application) - { - contentType = "application"; - } - //---------------------------------------------// - - //--- Multipart/xxx ---------------------------// - else if (value == MediaType_enum.Multipart_mixed) - { - contentType = "multipart/mixed; boundary=\"part_" + - Guid.NewGuid().ToString().Replace("-", "_") + "\""; - } - else if (value == MediaType_enum.Multipart_alternative) - { - contentType = "multipart/alternative; boundary=\"part_" + - Guid.NewGuid().ToString().Replace("-", "_") + "\""; - } - else if (value == MediaType_enum.Multipart_parallel) - { - contentType = "multipart/parallel; boundary=\"part_" + - Guid.NewGuid().ToString().Replace("-", "_") + "\""; - } - else if (value == MediaType_enum.Multipart_related) - { - contentType = "multipart/related; boundary=\"part_" + - Guid.NewGuid().ToString().Replace("-", "_") + "\""; - } - else if (value == MediaType_enum.Multipart_signed) - { - contentType = "multipart/signed; boundary=\"part_" + - Guid.NewGuid().ToString().Replace("-", "_") + "\""; - } - else if (value == MediaType_enum.Multipart) - { - contentType = "multipart; boundary=\"part_" + Guid.NewGuid().ToString().Replace("-", "_") + - "\""; - } - //---------------------------------------------// - - //--- Message/xxx -----------------------------// - else if (value == MediaType_enum.Message_rfc822) - { - contentType = "message/rfc822"; - } - else if (value == MediaType_enum.Message) - { - contentType = "message"; - } - //---------------------------------------------// - - else - { - throw new Exception("Invalid flags combination of MediaType_enum was specified !"); - } - - if (m_pHeader.Contains("Content-Type:")) - { - m_pHeader.GetFirst("Content-Type:").Value = contentType; - } - else - { - m_pHeader.Add("Content-Type:", contentType); - } - } - } - - /// - /// Gets or sets "Content-Type:" header field "boundary" parameter. - /// Returns null if Content-Type: header field value isn't set or Content-Type: header field "boundary" parameter isn't set. - /// Note: Content-Type must be multipart/xxx or exception is thrown. - /// - public string ContentType_Boundary - { - get - { - if (m_pHeader.Contains("Content-Type:")) - { - ParametizedHeaderField contentDisposition = - new ParametizedHeaderField(m_pHeader.GetFirst("Content-Type:")); - if (contentDisposition.Parameters.Contains("boundary")) - { - return contentDisposition.Parameters["boundary"]; - } - else - { - return null; - } - } - else - { - return null; - } - } - - set - { - if (!m_pHeader.Contains("Content-Type:")) - { - throw new Exception("Please specify Content-Type first !"); - } - if ((ContentType & MediaType_enum.Multipart) == 0) - { - throw new Exception("Parameter boundary is available only for ContentType multipart/xxx !"); - } - - ParametizedHeaderField contentType = - new ParametizedHeaderField(m_pHeader.GetFirst("Content-Type:")); - if (contentType.Parameters.Contains("boundary")) - { - contentType.Parameters["boundary"] = value; - } - else - { - contentType.Parameters.Add("boundary", value); - } - } - } - - /// - /// Gets or sets "Content-Type:" header field "charset" parameter. - /// Returns null if Content-Type: header field value isn't set or Content-Type: header field "charset" parameter isn't set. - /// If you don't know what charset to use then utf-8 is recommended, most of times this is sufficient. - /// Note: Content-Type must be text/xxx or exception is thrown. - /// - public string ContentType_CharSet - { - get - { - if (m_pHeader.Contains("Content-Type:")) - { - ParametizedHeaderField contentType = - new ParametizedHeaderField(m_pHeader.GetFirst("Content-Type:")); - if (contentType.Parameters.Contains("charset")) - { - return contentType.Parameters["charset"]; - } - else - { - return null; - } - } - else - { - return null; - } - } - - set - { - if (!m_pHeader.Contains("Content-Type:")) - { - throw new Exception("Please specify Content-Type first !"); - } - if ((ContentType & MediaType_enum.Text) == 0) - { - throw new Exception("Parameter boundary is available only for ContentType text/xxx !"); - } - - // There is data specified, we need to convert it because charset changed - if (DataEncoded != null) - { - string currentCharSet = ContentType_CharSet; - if (currentCharSet == null) - { - currentCharSet = "ascii"; - } - - if (EncodingTools.GetEncodingByCodepageName(currentCharSet) == null) - { - throw new Exception("Data can't be converted because current charset '" + - currentCharSet + "' isn't supported !"); - } - - Encoding encoding = EncodingTools.GetEncodingByCodepageName(value); - if(encoding == null) - { - throw new Exception("Data can't be converted because new charset '" + value + - "' isn't supported !"); - } - Data = encoding.GetBytes(DataText); - } - - ParametizedHeaderField contentType = - new ParametizedHeaderField(m_pHeader.GetFirst("Content-Type:")); - if (contentType.Parameters.Contains("charset")) - { - contentType.Parameters["charset"] = value; - } - else - { - contentType.Parameters.Add("charset", value); - } - } - } - - /// - /// Gets or sets "Content-Type:" header field "name" parameter. - /// Returns null if Content-Type: header field value isn't set or Content-Type: header field "name" parameter isn't set. - ///

    - /// Note: Content-Type must be application/xxx or exception is thrown. - /// This property is obsolete today, it's replaced with Content-Disposition: filename parameter. - /// If possible always set FileName property instead of it. - ///

    - public string ContentType_Name - { - get - { - if (m_pHeader.Contains("Content-Type:")) - { - ParametizedHeaderField contentType = - new ParametizedHeaderField(m_pHeader.GetFirst("Content-Type:")); - if (contentType.Parameters.Contains("name")) - { - return contentType.Parameters["name"]; - } - else - { - return null; - } - } - else - { - return null; - } - } - - set - { - if (!m_pHeader.Contains("Content-Type:")) - { - throw new Exception("Please specify Content-Type first !"); - } - if ((ContentType & MediaType_enum.Application) == 0) - { - throw new Exception("Parameter name is available only for ContentType application/xxx !"); - } - - ParametizedHeaderField contentType = - new ParametizedHeaderField(m_pHeader.GetFirst("Content-Type:")); - if (contentType.Parameters.Contains("name")) - { - contentType.Parameters["name"] = value; - } - else - { - contentType.Parameters.Add("name", value); - } - } - } - - /// - /// Gets or sets header field "Content-Type:" value. Returns null if value isn't set. This property specifies what entity data is. - /// This property is meant for advanced users, who needs other values what defined MediaType_enum provides. - /// Example value: text/plain; charset="utf-8". - /// NOTE: ContentType can't be changed while there is data specified(Exception is thrown) in this mime entity, because it isn't - /// possible todo data conversion between different types. For example text/xx has charset parameter and other types don't, - /// changing loses it and text data becomes useless. - /// - public string ContentTypeString - { - get - { - if (m_pHeader.Contains("Content-Type:")) - { - return m_pHeader.GetFirst("Content-Type:").Value; - } - else - { - return null; - } - } - - set - { - if (DataEncoded != null) - { - throw new Exception( - "ContentType can't be changed while there is data specified, set data to null before !"); - } - if (m_pHeader.Contains("Content-Type:")) - { - m_pHeader.GetFirst("Content-Type:").Value = value; - } - else - { - m_pHeader.Add("Content-Type:", value); - } - } - } - - /// - /// Gets or sets entity data. Data is encoded/decoded with "Content-Transfer-Encoding:" header field value. - /// Note: This property can be set only if Content-Type: isn't multipart. - /// - public byte[] Data - { - get - { - // Decode Data - ContentTransferEncoding_enum encoding = ContentTransferEncoding; - if (encoding == ContentTransferEncoding_enum.Base64) - { - return Core.Base64Decode(DataEncoded); - } - else if (encoding == ContentTransferEncoding_enum.QuotedPrintable) - { - Encoding enc = ContentType_CharSet == null ? - Encoding.ASCII : - (EncodingTools.GetEncodingByCodepageName(ContentType_CharSet) ?? Encoding.ASCII); - return enc.GetBytes(Core.QuotedPrintableDecode(Encoding.ASCII.GetString(DataEncoded), enc));//decode using ASCII - } - else - { - return DataEncoded; - } - } - - set - { - if (value == null) - { - DataEncoded = null; - return; - } - - ContentTransferEncoding_enum encoding = ContentTransferEncoding; - DataEncoded = EncodeData(value, encoding); - } - } - - /// - /// Gets or sets entity encoded data. If you set this value, be sure that you encode this value as specified by Content-Transfer-Encoding: header field. - /// Set this value only if you need custom Content-Transfer-Encoding: what current Mime class won't support, other wise set data through this.Data property. - /// Note: This property can be set only if Content-Type: isn't multipart. - /// - public byte[] DataEncoded - { - get { return m_EncodedData; } - - set { m_EncodedData = value; } - } - - /// - /// Gets or sets entity text data. Data is encoded/decoded with "Content-Transfer-Encoding:" header field value with this.Charset charset. - /// Note: This property is available only if ContentType is Text/xxx... or no content type specified, othwerwise Excpetion is thrown. - /// - public string DataText - { - get - { - if ((ContentType & MediaType_enum.Text) == 0 && - (ContentType & MediaType_enum.NotSpecified) == 0) - { - throw new Exception("This property is only available if ContentType is Text/xxx... !"); - } - - string charSet = ContentType_CharSet; - // Charset isn't specified, use system default - if (charSet == null) - { - return Encoding.Default.GetString(Data); - } - else - { - // If charset is not supported then use default - return (EncodingTools.GetEncodingByCodepageName(charSet) ?? Encoding.Default).GetString(Data); - } - } - - set - { - if (value == null) - { - DataEncoded = null; - return; - } - - // Check charset - string charSet = ContentType_CharSet; - if (charSet == null) - { - throw new Exception("Please specify CharSet property first !"); - } - - Encoding encoding = EncodingTools.GetEncodingByCodepageName(charSet); - if(encoding == null) - { - throw new Exception("Not supported charset '" + charSet + - "' ! If you need to use this charset, then set data through Data or DataEncoded property."); - } - Data = encoding.GetBytes(value); - } - } - - /// - /// Gets or sets header field "Date:" value. - /// - public DateTime Date - { - get - { - if (m_pHeader.Contains("Date:")) - { - try - { - return MIME_Utils.ParseRfc2822DateTime(m_pHeader.GetFirst("Date:").Value); - } - catch - { - return DateTime.MinValue; - } - } - else - { - return DateTime.MinValue; - } - } - - set - { - if (m_pHeader.Contains("Date:")) - { - m_pHeader.GetFirst("Date:").Value = MIME_Utils.DateTimeToRfc2822(value); - } - else - { - m_pHeader.Add("Date:", MimeUtils.DateTimeToRfc2822(value)); - } - } - } - - /// - /// Gets or sets header field "Disposition-Notification-To:" value. Returns null if value isn't set. - /// - public string DSN - { - get - { - if (m_pHeader.Contains("Disposition-Notification-To:")) - { - return m_pHeader.GetFirst("Disposition-Notification-To:").Value; - } - else - { - return null; - } - } - - set - { - if (m_pHeader.Contains("Disposition-Notification-To:")) - { - m_pHeader.GetFirst("Disposition-Notification-To:").Value = value; - } - else - { - m_pHeader.Add("Disposition-Notification-To:", value); - } - } - } - - /// - /// Gets or sets header field "From:" value. Returns null if value isn't set. - /// - public AddressList From - { - get - { - if (m_pHeader.Contains("From:")) - { - // There is already cached version, return it - if (m_pHeaderFieldCache.Contains("From:")) - { - return (AddressList) m_pHeaderFieldCache["From:"]; - } - // These isn't cached version, we need to create it - else - { - // Create and bound address-list to existing header field - HeaderField field = m_pHeader.GetFirst("From:"); - AddressList list = new AddressList(); - list.Parse(field.EncodedValue); - list.BoundedHeaderField = field; - - // Cache header field - m_pHeaderFieldCache["From:"] = list; - - return list; - } - } - else - { - return null; - } - } - - set - { - // Just remove header field - if (value == null && m_pHeader.Contains("From:")) - { - m_pHeader.Remove(m_pHeader.GetFirst("From:")); - return; - } - - // Release old address collection - if (m_pHeaderFieldCache["From:"] != null) - { - ((AddressList) m_pHeaderFieldCache["From:"]).BoundedHeaderField = null; - } - - // Bound address-list to To: header field. If header field doesn't exist, add it. - HeaderField from = m_pHeader.GetFirst("From:"); - if (from == null) - { - from = new HeaderField("From:", value.ToAddressListString()); - m_pHeader.Add(from); - } - else - { - from.Value = value.ToAddressListString(); - } - value.BoundedHeaderField = from; - - m_pHeaderFieldCache["From:"] = value; - } - } - - /// - /// Gets message header. - /// - public HeaderFieldCollection Header - { - get { return m_pHeader; } - } - - /// - /// Gets header as RFC 2822 message headers. - /// - public string HeaderString - { - get { return m_pHeader.ToHeaderString("utf-8"); } - } - - /// - /// Gets or sets header field "In-Reply-To:" value. Returns null if value isn't set. - /// - public string InReplyTo - { - get - { - if (m_pHeader.Contains("In-Reply-To:")) - { - return m_pHeader.GetFirst("In-Reply-To:").Value; - } - else - { - return null; - } - } - - set - { - if (m_pHeader.Contains("In-Reply-To:")) - { - m_pHeader.GetFirst("In-Reply-To:").Value = value; - } - else - { - m_pHeader.Add("In-Reply-To:", value); - } - } - } - - /// - /// Gets or sets header field "Message-ID:" value. Returns null if value isn't set. - /// Syntax: '<'id-left@id-right'>'. Example: <621bs724bfs8@jnfsjaas4263> - /// - public string MessageID - { - get - { - if (m_pHeader.Contains("Message-ID:")) - { - return m_pHeader.GetFirst("Message-ID:").Value; - } - else - { - return null; - } - } - - set - { - if (m_pHeader.Contains("Message-ID:")) - { - m_pHeader.GetFirst("Message-ID:").Value = value; - } - else - { - m_pHeader.Add("Message-ID:", value); - } - } - } - - /// - /// Gets or sets header field "Mime-Version:" value. Returns null if value isn't set. - /// - public string MimeVersion - { - get - { - if (m_pHeader.Contains("Mime-Version:")) - { - return m_pHeader.GetFirst("Mime-Version:").Value; - } - else - { - return null; - } - } - - set - { - if (m_pHeader.Contains("Mime-Version:")) - { - m_pHeader.GetFirst("Mime-Version:").Value = value; - } - else - { - m_pHeader.Add("Mime-Version:", value); - } - } - } - - /// - /// Gets parent entity of this entity. If this entity is top level, then this property returns null. - /// - public MimeEntity ParentEntity - { - get { return m_pParentEntity; } - } - - /// - /// Gets or sets header field "Reply-To:" value. Returns null if value isn't set. - /// - public AddressList ReplyTo - { - get - { - if (m_pHeader.Contains("Reply-To:")) - { - // There is already cached version, return it - if (m_pHeaderFieldCache.Contains("Reply-To:")) - { - return (AddressList) m_pHeaderFieldCache["Reply-To:"]; - } - // These isn't cached version, we need to create it - else - { - // Create and bound address-list to existing header field - HeaderField field = m_pHeader.GetFirst("Reply-To:"); - AddressList list = new AddressList(); - list.Parse(field.Value); - list.BoundedHeaderField = field; - - // Cache header field - m_pHeaderFieldCache["Reply-To:"] = list; - - return list; - } - } - else - { - return null; - } - } - - set - { - // Just remove header field - if (value == null && m_pHeader.Contains("Reply-To:")) - { - m_pHeader.Remove(m_pHeader.GetFirst("Reply-To:")); - return; - } - - // Release old address collection - if (m_pHeaderFieldCache["Reply-To:"] != null) - { - ((AddressList) m_pHeaderFieldCache["Reply-To:"]).BoundedHeaderField = null; - } - - // Bound address-list to To: header field. If header field doesn't exist, add it. - HeaderField replyTo = m_pHeader.GetFirst("Reply-To:"); - if (replyTo == null) - { - replyTo = new HeaderField("Reply-To:", value.ToAddressListString()); - m_pHeader.Add(replyTo); - } - else - { - replyTo.Value = value.ToAddressListString(); - } - value.BoundedHeaderField = replyTo; - - m_pHeaderFieldCache["Reply-To:"] = value; - } - } - - /// - /// Gets or sets header field "Sender:" value. Returns null if value isn't set. - /// - public MailboxAddress Sender - { - get - { - if (m_pHeader.Contains("Sender:")) - { - return MailboxAddress.Parse(m_pHeader.GetFirst("Sender:").EncodedValue); - } - else - { - return null; - } - } - - set - { - if (m_pHeader.Contains("Sender:")) - { - m_pHeader.GetFirst("Sender:").Value = value.ToMailboxAddressString(); - } - else - { - m_pHeader.Add("Sender:", value.ToMailboxAddressString()); - } - } - } - - /// - /// Gets or sets header field "Subject:" value. Returns null if value isn't set. - /// - public string Subject - { - get - { - if (m_pHeader.Contains("Subject:")) - { - return m_pHeader.GetFirst("Subject:").Value; - } - else - { - return null; - } - } - - set - { - if (m_pHeader.Contains("Subject:")) - { - m_pHeader.GetFirst("Subject:").Value = value; - } - else - { - m_pHeader.Add("Subject:", value); - } - } - } - - /// - /// Gets or sets header field "To:" value. Returns null if value isn't set. - /// - public AddressList To - { - get - { - if (m_pHeader.Contains("To:")) - { - // There is already cached version, return it - if (m_pHeaderFieldCache.Contains("To:")) - { - return (AddressList) m_pHeaderFieldCache["To:"]; - } - // These isn't cached version, we need to create it - else - { - // Create and bound address-list to existing header field - HeaderField field = m_pHeader.GetFirst("To:"); - AddressList list = new AddressList(); - list.Parse(field.EncodedValue); - list.BoundedHeaderField = field; - - // Cache header field - m_pHeaderFieldCache["To:"] = list; - - return list; - } - } - else - { - return null; - } - } - - set - { - // Just remove header field - if (value == null) - { - m_pHeader.Remove(m_pHeader.GetFirst("To:")); - return; - } - - // Release old address collection - if (m_pHeaderFieldCache["To:"] != null) - { - ((AddressList) m_pHeaderFieldCache["To:"]).BoundedHeaderField = null; - } - - // Bound address-list to To: header field. If header field doesn't exist, add it. - HeaderField to = m_pHeader.GetFirst("To:"); - if (to == null) - { - to = new HeaderField("To:", value.ToAddressListString()); - m_pHeader.Add(to); - } - else - { - to.Value = value.ToAddressListString(); - } - value.BoundedHeaderField = to; - - m_pHeaderFieldCache["To:"] = value; - } - } - - #endregion - - #region Methods - - /// - /// Stores mime entity and it's child entities to specified stream. - /// - /// Stream where to store mime entity. - public void ToStream(Stream storeStream) - { - // Write headers - byte[] data = Encoding.Default.GetBytes(FoldHeader(HeaderString)); - storeStream.Write(data, 0, data.Length); - - // If multipart entity, write child entities.(multipart entity don't contain data, it contains nested entities ) - if ((ContentType & MediaType_enum.Multipart) != 0) - { - string boundary = ContentType_Boundary; - foreach (MimeEntity entity in ChildEntities) - { - // Write boundary start. Syntax: --BoundaryID - data = Encoding.Default.GetBytes("\r\n--" + boundary + "\r\n"); - storeStream.Write(data, 0, data.Length); - - // Force child entity to store itself - entity.ToStream(storeStream); - } - - // Write boundaries end Syntax: --BoundaryID-- - data = Encoding.Default.GetBytes("\r\n--" + boundary + "--\r\n"); - storeStream.Write(data, 0, data.Length); - } - // If singlepart (text,image,audio,video,message, ...), write entity data. - else - { - // Write blank line between headers and content - storeStream.Write(new[] {(byte) '\r', (byte) '\n'}, 0, 2); - - if (DataEncoded != null) - { - storeStream.Write(DataEncoded, 0, DataEncoded.Length); - } - } - } - - /// - /// Saves this.Data property value to specified file. - /// - /// File name where to store data. - public void DataToFile(string fileName) - { - using (FileStream fs = File.Create(fileName)) - { - DataToStream(fs); - } - } - - /// - /// Saves this.Data property value to specified stream. - /// - /// Stream where to store data. - public void DataToStream(Stream stream) - { - byte[] data = Data; - stream.Write(data, 0, data.Length); - } - - /// - /// Loads MimeEntity.Data property from file. - /// - /// File name. - public void DataFromFile(string fileName) - { - using (FileStream fs = File.OpenRead(fileName)) - { - DataFromStream(fs); - } - } - - /// - /// Loads MimeEntity.Data property from specified stream. Note: reading starts from current position and stream isn't closed. - /// - /// Data stream. - public void DataFromStream(Stream stream) - { - byte[] data = new byte[stream.Length]; - stream.Read(data, 0, (int) stream.Length); - - Data = data; - } - - #endregion - - #region Internal methods - - /// - /// Parses mime entity from stream. - /// - /// Data stream from where to read data. - /// Entity data is readed to specified boundary. - /// Returns false if last entity. Returns true for mulipart entity, if there are more entities. - internal bool Parse(SmartStream stream, string toBoundary) - { - // Clear header fields - m_pHeader.Clear(); - m_pHeaderFieldCache.Clear(); - - // Parse header - m_pHeader.Parse(stream); - - // Parse entity and child entities if any (Conent-Type: multipart/xxx...) - - // Multipart entity - if ((ContentType & MediaType_enum.Multipart) != 0) - { - // There must be be boundary ID (rfc 1341 7.2.1 The Content-Type field for multipart entities requires one parameter, - // "boundary", which is used to specify the encapsulation boundary.) - string boundaryID = ContentType_Boundary; - if (boundaryID == null) - { - // This is invalid message, just skip this mime entity - } - else - { - // There is one or more mime entities - - // Find first boundary start position - SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(new byte[8000], - SizeExceededAction. - JunkAndThrowException); - stream.ReadLine(args, false); - if (args.Error != null) - { - throw args.Error; - } - string lineString = args.LineUtf8; - - while (lineString != null) - { - if (lineString.StartsWith("--" + boundaryID)) - { - break; - } - - stream.ReadLine(args, false); - if (args.Error != null) - { - throw args.Error; - } - lineString = args.LineUtf8; - } - // This is invalid entity, boundary start not found. Skip that entity. - if (string.IsNullOrEmpty(lineString)) - { - return false; - } - - // Start parsing child entities of this entity - while (true) - { - // Parse and add child entity - MimeEntity childEntity = new MimeEntity(); - ChildEntities.Add(childEntity); - - // This is last entity, stop parsing - if (childEntity.Parse(stream, boundaryID) == false) - { - break; - } - // else{ - // There are more entities, parse them - } - - // This entity is child of mulipart entity. - // All this entity child entities are parsed, - // we need to move stream position to next entity start. - if (!string.IsNullOrEmpty(toBoundary)) - { - stream.ReadLine(args, false); - if (args.Error != null) - { - throw args.Error; - } - lineString = args.LineUtf8; - - while (lineString != null) - { - if (lineString.StartsWith("--" + toBoundary)) - { - break; - } - - stream.ReadLine(args, false); - if (args.Error != null) - { - throw args.Error; - } - lineString = args.LineUtf8; - } - - // Invalid boundary end, there can't be more entities - if (string.IsNullOrEmpty(lineString)) - { - return false; - } - - // See if last boundary or there is more. Last boundary ends with -- - if (lineString.EndsWith(toBoundary + "--")) - { - return false; - } - // else{ - // There are more entities - return true; - } - } - } - // Singlepart entity. - else - { - // Boundary is specified, read data to specified boundary. - if (!string.IsNullOrEmpty(toBoundary)) - { - MemoryStream entityData = new MemoryStream(); - SmartStream.ReadLineAsyncOP readLineOP = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength], - SizeExceededAction - . - JunkAndThrowException); - - // Read entity data while get boundary end tag --boundaryID-- or EOS. - while (true) - { - stream.ReadLine(readLineOP, false); - if (readLineOP.Error != null) - { - throw readLineOP.Error; - } - // End of stream reached. Normally we should get boundary end tag --boundaryID--, but some x mailers won't add it, so - // if we reach EOS, consider boundary closed. - if (readLineOP.BytesInBuffer == 0) - { - // Just return data what was readed. - m_EncodedData = entityData.ToArray(); - return false; - } - // We readed a line. - else - { - // We have boundary start/end tag or just "--" at the beginning of line. - if (readLineOP.LineBytesInBuffer >= 2 && readLineOP.Buffer[0] == '-' && - readLineOP.Buffer[1] == '-') - { - string lineString = readLineOP.LineUtf8; - // We have boundary end tag, no more boundaries. - if (lineString == "--" + toBoundary + "--") - { - m_EncodedData = entityData.ToArray(); - return false; - } - // We have new boundary start. - else if (lineString == "--" + toBoundary) - { - m_EncodedData = entityData.ToArray(); - return true; - } - else - { - // Just skip - } - } - - // Write readed line. - entityData.Write(readLineOP.Buffer, 0, readLineOP.BytesInBuffer); - } - } - } - // Boundary isn't specified, read data to the stream end. - else - { - MemoryStream ms = new MemoryStream(); - stream.ReadAll(ms); - m_EncodedData = ms.ToArray(); - } - } - - return false; - } - - #endregion - - #region Utility methods - - /// - /// Encodes data with specified content transfer encoding. - /// - /// Data to encode. - /// Encoding with what to encode data. - private byte[] EncodeData(byte[] data, ContentTransferEncoding_enum encoding) - { - // Allow only known Content-Transfer-Encoding (ContentTransferEncoding_enum value), - // otherwise we don't know how to encode data. - if (encoding == ContentTransferEncoding_enum.NotSpecified) - { - throw new Exception("Please specify Content-Transfer-Encoding first !"); - } - if (encoding == ContentTransferEncoding_enum.Unknown) - { - throw new Exception( - "Not supported Content-Transfer-Encoding. If it's your custom encoding, encode data yourself and set it with DataEncoded property !"); - } - - if (encoding == ContentTransferEncoding_enum.Base64) - { - return Core.Base64Encode(data); - } - else if (encoding == ContentTransferEncoding_enum.QuotedPrintable) - { - return Core.QuotedPrintableEncode(data); - } - else - { - return data; - } - } - - /// - /// Folds header. - /// - /// Header string. - /// - private string FoldHeader(string header) - { - /* Rfc 2822 2.2.3 Long Header Fields - Each header field is logically a single line of characters comprising - the field name, the colon, and the field body. For convenience - however, and to deal with the 998/78 character limitations per line, - the field body portion of a header field can be split into a multiple - line representation; this is called "folding". The general rule is - imply WSP characters), a CRLF may be inserted before any WSP. For - example, the header field: - - Subject: This is a test - - can be represented as: - - Subject: This - is a test - */ - - // Just fold header fields what contain - - StringBuilder retVal = new StringBuilder(); - - header = header.Replace("\r\n", "\n"); - string[] headerLines = header.Split('\n'); - foreach (string headerLine in headerLines) - { - // Folding is needed - if (headerLine.IndexOf('\t') > -1) - { - retVal.Append(headerLine.Replace("\t", "\r\n\t") + "\r\n"); - } - // No folding needed, just write back header line - else - { - retVal.Append(headerLine + "\r\n"); - } - } - // Split splits last line to element, but we don't need it - if (retVal.Length > 1) - { - return retVal.ToString(0, retVal.Length - 2); - } - else - { - return retVal.ToString(); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/MimeEntityCollection.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/MimeEntityCollection.cs deleted file mode 100644 index e334741c0..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/MimeEntityCollection.cs +++ /dev/null @@ -1,180 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime -{ - #region usings - - using System; - using System.Collections; - using System.Collections.Generic; - - #endregion - - /// - /// Mime entity collection. - /// - [Obsolete("See LumiSoft.Net.MIME or LumiSoft.Net.Mail namepaces for replacement.")] - public class MimeEntityCollection : IEnumerable - { - #region Members - - private readonly List m_pEntities; - private readonly MimeEntity m_pOwnerEntity; - - #endregion - - #region Properties - - /// - /// Gets mime entity at specified index. - /// - public MimeEntity this[int index] - { - get { return m_pEntities[index]; } - } - - /// - /// Gets mime entities count in the collection. - /// - public int Count - { - get { return m_pEntities.Count; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Mime entity what owns this collection. - internal MimeEntityCollection(MimeEntity ownerEntity) - { - m_pOwnerEntity = ownerEntity; - - m_pEntities = new List(); - } - - #endregion - - #region Methods - - /// - /// Creates a new mime entity to the end of the collection. - /// - /// - public MimeEntity Add() - { - MimeEntity entity = new MimeEntity(); - Add(entity); - - return entity; - } - - /// - /// Adds specified mime entity to the end of the collection. - /// - /// Mime entity to add to the collection. - public void Add(MimeEntity entity) - { - // Allow to add only for multipart/xxx... - if ((m_pOwnerEntity.ContentType & MediaType_enum.Multipart) == 0) - { - throw new Exception( - "You don't have Content-Type: multipart/xxx. Only Content-Type: multipart/xxx can have nested mime entities !"); - } - // Check boundary, this is required parameter for multipart/xx - if (m_pOwnerEntity.ContentType_Boundary == null || m_pOwnerEntity.ContentType_Boundary.Length == 0) - { - throw new Exception("Please specify Boundary property first !"); - } - - m_pEntities.Add(entity); - } - - /// - /// Inserts a new mime entity into the collection at the specified location. - /// - /// The location in the collection where you want to add the mime entity. - /// Mime entity. - public void Insert(int index, MimeEntity entity) - { - // Allow to add only for multipart/xxx... - if ((m_pOwnerEntity.ContentType & MediaType_enum.Multipart) == 0) - { - throw new Exception( - "You don't have Content-Type: multipart/xxx. Only Content-Type: multipart/xxx can have nested mime entities !"); - } - // Check boundary, this is required parameter for multipart/xx - if (m_pOwnerEntity.ContentType_Boundary == null || m_pOwnerEntity.ContentType_Boundary.Length == 0) - { - throw new Exception("Please specify Boundary property first !"); - } - - m_pEntities.Insert(index, entity); - } - - /// - /// Removes mime entity at the specified index from the collection. - /// - /// Index of mime entity to remove. - public void Remove(int index) - { - m_pEntities.RemoveAt(index); - } - - /// - /// Removes specified mime entity from the collection. - /// - /// Mime entity to remove. - public void Remove(MimeEntity entity) - { - m_pEntities.Remove(entity); - } - - /// - /// Clears the collection of all mome entities. - /// - public void Clear() - { - m_pEntities.Clear(); - } - - /// - /// Gets if collection contains specified mime entity. - /// - /// Mime entity. - /// - public bool Contains(MimeEntity entity) - { - return m_pEntities.Contains(entity); - } - - /// - /// Gets enumerator. - /// - /// - public IEnumerator GetEnumerator() - { - return m_pEntities.GetEnumerator(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/MimeUtils.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/MimeUtils.cs deleted file mode 100644 index 385df316c..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/MimeUtils.cs +++ /dev/null @@ -1,1143 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime -{ - #region usings - - using System; - using System.Globalization; - using System.IO; - using System.Text; - using StringReader=ASC.Mail.Net.StringReader; - - #endregion - - /// - /// Provides mime related utility methods. - /// - [Obsolete("See LumiSoft.Net.MIME or LumiSoft.Net.Mail namepaces for replacement.")] - public class MimeUtils - { - // TODO get rid of this method, only IMAP uses it - - #region Methods - - /// - /// Parses rfc 2822 datetime. - /// - /// Date string. - /// - public static DateTime ParseDate(string date) - { - /* Rfc 2822 3.3. Date and Time Specification. - date-time = [ day-of-week "," ] date FWS time [CFWS] - date = day month year - time = hour ":" minute [ ":" second ] FWS zone - */ - - /* IMAP date format. - date-time = date FWS time [CFWS] - date = day-month-year - time = hour ":" minute [ ":" second ] FWS zone - */ - - // zone = (( "+" / "-" ) 4DIGIT) - - //--- Replace timezone constants -------// - /* - UT -0000 - GMT -0000 - EDT -0400 - EST -0500 - CDT -0500 - CST -0600 - MDT -0600 - MST -0700 - PDT -0700 - PST -0800 - BST +0100 British Summer Time - */ - - date = date.ToLower(); - date = date.Replace("ut", "-0000"); - date = date.Replace("gmt", "-0000"); - date = date.Replace("edt", "-0400"); - date = date.Replace("est", "-0500"); - date = date.Replace("cdt", "-0500"); - date = date.Replace("cst", "-0600"); - date = date.Replace("mdt", "-0600"); - date = date.Replace("mst", "-0700"); - date = date.Replace("pdt", "-0700"); - date = date.Replace("pst", "-0800"); - date = date.Replace("bst", "+0100"); - //----------------------------------------// - - //--- Replace month constants ---// - date = date.Replace("jan", "01"); - date = date.Replace("feb", "02"); - date = date.Replace("mar", "03"); - date = date.Replace("apr", "04"); - date = date.Replace("may", "05"); - date = date.Replace("jun", "06"); - date = date.Replace("jul", "07"); - date = date.Replace("aug", "08"); - date = date.Replace("sep", "09"); - date = date.Replace("oct", "10"); - date = date.Replace("nov", "11"); - date = date.Replace("dec", "12"); - //-------------------------------// - - // If date contains optional "day-of-week,", remove it - if (date.IndexOf(',') > -1) - { - date = date.Substring(date.IndexOf(',') + 1); - } - - // Remove () from date. "Mon, 13 Oct 2003 20:50:57 +0300 (EEST)" - if (date.IndexOf(" (") > -1) - { - date = date.Substring(0, date.IndexOf(" (")); - } - - int year = 1900; - int month = 1; - int day = 1; - int hour = -1; - int minute = -1; - int second = -1; - int zoneMinutes = -1; - - StringReader s = new StringReader(date); - - //--- Pase date --------------------------------------------------------------------// - try - { - day = Convert.ToInt32(s.ReadWord(true, new[] {'.', '-', ' '}, true)); - } - catch - { - throw new Exception("Invalid date value '" + date + "', invalid day value !"); - } - - try - { - month = Convert.ToInt32(s.ReadWord(true, new[] {'.', '-', ' '}, true)); - } - catch - { - throw new Exception("Invalid date value '" + date + "', invalid month value !"); - } - - try - { - year = Convert.ToInt32(s.ReadWord(true, new[] {'.', '-', ' '}, true)); - } - catch - { - throw new Exception("Invalid date value '" + date + "', invalid year value !"); - } - //----------------------------------------------------------------------------------// - - //--- Parse time -------------------------------------------------------------------// - // Time is optional, so parse it if its included. - if (s.Available > 0) - { - try - { - hour = Convert.ToInt32(s.ReadWord(true, new[] {':'}, true)); - } - catch - { - throw new Exception("Invalid date value '" + date + "', invalid hour value !"); - } - - try - { - minute = Convert.ToInt32(s.ReadWord(true, new[] {':'}, false)); - } - catch - { - throw new Exception("Invalid date value '" + date + "', invalid minute value !"); - } - - s.ReadToFirstChar(); - if (s.StartsWith(":")) - { - s.ReadSpecifiedLength(1); - try - { - string secondString = s.ReadWord(true, new[] {' '}, true); - // Milli seconds specified, remove them. - if (secondString.IndexOf('.') > -1) - { - secondString = secondString.Substring(0, secondString.IndexOf('.')); - } - second = Convert.ToInt32(secondString); - } - catch - { - throw new Exception("Invalid date value '" + date + "', invalid second value !"); - } - } - - s.ReadToFirstChar(); - if (s.Available > 3) - { - string timezone = s.SourceString.Replace(":", ""); - if (timezone.StartsWith("+") || timezone.StartsWith("-")) - { - bool utc_add_time = timezone.StartsWith("+"); - - // Remove +/- sign - timezone = timezone.Substring(1); - - // padd time zone to 4 symbol. For example 200, will be 0200. - while (timezone.Length < 4) - { - timezone = "0" + timezone; - } - - try - { - // time zone format hours|minutes - int h = Convert.ToInt32(timezone.Substring(0, 2)); - int m = Convert.ToInt32(timezone.Substring(2)); - - if (utc_add_time) - { - zoneMinutes = 0 - ((h*60) + m); - } - else - { - zoneMinutes = (h*60) + m; - } - } - catch - { - // Just skip time zone, if can't parse - } - } - } - } - //---------------------------------------------------------------------------------// - - // Convert time to UTC - if (hour != -1 && minute != -1 && second != -1) - { - DateTime d = new DateTime(year, month, day, hour, minute, second).AddMinutes(zoneMinutes); - return - new DateTime(d.Year, d.Month, d.Day, d.Hour, d.Minute, d.Second, DateTimeKind.Utc). - ToLocalTime(); - } - else - { - return new DateTime(year, month, day); - } - } - - /// - /// Converts date to rfc 2822 date time string. - /// - /// Date time value. - /// - public static string DateTimeToRfc2822(DateTime dateTime) - { - return dateTime.ToUniversalTime().ToString("r", DateTimeFormatInfo.InvariantInfo); - } - - /// - /// Parses headers from message or mime entry. - /// - /// Stream from where to read headers. - /// Returns header lines. - public static string ParseHeaders(Stream entryStrm) - { - /* Rfc 2822 3.1. GENERAL DESCRIPTION - A message consists of header fields and, optionally, a body. - The body is simply a sequence of lines containing ASCII charac- - ters. It is separated from the headers by a null line (i.e., a - line with nothing preceding the CRLF). - */ - - byte[] crlf = new[] {(byte) '\r', (byte) '\n'}; - MemoryStream msHeaders = new MemoryStream(); - StreamLineReader r = new StreamLineReader(entryStrm); - byte[] lineData = r.ReadLine(); - while (lineData != null) - { - if (lineData.Length == 0) - { - break; - } - - msHeaders.Write(lineData, 0, lineData.Length); - msHeaders.Write(crlf, 0, crlf.Length); - lineData = r.ReadLine(); - } - - return Encoding.Default.GetString(msHeaders.ToArray()); - } - - /// - /// Parse header specified header field value. - /// - /// Use this method only if you need to get only one header field, otherwise use - /// MimeParser.ParseHeaderField(string fieldName,string headers). - /// This avoid parsing headers multiple times. - /// - /// Header field which to parse. Eg. Subject: . - /// Stream from where to read headers. - /// - public static string ParseHeaderField(string fieldName, Stream entryStrm) - { - return ParseHeaderField(fieldName, ParseHeaders(entryStrm)); - } - - /// - /// Parse header specified header field value. - /// - /// Header field which to parse. Eg. Subject: . - /// Full headers string. Use MimeParser.ParseHeaders() to get this value. - public static string ParseHeaderField(string fieldName, string headers) - { - /* Rfc 2822 2.2 Header Fields - Header fields are lines composed of a field name, followed by a colon - (":"), followed by a field body, and terminated by CRLF. A field - name MUST be composed of printable US-ASCII characters (i.e., - characters that have values between 33 and 126, inclusive), except - colon. A field body may be composed of any US-ASCII characters, - except for CR and LF. However, a field body may contain CRLF when - used in header "folding" and "unfolding" as described in section - 2.2.3. All field bodies MUST conform to the syntax described in - sections 3 and 4 of this standard. - - Rfc 2822 2.2.3 (Multiline header fields) - The process of moving from this folded multiple-line representation - of a header field to its single line representation is called - "unfolding". Unfolding is accomplished by simply removing any CRLF - that is immediately followed by WSP. Each header field should be - treated in its unfolded form for further syntactic and semantic - evaluation. - - Example: - Subject: aaaaa - aaaaa - */ - - using (TextReader r = new StreamReader(new MemoryStream(Encoding.Default.GetBytes(headers)))) - { - string line = r.ReadLine(); - while (line != null) - { - // Find line where field begins - if (line.ToUpper().StartsWith(fieldName.ToUpper())) - { - // Remove field name and start reading value - string fieldValue = line.Substring(fieldName.Length).Trim(); - - // see if multi line value. See commnt above. - line = r.ReadLine(); - while (line != null && (line.StartsWith("\t") || line.StartsWith(" "))) - { - fieldValue += line; - line = r.ReadLine(); - } - - return fieldValue; - } - - line = r.ReadLine(); - } - } - - return ""; - } - - /// - /// Parses header field parameter value. - /// For example: CONTENT-TYPE: application\octet-stream; name="yourFileName.xxx", - /// fieldName="CONTENT-TYPE:" and subFieldName="name". - /// - /// Main header field name. - /// Header field's parameter name. - /// Full headrs string. - /// - public static string ParseHeaderFiledParameter(string fieldName, string parameterName, string headers) - { - string mainFiled = ParseHeaderField(fieldName, headers); - // Parse sub field value - if (mainFiled.Length > 0) - { - int index = mainFiled.ToUpper().IndexOf(parameterName.ToUpper()); - if (index > -1) - { - mainFiled = mainFiled.Substring(index + parameterName.Length + 1); - // Remove "subFieldName=" - - // subFieldName value may be in "" and without - if (mainFiled.StartsWith("\"")) - { - return mainFiled.Substring(1, mainFiled.IndexOf("\"", 1) - 1); - } - // value without "" - else - { - int endIndex = mainFiled.Length; - if (mainFiled.IndexOf(" ") > -1) - { - endIndex = mainFiled.IndexOf(" "); - } - - return mainFiled.Substring(0, endIndex); - } - } - } - - return ""; - } - - /// - /// Parses MediaType_enum from Content-Type: header field value. - /// - /// Content-Type: header field value. This value can be null, then MediaType_enum.NotSpecified. - /// - public static MediaType_enum ParseMediaType(string headerFieldValue) - { - if (headerFieldValue == null) - { - return MediaType_enum.NotSpecified; - } - - string contentType = TextUtils.SplitString(headerFieldValue, ';')[0].ToLower(); - //--- Text/xxx --------------------------------// - if (contentType.IndexOf("text/plain") > -1) - { - return MediaType_enum.Text_plain; - } - else if (contentType.IndexOf("text/html") > -1) - { - return MediaType_enum.Text_html; - } - else if (contentType.IndexOf("text/xml") > -1) - { - return MediaType_enum.Text_xml; - } - else if (contentType.IndexOf("text/rtf") > -1) - { - return MediaType_enum.Text_rtf; - } - else if (contentType.IndexOf("text") > -1) - { - return MediaType_enum.Text; - } - //---------------------------------------------// - - //--- Image/xxx -------------------------------// - else if (contentType.IndexOf("image/gif") > -1) - { - return MediaType_enum.Image_gif; - } - else if (contentType.IndexOf("image/tiff") > -1) - { - return MediaType_enum.Image_tiff; - } - else if (contentType.IndexOf("image/jpeg") > -1) - { - return MediaType_enum.Image_jpeg; - } - else if (contentType.IndexOf("image") > -1) - { - return MediaType_enum.Image; - } - //---------------------------------------------// - - //--- Audio/xxx -------------------------------// - else if (contentType.IndexOf("audio") > -1) - { - return MediaType_enum.Audio; - } - //---------------------------------------------// - - //--- Video/xxx -------------------------------// - else if (contentType.IndexOf("video") > -1) - { - return MediaType_enum.Video; - } - //---------------------------------------------// - - //--- Application/xxx -------------------------// - else if (contentType.IndexOf("application/octet-stream") > -1) - { - return MediaType_enum.Application_octet_stream; - } - else if (contentType.IndexOf("application") > -1) - { - return MediaType_enum.Application; - } - //---------------------------------------------// - - //--- Multipart/xxx ---------------------------// - else if (contentType.IndexOf("multipart/mixed") > -1) - { - return MediaType_enum.Multipart_mixed; - } - else if (contentType.IndexOf("multipart/alternative") > -1) - { - return MediaType_enum.Multipart_alternative; - } - else if (contentType.IndexOf("multipart/parallel") > -1) - { - return MediaType_enum.Multipart_parallel; - } - else if (contentType.IndexOf("multipart/related") > -1) - { - return MediaType_enum.Multipart_related; - } - else if (contentType.IndexOf("multipart/signed") > -1) - { - return MediaType_enum.Multipart_signed; - } - else if (contentType.IndexOf("multipart") > -1) - { - return MediaType_enum.Multipart; - } - //---------------------------------------------// - - //--- Message/xxx -----------------------------// - else if (contentType.IndexOf("message/rfc822") > -1) - { - return MediaType_enum.Message_rfc822; - } - else if (contentType.IndexOf("message") > -1) - { - return MediaType_enum.Message; - } - //---------------------------------------------// - - else - { - return MediaType_enum.Unknown; - } - } - - /// - /// Converts MediaType_enum to string. NOTE: Returns null for MediaType_enum.NotSpecified. - /// - /// MediaType_enum value to convert. - /// - public static string MediaTypeToString(MediaType_enum mediaType) - { - //--- Text/xxx --------------------------------// - if (mediaType == MediaType_enum.Text_plain) - { - return "text/plain"; - } - else if (mediaType == MediaType_enum.Text_html) - { - return "text/html"; - } - else if (mediaType == MediaType_enum.Text_xml) - { - return "text/xml"; - } - else if (mediaType == MediaType_enum.Text_rtf) - { - return "text/rtf"; - } - else if (mediaType == MediaType_enum.Text) - { - return "text"; - } - //---------------------------------------------// - - //--- Image/xxx -------------------------------// - else if (mediaType == MediaType_enum.Image_gif) - { - return "image/gif"; - } - else if (mediaType == MediaType_enum.Image_tiff) - { - return "image/tiff"; - } - else if (mediaType == MediaType_enum.Image_jpeg) - { - return "image/jpeg"; - } - else if (mediaType == MediaType_enum.Image) - { - return "image"; - } - //---------------------------------------------// - - //--- Audio/xxx -------------------------------// - else if (mediaType == MediaType_enum.Audio) - { - return "audio"; - } - //---------------------------------------------// - - //--- Video/xxx -------------------------------// - else if (mediaType == MediaType_enum.Video) - { - return "video"; - } - //---------------------------------------------// - - //--- Application/xxx -------------------------// - else if (mediaType == MediaType_enum.Application_octet_stream) - { - return "application/octet-stream"; - } - else if (mediaType == MediaType_enum.Application) - { - return "application"; - } - //---------------------------------------------// - - //--- Multipart/xxx ---------------------------// - else if (mediaType == MediaType_enum.Multipart_mixed) - { - return "multipart/mixed"; - } - else if (mediaType == MediaType_enum.Multipart_alternative) - { - return "multipart/alternative"; - } - else if (mediaType == MediaType_enum.Multipart_parallel) - { - return "multipart/parallel"; - } - else if (mediaType == MediaType_enum.Multipart_related) - { - return "multipart/related"; - } - else if (mediaType == MediaType_enum.Multipart_signed) - { - return "multipart/signed"; - } - else if (mediaType == MediaType_enum.Multipart) - { - return "multipart"; - } - //---------------------------------------------// - - //--- Message/xxx -----------------------------// - else if (mediaType == MediaType_enum.Message_rfc822) - { - return "message/rfc822"; - } - else if (mediaType == MediaType_enum.Message) - { - return "message"; - } - //---------------------------------------------// - - else if (mediaType == MediaType_enum.Unknown) - { - return "unknown"; - } - else - { - return null; - } - } - - /// - /// Parses ContentTransferEncoding_enum from Content-Transfer-Encoding: header field value. - /// - /// Content-Transfer-Encoding: header field value. This value can be null, then ContentTransferEncoding_enum.NotSpecified. - /// - public static ContentTransferEncoding_enum ParseContentTransferEncoding(string headerFieldValue) - { - if (headerFieldValue == null) - { - return ContentTransferEncoding_enum.NotSpecified; - } - - string encoding = headerFieldValue.ToLower(); - if (encoding == "7bit") - { - return ContentTransferEncoding_enum._7bit; - } - else if (encoding == "quoted-printable") - { - return ContentTransferEncoding_enum.QuotedPrintable; - } - else if (encoding == "base64") - { - return ContentTransferEncoding_enum.Base64; - } - else if (encoding == "8bit") - { - return ContentTransferEncoding_enum._8bit; - } - else if (encoding == "binary") - { - return ContentTransferEncoding_enum.Binary; - } - else - { - return ContentTransferEncoding_enum.Unknown; - } - } - - /// - /// Converts ContentTransferEncoding_enum to string. NOTE: Returns null for ContentTransferEncoding_enum.NotSpecified. - /// - /// ContentTransferEncoding_enum value to convert. - /// - public static string ContentTransferEncodingToString(ContentTransferEncoding_enum encoding) - { - if (encoding == ContentTransferEncoding_enum._7bit) - { - return "7bit"; - } - else if (encoding == ContentTransferEncoding_enum.QuotedPrintable) - { - return "quoted-printable"; - } - else if (encoding == ContentTransferEncoding_enum.Base64) - { - return "base64"; - } - else if (encoding == ContentTransferEncoding_enum._8bit) - { - return "8bit"; - } - else if (encoding == ContentTransferEncoding_enum.Binary) - { - return "binary"; - } - else if (encoding == ContentTransferEncoding_enum.Unknown) - { - return "unknown"; - } - else - { - return null; - } - } - - /// - /// Parses ContentDisposition_enum from Content-Disposition: header field value. - /// - /// Content-Disposition: header field value. This value can be null, then ContentDisposition_enum.NotSpecified. - /// - public static ContentDisposition_enum ParseContentDisposition(string headerFieldValue) - { - if (headerFieldValue == null) - { - return ContentDisposition_enum.NotSpecified; - } - - string disposition = headerFieldValue.ToLower(); - if (disposition.IndexOf("attachment") > -1) - { - return ContentDisposition_enum.Attachment; - } - else if (disposition.IndexOf("inline") > -1) - { - return ContentDisposition_enum.Inline; - } - else - { - return ContentDisposition_enum.Unknown; - } - } - - /// - /// Converts ContentDisposition_enum to string. NOTE: Returns null for ContentDisposition_enum.NotSpecified. - /// - /// ContentDisposition_enum value to convert. - /// - public static string ContentDispositionToString(ContentDisposition_enum disposition) - { - if (disposition == ContentDisposition_enum.Attachment) - { - return "attachment"; - } - else if (disposition == ContentDisposition_enum.Inline) - { - return "inline"; - } - else if (disposition == ContentDisposition_enum.Unknown) - { - return "unknown"; - } - else - { - return null; - } - } - - /// - /// Encodes specified text as "encoded-word" if encode is required. For more information see RFC 2047. - /// - /// Text to encode. - /// Returns encoded word. - public static string EncodeWord(string text) - { - if (text == null) - { - return null; - } - if (Core.IsAscii(text)) - { - return text; - } - - /* RFC 2047 2. Syntax of encoded-words. - An 'encoded-word' is defined by the following ABNF grammar. The - notation of RFC 822 is used, with the exception that white space - characters MUST NOT appear between components of an 'encoded-word'. - - encoded-word = "=?" charset "?" encoding "?" encoded-text "?=" - charset = token ; see section 3 - encoding = token ; see section 4 - token = 1* - especials = "(" / ")" / "<" / ">" / "@" / "," / ";" / ":" / " - <"> / "/" / "[" / "]" / "?" / "." / "=" - encoded-text = 1* - ; (but see "Use of encoded-words in message headers", section 5) - - Both 'encoding' and 'charset' names are case-independent. Thus the - charset name "ISO-8859-1" is equivalent to "iso-8859-1", and the - encoding named "Q" may be spelled either "Q" or "q". - - An 'encoded-word' may not be more than 75 characters long, including - 'charset', 'encoding', 'encoded-text', and delimiters. If it is - desirable to encode more text than will fit in an 'encoded-word' of - 75 characters, multiple 'encoded-word's (separated by CRLF SPACE) may - be used. - - IMPORTANT: 'encoded-word's are designed to be recognized as 'atom's - by an RFC 822 parser. As a consequence, unencoded white space - characters (such as SPACE and HTAB) are FORBIDDEN within an - 'encoded-word'. For example, the character sequence - - =?iso-8859-1?q?this is some text?= - - would be parsed as four 'atom's, rather than as a single 'atom' (by - an RFC 822 parser) or 'encoded-word' (by a parser which understands - 'encoded-words'). The correct way to encode the string "this is some - text" is to encode the SPACE characters as well, e.g. - - =?iso-8859-1?q?this=20is=20some=20text?= - */ - /* - char[] especials = new char[]{'(',')','<','>','@',',',';',':','"','/','[',']','?','.','='}; - - // See if need to enode. - if(!Core.IsAscii(text)){ - }*/ - - return Core.CanonicalEncode(text, "utf-8"); - } - - /// - /// Decodes "encoded-word"'s from the specified text. For more information see RFC 2047. - /// - /// Text to decode. - /// Returns decoded text. - public static string DecodeWords(string text) - { - if (text == null) - { - return null; - } - - /* RFC 2047 2. Syntax of encoded-words. - An 'encoded-word' is defined by the following ABNF grammar. The - notation of RFC 822 is used, with the exception that white space - characters MUST NOT appear between components of an 'encoded-word'. - - encoded-word = "=?" charset "?" encoding "?" encoded-text "?=" - charset = token ; see section 3 - encoding = token ; see section 4 - token = 1* - especials = "(" / ")" / "<" / ">" / "@" / "," / ";" / ":" / " - <"> / "/" / "[" / "]" / "?" / "." / "=" - encoded-text = 1* - ; (but see "Use of encoded-words in message headers", section 5) - - Both 'encoding' and 'charset' names are case-independent. Thus the - charset name "ISO-8859-1" is equivalent to "iso-8859-1", and the - encoding named "Q" may be spelled either "Q" or "q". - - An 'encoded-word' may not be more than 75 characters long, including - 'charset', 'encoding', 'encoded-text', and delimiters. If it is - desirable to encode more text than will fit in an 'encoded-word' of - 75 characters, multiple 'encoded-word's (separated by CRLF SPACE) may - be used. - - IMPORTANT: 'encoded-word's are designed to be recognized as 'atom's - by an RFC 822 parser. As a consequence, unencoded white space - characters (such as SPACE and HTAB) are FORBIDDEN within an - 'encoded-word'. For example, the character sequence - - =?iso-8859-1?q?this is some text?= - - would be parsed as four 'atom's, rather than as a single 'atom' (by - an RFC 822 parser) or 'encoded-word' (by a parser which understands - 'encoded-words'). The correct way to encode the string "this is some - text" is to encode the SPACE characters as well, e.g. - - =?iso-8859-1?q?this=20is=20some=20text?= - */ - - StringReader r = new StringReader(text); - StringBuilder retVal = new StringBuilder(); - - // We need to loop all words, if encoded word, decode it, othwerwise just append to return value. - bool lastIsEncodedWord = false; - while (r.Available > 0) - { - string whiteSpaces = r.ReadToFirstChar(); - - // Probably is encoded-word, we try to parse it. - if (r.StartsWith("=?") && r.SourceString.IndexOf("?=") > -1) - { - StringBuilder encodedWord = new StringBuilder(); - string decodedWord = null; - - try - { - // NOTE: We can't read encoded word and then split !!!, we need to read each part. - - // Remove =? - encodedWord.Append(r.ReadSpecifiedLength(2)); - - // Read charset - string charset = r.QuotedReadToDelimiter('?'); - encodedWord.Append(charset + "?"); - - // Read encoding - string encoding = r.QuotedReadToDelimiter('?'); - encodedWord.Append(encoding + "?"); - - // Read text - string encodedText = r.QuotedReadToDelimiter('?'); - encodedWord.Append(encodedText + "?"); - - // We must have remaining '=' here - if (r.StartsWith("=")) - { - encodedWord.Append(r.ReadSpecifiedLength(1)); - - Encoding c = EncodingTools.GetEncodingByCodepageName(charset); - if (c != null) - { - if (encoding.ToLower() == "q") - { - decodedWord = Core.QDecode(c, encodedText); - } - else if (encoding.ToLower() == "b") - { - decodedWord = - c.GetString(Core.Base64Decode(Encoding.Default.GetBytes(encodedText))); - } - } - } - } - catch - { - // Not encoded-word or contains unknwon charset/encoding, so leave - // encoded-word as is. - } - - /* RFC 2047 6.2. - When displaying a particular header field that contains multiple - 'encoded-word's, any 'linear-white-space' that separates a pair of - adjacent 'encoded-word's is ignored. (This is to allow the use of - multiple 'encoded-word's to represent long strings of unencoded text, - without having to separate 'encoded-word's where spaces occur in the - unencoded text.) - */ - if (!lastIsEncodedWord) - { - retVal.Append(whiteSpaces); - } - - // Decoding failed for that encoded-word, leave encoded-word as is. - if (decodedWord == null) - { - retVal.Append(encodedWord.ToString()); - } - // We deocded encoded-word successfully. - else - { - retVal.Append(decodedWord); - } - - lastIsEncodedWord = true; - } - // Normal word. - else if (r.StartsWithWord()) - { - retVal.Append(whiteSpaces + r.ReadWord(false)); - lastIsEncodedWord = false; - } - // We have some separator or parenthesize. - else - { - retVal.Append(whiteSpaces + r.ReadSpecifiedLength(1)); - } - } - - return retVal.ToString(); - } - - /// - /// Encodes header field with quoted-printable encoding, if value contains ANSI or UNICODE chars. - /// - /// Text to encode. - /// - public static string EncodeHeaderField(string text) - { - if (Core.IsAscii(text)) - { - return text; - } - - // First try to encode quoted strings("unicode-text") only, if no - // quoted strings, encode full text. - - if (text.IndexOf("\"") > -1) - { - string retVal = text; - int offset = 0; - while (offset < retVal.Length - 1) - { - int quoteStartIndex = retVal.IndexOf("\"", offset); - // There is no more qouted strings, but there are some text left - if (quoteStartIndex == -1) - { - break; - } - int quoteEndIndex = retVal.IndexOf("\"", quoteStartIndex + 1); - // If there isn't closing quote, encode full text - if (quoteEndIndex == -1) - { - break; - } - - string leftPart = retVal.Substring(0, quoteStartIndex); - string rightPart = retVal.Substring(quoteEndIndex + 1); - string quotedString = retVal.Substring(quoteStartIndex + 1, - quoteEndIndex - quoteStartIndex - 1); - - // Encode only not ASCII text - if (!Core.IsAscii(quotedString)) - { - string quotedStringCEncoded = Core.CanonicalEncode(quotedString, "utf-8"); - retVal = leftPart + "\"" + quotedStringCEncoded + "\"" + rightPart; - offset += quoteEndIndex + 1 + quotedStringCEncoded.Length - quotedString.Length; - } - else - { - offset += quoteEndIndex + 1; - } - } - - // See if all encoded ok, if not encode all text - if (Core.IsAscii(retVal)) - { - return retVal; - } - else - { - // REMOVE ME:(12.10.2006) Fixed, return Core.CanonicalEncode(retVal,"utf-8"); - return Core.CanonicalEncode(text, "utf-8"); - } - } - - return Core.CanonicalEncode(text, "utf-8"); - } - - /// - /// Creates Rfc 2822 3.6.4 message-id. Syntax: '<' id-left '@' id-right '>'. - /// - /// - public static string CreateMessageID() - { - return "<" + Guid.NewGuid().ToString().Replace("-", "") + "@" + - Guid.NewGuid().ToString().Replace("-", "") + ">"; - } - - /// - /// Folds long data line to folded lines. - /// - /// Data to fold. - /// - public static string FoldData(string data) - { - /* Folding rules: - *) Line may not be bigger than 76 chars. - *) If possible fold between TAB or SP - *) If no fold point, just fold from char 76 - */ - - // Data line too big, we need to fold data. - if (data.Length > 76) - { - int startPosition = 0; - int lastPossibleFoldPos = -1; - StringBuilder retVal = new StringBuilder(); - for (int i = 0; i < data.Length; i++) - { - char c = data[i]; - // We have possible fold point - if (c == ' ' || c == '\t') - { - lastPossibleFoldPos = i; - } - - // End of data reached - if (i == (data.Length - 1)) - { - retVal.Append(data.Substring(startPosition)); - } - // We need to fold - else if ((i - startPosition) >= 76) - { - // There wasn't any good fold point(word is bigger than line), just fold from current position. - if (lastPossibleFoldPos == -1) - { - lastPossibleFoldPos = i; - } - - retVal.Append(data.Substring(startPosition, lastPossibleFoldPos - startPosition) + - "\r\n\t"); - - i = lastPossibleFoldPos; - lastPossibleFoldPos = -1; - startPosition = i; - } - } - - return retVal.ToString(); - } - else - { - return data; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/ParametizedHeaderField.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/ParametizedHeaderField.cs deleted file mode 100644 index c0fe18d97..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/MIME/ParametizedHeaderField.cs +++ /dev/null @@ -1,152 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime -{ - #region usings - - using System; - using System.Collections; - - #endregion - - /// - /// Parametized header field. - ///

    - /// Syntax: value;parameterName=parameterValue;parameterName=parameterValue;... . - /// Example: (Content-Type:) text/html; charset="ascii". - ///

    - [Obsolete("See LumiSoft.Net.MIME or LumiSoft.Net.Mail namepaces for replacement.")] - public class ParametizedHeaderField - { - #region Members - - private readonly HeaderField m_pHeaderField; - private readonly HeaderFieldParameterCollection m_pParameters; - - #endregion - - #region Properties - - /// - /// Gets header field name. - /// - public string Name - { - get { return m_pHeaderField.Name; } - } - - /// - /// Gets or sets header field value. - /// - public string Value - { - get - { - // Syntax: value;parameterName=parameterValue;parameterName=parameterValue;... ; - // First item is value - return TextUtils.SplitQuotedString(m_pHeaderField.Value, ';')[0]; - } - - set { StoreParameters(value, ParseParameters()); } - } - - /// - /// Gets header field parameters. - /// - public HeaderFieldParameterCollection Parameters - { - get { return m_pParameters; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Source header field. - public ParametizedHeaderField(HeaderField headerField) - { - m_pHeaderField = headerField; - - m_pParameters = new HeaderFieldParameterCollection(this); - } - - #endregion - - #region Internal methods - - /// - /// Parses parameters from header field. - /// - /// - internal Hashtable ParseParameters() - { - // Syntax: value;parameterName=parameterValue;parameterName=parameterValue;... - string[] paramNameValues = TextUtils.SplitQuotedString(m_pHeaderField.EncodedValue, ';'); - - Hashtable retVal = new Hashtable(); - // Skip value, other entries are parameters - for (int i = 1; i < paramNameValues.Length; i++) - { - string[] paramNameValue = paramNameValues[i].Trim().Split(new[] {'='}, 2); - if (!retVal.ContainsKey(paramNameValue[0].ToLower())) - { - if (paramNameValue.Length == 2) - { - string value = paramNameValue[1]; - - // Quotes-string, unqoute. - if (value.StartsWith("\"")) - { - value = TextUtils.UnQuoteString(paramNameValue[1]); - } - - retVal.Add(paramNameValue[0].ToLower(), value); - } - else - { - retVal.Add(paramNameValue[0].ToLower(), ""); - } - } - } - - return retVal; - } - - /// - /// Stores parameters to header field Value property. - /// - /// - /// - internal void StoreParameters(string value, Hashtable parameters) - { - string retVal = value; - foreach (DictionaryEntry entry in parameters) - { - retVal += ";\t" + entry.Key + "=\"" + entry.Value + "\""; - } - - // Syntax: value;parameterName=parameterValue;parameterName=parameterValue;... ; - m_pHeaderField.Value = retVal; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/ReadException.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/ReadException.cs deleted file mode 100644 index a1ffb5bb7..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/ReadException.cs +++ /dev/null @@ -1,98 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - #region usings - - using System; - - #endregion - - #region public enum ReadReplyCode - - /// - /// Reply reading return codes. - /// - public enum ReadReplyCode - { - /// - /// Read completed successfully. - /// - Ok = 0, - - /// - /// Read timed out. - /// - TimeOut = 1, - - /// - /// Maximum allowed Length exceeded. - /// - LengthExceeded = 2, - - /// - /// Connected client closed connection. - /// - SocketClosed = 3, - - /// - /// UnKnown error, eception raised. - /// - UnKnownError = 4, - } - - #endregion - - /// - /// Summary description for ReadException. - /// - public class ReadException : Exception - { - #region Members - - private readonly ReadReplyCode m_ReadReplyCode; - - #endregion - - #region Properties - - /// - /// Gets read error. - /// - public ReadReplyCode ReadReplyCode - { - get { return m_ReadReplyCode; } - } - - #endregion - - #region Constructor - - /// - /// - /// - /// - /// - public ReadException(ReadReplyCode code, string message) : base(message) - { - m_ReadReplyCode = code; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/ReadLine_EventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/ReadLine_EventArgs.cs deleted file mode 100644 index 5a83147ef..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/ReadLine_EventArgs.cs +++ /dev/null @@ -1,191 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IO -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// This class proviedes data to asynchronous read line callback method. - /// NOTE: ReadLine_EventArgs is reused for next read line call, so don't store references to this class. - /// - public class ReadLine_EventArgs - { - #region Members - - private int m_Count; - private Exception m_pException; - private byte[] m_pLineBuffer; - private object m_pTag; - private int m_ReadedCount; - - #endregion - - #region Properties - - /// - /// Gets exception what happened while reading line. Returns null if read line completed sucessfully. - /// - public Exception Exception - { - get { return m_pException; } - } - - /// - /// Gets number of bytes actualy readed from source stream. Returns 0 if end of stream reached - /// and no more data. This value includes any readed byte, including line feed, ... . - /// - public int ReadedCount - { - get { return m_ReadedCount; } - } - - /// - /// Gets buffer argumnet what was passed to BeginReadLine mehtod. - /// - public byte[] LineBuffer - { - get { return m_pLineBuffer; } - } - - /// - /// Gets number of bytes stored to LineBuffer. - /// - public int Count - { - get { return m_Count; } - } - - /// - /// Gets readed line data or null if end of stream reached and no more data. - /// - public byte[] Data - { - get - { - byte[] retVal = new byte[m_Count]; - Array.Copy(m_pLineBuffer, retVal, m_Count); - - return retVal; - } - } - - /// - /// Gets readed line data as string with system default encoding or returns null if end of stream reached and no more data. - /// - public string DataStringDefault - { - get { return DataToString(Encoding.Default); } - } - - /// - /// Gets readed line data as string with ASCII encoding or returns null if end of stream reached and no more data. - /// - public string DataStringAscii - { - get { return DataToString(Encoding.ASCII); } - } - - /// - /// Gets readed line data as string with UTF8 encoding or returns null if end of stream reached and no more data. - /// - public string DataStringUtf8 - { - get { return DataToString(Encoding.UTF8); } - } - - /// - /// Gets tag argument what was pased to BeginReadLine method. - /// - public object Tag - { - get { return m_pTag; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - internal ReadLine_EventArgs() {} - - /// - /// Default constructor. - /// - /// Exception what happened while reading data or null if read line was successfull. - /// Specifies how many raw bytes was readed. - /// Line data buffer. - /// Specifies how many bytes stored to data. - /// User data. - internal ReadLine_EventArgs(Exception exception, int readedCount, byte[] data, int count, object tag) - { - m_pException = exception; - m_ReadedCount = readedCount; - m_pLineBuffer = data; - m_Count = count; - m_pTag = tag; - } - - #endregion - - #region Methods - - /// - /// Converts byte[] line data to the specified encoding string. - /// - /// Encoding to use for convert. - /// Returns line data as string. - public string DataToString(Encoding encoding) - { - if (encoding == null) - { - throw new ArgumentNullException("encoding"); - } - - if (m_pLineBuffer == null) - { - return null; - } - else - { - return encoding.GetString(m_pLineBuffer, 0, m_Count); - } - } - - #endregion - - #region Internal methods - - internal void Reuse(Exception exception, int readedCount, byte[] data, int count, object tag) - { - m_pException = exception; - m_ReadedCount = readedCount; - m_pLineBuffer = data; - m_Count = count; - m_pTag = tag; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/ReadToStream_EventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/ReadToStream_EventArgs.cs deleted file mode 100644 index d215728b1..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/ReadToStream_EventArgs.cs +++ /dev/null @@ -1,167 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IO -{ - #region usings - - using System; - using System.IO; - using System.Text; - - #endregion - - /// - /// This class provides data to asynchronous read to stream methods callback. - /// - public class ReadToStream_EventArgs - { - #region Members - - private readonly int m_Count; - private readonly Exception m_pException; - private readonly Stream m_pStream; - - #endregion - - #region Properties - - /// - /// Gets exception what happened while reading data. Returns null if data reading completed sucessfully. - /// - public Exception Exception - { - get { return m_pException; } - } - - /// - /// Gets stream where data is stored. - /// - public Stream Stream - { - get { return m_pStream; } - } - - /// - /// Gets number of bytes readed and written to Stream. - /// - public int Count - { - get { return m_Count; } - } - - /// - /// Gets readed data. NOTE: This property is available only is Stream supports seeking ! - /// - public byte[] Data - { - get - { - if (!m_pStream.CanSeek) - { - throw new InvalidOperationException("Underlaying stream won't support seeking !"); - } - - long currentPos = m_pStream.Position; - m_pStream.Position = 0; - byte[] data = new byte[m_pStream.Length]; - m_pStream.Read(data, 0, data.Length); - - return data; - } - } - - /// - /// Gets readed line data as string with system default encoding. - /// NOTE: This property is available only is Stream supports seeking ! - /// - public string DataStringDefault - { - get { return DataToString(Encoding.Default); } - } - - /// - /// Gets readed line data as string with ASCII encoding. - /// NOTE: This property is available only is Stream supports seeking ! - /// - public string DataStringAscii - { - get { return DataToString(Encoding.ASCII); } - } - - /// - /// Gets readed line data as string with UTF8 encoding. - /// NOTE: This property is available only is Stream supports seeking ! - /// - public string DataStringUtf8 - { - get { return DataToString(Encoding.UTF8); } - } - - /// - /// Gets or stes user data. - /// - public object Tag { get; set; } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Exception what happened while reading data or null if data reading was successfull. - /// Stream where data was stored. - /// Number of bytes readed. - /// User data. - public ReadToStream_EventArgs(Exception exception, Stream stream, int count, object tag) - { - m_pException = exception; - m_pStream = stream; - m_Count = count; - Tag = tag; - } - - #endregion - - #region Methods - - /// - /// Converts byte[] line data to the specified encoding string. - /// - /// Encoding to use for convert. - /// Returns line data as string. - public string DataToString(Encoding encoding) - { - if (encoding == null) - { - throw new ArgumentNullException("encoding"); - } - - if (Data == null) - { - return null; - } - else - { - return encoding.GetString(Data); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SIP_RequestSender.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SIP_RequestSender.cs deleted file mode 100644 index f4453e96d..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SIP_RequestSender.cs +++ /dev/null @@ -1,115 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.Text; - -namespace LumiSoft.Net.SIP.Stack -{ - /// - /// This class is responsible for sending request to one of the hops. - /// Hops are tried one by one until a server is contacted. - /// - public class SIP_RequestSender - { - private bool m_IsStarted = false; - private SIP_Stack m_pStack = null; - private SIP_Request m_pRequest = null; - private Queue m_pHops = null; - private int m_Timeout = 15; - private SIP_ClientTransaction m_pTransaction = null; - - /// - /// Default constructor. - /// - /// SIP stack. - /// SIP request. - /// Priority ordered "hops" where to send request. - /// Is raised when stack,request or hops is null reference. - /// Is raised when any of the arguments has invalid value. - public SIP_RequestSender(SIP_Stack stack,SIP_Request request,Queue hops) - { - if(stack == null){ - throw new ArgumentNullException("stack"); - } - if(request == null){ - throw new ArgumentNullException("request"); - } - if(hops == null){ - throw new ArgumentNullException("hops"); - } - if(hops.Count == 0){ - throw new ArgumentException("There must be at least 1 hop in 'hops' queue."); - } - - m_pStack = stack; - m_pRequest = request; - m_pHops = hops; - } - - - #region method Start - - /// - /// Starts sending request. - /// - public void Start() - { - if(m_IsStarted){ - throw new InvalidOperationException("Request sending is already started."); - } - m_IsStarted = true; - - // RFC 3261 8.1.2 and . 16.6. We need to create new client transaction for each attempt. - } - - #endregion - - #region method Cancel - - /// - /// Cancels request sending. - /// - public void Cancel() - { - } - - #endregion - - - #region Properties implementation - - /// - /// Gets SIP request what this sender is responsible for. - /// - public SIP_Request Request - { - get{ return m_pRequest; } - } - - #endregion - - #region Events handling - - // public event EventHandler NewTransaction - // public event EventHandler - - #endregion - - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SIP_Target.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SIP_Target.cs deleted file mode 100644 index 4a68eff2b..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SIP_Target.cs +++ /dev/null @@ -1,129 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.Text; - -namespace LumiSoft.Net.SIP.Stack -{ - /// - /// This class represents remote SIP target info. - /// - public class SIP_Target - { - private SIP_EndPointInfo m_pLocalEndPoint = null; - private string m_Transport = SIP_Transport.UDP; - private string m_Host = ""; - private int m_Port = 5060; - - /// - /// Default constructor. - /// - /// Transport to use. - /// Host name or IP. - /// Host port. - public SIP_Target(string transport,string host,int port) - { - m_Transport = transport; - m_Host = host; - m_Port = port; - } - - /// - /// Default constructor. - /// - /// Remote destination URI. - public SIP_Target(SIP_Uri destination) : this(null,destination) - { - } - - /// - /// Default constructor. - /// - /// Local end point to use to connect remote destination. - /// Remote destination URI. - public SIP_Target(SIP_EndPointInfo localEP,SIP_Uri destination) - { - m_pLocalEndPoint = localEP; - - if(destination.Param_Transport != null){ - m_Transport = destination.Param_Transport; - } - else{ - // If SIPS, use always TLS. - if(destination.IsSecure){ - m_Transport = SIP_Transport.TLS; - } - else{ - m_Transport = SIP_Transport.UDP; - } - } - m_Host = destination.Host; - if(destination.Port != -1){ - m_Port = destination.Port; - } - else{ - if(m_Transport == SIP_Transport.TLS){ - m_Port = 5061; - } - else{ - m_Port = 5060; - } - } - } - - - #region Properties Implementation - - /// - /// Gets local destination to use to connect remote destination. - /// Value null means not specified. NOTE This is used only by UDP. - /// - public SIP_EndPointInfo LocalEndPoint - { - get{ return m_pLocalEndPoint; } - } - - /// - /// Gets transport to use. - /// - public string Transport - { - get{ return m_Transport; } - } - - /// - /// Gets remote host name or IP address. - /// - public string Host - { - get{ return m_Host; } - } - - /// - /// Gets remote host port. - /// - public int Port - { - get{ return m_Port; } - } - - #endregion - - } -} diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SaslAuthTypes.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SaslAuthTypes.cs deleted file mode 100644 index d44a81dc6..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SaslAuthTypes.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.AUTH -{ - /// - /// SASL authentications - /// - public enum SaslAuthTypes - { - /// - /// Non authentication - /// - None = 0, - - /// - /// Plain text authentication. For POP3 USER/PASS commands, for IMAP LOGIN command. - /// - Plain = 1, - - /// - /// LOGIN. - /// - Login = 2, - - /// - /// CRAM-MD5 - /// - Cram_md5 = 4, - - /// - /// DIGEST-MD5. - /// - Digest_md5 = 8, - - /// - /// All authentications. - /// - All = 0xF, - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SmtpClientEx.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SmtpClientEx.cs deleted file mode 100644 index 1d08a011d..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SmtpClientEx.cs +++ /dev/null @@ -1,2553 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.SMTP.Client -{ - #region usings - - using System; - using System.Collections; - using System.Collections.Generic; - using System.IO; - using System.Net; - using System.Net.Sockets; - using System.Security.Cryptography; - using System.Text; - using System.Threading; - using Dns.Client; - using Mime; - - #endregion - - /// - /// Is called when asynchronous command had completed. - /// - public delegate void CommadCompleted(SocketCallBackResult result, Exception exception); - - /// - /// SMTP client. - /// - [Obsolete("Use SMTP_Client instead !")] - public class SmtpClientEx : IDisposable - { - #region Nested type: Auth_state_data - - /// - /// Provides state date for BeginAuthenticate method. - /// - private class Auth_state_data - { - #region Members - - private readonly string m_Password = ""; - private readonly CommadCompleted m_pCallback; - private readonly string m_UserName = ""; - - #endregion - - #region Properties - - /// - /// Gets user name. - /// - public string UserName - { - get { return m_UserName; } - } - - /// - /// Gets user password. - /// - public string Password - { - get { return m_Password; } - } - - /// - /// Gets callback what must be called when aynchrounous execution completes. - /// - public CommadCompleted Callback - { - get { return m_pCallback; } - } - - /// - /// Gets or sets user data. - /// - public object Tag { get; set; } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// User name. - /// Password. - /// Callback what must be called when aynchrounous execution completes. - public Auth_state_data(string userName, string password, CommadCompleted callback) - { - m_UserName = userName; - m_Password = password; - m_pCallback = callback; - } - - #endregion - } - - #endregion - - #region Events - - /// - /// Occurs when SMTP session has finished and session log is available. - /// - public event LogEventHandler SessionLog = null; - - #endregion - - #region Members - - private bool m_Authenticated; - private bool m_Connected; - private string[] m_pDnsServers; - private SocketLogger m_pLogger; - private SocketEx m_pSocket; - private bool m_Supports_Bdat; - private bool m_Supports_CramMd5; - private bool m_Supports_Login; - private bool m_Supports_Size; - - #endregion - - #region Properties - - /// - /// Gets local endpoint. Returns null if smtp client isn't connected. - /// - public EndPoint LocalEndpoint - { - get - { - if (m_pSocket != null) - { - return m_pSocket.LocalEndPoint; - } - else - { - return null; - } - } - } - - /// - /// Gets remote endpoint. Returns null if smtp client isn't connected. - /// - public EndPoint RemoteEndPoint - { - get - { - if (m_pSocket != null) - { - return m_pSocket.RemoteEndPoint; - } - else - { - return null; - } - } - } - - /// - /// Gets or sets dns servers. - /// - public string[] DnsServers - { - get { return m_pDnsServers; } - - set { m_pDnsServers = value; } - } - - /// - /// Gets if smtp client is connected. - /// - public bool Connected - { - get { return m_Connected; } - } - - /// - /// Gets if pop3 client is authenticated. - /// - public bool Authenticated - { - get { return m_Authenticated; } - } - - /// - /// Gets when was last activity. - /// - public DateTime LastDataTime - { - get { return m_pSocket.LastActivity; } - } - - /// - /// Gets log entries that are currently in log buffer. Returns null if socket not connected or no logging enabled. - /// - public SocketLogger SessionActiveLog - { - get - { - if (m_pSocket == null) - { - return null; - } - else - { - return m_pSocket.Logger; - } - } - } - - /// - /// Gets how many bytes are readed through smtp client. - /// - public long ReadedCount - { - get - { - if (!m_Connected) - { - throw new Exception("You must connect first"); - } - - return m_pSocket.ReadedCount; - } - } - - /// - /// Gets how many bytes are written through smtp client. - /// - public long WrittenCount - { - get - { - if (!m_Connected) - { - throw new Exception("You must connect first"); - } - - return m_pSocket.WrittenCount; - } - } - - /// - /// Gets if the connection is an SSL connection. - /// - public bool IsSecureConnection - { - get - { - if (!m_Connected) - { - throw new Exception("You must connect first"); - } - - return m_pSocket.SSL; - } - } - - #endregion - - #region Methods - - /// - /// Sends specified message to specified smart host. - /// - /// Smarthost name or IP. - /// SMTP port number. Normally this is 25. - /// Host name reported to SMTP server. - /// Mime message to send. - public static void QuickSendSmartHost(string smartHost, int port, string hostName, Mime message) - { - QuickSendSmartHost(smartHost, port, hostName, "", "", message); - } - - /// - /// Sends specified message to specified smart host. - /// - /// Smarthost name or IP. - /// SMTP port number. Normally this is 25. - /// Host name reported to SMTP server. - /// SMTP user name. Note: Pass empty string if no authentication wanted. - /// SMTP password. - /// Mime message to send. - public static void QuickSendSmartHost(string smartHost, - int port, - string hostName, - string userName, - string password, - Mime message) - { - QuickSendSmartHost(smartHost, port, false, hostName, userName, password, message); - } - - /// - /// Sends specified message to specified smart host. - /// - /// Smarthost name or IP. - /// SMTP port number. Default SMTP port is 25 and SSL port is 465. - /// Specifies if to connected via SSL. - /// Host name reported to SMTP server. - /// SMTP user name. Note: Pass empty string if no authentication wanted. - /// SMTP password. - /// Mime message to send. - public static void QuickSendSmartHost(string smartHost, - int port, - bool ssl, - string hostName, - string userName, - string password, - Mime message) - { - string from = ""; - if (message.MainEntity.From != null) - { - MailboxAddress[] addresses = message.MainEntity.From.Mailboxes; - if (addresses.Length > 0) - { - from = addresses[0].EmailAddress; - } - } - - ArrayList recipients = new ArrayList(); - if (message.MainEntity.To != null) - { - MailboxAddress[] addresses = message.MainEntity.To.Mailboxes; - foreach (MailboxAddress address in addresses) - { - recipients.Add(address.EmailAddress); - } - } - if (message.MainEntity.Cc != null) - { - MailboxAddress[] addresses = message.MainEntity.Cc.Mailboxes; - foreach (MailboxAddress address in addresses) - { - recipients.Add(address.EmailAddress); - } - } - if (message.MainEntity.Bcc != null) - { - MailboxAddress[] addresses = message.MainEntity.Bcc.Mailboxes; - foreach (MailboxAddress address in addresses) - { - recipients.Add(address.EmailAddress); - } - } - string[] to = new string[recipients.Count]; - recipients.CopyTo(to); - - MemoryStream messageStream = new MemoryStream(); - message.ToStream(messageStream); - messageStream.Position = 0; - - // messageStream - QuickSendSmartHost(smartHost, port, ssl, hostName, userName, password, from, to, messageStream); - } - - /// - /// Sends specified message to specified smart host. NOTE: Message sending starts from message stream current posision. - /// - /// Smarthost name or IP. - /// SMTP port number. Normally this is 25. - /// Host name reported to SMTP server. - /// From address reported to SMTP server. - /// Message recipients. - /// Message stream. NOTE: Message sending starts from message stream current posision. - public static void QuickSendSmartHost(string smartHost, - int port, - string hostName, - string from, - string[] to, - Stream messageStream) - { - QuickSendSmartHost(smartHost, port, false, hostName, "", "", from, to, messageStream); - } - - /// - /// Sends specified message to specified smart host. NOTE: Message sending starts from message stream current posision. - /// - /// Smarthost name or IP. - /// SMTP port number. Normally this is 25. - /// Host name reported to SMTP server. - /// SMTP user name. Note: Pass empty string if no authentication wanted. - /// SMTP password. - /// From address reported to SMTP server. - /// Message recipients. - /// Message stream. NOTE: Message sending starts from message stream current posision. - public static void QuickSendSmartHost(string smartHost, - int port, - string hostName, - string userName, - string password, - string from, - string[] to, - Stream messageStream) - { - QuickSendSmartHost(smartHost, port, false, hostName, userName, password, from, to, messageStream); - } - - /// - /// Sends specified message to specified smart host. NOTE: Message sending starts from message stream current posision. - /// - /// Smarthost name or IP. - /// SMTP port number. Default SMTP port is 25 and SSL port is 465. - /// Specifies if to connected via SSL. - /// Host name reported to SMTP server. - /// SMTP user name. Note: Pass empty string if no authentication wanted. - /// SMTP password. - /// From address reported to SMTP server. - /// Message recipients. - /// Message stream. NOTE: Message sending starts from message stream current posision. - public static void QuickSendSmartHost(string smartHost, - int port, - bool ssl, - string hostName, - string userName, - string password, - string from, - string[] to, - Stream messageStream) - { - using (SmtpClientEx smtp = new SmtpClientEx()) - { - smtp.Connect(smartHost, port, ssl); - smtp.Ehlo(hostName); - if (userName.Length > 0) - { - smtp.Authenticate(userName, password); - } - smtp.SetSender(MailboxAddress.Parse(from).EmailAddress, - messageStream.Length - messageStream.Position); - foreach (string t in to) - { - smtp.AddRecipient(MailboxAddress.Parse(t).EmailAddress); - } - - smtp.SendMessage(messageStream); - } - } - - /// - /// Cleasns up resources and disconnect smtp client if open. - /// - public void Dispose() - { - try - { - Disconnect(); - } - catch {} - } - - /// - /// Connects to sepcified host. - /// - /// Host name or IP address. - /// Port where to connect. - public void Connect(string host, int port) - { - Connect(null, host, port, false); - } - - /// - /// Connects to sepcified host. - /// - /// Host name or IP address. - /// Port where to connect. Default SMTP port is 25 and SSL port is 465. - /// Specifies if to connected via SSL. - public void Connect(string host, int port, bool ssl) - { - Connect(null, host, port, ssl); - } - - /// - /// Connects to sepcified host. - /// - /// Sets local endpoint. Pass null, to use default. - /// Host name or IP address. - /// Port where to connect. - public void Connect(IPEndPoint localEndpoint, string host, int port) - { - Connect(localEndpoint, host, port, false); - } - - /// - /// Connects to sepcified host. - /// - /// Sets local endpoint. Pass null, to use default. - /// Host name or IP address. - /// Port where to connect. - /// Specifies if to connected via SSL. Default SMTP port is 25 and SSL port is 465. - public void Connect(IPEndPoint localEndpoint, string host, int port, bool ssl) - { - m_pSocket = new SocketEx(); - if (localEndpoint != null) - { - m_pSocket.Bind(localEndpoint); - } - - // Create logger - if (SessionLog != null) - { - m_pLogger = new SocketLogger(m_pSocket.RawSocket, SessionLog); - m_pLogger.SessionID = Guid.NewGuid().ToString(); - m_pSocket.Logger = m_pLogger; - } - - if (host.IndexOf("@") == -1) - { - m_pSocket.Connect(host, port, ssl); - } - else - { - //---- Parse e-domain -------------------------------// - string domain = host; - - // eg. Ivx - if (domain.IndexOf("<") > -1 && domain.IndexOf(">") > -1) - { - domain = domain.Substring(domain.IndexOf("<") + 1, - domain.IndexOf(">") - domain.IndexOf("<") - 1); - } - - if (domain.IndexOf("@") > -1) - { - domain = domain.Substring(domain.LastIndexOf("@") + 1); - } - - if (domain.Trim().Length == 0) - { - if (m_pLogger != null) - { - m_pLogger.AddTextEntry("Destination address '" + host + "' is invalid, aborting !"); - } - throw new Exception("Destination address '" + host + "' is invalid, aborting !"); - } - - //--- Get MX record -------------------------------------------// - Dns_Client dns = new Dns_Client(); - Dns_Client.DnsServers = m_pDnsServers; - DnsServerResponse dnsResponse = dns.Query(domain, QTYPE.MX); - - bool connected = false; - switch (dnsResponse.ResponseCode) - { - case RCODE.NO_ERROR: - DNS_rr_MX[] mxRecords = dnsResponse.GetMXRecords(); - - // Try all available hosts by MX preference order, if can't connect specified host. - foreach (DNS_rr_MX mx in mxRecords) - { - try - { - if (m_pLogger != null) - { - m_pLogger.AddTextEntry("Connecting with mx record to: " + mx.Host); - } - m_pSocket.Connect(mx.Host, port, ssl); - connected = true; - break; - } - catch (Exception x) - { - // Just skip and let for to try next host. - if (m_pLogger != null) - { - m_pLogger.AddTextEntry("Failed connect to: " + mx.Host + " error:" + - x.Message); - } - } - } - - // None of MX didn't connect - if (mxRecords.Length > 0 && !connected) - { - throw new Exception("Destination email server is down"); - } - - /* Rfc 2821 5 - If no MX records are found, but an A RR is found, the A RR is treated as - if it was associated with an implicit MX RR, with a preference of 0, - pointing to that host. - */ - if (!connected) - { - // Try to connect with A record - IPAddress[] ipEntry = null; - try - { - if (m_pLogger != null) - { - m_pLogger.AddTextEntry("No mx record, trying to get A record for: " + - domain); - } - ipEntry = Dns_Client.Resolve(domain); - } - catch - { - if (m_pLogger != null) - { - m_pLogger.AddTextEntry("Invalid domain,no MX or A record: " + domain); - } - throw new Exception("Invalid domain,no MX or A record: " + domain); - } - - try - { - if (m_pLogger != null) - { - m_pLogger.AddTextEntry("Connecting with A record to:" + domain); - } - m_pSocket.Connect(domain, port, ssl); - } - catch - { - if (m_pLogger != null) - { - m_pLogger.AddTextEntry("Failed connect to:" + domain); - } - throw new Exception("Destination email server is down"); - } - } - break; - - case RCODE.NAME_ERROR: - if (m_pLogger != null) - { - m_pLogger.AddTextEntry("Invalid domain,no MX or A record: " + domain); - } - throw new Exception("Invalid domain,no MX or A record: " + domain); - - case RCODE.SERVER_FAILURE: - if (m_pLogger != null) - { - m_pLogger.AddTextEntry("Dns server unvailable."); - } - throw new Exception("Dns server unvailable."); - } - } - - /* - * Notes: Greeting may be single or multiline response. - * - * Examples: - * 220SMTP server ready - * - * 220-SMTP server ready - * 220-Addtitional text - * 220final row - * - */ - - // Read server response - string responseLine = m_pSocket.ReadLine(1000); - while (!responseLine.StartsWith("220 ")) - { - // If lisne won't start with 220, then its error response - if (!responseLine.StartsWith("220")) - { - throw new Exception(responseLine); - } - - responseLine = m_pSocket.ReadLine(1000); - } - - m_Connected = true; - } - - /// - /// Starts connection to specified host. - /// - /// Host name or IP address. - /// Port where to connect. - /// Callback to be called if connect ends. - public void BeginConnect(string host, int port, CommadCompleted callback) - { - BeginConnect(null, host, port, false, callback); - } - - /// - /// Starts connection to specified host. - /// - /// Host name or IP address. - /// Port where to connect. - /// Specifies if to connected via SSL. - /// Callback to be called if connect ends. - public void BeginConnect(string host, int port, bool ssl, CommadCompleted callback) - { - BeginConnect(null, host, port, ssl, callback); - } - - /// - /// Starts connection to specified host. - /// - /// Sets local endpoint. Pass null, to use default. - /// Host name or IP address. - /// Port where to connect. - /// Callback to be called if connect ends. - public void BeginConnect(IPEndPoint localEndpoint, string host, int port, CommadCompleted callback) - { - BeginConnect(localEndpoint, host, port, false, callback); - } - - /// - /// Starts connection to specified host. - /// - /// Sets local endpoint. Pass null, to use default. - /// Host name or IP address. - /// Port where to connect. - /// Specifies if to connected via SSL. - /// Callback to be called if connect ends. - public void BeginConnect(IPEndPoint localEndpoint, - string host, - int port, - bool ssl, - CommadCompleted callback) - { - ThreadPool.QueueUserWorkItem(BeginConnect_workerThread, - new object[] {localEndpoint, host, port, ssl, callback}); - } - - /* /// - /// Starts disconnecting SMTP client. - /// - public void BeginDisconnect() - { - if(!m_Connected){ - throw new Exception("You must connect first"); - } - }*/ - - /// - /// Disconnects smtp client from server. - /// - public void Disconnect() - { - try - { - if (m_pSocket != null && m_pSocket.Connected) - { - m_pSocket.WriteLine("QUIT"); - - m_pSocket.Shutdown(SocketShutdown.Both); - } - } - catch {} - - m_pSocket = null; - m_Connected = false; - m_Supports_Size = false; - m_Supports_Bdat = false; - m_Supports_Login = false; - m_Supports_CramMd5 = false; - - if (m_pLogger != null) - { - m_pLogger.Flush(); - m_pLogger = null; - } - } - - /// - /// Switches SMTP connection to SSL. - /// - public void StartTLS() - { - /* RFC 2487 STARTTLS 5. STARTTLS Command. - STARTTLS with no parameters. - - After the client gives the STARTTLS command, the server responds with - one of the following reply codes: - - 220 Ready to start TLS - 501 Syntax error (no parameters allowed) - 454 TLS not available due to temporary reason - */ - - if (!m_Connected) - { - throw new Exception("You must connect first !"); - } - if (m_Authenticated) - { - throw new Exception("The STLS command is only valid in non-authenticated state !"); - } - if (m_pSocket.SSL) - { - throw new Exception("Connection is already secure !"); - } - - m_pSocket.WriteLine("STARTTLS"); - - string reply = m_pSocket.ReadLine(); - if (!reply.ToUpper().StartsWith("220")) - { - throw new Exception("Server returned:" + reply); - } - - m_pSocket.SwitchToSSL_AsClient(); - } - - /// - /// Start TLS(SSL) negotiation asynchronously. - /// - /// The method to be called when the asynchronous StartTLS operation is completed. - public void BeginStartTLS(CommadCompleted callback) - { - ThreadPool.QueueUserWorkItem(BeginStartTLS_workerThread, callback); - } - - /// - /// Does EHLO command. If server don't support EHLO, tries HELO. - /// - /// Host name which is reported to SMTP server. - public void Ehlo(string hostName) - { - if (!m_Connected) - { - throw new Exception("You must connect first"); - } - - /* Rfc 2821 4.1.1.1 EHLO - * Syntax: "EHLO" SP Domain CRLF - */ - - if (hostName.Length == 0) - { - hostName = Dns.GetHostName(); - } - - // Send EHLO command to server - m_pSocket.WriteLine("EHLO " + hostName); - - string responseLine = m_pSocket.ReadLine(); - // Response line must start with 250 or otherwise it's error response, - // try HELO - if (!responseLine.StartsWith("250")) - { - // Send HELO command to server - m_pSocket.WriteLine("HELO " + hostName); - - responseLine = m_pSocket.ReadLine(); - // HELO failed, return error - if (!responseLine.StartsWith("250")) - { - throw new Exception(responseLine); - } - } - - /* RFC 2821 4.1.1.1 EHLO - * Examples: - * 250-domainfree_text - * 250-EHLO_keyword - * 250EHLO_keyword - * - * 250 specifies that last EHLO response line. - */ - - while (!responseLine.StartsWith("250 ")) - { - //---- Store supported ESMTP features --------------------// - if (responseLine.ToLower().IndexOf("size") > -1) - { - m_Supports_Size = true; - } - else if (responseLine.ToLower().IndexOf("chunking") > -1) - { - m_Supports_Bdat = true; - } - else if (responseLine.ToLower().IndexOf("cram-md5") > -1) - { - m_Supports_CramMd5 = true; - } - else if (responseLine.ToLower().IndexOf("login") > -1) - { - m_Supports_Login = true; - } - //--------------------------------------------------------// - - // Read next EHLO response line - responseLine = m_pSocket.ReadLine(); - } - } - - /// - /// Begins EHLO command. - /// - /// Host name which is reported to SMTP server. - /// Callback to be called if command ends. - public void BeginEhlo(string hostName, CommadCompleted callback) - { - if (!m_Connected) - { - throw new Exception("You must connect first"); - } - - /* Rfc 2821 4.1.1.1 EHLO - * Syntax: "EHLO" SP Domain CRLF - */ - - if (hostName.Length == 0) - { - hostName = Dns.GetHostName(); - } - - // Start sending EHLO command to server - m_pSocket.BeginWriteLine("EHLO " + hostName, new object[] {hostName, callback}, OnEhloSendFinished); - } - - /// - /// Does AUTH command. - /// - /// Uesr name. - /// Password. - public void Authenticate(string userName, string password) - { - if (!m_Connected) - { - throw new Exception("You must connect first !"); - } - if (!(m_Supports_CramMd5 || m_Supports_Login)) - { - throw new Exception("Authentication isn't supported."); - } - - /* LOGIN - * Example: - * C: AUTHLOGIN - * S: 334base64(USERNAME) // USERNAME is string constant - * C: base64(username) - * S: 334base64(PASSWORD) // PASSWORD is string constant - * C: base64(password) - * S: 235 Ok - */ - - /* Cram-M5 - Example: - C: AUTHCRAM-MD5 - S: 334base64(md5_calculation_hash) - C: base64(usernamepassword_hash) - S: 235 Ok - */ - - if (m_Supports_CramMd5) - { - m_pSocket.WriteLine("AUTH CRAM-MD5"); - - string responseLine = m_pSocket.ReadLine(); - // Response line must start with 334 or otherwise it's error response - if (!responseLine.StartsWith("334")) - { - throw new Exception(responseLine); - } - - string md5HashKey = - Encoding.ASCII.GetString(Convert.FromBase64String(responseLine.Split(' ')[1])); - - HMACMD5 kMd5 = new HMACMD5(Encoding.ASCII.GetBytes(password)); - byte[] md5HashByte = kMd5.ComputeHash(Encoding.ASCII.GetBytes(md5HashKey)); - string hashedPwd = BitConverter.ToString(md5HashByte).ToLower().Replace("-", ""); - - m_pSocket.WriteLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(userName + " " + hashedPwd))); - - responseLine = m_pSocket.ReadLine(); - // Response line must start with 235 or otherwise it's error response - if (!responseLine.StartsWith("235")) - { - throw new Exception(responseLine); - } - - m_Authenticated = true; - } - else if (m_Supports_Login) - { - m_pSocket.WriteLine("AUTH LOGIN"); - - string responseLine = m_pSocket.ReadLine(); - // Response line must start with 334 or otherwise it's error response - if (!responseLine.StartsWith("334")) - { - throw new Exception(responseLine); - } - - // Send user name to server - m_pSocket.WriteLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(userName))); - - responseLine = m_pSocket.ReadLine(); - // Response line must start with 334 or otherwise it's error response - if (!responseLine.StartsWith("334")) - { - throw new Exception(responseLine); - } - - // Send password to server - m_pSocket.WriteLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(password))); - - responseLine = m_pSocket.ReadLine(); - // Response line must start with 235 or otherwise it's error response - if (!responseLine.StartsWith("235")) - { - throw new Exception(responseLine); - } - - m_Authenticated = true; - } - - if (m_Authenticated && m_pSocket.Logger != null) - { - m_pSocket.Logger.UserName = userName; - } - } - - /// - /// Begins authenticate. - /// - /// Uesr name. - /// Password. - /// Callback to be called if command ends. - public void BeginAuthenticate(string userName, string password, CommadCompleted callback) - { - if (!m_Connected) - { - throw new Exception("You must connect first !"); - } - if (!(m_Supports_CramMd5 || m_Supports_Login)) - { - throw new Exception("Authentication isn't supported."); - } - - /* LOGIN - * Example: - * C: AUTHLOGIN - * S: 334base64(USERNAME) // USERNAME is string constant - * C: base64(username) - * S: 334base64(PASSWORD) // PASSWORD is string constant - * C: base64(password) - * S: 235 Ok - */ - - /* Cram-M5 - Example: - C: AUTHCRAM-MD5 - S: 334base64(md5_calculation_hash) - C: base64(usernamepassword_hash) - S: 235 Ok - */ - - if (m_Supports_CramMd5) - { - m_pSocket.BeginWriteLine("AUTH CRAM-MD5", - new Auth_state_data(userName, password, callback), - OnAuthCramMd5SendFinished); - } - else if (m_Supports_Login) - { - m_pSocket.BeginWriteLine("AUTH LOGIN", - new Auth_state_data(userName, password, callback), - OnAuthLoginSendFinished); - } - } - - /// - /// Does MAIL FROM: command. - /// - /// Sender email address what is reported to smtp server - /// Message size in bytes or -1 if message size isn't known. - public void SetSender(string senderEmail, long messageSize) - { - if (!m_Connected) - { - throw new Exception("You must connect first"); - } - - /* RFC 2821 4.1.1.2 MAIL - * Examples: - * MAIL FROM: - * - * RFC 1870 adds optional SIZE keyword support. - * SIZE keyword may only be used if it's reported in EHLO command response. - * Examples: - * MAIL FROM: SIZE=1000 - */ - - if (m_Supports_Size && messageSize > -1) - { - m_pSocket.WriteLine("MAIL FROM:<" + senderEmail + "> SIZE=" + messageSize); - } - else - { - m_pSocket.WriteLine("MAIL FROM:<" + senderEmail + ">"); - } - - string responseLine = m_pSocket.ReadLine(); - // Response line must start with 250 or otherwise it's error response - if (!responseLine.StartsWith("250")) - { - throw new Exception(responseLine); - } - } - - /// - /// Begin setting sender. - /// - /// Sender email address what is reported to smtp server. - /// Message size in bytes or -1 if message size isn't known. - /// Callback to be called if command ends. - public void BeginSetSender(string senderEmail, long messageSize, CommadCompleted callback) - { - if (!m_Connected) - { - throw new Exception("You must connect first"); - } - - /* RFC 2821 4.1.1.2 MAIL - * Examples: - * MAIL FROM: - * - * RFC 1870 adds optional SIZE keyword support. - * SIZE keyword may only be used if it's reported in EHLO command response. - * Examples: - * MAIL FROM: SIZE=1000 - */ - - if (m_Supports_Size && messageSize > -1) - { - m_pSocket.BeginWriteLine("MAIL FROM:<" + senderEmail + "> SIZE=" + messageSize, - callback, - OnMailSendFinished); - } - else - { - m_pSocket.BeginWriteLine("MAIL FROM:<" + senderEmail + ">", callback, OnMailSendFinished); - } - } - - /// - /// Does RCPT TO: command. - /// - /// Recipient email address. - public void AddRecipient(string recipientEmail) - { - if (!m_Connected) - { - throw new Exception("You must connect first"); - } - - /* RFC 2821 4.1.1.2 RCPT - * Examples: - * RCPT TO: - */ - - m_pSocket.WriteLine("RCPT TO:<" + recipientEmail + ">"); - - string responseLine = m_pSocket.ReadLine(); - // Response line must start with 250 or otherwise it's error response - if (!responseLine.StartsWith("250")) - { - throw new Exception(responseLine); - } - } - - /// - /// Begin adding recipient. - /// - /// Recipient email address. - /// Callback to be called if command ends. - public void BeginAddRecipient(string recipientEmail, CommadCompleted callback) - { - if (!m_Connected) - { - throw new Exception("You must connect first"); - } - - /* RFC 2821 4.1.1.2 RCPT - * Examples: - * RCPT TO: - */ - - m_pSocket.BeginWriteLine("RCPT TO:<" + recipientEmail + ">", callback, OnRcptSendFinished); - } - - /// - /// Sends message to server. NOTE: Message sending starts from message stream current posision. - /// - /// Message what will be sent to server. NOTE: Message sending starts from message stream current posision. - public void SendMessage(Stream message) - { - if (!m_Connected) - { - throw new Exception("You must connect first"); - } - - /* RFC 2821 4.1.1.4 DATA - * Notes: - * Message must be period handled for DATA command. This meas if message line starts with ., - * additional .(period) must be added. - * Message send is ended with .. - * Examples: - * C: DATA - * S: 354 Start sending message, end with . - * C: send_message - * C: . - */ - - /* RFC 3030 BDAT - * Syntax:BDATmessage_size_in_bytesLAST - * - * Exapmle: - * C: BDAT 1000 LAST - * C: send_1000_byte_message - * S: 250 OK - * - */ - - if (m_Supports_Bdat) - { - m_pSocket.WriteLine("BDAT " + (message.Length - message.Position) + " LAST"); - - m_pSocket.Write(message); - - string responseLine = m_pSocket.ReadLine(); - // Response line must start with 250 or otherwise it's error response - if (!responseLine.StartsWith("250")) - { - throw new Exception(responseLine); - } - } - else - { - m_pSocket.WriteLine("DATA"); - - string responseLine = m_pSocket.ReadLine(); - // Response line must start with 334 or otherwise it's error response - if (!responseLine.StartsWith("354")) - { - throw new Exception(responseLine); - } - - m_pSocket.WritePeriodTerminated(message); - - responseLine = m_pSocket.ReadLine(); - // Response line must start with 250 or otherwise it's error response - if (!responseLine.StartsWith("250")) - { - throw new Exception(responseLine); - } - } - } - - /// - /// Starts sending message. - /// - /// Message what will be sent to server. NOTE: Message sending starts from message stream current posision. - /// Callback to be called if command ends. - public void BeginSendMessage(Stream message, CommadCompleted callback) - { - if (!m_Connected) - { - throw new Exception("You must connect first"); - } - - /* RFC 2821 4.1.1.4 DATA - * Notes: - * Message must be period handled for DATA command. This meas if message line starts with ., - * additional .(period) must be added. - * Message send is ended with .. - * Examples: - * C: DATA - * S: 354 Start sending message, end with . - * C: send_message - * C: . - */ - - /* RFC 3030 BDAT - * Syntax:BDATmessage_size_in_bytesLAST - * - * Exapmle: - * C: BDAT 1000 LAST - * C: send_1000_byte_message - * S: 250 OK - * - */ - - if (m_Supports_Bdat) - { - m_pSocket.BeginWriteLine("BDAT " + (message.Length - message.Position) + " LAST", - new object[] {message, callback}, - OnBdatSendFinished); - } - else - { - m_pSocket.BeginWriteLine("DATA", new object[] {message, callback}, OnDataSendFinished); - } - } - - /// - /// Send RSET command to SMTP server, resets SMTP session. - /// - public void Reset() - { - if (!m_Connected) - { - throw new Exception("You must connect first"); - } - - m_pSocket.WriteLine("RSET"); - - string responseLine = m_pSocket.ReadLine(); - if (!responseLine.StartsWith("250")) - { - throw new Exception(responseLine); - } - } - - /// - /// Gets specified email domain possible connect points. Values are in priority descending order. - /// - /// Email address or domain name. - /// Returns possible email server connection points. - public IPAddress[] GetDestinations(string domain) - { - /* - 1) Add MX records - 2) Add A records - */ - - // We have email address, just get domain from it. - if (domain.IndexOf('@') > -1) - { - domain = domain.Substring(domain.IndexOf('@') + 1); - } - - List retVal = new List(); - Dns_Client dns = new Dns_Client(); - Dns_Client.DnsServers = DnsServers; - DnsServerResponse response = dns.Query(domain, QTYPE.MX); - // Add MX - foreach (DNS_rr_MX mx in response.GetMXRecords()) - { - try - { - IPAddress[] ips = Dns.GetHostAddresses(mx.Host); - foreach (IPAddress ip in ips) - { - if (!retVal.Contains(ip)) - { - retVal.Add(ip); - } - } - } - catch - { - // Probably wrong MX record, no A reocrd for it, so we don't get IP. Just skip it. - } - } - // Add A records only if no MX records. - if (retVal.Count == 0) - { - response = dns.Query(domain, QTYPE.A); - foreach (DNS_rr_A a in response.GetARecords()) - { - IPAddress ip = a.IP; - if (!retVal.Contains(ip)) - { - retVal.Add(ip); - } - } - } - - return retVal.ToArray(); - } - - #endregion - - #region Utility methods - - /// - /// Is called from ThreadPool Thread. This method just call synchrounous Connect. - /// - /// - private void BeginConnect_workerThread(object tag) - { - CommadCompleted callback = (CommadCompleted) ((object[]) tag)[4]; - - try - { - IPEndPoint localEndpoint = (IPEndPoint) ((object[]) tag)[0]; - string host = (string) ((object[]) tag)[1]; - int port = (int) ((object[]) tag)[2]; - bool ssl = (bool) ((object[]) tag)[3]; - - Connect(localEndpoint, host, port, ssl); - - // Connect completed susscessfully, call callback method. - callback(SocketCallBackResult.Ok, null); - } - catch (Exception x) - { - // Pass exception to callback method - callback(SocketCallBackResult.Exception, x); - } - } - - /// - /// Is called from ThreadPool Thread. This method just call synchrounous StartTLS. - /// - /// User data. - private void BeginStartTLS_workerThread(object tag) - { - CommadCompleted callback = (CommadCompleted) tag; - - try - { - StartTLS(); - - // Connect completed susscessfully, call callback method. - callback(SocketCallBackResult.Ok, null); - } - catch (Exception x) - { - // Pass exception to callback method - callback(SocketCallBackResult.Exception, x); - } - } - - /// - /// Is called when smtp client has finished EHLO command sending. - /// - /// - /// - /// - /// - private void OnEhloSendFinished(SocketCallBackResult result, - long count, - Exception exception, - object tag) - { - CommadCompleted callback = (CommadCompleted) (((object[]) tag)[1]); - - try - { - if (result == SocketCallBackResult.Ok) - { - // Begin reading server EHLO command response - MemoryStream ms = new MemoryStream(); - m_pSocket.BeginReadLine(ms, - 1000, - new[] {((object[]) tag)[0], callback, ms}, - OnEhloReadServerResponseFinished); - } - else - { - HandleSocketError(result, exception); - } - } - catch (Exception x) - { - // Pass exception to callback method - callback(SocketCallBackResult.Exception, x); - } - } - - /// - /// Is called when smtp client has finished reading EHLO command server response line. - /// - /// - /// - /// - /// - private void OnEhloReadServerResponseFinished(SocketCallBackResult result, - long count, - Exception exception, - object tag) - { - CommadCompleted callback = (CommadCompleted) (((object[]) tag)[1]); - - try - { - if (result == SocketCallBackResult.Ok) - { - string responseLine = - Encoding.ASCII.GetString(((MemoryStream) (((object[]) tag)[2])).ToArray()); - - /* RFC 2821 4.1.1.1 EHLO - * Examples: - * 250-domainfree_text - * 250-EHLO_keyword - * 250EHLO_keyword - * - * 250 specifies that last EHLO response line. - */ - - // Response line must start with 250 or otherwise it's error response - if (!responseLine.StartsWith("250")) - { - // Server isn't required to support EHLO, try HELO - string hostName = (string) (((object[]) tag)[0]); - m_pSocket.BeginWriteLine("HELO " + hostName, callback, OnHeloSendFinished); - } - else - { - //---- Store supported ESMTP features --------------------// - if (responseLine.ToLower().IndexOf("size") > -1) - { - m_Supports_Size = true; - } - else if (responseLine.ToLower().IndexOf("chunking") > -1) - { - m_Supports_Bdat = true; - } - else if (responseLine.ToLower().IndexOf("cram-md5") > -1) - { - m_Supports_CramMd5 = true; - } - else if (responseLine.ToLower().IndexOf("login") > -1) - { - m_Supports_Login = true; - } - //--------------------------------------------------------// - - // This isn't last EHLO response line - if (!responseLine.StartsWith("250 ")) - { - MemoryStream ms = new MemoryStream(); - m_pSocket.BeginReadLine(ms, - 1000, - new[] {(((object[]) tag)[0]), callback, ms}, - OnEhloReadServerResponseFinished); - } - else - { - // EHLO completed susscessfully, call callback method. - callback(SocketCallBackResult.Ok, null); - } - } - } - else - { - HandleSocketError(result, exception); - } - } - catch (Exception x) - { - // Pass exception to callback method - callback(SocketCallBackResult.Exception, x); - } - } - - /// - /// Is called when smtp client has finished HELO command sending. - /// - /// - /// - /// - /// - private void OnHeloSendFinished(SocketCallBackResult result, - long count, - Exception exception, - object tag) - { - CommadCompleted callback = (CommadCompleted) tag; - - try - { - if (result == SocketCallBackResult.Ok) - { - // Begin reading server HELO command response - MemoryStream ms = new MemoryStream(); - m_pSocket.BeginReadLine(ms, - 1000, - new object[] {callback, ms}, - OnHeloReadServerResponseFinished); - } - else - { - HandleSocketError(result, exception); - } - } - catch (Exception x) - { - // Pass exception to callback method - callback(SocketCallBackResult.Exception, x); - } - } - - /// - /// Is called when smtp client has finished reading EHLO command server response line. - /// - /// - /// - /// - /// - private void OnHeloReadServerResponseFinished(SocketCallBackResult result, - long count, - Exception exception, - object tag) - { - CommadCompleted callback = (CommadCompleted) (((object[]) tag)[0]); - - try - { - if (result == SocketCallBackResult.Ok) - { - string responseLine = - Encoding.ASCII.GetString(((MemoryStream) (((object[]) tag)[1])).ToArray()); - - /* RFC 2821 4.1.1.1 HELO - * Examples: - * 250domainfree_text - * - */ - - // Response line must start with 250 or otherwise it's error response - if (!responseLine.StartsWith("250")) - { - throw new Exception(responseLine); - } - else - { - // EHLO completed susscessfully, call callback method. - callback(SocketCallBackResult.Ok, null); - } - } - else - { - HandleSocketError(result, exception); - } - } - catch (Exception x) - { - // Pass exception to callback method - callback(SocketCallBackResult.Exception, x); - } - } - - /// - /// Is called when smtp client has finished AUTH CRAM-MD5 command sending. - /// - /// - /// - /// - /// - private void OnAuthCramMd5SendFinished(SocketCallBackResult result, - long count, - Exception exception, - object tag) - { - Auth_state_data stateData = (Auth_state_data) tag; - - try - { - if (result == SocketCallBackResult.Ok) - { - MemoryStream ms = new MemoryStream(); - stateData.Tag = ms; - m_pSocket.BeginReadLine(ms, 1000, stateData, OnAuthCramMd5ReadServerResponseFinished); - } - else - { - HandleSocketError(result, exception); - } - } - catch (Exception x) - { - // Pass exception to callback method - stateData.Callback(SocketCallBackResult.Exception, x); - } - } - - /// - /// Is called when smtp client has finished reading AUTH CRAM-MD% server response line. - /// - /// - /// - /// - /// - private void OnAuthCramMd5ReadServerResponseFinished(SocketCallBackResult result, - long count, - Exception exception, - object tag) - { - Auth_state_data stateData = (Auth_state_data) tag; - - try - { - if (result == SocketCallBackResult.Ok) - { - string responseLine = Encoding.ASCII.GetString(((MemoryStream) stateData.Tag).ToArray()); - - // Response line must start with 334 or otherwise it's error response - if (!responseLine.StartsWith("334")) - { - throw new Exception(responseLine); - } - else - { - string md5HashKey = - Encoding.ASCII.GetString(Convert.FromBase64String(responseLine.Split(' ')[1])); - - HMACMD5 kMd5 = new HMACMD5(Encoding.ASCII.GetBytes(stateData.Password)); - byte[] md5HashByte = kMd5.ComputeHash(Encoding.ASCII.GetBytes(md5HashKey)); - string hashedPwd = BitConverter.ToString(md5HashByte).ToLower().Replace("-", ""); - - // Start sending user name to server - m_pSocket.BeginWriteLine( - Convert.ToBase64String( - Encoding.ASCII.GetBytes(stateData.UserName + " " + hashedPwd)), - stateData, - OnAuthCramMd5UserPwdSendFinished); - } - } - else - { - HandleSocketError(result, exception); - } - } - catch (Exception x) - { - // Pass exception to callback method - stateData.Callback(SocketCallBackResult.Exception, x); - } - } - - /// - /// Is called when smtp client has finished sending username and password to smtp server. - /// - /// - /// - /// - /// - private void OnAuthCramMd5UserPwdSendFinished(SocketCallBackResult result, - long count, - Exception exception, - object tag) - { - Auth_state_data stateData = (Auth_state_data) tag; - - try - { - if (result == SocketCallBackResult.Ok) - { - MemoryStream ms = new MemoryStream(); - stateData.Tag = ms; - m_pSocket.BeginReadLine(ms, - 1000, - stateData, - OnAuthCramMd5UserPwdReadServerResponseFinished); - } - else - { - HandleSocketError(result, exception); - } - } - catch (Exception x) - { - // Pass exception to callback method - stateData.Callback(SocketCallBackResult.Exception, x); - } - } - - /// - /// Is called when smtp client has finished reading user name and password send server response line. - /// - /// - /// - /// - /// - private void OnAuthCramMd5UserPwdReadServerResponseFinished(SocketCallBackResult result, - long count, - Exception exception, - object tag) - { - Auth_state_data stateData = (Auth_state_data) tag; - - try - { - if (result == SocketCallBackResult.Ok) - { - string responseLine = Encoding.ASCII.GetString(((MemoryStream) stateData.Tag).ToArray()); - - // Response line must start with 235 or otherwise it's error response - if (!responseLine.StartsWith("235")) - { - throw new Exception(responseLine); - } - else - { - m_Authenticated = true; - - if (m_Authenticated && m_pSocket.Logger != null) - { - m_pSocket.Logger.UserName = stateData.UserName; - } - - // AUTH CRAM-MD5 completed susscessfully, call callback method. - stateData.Callback(SocketCallBackResult.Ok, null); - } - } - else - { - HandleSocketError(result, exception); - } - } - catch (Exception x) - { - // Pass exception to callback method - stateData.Callback(SocketCallBackResult.Exception, x); - } - } - - /// - /// Is called when smtp client has finished AUTH LOGIN command sending. - /// - /// - /// - /// - /// - private void OnAuthLoginSendFinished(SocketCallBackResult result, - long count, - Exception exception, - object tag) - { - Auth_state_data stateData = (Auth_state_data) tag; - - try - { - if (result == SocketCallBackResult.Ok) - { - MemoryStream ms = new MemoryStream(); - stateData.Tag = ms; - m_pSocket.BeginReadLine(ms, 1000, stateData, OnAuthLoginReadServerResponseFinished); - } - else - { - HandleSocketError(result, exception); - } - } - catch (Exception x) - { - // Pass exception to callback method - stateData.Callback(SocketCallBackResult.Exception, x); - } - } - - /// - /// Is called when smtp client has finished reading MAIL FROM: command server response line. - /// - /// - /// - /// - /// - private void OnAuthLoginReadServerResponseFinished(SocketCallBackResult result, - long count, - Exception exception, - object tag) - { - Auth_state_data stateData = (Auth_state_data) tag; - - try - { - if (result == SocketCallBackResult.Ok) - { - string responseLine = Encoding.ASCII.GetString(((MemoryStream) (stateData.Tag)).ToArray()); - - // Response line must start with 334 or otherwise it's error response - if (!responseLine.StartsWith("334")) - { - throw new Exception(responseLine); - } - else - { - // Start sending user name to server - m_pSocket.BeginWriteLine( - Convert.ToBase64String(Encoding.ASCII.GetBytes(stateData.UserName)), - stateData, - OnAuthLoginUserSendFinished); - } - } - else - { - HandleSocketError(result, exception); - } - } - catch (Exception x) - { - // Pass exception to callback method - stateData.Callback(SocketCallBackResult.Exception, x); - } - } - - /// - /// Is called when smtp client has finished sending user name to SMTP server. - /// - /// - /// - /// - /// - private void OnAuthLoginUserSendFinished(SocketCallBackResult result, - long count, - Exception exception, - object tag) - { - Auth_state_data stateData = (Auth_state_data) tag; - - try - { - if (result == SocketCallBackResult.Ok) - { - MemoryStream ms = new MemoryStream(); - stateData.Tag = ms; - m_pSocket.BeginReadLine(ms, 1000, stateData, OnAuthLoginUserReadServerResponseFinished); - } - else - { - HandleSocketError(result, exception); - } - } - catch (Exception x) - { - // Pass exception to callback method - stateData.Callback(SocketCallBackResult.Exception, x); - } - } - - /// - /// Is called when smtp client has finished reading AUTH LOGIN user send server response line. - /// - /// - /// - /// - /// - private void OnAuthLoginUserReadServerResponseFinished(SocketCallBackResult result, - long count, - Exception exception, - object tag) - { - Auth_state_data stateData = (Auth_state_data) tag; - - try - { - if (result == SocketCallBackResult.Ok) - { - string responseLine = Encoding.ASCII.GetString(((MemoryStream) stateData.Tag).ToArray()); - - // Response line must start with 334 or otherwise it's error response - if (!responseLine.StartsWith("334")) - { - throw new Exception(responseLine); - } - else - { - // Start sending password to server - m_pSocket.BeginWriteLine( - Convert.ToBase64String(Encoding.ASCII.GetBytes(stateData.Password)), - stateData, - OnAuthLoginPasswordSendFinished); - } - } - else - { - HandleSocketError(result, exception); - } - } - catch (Exception x) - { - // Pass exception to callback method - stateData.Callback(SocketCallBackResult.Exception, x); - } - } - - /// - /// Is called when smtp client has finished sending password to SMTP server. - /// - /// - /// - /// - /// - private void OnAuthLoginPasswordSendFinished(SocketCallBackResult result, - long count, - Exception exception, - object tag) - { - Auth_state_data stateData = (Auth_state_data) tag; - - try - { - if (result == SocketCallBackResult.Ok) - { - MemoryStream ms = new MemoryStream(); - stateData.Tag = ms; - m_pSocket.BeginReadLine(ms, 1000, stateData, OnAuthLoginPwdReadServerResponseFinished); - } - else - { - HandleSocketError(result, exception); - } - } - catch (Exception x) - { - // Pass exception to callback method - stateData.Callback(SocketCallBackResult.Exception, x); - } - } - - /// - /// Is called when smtp client has finished reading AUTH LOGIN password send server response line. - /// - /// - /// - /// - /// - private void OnAuthLoginPwdReadServerResponseFinished(SocketCallBackResult result, - long count, - Exception exception, - object tag) - { - Auth_state_data stateData = (Auth_state_data) tag; - - try - { - if (result == SocketCallBackResult.Ok) - { - string responseLine = Encoding.ASCII.GetString(((MemoryStream) stateData.Tag).ToArray()); - - // Response line must start with 235 or otherwise it's error response - if (!responseLine.StartsWith("235")) - { - throw new Exception(responseLine); - } - else - { - m_Authenticated = true; - - if (m_Authenticated && m_pSocket.Logger != null) - { - m_pSocket.Logger.UserName = stateData.UserName; - } - - // AUTH LOGIN completed susscessfully, call callback method. - stateData.Callback(SocketCallBackResult.Ok, null); - } - } - else - { - HandleSocketError(result, exception); - } - } - catch (Exception x) - { - // Pass exception to callback method - stateData.Callback(SocketCallBackResult.Exception, x); - } - } - - /// - /// Is called when smtp client has finished MAIL FROM: command sending. - /// - /// - /// - /// - /// - private void OnMailSendFinished(SocketCallBackResult result, - long count, - Exception exception, - object tag) - { - CommadCompleted callback = (CommadCompleted) tag; - - try - { - if (result == SocketCallBackResult.Ok) - { - MemoryStream ms = new MemoryStream(); - m_pSocket.BeginReadLine(ms, - 1000, - new object[] {callback, ms}, - OnMailReadServerResponseFinished); - } - else - { - HandleSocketError(result, exception); - } - } - catch (Exception x) - { - // Pass exception to callback method - callback(SocketCallBackResult.Exception, x); - } - } - - /// - /// Is called when smtp client has finished reading MAIL FROM: command server response line. - /// - /// - /// - /// - /// - private void OnMailReadServerResponseFinished(SocketCallBackResult result, - long count, - Exception exception, - object tag) - { - CommadCompleted callback = (CommadCompleted) (((object[]) tag)[0]); - - try - { - if (result == SocketCallBackResult.Ok) - { - string responseLine = - Encoding.ASCII.GetString(((MemoryStream) (((object[]) tag)[1])).ToArray()); - - // Response line must start with 250 or otherwise it's error response - if (!responseLine.StartsWith("250")) - { - throw new Exception(responseLine); - } - else - { - // MAIL FROM: completed susscessfully, call callback method. - callback(SocketCallBackResult.Ok, null); - } - } - else - { - HandleSocketError(result, exception); - } - } - catch (Exception x) - { - // Pass exception to callback method - callback(SocketCallBackResult.Exception, x); - } - } - - /// - /// Is called when smtp client has finished RCPT TO: command sending. - /// - /// - /// - /// - /// - private void OnRcptSendFinished(SocketCallBackResult result, - long count, - Exception exception, - object tag) - { - CommadCompleted callback = (CommadCompleted) tag; - - try - { - if (result == SocketCallBackResult.Ok) - { - MemoryStream ms = new MemoryStream(); - m_pSocket.BeginReadLine(ms, - 1000, - new object[] {callback, ms}, - OnRcptReadServerResponseFinished); - } - else - { - HandleSocketError(result, exception); - } - } - catch (Exception x) - { - // Pass exception to callback method - callback(SocketCallBackResult.Exception, x); - } - } - - /// - /// Is called when smtp client has finished reading RCPT TO: command server response line. - /// - /// - /// - /// - /// - private void OnRcptReadServerResponseFinished(SocketCallBackResult result, - long count, - Exception exception, - object tag) - { - CommadCompleted callback = (CommadCompleted) (((object[]) tag)[0]); - - try - { - if (result == SocketCallBackResult.Ok) - { - string responseLine = - Encoding.ASCII.GetString(((MemoryStream) (((object[]) tag)[1])).ToArray()); - - // Response line must start with 250 or otherwise it's error response - if (!responseLine.StartsWith("250")) - { - throw new Exception(responseLine); - } - else - { - // RCPT TO: completed susscessfully, call callback method. - callback(SocketCallBackResult.Ok, null); - } - } - else - { - HandleSocketError(result, exception); - } - } - catch (Exception x) - { - // Pass exception to callback method - callback(SocketCallBackResult.Exception, x); - } - } - - /// - /// Is called when smtp client has finished BDAT command sending. - /// - /// - /// - /// - /// - private void OnBdatSendFinished(SocketCallBackResult result, - long count, - Exception exception, - object tag) - { - CommadCompleted callback = (CommadCompleted) (((object[]) tag)[1]); - - try - { - if (result == SocketCallBackResult.Ok) - { - // BDAT command successfully sent to SMTP server, start sending DATA. - m_pSocket.BeginWrite((Stream) (((object[]) tag)[0]), callback, OnBdatDataSendFinished); - } - else - { - HandleSocketError(result, exception); - } - } - catch (Exception x) - { - // Pass exception to callback method - callback(SocketCallBackResult.Exception, x); - } - } - - /// - /// Is called when smtp client has finished sending BDAT message data to smtp server. - /// - /// - /// - /// - /// - private void OnBdatDataSendFinished(SocketCallBackResult result, - long count, - Exception exception, - object tag) - { - CommadCompleted callback = (CommadCompleted) tag; - - try - { - if (result == SocketCallBackResult.Ok) - { - // BDAT message data successfully sent to SMTP server, start reading server response - MemoryStream ms = new MemoryStream(); - m_pSocket.BeginReadLine(ms, - 1000, - new object[] {callback, ms}, - OnBdatReadServerResponseFinished); - } - else - { - HandleSocketError(result, exception); - } - } - catch (Exception x) - { - // Pass exception to callback method - callback(SocketCallBackResult.Exception, x); - } - } - - /// - /// Is called when smtp client has finished reading BDAT: command server response line. - /// - /// - /// - /// - /// - private void OnBdatReadServerResponseFinished(SocketCallBackResult result, - long count, - Exception exception, - object tag) - { - CommadCompleted callback = (CommadCompleted) (((object[]) tag)[0]); - - try - { - if (result == SocketCallBackResult.Ok) - { - string responseLine = - Encoding.ASCII.GetString(((MemoryStream) (((object[]) tag)[1])).ToArray()); - - // Response line must start with 250 or otherwise it's error response - if (!responseLine.StartsWith("250")) - { - throw new Exception(responseLine); - } - else - { - // BDAT: completed susscessfully, call callback method. - callback(SocketCallBackResult.Ok, null); - } - } - else - { - HandleSocketError(result, exception); - } - } - catch (Exception x) - { - // Pass exception to callback method - callback(SocketCallBackResult.Exception, x); - } - } - - /// - /// Is called when smtp client has finished DATA command sending. - /// - /// - /// - /// - /// - private void OnDataSendFinished(SocketCallBackResult result, - long count, - Exception exception, - object tag) - { - CommadCompleted callback = (CommadCompleted) (((object[]) tag)[1]); - - try - { - if (result == SocketCallBackResult.Ok) - { - // DATA command has sent to SMTP server, start reading server response. - MemoryStream ms = new MemoryStream(); - m_pSocket.BeginReadLine(ms, - 1000, - new object[] {(((object[]) tag)[0]), callback, ms}, - OnDataReadServerResponseFinished); - } - else - { - HandleSocketError(result, exception); - } - } - catch (Exception x) - { - // Pass exception to callback method - callback(SocketCallBackResult.Exception, x); - } - } - - /// - /// Is called when smtp client has finished reading DATA command server response line. - /// - /// - /// - /// - /// - private void OnDataReadServerResponseFinished(SocketCallBackResult result, - long count, - Exception exception, - object tag) - { - CommadCompleted callback = (CommadCompleted) (((object[]) tag)[1]); - - try - { - if (result == SocketCallBackResult.Ok) - { - string responseLine = - Encoding.ASCII.GetString(((MemoryStream) (((object[]) tag)[2])).ToArray()); - - // Response line must start with 334 or otherwise it's error response - if (!responseLine.StartsWith("354")) - { - throw new Exception(responseLine); - } - else - { - Stream message = (Stream) (((object[]) tag)[0]); - - // Start sending message to smtp server - m_pSocket.BeginWritePeriodTerminated(message, callback, OnDataMessageSendFinished); - } - } - else - { - HandleSocketError(result, exception); - } - } - catch (Exception x) - { - // Pass exception to callback method - callback(SocketCallBackResult.Exception, x); - } - } - - /// - /// Is called when smtp client has sending MESSAGE to smtp server. - /// - /// - /// - /// - /// - private void OnDataMessageSendFinished(SocketCallBackResult result, - long count, - Exception exception, - object tag) - { - CommadCompleted callback = (CommadCompleted) tag; - - try - { - if (result == SocketCallBackResult.Ok) - { - // Message has successfully sent to smtp server, start reading server response - MemoryStream ms = new MemoryStream(); - m_pSocket.BeginReadLine(ms, - 1000, - new object[] {callback, ms}, - OnDataMessageSendReadServerResponseFinished); - } - else - { - HandleSocketError(result, exception); - } - } - catch (Exception x) - { - // Pass exception to callback method - callback(SocketCallBackResult.Exception, x); - } - } - - /// - /// Is called when smtp client has finished reading MESSAGE send smtp server response line. - /// - /// - /// - /// - /// - private void OnDataMessageSendReadServerResponseFinished(SocketCallBackResult result, - long count, - Exception exception, - object tag) - { - CommadCompleted callback = (CommadCompleted) (((object[]) tag)[0]); - - try - { - // TODO: some servers close connection after DATA command, hanndle Socket closed. - - if (result == SocketCallBackResult.Ok) - { - string responseLine = - Encoding.ASCII.GetString(((MemoryStream) (((object[]) tag)[1])).ToArray()); - - // Response line must start with 250 or otherwise it's error response - if (!responseLine.StartsWith("250")) - { - throw new Exception(responseLine); - } - else - { - // DATA: completed susscessfully, call callback method. - callback(SocketCallBackResult.Ok, null); - } - } - else - { - HandleSocketError(result, exception); - } - } - catch (Exception x) - { - // Pass exception to callback method - callback(SocketCallBackResult.Exception, x); - } - } - - /// - /// Handles socket errors. - /// - /// - /// - private void HandleSocketError(SocketCallBackResult result, Exception x) - { - // Log socket errors to log - if (m_pSocket.Logger != null) - { - if (result == SocketCallBackResult.SocketClosed) - { - m_pSocket.Logger.AddTextEntry("Server closed socket !"); - } - else if (x != null && x is SocketException) - { - SocketException socketException = (SocketException) x; - // Server disconnected or aborted connection - if (socketException.ErrorCode == 10054 || socketException.ErrorCode == 10053) - { - m_pSocket.Logger.AddTextEntry("Server closed socket or aborted connection !"); - } - } - else - { - m_pSocket.Logger.AddTextEntry("Unknown error !"); - } - } - - if (result == SocketCallBackResult.Exception) - { - throw x; - } - else - { - throw new Exception(result.ToString()); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SocketCallBackResult.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SocketCallBackResult.cs deleted file mode 100644 index b0f3a18dc..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SocketCallBackResult.cs +++ /dev/null @@ -1,56 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - #region usings - - using System; - - #endregion - - /// - /// - /// - public delegate void SocketCallBack(SocketCallBackResult result, long count, Exception x, object tag); - - /// - /// Asynchronous command execute result. - /// - public enum SocketCallBackResult - { - /// - /// Operation was successfull. - /// - Ok, - - /// - /// Exceeded maximum allowed size. - /// - LengthExceeded, - - /// - /// Connected client closed connection. - /// - SocketClosed, - - /// - /// Exception happened. - /// - Exception - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SocketEx.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SocketEx.cs deleted file mode 100644 index 506c1e68d..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SocketEx.cs +++ /dev/null @@ -1,2293 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - #region usings - - using System; - using System.IO; - using System.Net; - using System.Net.Security; - using System.Net.Sockets; - using System.Security.Cryptography.X509Certificates; - using System.Text; - - #endregion - - /// - /// This class implements extended socket, provides usefull methods for reading and writing data to socket. - /// - public class SocketEx : IDisposable - { - #region Nested type: _BeginWritePeriodTerminated_State - - /// - /// BeginWritePeriodTerminated state obejct. - /// - private struct _BeginWritePeriodTerminated_State - { - #region Members - - private readonly SocketCallBack m_Callback; - private readonly bool m_CloseStream; - private readonly Stream m_Stream; - private readonly object m_Tag; - private int m_CountSent; - private bool m_HasCRLF; - private int m_LastByte; - - #endregion - - #region Properties - - /// - /// Gets source stream. - /// - public Stream Stream - { - get { return m_Stream; } - } - - /// - /// Gets if stream must be closed if reading completed. - /// - public bool CloseStream - { - get { return m_CloseStream; } - } - - /// - /// Gets user data. - /// - public object Tag - { - get { return m_Tag; } - } - - /// - /// Gets callback what must be called if asynchronous write ends. - /// - public SocketCallBack Callback - { - get { return m_Callback; } - } - - /// - /// Gets or sets if last sent data ends with CRLF. - /// - public bool HasCRLF - { - get { return m_HasCRLF; } - - set { m_HasCRLF = value; } - } - - /// - /// Gets or sets what is last sent byte. - /// - public int LastByte - { - get { return m_LastByte; } - - set { m_LastByte = value; } - } - - /// - /// Gets or sets how many bytes has written to socket. - /// - public int CountSent - { - get { return m_CountSent; } - - set { m_CountSent = value; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Source stream. - /// Specifies if stream must be closed after reading is completed. - /// User data. - /// Callback what to call if asynchronous data writing completes. - public _BeginWritePeriodTerminated_State(Stream stream, - bool closeStream, - object tag, - SocketCallBack callback) - { - m_Stream = stream; - m_CloseStream = closeStream; - m_Tag = tag; - m_Callback = callback; - m_HasCRLF = false; - m_LastByte = -1; - m_CountSent = 0; - } - - #endregion - } - - #endregion - - #region Nested type: BufferDataBlockCompleted - - private delegate void BufferDataBlockCompleted(Exception x, object tag); - - #endregion - - #region Members - - private readonly byte[] m_Buffer; - private int m_AvailableInBuffer; - private string m_Host = ""; - private DateTime m_LastActivityDate; - private int m_OffsetInBuffer; - private Encoding m_pEncoding; - private SocketLogger m_pLogger; - private Socket m_pSocket; - private NetworkStream m_pSocketStream; - private SslStream m_pSslStream; - private long m_ReadedCount; - private bool m_SSL; - private long m_WrittenCount; - - #endregion - - #region Properties - - /// - /// Gets or sets socket default encoding. - /// - public Encoding Encoding - { - get { return m_pEncoding; } - - set - { - if (m_pEncoding == null) - { - throw new ArgumentNullException("Encoding"); - } - - m_pEncoding = value; - } - } - - /// - /// Gets or sets logging source. If this is setted, reads/writes are logged to it. - /// - public SocketLogger Logger - { - get { return m_pLogger; } - - set { m_pLogger = value; } - } - - /// - /// Gets raw uderlaying socket. - /// - public Socket RawSocket - { - get { return m_pSocket; } - } - - /// - /// Gets if socket is connected. - /// - public bool Connected - { - get { return m_pSocket != null && m_pSocket.Connected; } - } - - /// - /// Gets the local endpoint. - /// - public EndPoint LocalEndPoint - { - get - { - if (m_pSocket == null) - { - return null; - } - else - { - return m_pSocket.LocalEndPoint; - } - } - } - - /// - /// Gets the remote endpoint. - /// - public EndPoint RemoteEndPoint - { - get - { - if (m_pSocket == null) - { - return null; - } - else - { - return m_pSocket.RemoteEndPoint; - } - } - } - - /// - /// Gets if socket is connected via SSL. - /// - public bool SSL - { - get { return m_SSL; } - } - - /// - /// Gets how many bytes are readed through this socket. - /// - public long ReadedCount - { - get { return m_ReadedCount; } - } - - /// - /// Gets how many bytes are written through this socket. - /// - public long WrittenCount - { - get { return m_WrittenCount; } - } - - /// - /// Gets when was last socket(read or write) activity. - /// - public DateTime LastActivity - { - get { return m_LastActivityDate; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public SocketEx() - { - m_Buffer = new byte[8000]; - m_pEncoding = Encoding.UTF8; - m_LastActivityDate = DateTime.Now; - - m_pSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - m_pSocket.ReceiveTimeout = 60000; - m_pSocket.SendTimeout = 60000; - } - - /// - /// Socket wrapper. NOTE: You must pass connected socket here ! - /// - /// Socket. - public SocketEx(Socket socket) - { - m_Buffer = new byte[8000]; - m_pEncoding = Encoding.UTF8; - m_LastActivityDate = DateTime.Now; - - m_pSocket = socket; - - if (socket.ProtocolType == ProtocolType.Tcp) - { - m_pSocketStream = new NetworkStream(socket, false); - } - m_pSocket.ReceiveTimeout = 60000; - m_pSocket.SendTimeout = 60000; - } - - #endregion - - #region Methods - - /// - /// Clean up any resouces being used. - /// - public void Dispose() - { - Disconnect(); - } - - /// - /// Connects to the specified host. - /// - /// IP endpoint where to connect. - public void Connect(IPEndPoint endpoint) - { - Connect(endpoint.Address.ToString(), endpoint.Port, false); - } - - /// - /// Connects to the specified host. - /// - /// IP endpoint where to connect. - /// Specifies if to connected via SSL. - public void Connect(IPEndPoint endpoint, bool ssl) - { - Connect(endpoint.Address.ToString(), endpoint.Port, ssl); - } - - /// - /// Connects to the specified host. - /// - /// Host name or IP where to connect. - /// TCP port number where to connect. - public void Connect(string host, int port) - { - Connect(host, port, false); - } - - /// - /// Connects to the specified host. - /// - /// Host name or IP where to connect. - /// TCP port number where to connect. - /// Specifies if to connected via SSL. - public void Connect(string host, int port, bool ssl) - { - m_pSocket.Connect(new IPEndPoint(System.Net.Dns.GetHostAddresses(host)[0], port)); - // mono won't support it - //m_pSocket.Connect(host,port); - - m_Host = host; - m_pSocketStream = new NetworkStream(m_pSocket, false); - - if (ssl) - { - SwitchToSSL_AsClient(); - } - } - - /// - /// Disconnects socket. - /// - public void Disconnect() - { - lock (this) - { - if (m_pSocket != null) - { - m_pSocket.Close(); - } - - m_SSL = false; - m_pSocketStream = null; - m_pSslStream = null; - m_pSocket = null; - m_OffsetInBuffer = 0; - m_AvailableInBuffer = 0; - m_Host = ""; - m_ReadedCount = 0; - m_WrittenCount = 0; - } - } - - /// - /// Shutdowns socket. - /// - /// - public void Shutdown(SocketShutdown how) - { - m_pSocket.Shutdown(how); - } - - /// - /// Associates a Socket with a local endpoint. - /// - /// - public void Bind(EndPoint loaclEP) - { - m_pSocket.Bind(loaclEP); - } - - /// - /// Places a Socket in a listening state. - /// - /// The maximum length of the pending connections queue. - public void Listen(int backlog) - { - m_pSocket.Listen(backlog); - } - - /// - /// TODO: - /// - /// - /// - public SocketEx Accept(bool ssl) - { - Socket s = m_pSocket.Accept(); - return new SocketEx(s); - } - - /// - /// Switches socket to SSL mode. Throws excpetion is socket is already in SSL mode. - /// - /// Certificate to use for SSL. - public void SwitchToSSL(X509Certificate certificate) - { - if (m_SSL) - { - throw new Exception("Error can't switch to SSL, socket is already in SSL mode !"); - } - - SslStream sslStream = new SslStream(m_pSocketStream); - sslStream.AuthenticateAsServer(certificate); - - m_SSL = true; - m_pSslStream = sslStream; - } - - /// - /// Switches socket to SSL mode. Throws excpetion is socket is already in SSL mode. - /// - public void SwitchToSSL_AsClient() - { - if (m_SSL) - { - throw new Exception("Error can't switch to SSL, socket is already in SSL mode !"); - } - - SslStream sslStream = new SslStream(m_pSocketStream, true, RemoteCertificateValidationCallback); - sslStream.AuthenticateAsClient(m_Host); - - m_SSL = true; - m_pSslStream = sslStream; - } - - /// - /// Reads byte from socket. Returns readed byte or -1 if socket is shutdown and tehre is no more data available. - /// - /// Returns readed byte or -1 if socket is shutdown and tehre is no more data available. - public int ReadByte() - { - BufferDataBlock(); - // Socket is shutdown - if (m_AvailableInBuffer == 0) - { - m_OffsetInBuffer = 0; - m_AvailableInBuffer = 0; - return -1; - } - - m_OffsetInBuffer++; - m_AvailableInBuffer--; - - return m_Buffer[m_OffsetInBuffer - 1]; - } - - /// - /// Reads line from socket. Maximum line length is 4000 bytes. NOTE: CRLF isn't written to destination stream. - /// If maximum allowed line length is exceeded line is read to end, but isn't stored to buffer and exception - /// is thrown after line reading. - /// - /// Returns readed line. - public string ReadLine() - { - return ReadLine(4000); - } - - /// - /// Reads line from socket.NOTE: CRLF isn't written to destination stream. - /// If maximum allowed line length is exceeded line is read to end, but isn't stored to buffer and exception - /// is thrown after line reading. - /// - /// Maximum line length in bytes. - /// Returns readed line. - public string ReadLine(int maxLineLength) - { - return m_pEncoding.GetString(ReadLineByte(maxLineLength)); - } - - /// - /// Reads line from socket.NOTE: CRLF isn't written to destination stream. - /// If maximum allowed line length is exceeded line is read to end, but isn't stored to buffer and exception - /// is thrown after line reading. - /// - /// Maximum line length in bytes. - /// Returns readed line. - public byte[] ReadLineByte(int maxLineLength) - { - MemoryStream strmLineBuf = new MemoryStream(); - ReadLine(strmLineBuf, maxLineLength); - - return strmLineBuf.ToArray(); - } - - /// - /// Reads line from socket and stores it to specified stream. NOTE: CRLF isn't written to destination stream. - /// If maximum allowed line length is exceeded line is read to end, but isn't stored to buffer and exception - /// is thrown after line reading. - /// - /// Stream where to store readed line. - /// Maximum line length in bytes. - public void ReadLine(Stream stream, int maxLineLength) - { - // Delay last byte writing, this is because CR, if next is LF, then skip CRLF and terminate reading. - - int lastByte = ReadByte(); - int currentByte = ReadByte(); - int readedCount = 2; - while (currentByte > -1) - { - // We got line - if (lastByte == (byte) '\r' && currentByte == (byte) '\n') - { - // Logging stuff - if (m_pLogger != null) - { - if (stream.CanSeek && stream.Length < 200) - { - byte[] readedData = new byte[stream.Length]; - stream.Position = 0; - stream.Read(readedData, 0, readedData.Length); - m_pLogger.AddReadEntry(m_pEncoding.GetString(readedData), readedCount); - } - else - { - m_pLogger.AddReadEntry("Big binary line, readed " + readedCount + " bytes.", - readedCount); - } - } - - stream.Flush(); - - // Maximum allowed length exceeded - if (readedCount > maxLineLength) - { - throw new ReadException(ReadReplyCode.LengthExceeded, - "Maximum allowed line length exceeded !"); - } - - return; - } - else - { - // Maximum allowed length exceeded, just don't store data. - if (readedCount < maxLineLength) - { - stream.WriteByte((byte) lastByte); - } - lastByte = currentByte; - } - - // Read next byte - currentByte = ReadByte(); - readedCount++; - } - - // We should not reach there, if so then socket closed - // Logging stuff - if (m_pLogger != null) - { - m_pLogger.AddTextEntry("Remote host closed socket !"); - } - throw new ReadException(ReadReplyCode.SocketClosed, - "Connected host closed socket, read line terminated unexpectedly !"); - } - - /// - /// Reads specified length of data from socket and store to specified stream. - /// - /// Specifies how much data to read from socket. - /// Stream where to store data. - public void ReadSpecifiedLength(int lengthToRead, Stream storeStream) - { - while (lengthToRead > 0) - { - BufferDataBlock(); - // Socket is shutdown - if (m_AvailableInBuffer == 0) - { - m_OffsetInBuffer = 0; - m_AvailableInBuffer = 0; - // Logging stuff - if (m_pLogger != null) - { - m_pLogger.AddTextEntry("Remote host closed socket, all data wans't readed !"); - } - throw new Exception("Remote host closed socket, all data wans't readed !"); - } - - // We have all data in buffer what we need. - if (m_AvailableInBuffer >= lengthToRead) - { - storeStream.Write(m_Buffer, m_OffsetInBuffer, lengthToRead); - storeStream.Flush(); - - m_OffsetInBuffer += lengthToRead; - m_AvailableInBuffer -= lengthToRead; - lengthToRead = 0; - - // Logging stuff - if (m_pLogger != null) - { - if (storeStream.CanSeek && storeStream.Length < 200) - { - byte[] readedData = new byte[storeStream.Length]; - storeStream.Position = 0; - storeStream.Read(readedData, 0, readedData.Length); - m_pLogger.AddReadEntry(m_pEncoding.GetString(readedData), lengthToRead); - } - else - { - m_pLogger.AddReadEntry("Big binary data, readed " + lengthToRead + " bytes.", - lengthToRead); - } - } - } - // We need more data than buffer has,read all buffer data. - else - { - storeStream.Write(m_Buffer, m_OffsetInBuffer, m_AvailableInBuffer); - storeStream.Flush(); - - lengthToRead -= m_AvailableInBuffer; - m_OffsetInBuffer = 0; - m_AvailableInBuffer = 0; - } - } - } - - /// - /// Reads period terminated string. The data is terminated by a line containing only a period, that is, - /// the character sequence "<CRLF>.<CRLF>". - /// When a line of text is received, it checks the line. If the line is composed of a single period, - /// it is treated as the end of data indicator. If the first character is a period and there are - /// other characters on the line, the first character is deleted. - /// If maximum allowed data length is exceeded data is read to end, but isn't stored to buffer and exception - /// is thrown after data reading. - /// - /// Maximum data length in bytes. - /// - public string ReadPeriodTerminated(int maxLength) - { - MemoryStream ms = new MemoryStream(); - ReadPeriodTerminated(ms, maxLength); - - return m_pEncoding.GetString(ms.ToArray()); - } - - /// - /// Reads period terminated data. The data is terminated by a line containing only a period, that is, - /// the character sequence "<CRLF>.<CRLF>". - /// When a line of text is received, it checks the line. If the line is composed of a single period, - /// it is treated as the end of data indicator. If the first character is a period and there are - /// other characters on the line, the first character is deleted. - /// If maximum allowed data length is exceeded data is read to end, but isn't stored to stream and exception - /// is thrown after data reading. - /// - /// Stream where to store readed data. - /// Maximum data length in bytes. - public void ReadPeriodTerminated(Stream stream, int maxLength) - { - /* When a line of text is received by the server, it checks the line. - If the line is composed of a single period, it is treated as the - end of data indicator. If the first character is a period and - there are other characters on the line, the first character is deleted. - */ - - // Delay last byte writing, this is because CR, if next is LF, then last byte isn't written. - - byte[] buffer = new byte[8000]; - int positionInBuffer = 0; - - int lastByte = ReadByte(); - int currentByte = ReadByte(); - int readedCount = 2; - bool lineBreak = false; - bool expectCRLF = false; - while (currentByte > -1) - { - // We got + 1 char, we must skip that char if it is '.'. - if (lineBreak) - { - lineBreak = false; - - // We must skip that char if it is '.' - if (currentByte == '.') - { - expectCRLF = true; - - currentByte = ReadByte(); - } - } - // We got - else if (lastByte == (byte) '\r' && currentByte == (byte) '\n') - { - lineBreak = true; - - // We have ., skip last . - if (expectCRLF) - { - // There is data in buffer, flush it - if (positionInBuffer > 0) - { - stream.Write(buffer, 0, positionInBuffer); - positionInBuffer = 0; - } - - // Logging stuff - if (m_pLogger != null) - { - if (stream.CanSeek && stream.Length < 200) - { - byte[] readedData = new byte[stream.Length]; - stream.Position = 0; - stream.Read(readedData, 0, readedData.Length); - m_pLogger.AddReadEntry(m_pEncoding.GetString(readedData), readedCount); - } - else - { - m_pLogger.AddReadEntry("Big binary data, readed " + readedCount + " bytes.", - readedCount); - } - } - - // Maximum allowed length exceeded - if (readedCount > maxLength) - { - throw new ReadException(ReadReplyCode.LengthExceeded, - "Maximum allowed line length exceeded !"); - } - - return; - } - } - - // current char isn't CRLF part, so it isn't . terminator. - if (expectCRLF && !(currentByte == (byte) '\r' || currentByte == (byte) '\n')) - { - expectCRLF = false; - } - - // Maximum allowed length exceeded, just don't store data. - if (readedCount < maxLength) - { - // Buffer is filled up, write buffer to stream - if (positionInBuffer > (buffer.Length - 2)) - { - stream.Write(buffer, 0, positionInBuffer); - positionInBuffer = 0; - } - - buffer[positionInBuffer] = (byte) lastByte; - positionInBuffer++; - } - - // Read next byte - lastByte = currentByte; - currentByte = ReadByte(); - readedCount++; - } - - // We never should reach there, only if data isn't . terminated. - // Logging stuff - if (m_pLogger != null) - { - m_pLogger.AddTextEntry("Remote host closed socket and data wasn't . terminated !"); - } - throw new Exception("Remote host closed socket and data wasn't . terminated !"); - } - - /// - /// Writes specified data to socket. - /// - /// Data to write to socket. - public void Write(string data) - { - if (Logger != null) - { - Logger.AddSendEntry(data, data.Length); - } - Write(new MemoryStream(m_pEncoding.GetBytes(data))); - } - - /// - /// Writes specified data to socket. - /// - /// Data to to wite to socket. - public void Write(byte[] data) - { - Write(new MemoryStream(data)); - } - - /// - /// Writes specified data to socket. - /// - /// Data to to wite to socket. - /// Offset in data from where to start sending data. - /// Lengh of data to send. - public void Write(byte[] data, int offset, int length) - { - MemoryStream ms = new MemoryStream(data); - ms.Position = offset; - Write(ms, length); - } - - /// - /// Writes specified data to socket. - /// - /// Stream which data to write to socket. Reading starts from stream current position and will be readed to EOS. - public void Write(Stream stream) - { - m_pSocket.NoDelay = false; - - byte[] buffer = new byte[4000]; - int sentCount = 0; - int readedCount = stream.Read(buffer, 0, buffer.Length); - while (readedCount > 0) - { - if (m_SSL) - { - m_pSslStream.Write(buffer, 0, readedCount); - m_pSslStream.Flush(); - } - else - { - m_pSocketStream.Write(buffer, 0, readedCount); - } - - sentCount += readedCount; - m_WrittenCount += readedCount; - m_LastActivityDate = DateTime.Now; - - readedCount = stream.Read(buffer, 0, buffer.Length); - } - - // Logging stuff - if (m_pLogger != null) - { - if (sentCount < 200) - { - m_pLogger.AddSendEntry(m_pEncoding.GetString(buffer, 0, sentCount), sentCount); - } - else - { - m_pLogger.AddSendEntry("Big binary data, sent " + sentCount + " bytes.", sentCount); - } - } - } - - /// - /// Writes specified data to socket. - /// - /// Stream which data to write to socket. Reading starts from stream current position and specified count will be readed. - /// Number of bytes to read from stream and write to socket. - public void Write(Stream stream, long count) - { - m_pSocket.NoDelay = false; - - byte[] buffer = new byte[4000]; - int sentCount = 0; - int readedCount = 0; - if ((count - sentCount) > buffer.Length) - { - readedCount = stream.Read(buffer, 0, buffer.Length); - } - else - { - readedCount = stream.Read(buffer, 0, (int) (count - sentCount)); - } - while (sentCount < count) - { - if (m_SSL) - { - m_pSslStream.Write(buffer, 0, readedCount); - m_pSslStream.Flush(); - } - else - { - m_pSocketStream.Write(buffer, 0, readedCount); - } - - sentCount += readedCount; - m_WrittenCount += readedCount; - m_LastActivityDate = DateTime.Now; - - if ((count - sentCount) > buffer.Length) - { - readedCount = stream.Read(buffer, 0, buffer.Length); - } - else - { - readedCount = stream.Read(buffer, 0, (int) (count - sentCount)); - } - } - - // Logging stuff - if (m_pLogger != null) - { - if (sentCount < 200) - { - m_pLogger.AddSendEntry(m_pEncoding.GetString(buffer, 0, sentCount), sentCount); - } - else - { - m_pLogger.AddSendEntry("Big binary data, sent " + sentCount + " bytes.", sentCount); - } - } - } - - /// - /// Writes specified line to socket. If line isn't CRLF terminated, CRLF is added automatically. - /// - /// Line to write to socket. - public void WriteLine(string line) - { - if (Logger != null) - { - Logger.AddSendEntry(line, line.Length); - } - WriteLine(m_pEncoding.GetBytes(line)); - } - - /// - /// Writes specified line to socket. If line isn't CRLF terminated, CRLF is added automatically. - /// - /// Line to write to socket. - public void WriteLine(byte[] line) - { - // Don't allow to wait after we send data, because there won't no more data - m_pSocket.NoDelay = true; - - // is missing, add it - if (line.Length < 2 || - (line[line.Length - 2] != (byte) '\r' && line[line.Length - 1] != (byte) '\n')) - { - byte[] newLine = new byte[line.Length + 2]; - Array.Copy(line, newLine, line.Length); - newLine[newLine.Length - 2] = (byte) '\r'; - newLine[newLine.Length - 1] = (byte) '\n'; - - line = newLine; - } - - if (m_SSL) - { - m_pSslStream.Write(line); - } - else - { - m_pSocketStream.Write(line, 0, line.Length); - } - - m_WrittenCount += line.Length; - m_LastActivityDate = DateTime.Now; - - // Logging stuff - if (m_pLogger != null) - { - if (line.Length < 200) - { - m_pLogger.AddSendEntry(m_pEncoding.GetString(line), line.Length); - } - else - { - m_pLogger.AddSendEntry("Big binary line, sent " + line.Length + " bytes.", line.Length); - } - } - } - - /// - /// Writes period terminated string to socket. The data is terminated by a line containing only a period, that is, - /// the character sequence "<CRLF>.<CRLF>". Before sending a line of text, check the first - /// character of the line.If it is a period, one additional period is inserted at the beginning of the line. - /// - /// String data to write. - public void WritePeriodTerminated(string data) - { - WritePeriodTerminated(new MemoryStream(m_pEncoding.GetBytes(data))); - } - - /// - /// Writes period terminated data to socket. The data is terminated by a line containing only a period, that is, - /// the character sequence "<CRLF>.<CRLF>". Before sending a line of text, check the first - /// character of the line.If it is a period, one additional period is inserted at the beginning of the line. - /// - /// Stream which data to write. Reading begins from stream current position and is readed to EOS. - public void WritePeriodTerminated(Stream stream) - { - /* Before sending a line of text, check the first character of the line. - If it is a period, one additional period is inserted at the beginning of the line. - */ - - int countSent = 0; - byte[] buffer = new byte[4000]; - int positionInBuffer = 0; - bool CRLF = false; - int lastByte = -1; - int currentByte = stream.ReadByte(); - while (currentByte > -1) - { - // We have CRLF, mark it up - if (lastByte == '\r' && currentByte == '\n') - { - CRLF = true; - } - // There is CRLF + current byte - else if (CRLF) - { - // If it is a period, one additional period is inserted at the beginning of the line. - if (currentByte == '.') - { - buffer[positionInBuffer] = (byte) '.'; - positionInBuffer++; - } - - // CRLF handled, reset it - CRLF = false; - } - - buffer[positionInBuffer] = (byte) currentByte; - positionInBuffer++; - - lastByte = currentByte; - - // Buffer is filled up, write buffer to socket. - if (positionInBuffer > (4000 - 10)) - { - if (m_SSL) - { - m_pSslStream.Write(buffer, 0, positionInBuffer); - } - else - { - m_pSocketStream.Write(buffer, 0, positionInBuffer); - } - countSent += positionInBuffer; - m_WrittenCount += positionInBuffer; - m_LastActivityDate = DateTime.Now; - positionInBuffer = 0; - } - - currentByte = stream.ReadByte(); - } - - // We have readed all data, write budder data + . or . if data not terminated. - if (!CRLF) - { - buffer[positionInBuffer] = (byte) '\r'; - positionInBuffer++; - buffer[positionInBuffer] = (byte) '\n'; - positionInBuffer++; - } - - buffer[positionInBuffer] = (byte) '.'; - positionInBuffer++; - buffer[positionInBuffer] = (byte) '\r'; - positionInBuffer++; - buffer[positionInBuffer] = (byte) '\n'; - positionInBuffer++; - - if (m_SSL) - { - m_pSslStream.Write(buffer, 0, positionInBuffer); - } - else - { - m_pSocketStream.Write(buffer, 0, positionInBuffer); - } - countSent += positionInBuffer; - m_WrittenCount += positionInBuffer; - m_LastActivityDate = DateTime.Now; - //-------------------------------------------------------------------------------------// - - // Logging stuff - if (m_pLogger != null) - { - if (countSent < 200) - { - m_pLogger.AddSendEntry(m_pEncoding.GetString(buffer), buffer.Length); - } - else - { - m_pLogger.AddSendEntry("Binary data, sent " + countSent + " bytes.", countSent); - } - } - } - - /// - /// Begins reading line from socket asynchrounously. - /// If maximum allowed line length is exceeded line is read to end, but isn't stored to buffer and exception - /// is thrown after line reading. - /// - /// Stream where to store readed line. - /// Maximum line length in bytes. - /// User data. - /// The method to be called when the asynchronous line read operation is completed. - public void BeginReadLine(Stream stream, int maxLineLength, object tag, SocketCallBack callback) - { - TryToReadLine(callback, tag, stream, maxLineLength, -1, 0); - } - - /// - /// Begins reading specified amount of data from socket asynchronously. - /// - /// Stream where to store readed data. - /// Specifies number of bytes to read from socket. - /// User data. - /// The method to be called when the asynchronous read operation is completed. - public void BeginReadSpecifiedLength(Stream stream, - int lengthToRead, - object tag, - SocketCallBack callback) - { - TryToReadReadSpecifiedLength(stream, lengthToRead, tag, callback, 0); - } - - /// - /// Begins reading period terminated data. The data is terminated by a line containing only a period, that is, - /// the character sequence "<CRLF>.<CRLF>". - /// When a line of text is received, it checks the line. If the line is composed of a single period, - /// it is treated as the end of data indicator. If the first character is a period and there are - /// other characters on the line, the first character is deleted. - /// If maximum allowed data length is exceeded data is read to end, but isn't stored to stream and exception - /// is thrown after data reading. - /// - /// Stream where to store readed data. - /// Maximum data length in bytes. - /// User data. - /// The method to be called when the asynchronous read operation is completed. - public void BeginReadPeriodTerminated(Stream stream, - int maxLength, - object tag, - SocketCallBack callback) - { - TryToReadPeriodTerminated(callback, tag, stream, maxLength, -1, 0, false, false); - } - - /// - /// Begins writing specified data to socket. - /// - /// Stream which data to write to socket. Reading starts from stream current position and will be readed to EOS. - /// User data. - /// The method to be called when the asynchronous write operation is completed. - public void BeginWrite(Stream stream, object tag, SocketCallBack callback) - { - // Allow socket to optimise sends - m_pSocket.NoDelay = false; - - BeginProcessingWrite(stream, tag, callback, 0); - } - - /// - /// Begins specified line sending to socket asynchronously. - /// - /// Line to send. - /// The method to be called when the asynchronous line write operation is completed. - public void BeginWriteLine(string line, SocketCallBack callback) - { - // Don't allow to wait after we send data, because there won't no more data - m_pSocket.NoDelay = true; - - BeginWriteLine(line, null, callback); - } - - /// - /// Begins specified line sending to socket asynchronously. - /// - /// Line to send. - /// User data. - /// The method to be called when the asynchronous line write operation is completed. - public void BeginWriteLine(string line, object tag, SocketCallBack callback) - { - // Don't allow to wait after we send data, because there won't no more data - m_pSocket.NoDelay = true; - - if (!line.EndsWith("\r\n")) - { - line += "\r\n"; - } - - byte[] lineBytes = m_pEncoding.GetBytes(line); - if (m_SSL) - { - m_pSslStream.BeginWrite(lineBytes, - 0, - lineBytes.Length, - OnBeginWriteLineCallback, - new[] {tag, callback, lineBytes}); - } - else - { - m_pSocketStream.BeginWrite(lineBytes, - 0, - lineBytes.Length, - OnBeginWriteLineCallback, - new[] {tag, callback, lineBytes}); - } - } - - /// - /// Begins writing period terminated data to socket. The data is terminated by a line containing only a period, that is, - /// the character sequence "<CRLF>.<CRLF>". Before sending a line of text, check the first - /// character of the line.If it is a period, one additional period is inserted at the beginning of the line. - /// - /// Stream which data to write. Reading begins from stream current position and is readed to EOS. - /// User data. - /// The method to be called when the asynchronous write operation is completed. - public void BeginWritePeriodTerminated(Stream stream, object tag, SocketCallBack callback) - { - BeginWritePeriodTerminated(stream, false, tag, callback); - } - - /// - /// Begins writing period terminated data to socket. The data is terminated by a line containing only a period, that is, - /// the character sequence "<CRLF>.<CRLF>". Before sending a line of text, check the first - /// character of the line.If it is a period, one additional period is inserted at the beginning of the line. - /// - /// Stream which data to write. Reading begins from stream current position and is readed to EOS. - /// Specifies if stream is closed after write operation has completed. - /// User data. - /// The method to be called when the asynchronous write operation is completed. - public void BeginWritePeriodTerminated(Stream stream, - bool closeStream, - object tag, - SocketCallBack callback) - { - // Allow socket to optimise sends - m_pSocket.NoDelay = false; - - _BeginWritePeriodTerminated_State state = new _BeginWritePeriodTerminated_State(stream, - closeStream, - tag, - callback); - BeginProcessingWritePeriodTerminated(state); - } - - /// - /// Sends data to the specified end point. - /// - /// Data to send. - /// Remote endpoint where to send data. - /// Returns number of bytes actualy sent. - public int SendTo(byte[] data, EndPoint remoteEP) - { - return m_pSocket.SendTo(data, remoteEP); - } - - #endregion - - #region Utility methods - - private bool RemoteCertificateValidationCallback(Object sender, - X509Certificate certificate, - X509Chain chain, - SslPolicyErrors sslPolicyErrors) - { - if (sslPolicyErrors == SslPolicyErrors.None || - sslPolicyErrors == SslPolicyErrors.RemoteCertificateNameMismatch) - { - return true; - } - - // Do not allow this client to communicate with unauthenticated servers. - return false; - } - - /// - /// Tries to read line from socket data buffer. If buffer doesn't contain line, - /// next buffer data block is getted asynchronously and this method is called again. - /// - /// The method to be called when the asynchronous line read operation is completed. - /// User data. - /// Stream where to store readed data. - /// Specifies maximum line legth. - /// Last byte what was readed pevious method call or -1 if first method call. - /// Specifies count of bytes readed. - private void TryToReadLine(SocketCallBack callback, - object tag, - Stream stream, - int maxLineLength, - int lastByte, - int readedCount) - { - // There is no data in buffer, buffer next block asynchronously. - if (m_AvailableInBuffer == 0) - { - BeginBufferDataBlock(OnBeginReadLineBufferingCompleted, - new[] {callback, tag, stream, maxLineLength, lastByte, readedCount}); - return; - } - - // Delay last byte writing, this is because CR, if next is LF, then skip CRLF and terminate reading. - - // This is first method call, buffer 1 byte - if (lastByte == -1) - { - lastByte = ReadByte(); - readedCount++; - - // We use last byte, buffer next block asynchronously. - if (m_AvailableInBuffer == 0) - { - BeginBufferDataBlock(OnBeginReadLineBufferingCompleted, - new[] {callback, tag, stream, maxLineLength, lastByte, readedCount}); - return; - } - } - - int currentByte = ReadByte(); - readedCount++; - while (currentByte > -1) - { - // We got line - if (lastByte == (byte) '\r' && currentByte == (byte) '\n') - { - // Logging stuff - if (m_pLogger != null) - { - if (stream.CanSeek && stream.Length < 200) - { - byte[] readedData = new byte[stream.Length]; - stream.Position = 0; - stream.Read(readedData, 0, readedData.Length); - m_pLogger.AddReadEntry(m_pEncoding.GetString(readedData), readedCount); - } - else - { - m_pLogger.AddReadEntry("Big binary line, readed " + readedCount + " bytes.", - readedCount); - } - } - - // Maximum allowed length exceeded - if (readedCount > maxLineLength) - { - if (callback != null) - { - callback(SocketCallBackResult.LengthExceeded, - 0, - new ReadException(ReadReplyCode.LengthExceeded, - "Maximum allowed data length exceeded !"), - tag); - } - } - - // Line readed ok, call callback. - if (callback != null) - { - callback(SocketCallBackResult.Ok, readedCount, null, tag); - } - - return; - } - else - { - // Maximum allowed length exceeded, just don't store data. - if (readedCount < maxLineLength) - { - stream.WriteByte((byte) lastByte); - } - } - - // Read next byte - lastByte = currentByte; - if (m_AvailableInBuffer > 0) - { - currentByte = ReadByte(); - readedCount++; - } - // We have use all data in the buffer, buffer next block asynchronously. - else - { - BeginBufferDataBlock(OnBeginReadLineBufferingCompleted, - new[] {callback, tag, stream, maxLineLength, lastByte, readedCount}); - return; - } - } - - // We should not reach there, if so then socket closed - // Logging stuff - if (m_pLogger != null) - { - m_pLogger.AddTextEntry("Remote host closed socket !"); - } - if (callback != null) - { - callback(SocketCallBackResult.SocketClosed, - 0, - new ReadException(ReadReplyCode.SocketClosed, - "Connected host closed socket, read line terminated unexpectedly !"), - tag); - } - } - - /// - /// This method is called after asynchronous data buffering is completed. - /// - /// Exception what happened on method execution or null, if operation completed sucessfully. - /// User data. - private void OnBeginReadLineBufferingCompleted(Exception x, object tag) - { - object[] param = (object[]) tag; - SocketCallBack callback = (SocketCallBack) param[0]; - object callbackTag = param[1]; - Stream stream = (Stream) param[2]; - int maxLineLength = (int) param[3]; - int lastByte = (int) param[4]; - int readedCount = (int) param[5]; - - if (x == null) - { - // We didn't get data, this can only happen if socket closed. - if (m_AvailableInBuffer == 0) - { - // Logging stuff - if (m_pLogger != null) - { - m_pLogger.AddTextEntry("Remote host closed socket !"); - } - - callback(SocketCallBackResult.SocketClosed, 0, null, callbackTag); - } - else - { - TryToReadLine(callback, callbackTag, stream, maxLineLength, lastByte, readedCount); - } - } - else - { - callback(SocketCallBackResult.Exception, 0, x, callbackTag); - } - } - - /// - /// Tries to read specified length of data from socket data buffer. If buffer doesn't contain data, - /// next buffer data block is getted asynchronously and this method is called again. - /// - /// Stream where to store readed data. - /// Specifies number of bytes to read from socket. - /// User data. - /// The method to be called when the asynchronous read operation is completed. - /// Specifies count of bytes readed. - private void TryToReadReadSpecifiedLength(Stream stream, - int lengthToRead, - object tag, - SocketCallBack callback, - int readedCount) - { - if (lengthToRead == 0) - { - // Data readed ok, call callback. - if (callback != null) - { - callback(SocketCallBackResult.Ok, readedCount, null, tag); - } - return; - } - - // There is no data in buffer, buffer next block asynchronously. - if (m_AvailableInBuffer == 0) - { - BeginBufferDataBlock(OnBeginReadSpecifiedLengthBufferingCompleted, - new[] {callback, tag, stream, lengthToRead, readedCount}); - return; - } - - // Buffer has less data than that we need - int lengthLeftForReading = lengthToRead - readedCount; - if (lengthLeftForReading > m_AvailableInBuffer) - { - stream.Write(m_Buffer, m_OffsetInBuffer, m_AvailableInBuffer); - stream.Flush(); - - readedCount += m_AvailableInBuffer; - // We used buffer directly, sync buffer info !!! - m_OffsetInBuffer = 0; - m_AvailableInBuffer = 0; - - BeginBufferDataBlock(OnBeginReadSpecifiedLengthBufferingCompleted, - new[] {callback, tag, stream, lengthToRead, readedCount}); - } - // Buffer contains all data we need - else - { - stream.Write(m_Buffer, m_OffsetInBuffer, lengthLeftForReading); - stream.Flush(); - - readedCount += lengthLeftForReading; - // We used buffer directly, sync buffer info !!! - m_OffsetInBuffer += lengthLeftForReading; - m_AvailableInBuffer -= lengthLeftForReading; - - // Logging stuff - if (m_pLogger != null) - { - if (stream.CanSeek && stream.Length < 200) - { - byte[] readedData = new byte[stream.Length]; - stream.Position = 0; - stream.Read(readedData, 0, readedData.Length); - m_pLogger.AddReadEntry(m_pEncoding.GetString(readedData), lengthToRead); - } - else - { - m_pLogger.AddReadEntry("Big binary data, readed " + readedCount + " bytes.", - readedCount); - } - } - - // Data readed ok, call callback. - if (callback != null) - { - callback(SocketCallBackResult.Ok, readedCount, null, tag); - } - } - } - - /// - /// This method is called after asynchronous data buffering is completed. - /// - /// Exception what happened on method execution or null, if operation completed sucessfully. - /// User data. - private void OnBeginReadSpecifiedLengthBufferingCompleted(Exception x, object tag) - { - object[] param = (object[]) tag; - SocketCallBack callback = (SocketCallBack) param[0]; - object callbackTag = param[1]; - Stream stream = (Stream) param[2]; - int lengthToRead = (int) param[3]; - int readedCount = (int) param[4]; - - if (x == null) - { - // We didn't get data, this can only happen if socket closed. - if (m_AvailableInBuffer == 0) - { - // Logging stuff - if (m_pLogger != null) - { - m_pLogger.AddTextEntry("Remote host closed socket !"); - } - - callback(SocketCallBackResult.SocketClosed, 0, null, callbackTag); - } - else - { - TryToReadReadSpecifiedLength(stream, lengthToRead, callbackTag, callback, readedCount); - } - } - else - { - callback(SocketCallBackResult.Exception, 0, x, callbackTag); - } - } - - /// - /// Tries to read period terminated data from socket data buffer. If buffer doesn't contain - /// period terminated data,next buffer data block is getted asynchronously and this method is called again. - /// - /// The method to be called when the asynchronous period terminated read operation is completed. - /// User data. - /// Stream where to store readed data. - /// Specifies maximum data legth in bytes. - /// Specifies count of bytes readed. - /// Last byte what was readed pevious method call or -1 if first method call. - /// Specifies if there is active line break. - /// Specifies if terminating CRLF is expected. - private void TryToReadPeriodTerminated(SocketCallBack callback, - object tag, - Stream stream, - int maxLength, - int lastByte, - int readedCount, - bool lineBreak, - bool expectCRLF) - { - /* When a line of text is received by the server, it checks the line. - If the line is composed of a single period, it is treated as the - end of data indicator. If the first character is a period and - there are other characters on the line, the first character is deleted. - */ - - // There is no data in buffer, buffer next block asynchronously. - if (m_AvailableInBuffer == 0) - { - BeginBufferDataBlock(OnBeginReadPeriodTerminatedBufferingCompleted, - new[] - { - callback, tag, stream, maxLength, lastByte, readedCount, lineBreak, - expectCRLF - }); - return; - } - - // Delay last byte writing, this is because CR, if next is LF, then last byte isn't written. - - // This is first method call, buffer 1 byte - if (lastByte == -1) - { - lastByte = ReadByte(); - readedCount++; - - // We used last byte, buffer next block asynchronously. - if (m_AvailableInBuffer == 0) - { - BeginBufferDataBlock(OnBeginReadPeriodTerminatedBufferingCompleted, - new[] - { - callback, tag, stream, maxLength, lastByte, readedCount, - lineBreak, expectCRLF - }); - return; - } - } - - byte[] buffer = new byte[8000]; - int positionInBuffer = 0; - - int currentByte = ReadByte(); - readedCount++; - while (currentByte > -1) - { - // We got + 1 char, we must skip that char if it is '.'. - if (lineBreak) - { - lineBreak = false; - - // We must skip this char if it is '.' - if (currentByte == '.') - { - expectCRLF = true; - - // Read next byte - if (m_AvailableInBuffer > 0) - { - currentByte = ReadByte(); - readedCount++; - } - // We have use all data in the buffer, buffer next block asynchronously. - else - { - // There is data in buffer, flush it - if (positionInBuffer > 0) - { - stream.Write(buffer, 0, positionInBuffer); - positionInBuffer = 0; - } - - BeginBufferDataBlock(OnBeginReadPeriodTerminatedBufferingCompleted, - new[] - { - callback, tag, stream, maxLength, lastByte, readedCount, - lineBreak, expectCRLF - }); - return; - } - } - } - // We got - else if (lastByte == (byte) '\r' && currentByte == (byte) '\n') - { - lineBreak = true; - - // We have ., skip last . - if (expectCRLF) - { - // There is data in buffer, flush it - if (positionInBuffer > 0) - { - stream.Write(buffer, 0, positionInBuffer); - positionInBuffer = 0; - } - - // Logging stuff - if (m_pLogger != null) - { - if (stream.CanSeek && stream.Length < 200) - { - byte[] readedData = new byte[stream.Length]; - stream.Position = 0; - stream.Read(readedData, 0, readedData.Length); - m_pLogger.AddReadEntry(m_pEncoding.GetString(readedData), readedCount); - } - else - { - m_pLogger.AddReadEntry("Big binary data, readed " + readedCount + " bytes.", - readedCount); - } - } - - // Maximum allowed length exceeded - if (readedCount > maxLength) - { - if (callback != null) - { - callback(SocketCallBackResult.LengthExceeded, - 0, - new ReadException(ReadReplyCode.LengthExceeded, - "Maximum allowed data length exceeded !"), - tag); - } - return; - } - - // Data readed ok, call callback. - if (callback != null) - { - callback(SocketCallBackResult.Ok, readedCount, null, tag); - } - return; - } - } - - // current char isn't CRLF part, so it isn't . terminator. - if (expectCRLF && !(currentByte == (byte) '\r' || currentByte == (byte) '\n')) - { - expectCRLF = false; - } - - // Maximum allowed length exceeded, just don't store data. - if (readedCount < maxLength) - { - // Buffer is filled up, write buffer to stream - if (positionInBuffer > (buffer.Length - 2)) - { - stream.Write(buffer, 0, positionInBuffer); - positionInBuffer = 0; - } - - buffer[positionInBuffer] = (byte) lastByte; - positionInBuffer++; - } - - // Read next byte - lastByte = currentByte; - if (m_AvailableInBuffer > 0) - { - currentByte = ReadByte(); - readedCount++; - } - // We have use all data in the buffer, buffer next block asynchronously. - else - { - // There is data in buffer, flush it - if (positionInBuffer > 0) - { - stream.Write(buffer, 0, positionInBuffer); - positionInBuffer = 0; - } - - BeginBufferDataBlock(OnBeginReadPeriodTerminatedBufferingCompleted, - new[] - { - callback, tag, stream, maxLength, lastByte, readedCount, - lineBreak, expectCRLF - }); - return; - } - } - - // We should never reach here. - if (callback != null) - { - callback(SocketCallBackResult.Exception, - 0, - new Exception( - "Never should reach there ! method TryToReadPeriodTerminated out of while loop."), - tag); - } - } - - /// - /// This method is called after asynchronous data buffering is completed. - /// - /// Exception what happened on method execution or null, if operation completed sucessfully. - /// User data. - private void OnBeginReadPeriodTerminatedBufferingCompleted(Exception x, object tag) - { - object[] param = (object[]) tag; - SocketCallBack callback = (SocketCallBack) param[0]; - object callbackTag = param[1]; - Stream stream = (Stream) param[2]; - int maxLength = (int) param[3]; - int lastByte = (int) param[4]; - int readedCount = (int) param[5]; - bool lineBreak = (bool) param[6]; - bool expectCRLF = (bool) param[7]; - - if (x == null) - { - // We didn't get data, this can only happen if socket closed. - if (m_AvailableInBuffer == 0) - { - // Logging stuff - if (m_pLogger != null) - { - m_pLogger.AddTextEntry("Remote host closed socket !"); - } - - callback(SocketCallBackResult.SocketClosed, 0, null, callbackTag); - } - else - { - TryToReadPeriodTerminated(callback, - callbackTag, - stream, - maxLength, - lastByte, - readedCount, - lineBreak, - expectCRLF); - } - } - else - { - callback(SocketCallBackResult.Exception, 0, x, callbackTag); - } - } - - /// - /// Starts sending data block to socket. - /// - /// Stream which data to write. - /// User data. - /// The method to be called when the asynchronous write operation is completed - /// Specifies how many data is sent. - private void BeginProcessingWrite(Stream stream, object tag, SocketCallBack callback, int countSent) - { - byte[] buffer = new byte[4000]; - int readedCount = stream.Read(buffer, 0, buffer.Length); - // There data to send - if (readedCount > 0) - { - countSent += readedCount; - m_WrittenCount += readedCount; - - if (m_SSL) - { - m_pSslStream.BeginWrite(buffer, - 0, - readedCount, - OnBeginWriteCallback, - new[] {stream, tag, callback, countSent}); - } - else - { - m_pSocketStream.BeginWrite(buffer, - 0, - readedCount, - OnBeginWriteCallback, - new[] {stream, tag, callback, countSent}); - } - } - // We have sent all data - else - { - // Logging stuff - if (m_pLogger != null) - { - if (stream.CanSeek && stream.Length < 200) - { - byte[] sentData = new byte[stream.Length]; - stream.Position = 0; - stream.Read(sentData, 0, sentData.Length); - m_pLogger.AddSendEntry(m_pEncoding.GetString(sentData), countSent); - } - else - { - m_pLogger.AddSendEntry("Big binary data, sent " + countSent + " bytes.", countSent); - } - } - - // Line sent ok, call callback. - if (callback != null) - { - callback(SocketCallBackResult.Ok, countSent, null, tag); - } - } - } - - /// - /// This method is called after asynchronous datablock send is completed. - /// - /// - private void OnBeginWriteCallback(IAsyncResult ar) - { - object[] param = (object[]) ar.AsyncState; - Stream stream = (Stream) param[0]; - object tag = param[1]; - SocketCallBack callBack = (SocketCallBack) param[2]; - int countSent = (int) param[3]; - - try - { - if (m_SSL) - { - m_pSslStream.EndWrite(ar); - } - else - { - m_pSocketStream.EndWrite(ar); - } - - m_LastActivityDate = DateTime.Now; - - BeginProcessingWrite(stream, tag, callBack, countSent); - } - catch (Exception x) - { - if (callBack != null) - { - callBack(SocketCallBackResult.Exception, 0, x, tag); - } - } - } - - /// - /// This method is called after asynchronous WriteLine is completed. - /// - /// - private void OnBeginWriteLineCallback(IAsyncResult ar) - { - object[] param = (object[]) ar.AsyncState; - object tag = param[0]; - SocketCallBack callBack = (SocketCallBack) param[1]; - byte[] lineBytes = (byte[]) param[2]; - - try - { - if (m_SSL) - { - m_pSslStream.EndWrite(ar); - } - else - { - m_pSocketStream.EndWrite(ar); - } - - m_WrittenCount += lineBytes.Length; - m_LastActivityDate = DateTime.Now; - - // Logging stuff - if (m_pLogger != null) - { - if (lineBytes.Length < 200) - { - m_pLogger.AddSendEntry(m_pEncoding.GetString(lineBytes), lineBytes.Length); - } - else - { - m_pLogger.AddSendEntry("Big binary line, sent " + lineBytes.Length + " bytes.", - lineBytes.Length); - } - } - - // Line sent ok, call callback. - if (callBack != null) - { - callBack(SocketCallBackResult.Ok, lineBytes.Length, null, tag); - } - } - catch (Exception x) - { - if (callBack != null) - { - callBack(SocketCallBackResult.Exception, 0, x, tag); - } - } - } - - /// - /// Reads data block from state.Stream and begins writing it to socket. - /// This method is looped while all data has been readed from state.Stream, then sate.Callback is called. - /// - /// State info. - private void BeginProcessingWritePeriodTerminated(_BeginWritePeriodTerminated_State state) - { - /* Before sending a line of text, check the first character of the line. - If it is a period, one additional period is inserted at the beginning of the line. - */ - - byte[] buffer = new byte[4000]; - int positionInBuffer = 0; - int currentByte = state.Stream.ReadByte(); - while (currentByte > -1) - { - // We have CRLF, mark it up - if (state.LastByte == '\r' && currentByte == '\n') - { - state.HasCRLF = true; - } - // There is CRLF + current byte - else if (state.HasCRLF) - { - // If it is a period, one additional period is inserted at the beginning of the line. - if (currentByte == '.') - { - buffer[positionInBuffer] = (byte) '.'; - positionInBuffer++; - } - - // CRLF handled, reset it - state.HasCRLF = false; - } - - buffer[positionInBuffer] = (byte) currentByte; - positionInBuffer++; - - state.LastByte = currentByte; - - // Buffer is filled up, begin writing buffer to socket. - if (positionInBuffer > (4000 - 10)) - { - state.CountSent += positionInBuffer; - m_WrittenCount += positionInBuffer; - - if (m_SSL) - { - m_pSslStream.BeginWrite(buffer, - 0, - positionInBuffer, - OnBeginWritePeriodTerminatedCallback, - state); - } - else - { - m_pSocketStream.BeginWrite(buffer, - 0, - positionInBuffer, - OnBeginWritePeriodTerminatedCallback, - state); - } - return; - } - - currentByte = state.Stream.ReadByte(); - } - - // We have readed all data, write . or . if data not terminated. - if (!state.HasCRLF) - { - buffer[positionInBuffer] = (byte) '\r'; - positionInBuffer++; - buffer[positionInBuffer] = (byte) '\n'; - positionInBuffer++; - } - - buffer[positionInBuffer] = (byte) '.'; - positionInBuffer++; - buffer[positionInBuffer] = (byte) '\r'; - positionInBuffer++; - buffer[positionInBuffer] = (byte) '\n'; - positionInBuffer++; - - if (m_SSL) - { - m_pSslStream.Write(buffer, 0, positionInBuffer); - } - else - { - m_pSocketStream.Write(buffer, 0, positionInBuffer); - } - state.CountSent += positionInBuffer; - m_WrittenCount += positionInBuffer; - m_LastActivityDate = DateTime.Now; - //-------------------------------------------------------------------------------------// - - // Logging stuff - if (m_pLogger != null) - { - if (state.CountSent < 200) - { - m_pLogger.AddSendEntry(m_pEncoding.GetString(buffer), buffer.Length); - } - else - { - m_pLogger.AddSendEntry("Binary data, sent " + state.CountSent + " bytes.", state.CountSent); - } - } - - // We don't need stream any more, close it - if (state.CloseStream) - { - try - { - state.Stream.Close(); - } - catch {} - } - - // Data sent ok, call callback. - if (state.Callback != null) - { - state.Callback(SocketCallBackResult.Ok, state.CountSent, null, state.Tag); - } - } - - /// - /// This method is called after asynchronous datablock send is completed. - /// - /// - private void OnBeginWritePeriodTerminatedCallback(IAsyncResult ar) - { - _BeginWritePeriodTerminated_State state = (_BeginWritePeriodTerminated_State) ar.AsyncState; - - try - { - if (m_SSL) - { - m_pSslStream.EndWrite(ar); - } - else - { - m_pSocketStream.EndWrite(ar); - } - - m_LastActivityDate = DateTime.Now; - - BeginProcessingWritePeriodTerminated(state); - } - catch (Exception x) - { - // We don't need stream any more, close it - if (state.CloseStream) - { - try - { - state.Stream.Close(); - } - catch {} - } - - if (state.Callback != null) - { - state.Callback(SocketCallBackResult.Exception, 0, x, state.Tag); - } - } - } - - /// - /// Buffers data from socket if needed. If there is data in buffer, no buffering is done. - /// - private void BufferDataBlock() - { - lock (this) - { - // There is no data in buffer, buffer next data block - if (m_AvailableInBuffer == 0) - { - m_OffsetInBuffer = 0; - - if (m_SSL) - { - m_AvailableInBuffer = m_pSslStream.Read(m_Buffer, 0, m_Buffer.Length); - } - else - { - m_AvailableInBuffer = m_pSocket.Receive(m_Buffer); - } - m_ReadedCount += m_AvailableInBuffer; - m_LastActivityDate = DateTime.Now; - } - } - } - - /// - /// Start buffering data from socket asynchronously. - /// - /// The method to be called when the asynchronous data buffering operation is completed. - /// User data. - private void BeginBufferDataBlock(BufferDataBlockCompleted callback, object tag) - { - if (m_AvailableInBuffer == 0) - { - m_OffsetInBuffer = 0; - - if (m_SSL) - { - m_pSslStream.BeginRead(m_Buffer, - 0, - m_Buffer.Length, - OnBeginBufferDataBlockCallback, - new[] {callback, tag}); - } - else - { - m_pSocket.BeginReceive(m_Buffer, - 0, - m_Buffer.Length, - SocketFlags.None, - OnBeginBufferDataBlockCallback, - new[] {callback, tag}); - } - } - } - - /// - /// This method is called after asynchronous BeginBufferDataBlock is completed. - /// - /// - private void OnBeginBufferDataBlockCallback(IAsyncResult ar) - { - object[] param = (object[]) ar.AsyncState; - BufferDataBlockCompleted callback = (BufferDataBlockCompleted) param[0]; - object tag = param[1]; - - try - { - // Socket closed by this.Disconnect() or closed by remote host. - if (m_pSocket == null || !m_pSocket.Connected) - { - m_AvailableInBuffer = 0; - } - else - { - if (m_SSL) - { - m_AvailableInBuffer = m_pSslStream.EndRead(ar); - } - else - { - m_AvailableInBuffer = m_pSocket.EndReceive(ar); - } - } - m_ReadedCount += m_AvailableInBuffer; - m_LastActivityDate = DateTime.Now; - - if (callback != null) - { - callback(null, tag); - } - } - catch (Exception x) - { - if (callback != null) - { - callback(x, tag); - } - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SocketLogEntry.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SocketLogEntry.cs deleted file mode 100644 index 10bf81e0e..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SocketLogEntry.cs +++ /dev/null @@ -1,78 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - /// - /// Socket log entry. - /// - public class SocketLogEntry - { - #region Members - - private readonly long m_Size; - private readonly string m_Text = ""; - private readonly SocketLogEntryType m_Type = SocketLogEntryType.FreeText; - - #endregion - - #region Properties - - /// - /// Gets log text. - /// - public string Text - { - get { return m_Text; } - } - - /// - /// Gets size of data readed or sent. - /// - public long Size - { - get { return m_Size; } - } - - /// - /// Gets log entry type. - /// - public SocketLogEntryType Type - { - get { return m_Type; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Log text. - /// Data size. - /// Log entry type - public SocketLogEntry(string text, long size, SocketLogEntryType type) - { - m_Text = text; - m_Type = type; - m_Size = size; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SocketLogEntryType.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SocketLogEntryType.cs deleted file mode 100644 index 23db94449..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SocketLogEntryType.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - /// - /// Log entry type. - /// - public enum SocketLogEntryType - { - /// - /// Data is readed from remote endpoint. - /// - ReadFromRemoteEP = 0, - - /// - /// Data is sent to remote endpoint. - /// - SendToRemoteEP = 1, - - /// - /// Comment log entry. - /// - FreeText = 2, - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SocketLogger.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SocketLogger.cs deleted file mode 100644 index 81a624b60..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SocketLogger.cs +++ /dev/null @@ -1,276 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - #region usings - - using System; - using System.Collections.Generic; - using System.Net; - using System.Net.Sockets; - - #endregion - - /// - /// Socket logger. - /// - public class SocketLogger - { -#if (DEBUG) - private const int logDumpCount = 1; -#else - private const int logDumpCount = 100; -#endif - - #region Members - - private readonly List m_pEntries; - private readonly LogEventHandler m_pLogHandler; - private readonly Socket m_pSocket; - private bool m_FirstLogPart = true; - private IPEndPoint m_pLoaclEndPoint; - private IPEndPoint m_pRemoteEndPoint; - private string m_SessionID = ""; - private string m_UserName = ""; - - #endregion - - #region Properties - - /// - /// Gets or sets session ID. - /// - public string SessionID - { - get { return m_SessionID; } - - set { m_SessionID = value; } - } - - /// - /// Gets or sets authenticated user name. - /// - public string UserName - { - get { return m_UserName; } - - set { m_UserName = value; } - } - - /// - /// Gets current cached log entries. - /// - public SocketLogEntry[] LogEntries - { - get { return m_pEntries.ToArray(); } - } - - /// - /// Gets local endpoint. - /// - public IPEndPoint LocalEndPoint - { - get { return m_pLoaclEndPoint; } - } - - /// - /// Gets remote endpoint. - /// - public IPEndPoint RemoteEndPoint - { - get { return m_pRemoteEndPoint; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// - /// - public SocketLogger(Socket socket, LogEventHandler logHandler) - { - m_pSocket = socket; - m_pLogHandler = logHandler; - - m_pEntries = new List(); - } - - #endregion - - #region Methods - - /// - /// Converts log entries to string. - /// - /// Socket logger. - /// Specifies if first log part of multipart log. - /// Specifies if last log part (logging ended). - /// - public static string LogEntriesToString(SocketLogger logger, bool firstLogPart, bool lastLogPart) - { - string logText = "//----- Sys: 'Session:'" + logger.SessionID + " added " + DateTime.Now + "\r\n"; - if (!firstLogPart) - { - logText = "//----- Sys: 'Session:'" + logger.SessionID + " partial log continues " + - DateTime.Now + "\r\n"; - } - - foreach (SocketLogEntry entry in logger.LogEntries) - { - if (entry.Type == SocketLogEntryType.ReadFromRemoteEP) - { - logText += CreateEntry(logger, entry.Text, ">>>"); - } - else if (entry.Type == SocketLogEntryType.SendToRemoteEP) - { - logText += CreateEntry(logger, entry.Text, "<<<"); - } - else - { - logText += CreateEntry(logger, entry.Text, "---"); - } - } - - if (lastLogPart) - { - logText += "//----- Sys: 'Session:'" + logger.SessionID + " removed " + DateTime.Now + "\r\n"; - } - else - { - logText += "//----- Sys: 'Session:'" + logger.SessionID + " partial log " + DateTime.Now + - "\r\n"; - } - - return logText; - } - - /// - /// Adds data read(from remoteEndpoint) entry. - /// - /// Log text. - /// Readed text size. - public void AddReadEntry(string text, long size) - { - if (m_pLoaclEndPoint == null || m_pRemoteEndPoint == null) - { - m_pLoaclEndPoint = (IPEndPoint) m_pSocket.LocalEndPoint; - m_pRemoteEndPoint = (IPEndPoint) m_pSocket.RemoteEndPoint; - } - - m_pEntries.Add(new SocketLogEntry(text, size, SocketLogEntryType.ReadFromRemoteEP)); - - OnEntryAdded(); - } - - /// - /// Adds data send(to remoteEndpoint) entry. - /// - /// Log text. - /// Sent text size. - public void AddSendEntry(string text, long size) - { - if (m_pLoaclEndPoint == null || m_pRemoteEndPoint == null) - { - m_pLoaclEndPoint = (IPEndPoint) m_pSocket.LocalEndPoint; - m_pRemoteEndPoint = (IPEndPoint) m_pSocket.RemoteEndPoint; - } - - m_pEntries.Add(new SocketLogEntry(text, size, SocketLogEntryType.SendToRemoteEP)); - - OnEntryAdded(); - } - - /// - /// Adds free text entry. - /// - /// Log text. - public void AddTextEntry(string text) - { - m_pEntries.Add(new SocketLogEntry(text, 0, SocketLogEntryType.FreeText)); - - OnEntryAdded(); - } - - /// - /// Requests to write all in memory log entries to log log file. - /// - public void Flush() - { - if (m_pLogHandler != null) - { - m_pLogHandler(this, new Log_EventArgs(this, m_FirstLogPart, true)); - } - } - - #endregion - - #region Utility methods - - private static string CreateEntry(SocketLogger logger, string text, string prefix) - { - string retVal = ""; - - if (text.EndsWith("\r\n")) - { - text = text.Substring(0, text.Length - 2); - } - - string remIP = "xxx.xxx.xxx.xxx"; - try - { - if (logger.RemoteEndPoint != null) - { - remIP = (logger.RemoteEndPoint).Address.ToString(); - } - } - catch {} - - string[] lines = text.Replace("\r\n", "\n").Split('\n'); - foreach (string line in lines) - { - retVal += "SessionID: " + logger.SessionID + " RemIP: " + remIP + " " + prefix + " '" + - line + "'\r\n"; - } - - return retVal; - } - - /// - /// This method is called when new loge entry has added. - /// - private void OnEntryAdded() - { - // Ask to server to write partial log - if (m_pEntries.Count > logDumpCount) - { - if (m_pLogHandler != null) - { - m_pLogHandler(this, new Log_EventArgs(this, m_FirstLogPart, false)); - } - - m_pEntries.Clear(); - m_FirstLogPart = false; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SocketServer.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SocketServer.cs deleted file mode 100644 index f23a9499d..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SocketServer.cs +++ /dev/null @@ -1,528 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - #region usings - - using System; - using System.Collections.Generic; - using System.ComponentModel; - using System.Diagnostics; - using System.Net; - using System.Net.Sockets; - using System.Threading; - using System.Timers; - using Timer=System.Timers.Timer; - - #endregion - - /// - /// This is base class for Socket and Session based servers. - /// - public abstract class SocketServer : Component - { - #region Nested type: QueuedConnection - - /// - /// This struct holds queued connection info. - /// - private struct QueuedConnection - { - #region Members - - private readonly IPBindInfo m_pBindInfo; - private readonly Socket m_pSocket; - - #endregion - - #region Properties - - /// - /// Gets socket. - /// - public Socket Socket - { - get { return m_pSocket; } - } - - /// - /// Gets bind info. - /// - public IPBindInfo BindInfo - { - get { return m_pBindInfo; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Socket. - /// Bind info. - public QueuedConnection(Socket socket, IPBindInfo bindInfo) - { - m_pSocket = socket; - m_pBindInfo = bindInfo; - } - - #endregion - } - - #endregion - - #region Events - - /// - /// Occurs when server or session has system error(unhandled error). - /// - public event ErrorEventHandler SysError = null; - - #endregion - - #region Members - - private readonly Queue m_pQueuedConnections; - private readonly List m_pSessions; - private readonly Timer m_pTimer; - private int m_MaxBadCommands = 8; - private int m_MaxConnections = 1000; - private IPBindInfo[] m_pBindInfo; - private bool m_Running; - private int m_SessionIdleTimeOut = 30000; - - #endregion - - #region Properties - - /// - /// Gets or set socket binding info. Use this property to specify on which IP,port server - /// listnes and also if is SSL or STARTTLS support. - /// - public IPBindInfo[] BindInfo - { - get { return m_pBindInfo; } - - set - { - if (value == null) - { - throw new NullReferenceException("BindInfo can't be null !"); - } - - //--- See if bindinfo has changed ----------- - bool changed = false; - if (m_pBindInfo.Length != value.Length) - { - changed = true; - } - else - { - for (int i = 0; i < m_pBindInfo.Length; i++) - { - if (!m_pBindInfo[i].Equals(value[i])) - { - changed = true; - break; - } - } - } - //------------------------------------------- - - if (changed) - { - // If server is currently running, stop it before applying bind info. - bool running = m_Running; - if (running) - { - StopServer(); - } - - m_pBindInfo = value; - - // We need to restart server to take effect IP or Port change - if (running) - { - StartServer(); - } - } - } - } - - /// - /// Gets or sets maximum allowed connections. - /// - public int MaxConnections - { - get { return m_MaxConnections; } - - set { m_MaxConnections = value; } - } - - /// - /// Runs and stops server. - /// - public bool Enabled - { - get { return m_Running; } - - set - { - if (value != m_Running & !DesignMode) - { - if (value) - { - StartServer(); - } - else - { - StopServer(); - } - } - } - } - - /// - /// Gets or sets if to log commands. - /// - public bool LogCommands { get; set; } - - /// - /// Session idle timeout in milliseconds. - /// - public int SessionIdleTimeOut - { - get { return m_SessionIdleTimeOut; } - - set { m_SessionIdleTimeOut = value; } - } - - /// - /// Gets or sets maximum bad commands allowed to session. - /// - public int MaxBadCommands - { - get { return m_MaxBadCommands; } - - set { m_MaxBadCommands = value; } - } - - /// - /// Gets active sessions. - /// - public SocketServerSession[] Sessions - { - get { return m_pSessions.ToArray(); } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public SocketServer() - { - m_pSessions = new List(); - m_pQueuedConnections = new Queue(); - m_pTimer = new Timer(15000); - m_pBindInfo = new[] - { - new IPBindInfo(System.Net.Dns.GetHostName(), - IPAddress.Any, - 10000, - SslMode.None, - null) - }; - - m_pTimer.AutoReset = true; - m_pTimer.Elapsed += m_pTimer_Elapsed; - } - - #endregion - - #region Methods - - /// - /// Clean up any resources being used and stops server. - /// - public new void Dispose() - { - base.Dispose(); - - StopServer(); - } - - /// - /// Starts server. - /// - public void StartServer() - { - if (!m_Running) - { - m_Running = true; - - // Start accepting ang queueing connections - Thread tr = new Thread(StartProcCons); - tr.Start(); - - // Start proccessing queued connections - Thread trSessionCreator = new Thread(StartProcQueuedCons); - trSessionCreator.Start(); - - m_pTimer.Enabled = true; - } - } - - /// - /// Stops server. NOTE: Active sessions aren't cancled. - /// - public void StopServer() - { - if (m_Running) - { - m_Running = false; - - // Stop accepting new connections - foreach (IPBindInfo bindInfo in m_pBindInfo) - { - if (bindInfo.Tag != null) - { - ((Socket) bindInfo.Tag).Close(); - bindInfo.Tag = null; - } - } - - // Wait method StartProcCons to exit - Thread.Sleep(100); - } - } - - #endregion - - #region Virtual methods - - /// - /// Initialize and start new session here. Session isn't added to session list automatically, - /// session must add itself to server session list by calling AddSession(). - /// - /// Connected client socket. - /// BindInfo what accepted socket. - protected virtual void InitNewSession(Socket socket, IPBindInfo bindInfo) {} - - #endregion - - #region Event handlers - - private void m_pTimer_Elapsed(object sender, ElapsedEventArgs e) - { - OnSessionTimeoutTimer(); - } - - #endregion - - #region Utility methods - - /// - /// Starts proccessiong incoming connections (Accepts and queues connections). - /// - private void StartProcCons() - { - try - { - CircleCollection binds = new CircleCollection(); - foreach (IPBindInfo bindInfo in m_pBindInfo) - { - Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - s.Bind(new IPEndPoint(bindInfo.IP, bindInfo.Port)); - s.Listen(500); - - bindInfo.Tag = s; - binds.Add(bindInfo); - } - - // Accept connections and queue them - while (m_Running) - { - // We have reached maximum connection limit - if (m_pSessions.Count > m_MaxConnections) - { - // Wait while some active connectins are closed - while (m_pSessions.Count > m_MaxConnections) - { - Thread.Sleep(100); - } - } - - // Get incomong connection - IPBindInfo bindInfo = binds.Next(); - - // There is waiting connection - if (m_Running && ((Socket) bindInfo.Tag).Poll(0, SelectMode.SelectRead)) - { - // Accept incoming connection - Socket s = ((Socket) bindInfo.Tag).Accept(); - - // Add session to queue - lock (m_pQueuedConnections) - { - m_pQueuedConnections.Enqueue(new QueuedConnection(s, bindInfo)); - } - } - - Thread.Sleep(2); - } - } - catch (SocketException x) - { - // Socket listening stopped, happens when StopServer is called. - // We need just skip this error. - if (x.ErrorCode == 10004) {} - else - { - OnSysError("WE MUST NEVER REACH HERE !!! StartProcCons:", x); - } - } - catch (Exception x) - { - OnSysError("WE MUST NEVER REACH HERE !!! StartProcCons:", x); - } - } - - /// - /// Starts queueed connections proccessing (Creates and starts session foreach queued connection). - /// - private void StartProcQueuedCons() - { - try - { - while (m_Running) - { - // There are queued connections, start sessions. - if (m_pQueuedConnections.Count > 0) - { - QueuedConnection connection; - lock (m_pQueuedConnections) - { - connection = m_pQueuedConnections.Dequeue(); - } - - try - { - InitNewSession(connection.Socket, connection.BindInfo); - } - catch (Exception x) - { - OnSysError("StartProcQueuedCons InitNewSession():", x); - } - } - // There are no connections to proccess, delay proccessing. We need to it - // because if there are no connections to proccess, while loop takes too much CPU. - else - { - Thread.Sleep(10); - } - } - } - catch (Exception x) - { - OnSysError("WE MUST NEVER REACH HERE !!! StartProcQueuedCons:", x); - } - } - - /// - /// This method must get timedout sessions and end them. - /// - private void OnSessionTimeoutTimer() - { - try - { - // Close/Remove timed out sessions - lock (m_pSessions) - { - SocketServerSession[] sessions = Sessions; - - // Loop sessions and and call OnSessionTimeout() for timed out sessions. - for (int i = 0; i < sessions.Length; i++) - { - // If session throws exception, handle it here or next sessions timouts are not handled. - try - { - // Session timed out - if (DateTime.Now > - sessions[i].SessionLastDataTime.AddMilliseconds(SessionIdleTimeOut)) - { - sessions[i].OnSessionTimeout(); - } - } - catch (Exception x) - { - OnSysError("OnTimer:", x); - } - } - } - } - catch (Exception x) - { - OnSysError("WE MUST NEVER REACH HERE !!! OnTimer:", x); - } - } - - #endregion - - /// - /// Adds specified session to sessions collection. - /// - /// Session to add. - protected internal void AddSession(SocketServerSession session) - { - lock (m_pSessions) - { - m_pSessions.Add(session); - } - } - - /// - /// Removes specified session from sessions collection. - /// - /// Session to remove. - protected internal void RemoveSession(SocketServerSession session) - { - lock (m_pSessions) - { - m_pSessions.Remove(session); - } - } - - /// - /// - /// - /// - /// - protected internal void OnSysError(string text, Exception x) - { - if (SysError != null) - { - SysError(this, new Error_EventArgs(x, new StackTrace())); - } - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SocketServerSession.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SocketServerSession.cs deleted file mode 100644 index 3664fbf5f..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/SocketServerSession.cs +++ /dev/null @@ -1,254 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - #region usings - - using System; - using System.Net; - - #endregion - - /// - /// This is base class for SocketServer sessions. - /// - public abstract class SocketServerSession - { - #region Members - - private readonly IPBindInfo m_pBindInfo; - private readonly SocketServer m_pServer; - private readonly SocketEx m_pSocket; - private readonly string m_SessionID = ""; - private readonly DateTime m_SessionStartTime; - private string m_UserName = ""; - - #endregion - - #region Properties - - /// - /// Gets session ID. - /// - public string SessionID - { - get { return m_SessionID; } - } - - /// - /// Gets session start time. - /// - public DateTime SessionStartTime - { - get { return m_SessionStartTime; } - } - - /// - /// Gets if session is authenticated. - /// - public bool Authenticated - { - get - { - if (m_UserName.Length > 0) - { - return true; - } - else - { - return false; - } - } - } - - /// - /// Gets authenticated user name. - /// - public string UserName - { - get { return m_UserName; } - } - - /// - /// Gets how many seconds has left before timout is triggered. - /// - public int ExpectedTimeout - { - get - { - return - (int) - ((m_pServer.SessionIdleTimeOut - ((DateTime.Now.Ticks - SessionLastDataTime.Ticks)/10000))/ - 1000); - } - } - - /// - /// Gets last data activity time. - /// - public DateTime SessionLastDataTime - { - get - { - if (m_pSocket == null) - { - return DateTime.MinValue; - } - else - { - return m_pSocket.LastActivity; - } - } - } - - /// - /// Gets EndPoint which accepted conection. - /// - public IPEndPoint LocalEndPoint - { - get { return (IPEndPoint) m_pSocket.LocalEndPoint; } - } - - /// - /// Gets connected Host(client) EndPoint. - /// - public IPEndPoint RemoteEndPoint - { - get - { - try - { - return (IPEndPoint) m_pSocket.RemoteEndPoint; - } - catch - { - // Socket closed/disposed already - return null; - } - } - } - - /// - /// Gets or sets custom user data. - /// - public object Tag { get; set; } - - /// - /// Gets log entries that are currently in log buffer. - /// - public SocketLogger SessionActiveLog - { - get { return m_pSocket.Logger; } - } - - /// - /// Gets how many bytes are readed through this session. - /// - public long ReadedCount - { - get { return m_pSocket.ReadedCount; } - } - - /// - /// Gets how many bytes are written through this session. - /// - public long WrittenCount - { - get { return m_pSocket.WrittenCount; } - } - - /// - /// Gets if the connection is an SSL connection. - /// - public bool IsSecureConnection - { - get { return m_pSocket.SSL; } - } - - /// - /// Gets access to SocketEx. - /// - protected SocketEx Socket - { - get { return m_pSocket; } - } - - /// - /// Gets access to BindInfo what accepted socket. - /// - protected IPBindInfo BindInfo - { - get { return m_pBindInfo; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Session ID. - /// Server connected socket. - /// BindInfo what accepted socket. - /// Reference to server. - public SocketServerSession(string sessionID, SocketEx socket, IPBindInfo bindInfo, SocketServer server) - { - m_SessionID = sessionID; - m_pSocket = socket; - m_pBindInfo = bindInfo; - m_pServer = server; - - m_SessionStartTime = DateTime.Now; - } - - #endregion - - #region Methods - - /// - /// Kills session. - /// - public virtual void Kill() {} - - #endregion - - #region Virtual methods - - /// - /// Times session out. - /// - protected internal virtual void OnSessionTimeout() {} - - #endregion - - /// - /// Sets property UserName value. - /// - /// User name. - protected void SetUserName(string userName) - { - m_UserName = userName; - - if (m_pSocket.Logger != null) - { - m_pSocket.Logger.UserName = m_UserName; - } - } - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/StreamHelper.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/StreamHelper.cs deleted file mode 100644 index f28e51a8a..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/StreamHelper.cs +++ /dev/null @@ -1,2702 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IO -{ - #region usings - - using System; - using System.IO; - using System.Text; - using Log; - - #endregion - - /// - /// This delegate represents callback method for BeginReadLine. - /// - /// Method data. - public delegate void ReadLineCallback(ReadLine_EventArgs e); - - /// - /// This delegate represents callback method for BeginReadToEnd,BeginReadHeader,BeginReadPeriodTerminated. - /// - /// Method data. - public delegate void ReadToStreamCallback(ReadToStream_EventArgs e); - - /// - /// This delegate represents callback method for BeginWrite. - /// - /// Method data. - public delegate void WriteCallback(Write_EventArgs e); - - /// - /// This delegate represents callback method for BeginWrite,BeginWritePeriodTerminated. - /// - /// Method data. - public delegate void WriteStreamCallback(WriteStream_EventArgs e); - - /// - /// Stream wrapper class, provides many usefull read and write methods for stream. - /// - [Obsolete("Use SmartStream instead.")] - public class StreamHelper - { - #region Nested type: _ToStreamReader - - /// - /// Asynchronous to stream reader implementation. - /// - private class _ToStreamReader - { - #region Members - - private readonly SizeExceededAction m_ExceededAction = SizeExceededAction.ThrowException; - private readonly int m_MaxSize = Workaround.Definitions.MaxStreamLineLength; - private readonly byte[] m_pBuffer; - private readonly BufferedStream m_pBufferedStream; - private readonly ReadToStreamCallback m_pCallback; - private readonly byte[] m_pLineBuffer; - private readonly Stream m_pStoreStream; - private readonly StreamHelper m_pStreamHelper; - private readonly object m_pTag; - private int m_CountInBuffer; - private int m_CountToRead; - private bool m_IsLineSizeExceeded; - private int m_TotalReadedCount; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Reference to StreamHelper. - /// Stream where to store readed data. - /// Maximum number of bytes to read. - /// Specifies how this method behaves when maximum size exceeded. - /// Callback what will be called if asynchronous reading compltes. - /// User data. - public _ToStreamReader(StreamHelper streamHelper, - Stream storeStream, - int maxSize, - SizeExceededAction exceededAction, - ReadToStreamCallback callback, - object tag) - { - m_pStreamHelper = streamHelper; - m_pStoreStream = storeStream; - m_pBufferedStream = new BufferedStream(m_pStoreStream, Workaround.Definitions.MaxStreamLineLength); - m_MaxSize = maxSize; - m_ExceededAction = exceededAction; - m_pCallback = callback; - m_pTag = tag; - m_pBuffer = new byte[Workaround.Definitions.MaxStreamLineLength]; - m_pLineBuffer = new byte[4096]; - } - - #endregion - - #region Methods - - /// - /// Starts reading specified amount of data. - /// - /// Number of bytes to read from source stream and store to store stream. - public void BeginRead(int count) - { - m_CountToRead = count; - - DoRead(); - } - - /// - /// Starts reading period terminated data. - /// - public void BeginReadPeriodTerminated() - { - try - { - m_pStreamHelper.BeginReadLineInternal(m_pLineBuffer, - m_ExceededAction, - null, - OnReadPeriodTerminated_ReadLine_Completed, - false, - false); - } - catch (Exception x) - { - ReadPeriodTerminatedCompleted(x); - } - } - - /// - /// Starts reading header data. - /// - public void BeginReadHeader() - { - try - { - m_pStreamHelper.BeginReadLineInternal(m_pLineBuffer, - m_ExceededAction, - null, - OnReadHeader_ReadLine_Completed, - false, - false); - } - catch (Exception x) - { - ReadHeaderCompleted(x); - } - } - - /// - /// Starts reading all source stream data. - /// - public void BeginReadAll() - { - DoReadAll(); - } - - #endregion - - #region Utility methods - - /// - /// Processes all buffer data and gets new buffer if active buffer consumed. - /// - private void DoRead() - { - /* Note: We do own read buffering here, because we cand do buffering here even if - * buffering not enabled for StreamHelper. All this because we read all data anyway. - * But we need to use StreamHelper read buffer first if there is any data in that buffer. - */ - - try - { - // We have data in buffer, consume it. - if (m_CountInBuffer > 0) - { - m_TotalReadedCount += m_CountInBuffer; - - // Write readed data to store stream. - m_pStoreStream.Write(m_pBuffer, 0, m_CountInBuffer); - m_CountInBuffer = 0; - } - - // We have readed all data that was requested. - if (m_TotalReadedCount == m_CountToRead) - { - OnRead_Completed(null); - } - else - { - // There is some data in StreamHelper read buffer, we need to consume it first. - if (m_pStreamHelper.m_ReadBufferOffset < m_pStreamHelper.m_ReadBufferEndPos) - { - int countReadedFromBuffer = - Math.Min( - m_pStreamHelper.m_ReadBufferEndPos - m_pStreamHelper.m_ReadBufferOffset, - m_CountToRead - m_TotalReadedCount); - Array.Copy(m_pStreamHelper.m_pReadBuffer, - m_pStreamHelper.m_ReadBufferOffset, - m_pBuffer, - 0, - countReadedFromBuffer); - m_pStreamHelper.m_ReadBufferOffset += countReadedFromBuffer; - } - // Start reading new (local)buffer data block. - else - { - m_pStreamHelper.Stream.BeginRead(m_pBuffer, - 0, - Math.Min(m_pBuffer.Length, - m_CountToRead - m_TotalReadedCount), - OnRead_ReadBuffer_Completed, - null); - } - } - } - catch (Exception x) - { - OnRead_Completed(x); - } - } - - /// - /// Is called when asynchrounous data buffer block reading has completed. - /// - /// - private void OnRead_ReadBuffer_Completed(IAsyncResult result) - { - try - { - m_CountInBuffer = m_pStreamHelper.Stream.EndRead(result); - - // We reached end of stream, no more data. - if (m_CountInBuffer == 0) - { - OnRead_Completed(new IncompleteDataException()); - } - else - { - // Continue reading. - DoRead(); - } - } - catch (Exception x) - { - OnRead_Completed(x); - } - } - - /// - /// Is called when ReadHeader has completed. - /// - /// Exception happened during read or null if operation was successfull. - private void OnRead_Completed(Exception x) - { - // Release read lock. - m_pStreamHelper.m_IsReadActive = false; - - // Log - if (m_pStreamHelper.Logger != null) - { - m_pStreamHelper.Logger.AddRead(m_TotalReadedCount, null); - } - - if (m_pCallback != null) - { - m_pCallback(new ReadToStream_EventArgs(x, m_pStoreStream, m_TotalReadedCount, m_pTag)); - } - } - - /// - /// Is called when asynchrounous line reading has completed. - /// - /// Callback data. - private void OnReadPeriodTerminated_ReadLine_Completed(ReadLine_EventArgs e) - { - try - { - m_TotalReadedCount += e.ReadedCount; - - // We got error. - if (e.Exception != null) - { - try - { - m_pBufferedStream.Flush(); - m_pStoreStream.Flush(); - } - catch - { - // Just skip excpetions here, otherwise we may hide original exception, - // if exceptions is thrown by flush. - } - - // Maximum line size exceeded, but junk data wanted. - if (m_ExceededAction == SizeExceededAction.JunkAndThrowException && - e.Exception is LineSizeExceededException) - { - m_IsLineSizeExceeded = true; - - // Start reading next data buffer block. - m_pStreamHelper.BeginReadLineInternal(m_pLineBuffer, - m_ExceededAction, - null, - OnReadPeriodTerminated_ReadLine_Completed, - false, - false); - } - // Unknown exception or ThrowException, so we are done. - else - { - ReadPeriodTerminatedCompleted(e.Exception); - } - } - // We reached end of stream before got period terminator. - else if (e.ReadedCount == 0) - { - ReadPeriodTerminatedCompleted( - new IncompleteDataException( - "Source stream was reached end of stream and data is not period terminated !")); - } - // We got terminator, so we are done now. - else if (e.Count == 1 && e.LineBuffer[0] == '.') - { - m_pBufferedStream.Flush(); - m_pStoreStream.Flush(); - - // LineSizeExceeded. - if (m_IsLineSizeExceeded) - { - ReadPeriodTerminatedCompleted(new LineSizeExceededException()); - } - // DataSizeExceeded. - else if (m_TotalReadedCount > m_MaxSize) - { - ReadPeriodTerminatedCompleted(new DataSizeExceededException()); - } - // Completed successfuly. - else - { - ReadPeriodTerminatedCompleted(null); - } - } - // Just append line to store stream and get next line. - else - { - // Maximum allowed data size exceeded. - if (m_TotalReadedCount > m_MaxSize) - { - if (m_ExceededAction == SizeExceededAction.ThrowException) - { - ReadPeriodTerminatedCompleted(new DataSizeExceededException()); - return; - } - // Junk data. - //else{ - //} - } - else - { - // If line starts with period, first period is removed. - if (e.LineBuffer[0] == '.') - { - m_pBufferedStream.Write(e.LineBuffer, 1, e.Count - 1); - } - // Normal line. - else - { - m_pBufferedStream.Write(e.LineBuffer, 0, e.Count); - } - // Add line break. - m_pBufferedStream.Write(m_pStreamHelper.m_LineBreak, - 0, - m_pStreamHelper.m_LineBreak.Length); - } - - // Start getting new data buffer block. - m_pStreamHelper.BeginReadLineInternal(m_pLineBuffer, - m_ExceededAction, - null, - OnReadPeriodTerminated_ReadLine_Completed, - false, - false); - } - } - catch (Exception x) - { - ReadPeriodTerminatedCompleted(x); - } - } - - /// - /// Is called when ReadPeriodTerminated has completd. - /// - /// Exeption happened or null if operation completed successfuly. - private void ReadPeriodTerminatedCompleted(Exception x) - { - // Release read lock. - m_pStreamHelper.m_IsReadActive = false; - - // Log - if (m_pStreamHelper.Logger != null) - { - m_pStreamHelper.Logger.AddRead(m_TotalReadedCount, null); - } - - if (m_pCallback != null) - { - m_pCallback(new ReadToStream_EventArgs(x, m_pStoreStream, m_TotalReadedCount, m_pTag)); - } - } - - /// - /// Is called when asynchrounous line reading has completed. - /// - /// Callback data. - private void OnReadHeader_ReadLine_Completed(ReadLine_EventArgs e) - { - try - { - m_TotalReadedCount += e.ReadedCount; - - // We got error. - if (e.Exception != null) - { - try - { - m_pBufferedStream.Flush(); - m_pStoreStream.Flush(); - } - catch - { - // Just skip excpetions here, otherwise we may hide original exception, - // if exceptions is thrown by flush. - } - - // Maximum line size exceeded, but junk data wanted. - if (m_ExceededAction == SizeExceededAction.JunkAndThrowException && - e.Exception is LineSizeExceededException) - { - m_IsLineSizeExceeded = true; - - // Start reading next data buffer block. - m_pStreamHelper.BeginReadLineInternal(m_pLineBuffer, - m_ExceededAction, - null, - OnReadHeader_ReadLine_Completed, - false, - false); - } - // Unknown exception or ThrowException, so we are done. - else - { - ReadHeaderCompleted(e.Exception); - } - } - // We got terminator, so we are done now. - else if (e.Count == 0 || e.ReadedCount == 0) - { - m_pBufferedStream.Flush(); - m_pStoreStream.Flush(); - - // LineSizeExceeded. - if (m_IsLineSizeExceeded) - { - ReadHeaderCompleted(new LineSizeExceededException()); - } - // DataSizeExceeded. - else if (m_TotalReadedCount > m_MaxSize) - { - ReadHeaderCompleted(new DataSizeExceededException()); - } - // Completed successfuly. - else - { - ReadHeaderCompleted(null); - } - } - // Just append line to store stream and get next line. - else - { - // Maximum allowed data size exceeded. - if (m_TotalReadedCount > m_MaxSize) - { - if (m_ExceededAction == SizeExceededAction.ThrowException) - { - ReadHeaderCompleted(new DataSizeExceededException()); - return; - } - // Junk data. - //else{ - //} - } - else - { - m_pBufferedStream.Write(e.LineBuffer, 0, e.Count); - m_pBufferedStream.Write(m_pStreamHelper.m_LineBreak, - 0, - m_pStreamHelper.m_LineBreak.Length); - } - - // Start reading new line. - m_pStreamHelper.BeginReadLineInternal(m_pLineBuffer, - m_ExceededAction, - null, - OnReadHeader_ReadLine_Completed, - false, - false); - } - } - catch (Exception x) - { - ReadHeaderCompleted(x); - } - } - - /// - /// Is called when ReadHeader has completed. - /// - /// Exception happened during read or null if operation was successfull. - private void ReadHeaderCompleted(Exception x) - { - // Release read lock. - m_pStreamHelper.m_IsReadActive = false; - - // Log - if (m_pStreamHelper.Logger != null) - { - m_pStreamHelper.Logger.AddRead(m_TotalReadedCount, null); - } - - if (m_pCallback != null) - { - m_pCallback(new ReadToStream_EventArgs(x, m_pStoreStream, m_TotalReadedCount, m_pTag)); - } - } - - /// - /// Processes all buffer data and gets new buffer if active buffer consumed. - /// - private void DoReadAll() - { - /* Note: We do own read buffering here, because we cand do buffering here even if - * buffering not enabled for StreamHelper. All this because we read all data anyway. - * But we need to use StreamHelper read buffer first if there is any data in that buffer. - */ - - try - { - // We have data in buffer, consume it. - if (m_CountInBuffer > 0) - { - m_TotalReadedCount += m_CountInBuffer; - - // Maximum allowed data size exceeded. - if (m_TotalReadedCount > m_MaxSize) - { - if (m_ExceededAction == SizeExceededAction.ThrowException) - { - ReadAllCompleted(new DataSizeExceededException()); - return; - } - // Junk data. - //else{ - //} - } - // Store readed data store stream. - else - { - m_pStoreStream.Write(m_pBuffer, 0, m_CountInBuffer); - } - m_CountInBuffer = 0; - } - - // There is some data in StreamHelper read buffer, we need to consume it first. - if (m_pStreamHelper.m_ReadBufferOffset < m_pStreamHelper.m_ReadBufferEndPos) - { - Array.Copy(m_pStreamHelper.m_pReadBuffer, - m_pStreamHelper.m_ReadBufferOffset, - m_pBuffer, - 0, - m_pStreamHelper.m_ReadBufferEndPos - m_pStreamHelper.m_ReadBufferOffset); - m_pStreamHelper.m_ReadBufferOffset = 0; - m_pStreamHelper.m_ReadBufferEndPos = 0; - } - // Start reading new (local)buffer data block. - else - { - m_pStreamHelper.Stream.BeginRead(m_pBuffer, - 0, - m_pBuffer.Length, - OnReadAll_ReadBuffer_Completed, - null); - } - } - catch (Exception x) - { - ReadAllCompleted(x); - } - } - - /// - /// Is called when asynchrounous data buffer block reading has completed. - /// - /// - private void OnReadAll_ReadBuffer_Completed(IAsyncResult result) - { - try - { - m_CountInBuffer = m_pStreamHelper.Stream.EndRead(result); - // We reached end of stream, no more data. - if (m_CountInBuffer == 0) - { - // Maximum allowed data size exceeded. - if (m_TotalReadedCount > m_MaxSize) - { - ReadAllCompleted(new DataSizeExceededException()); - } - // ReadToEnd completed successfuly. - else - { - ReadAllCompleted(null); - } - } - else - { - // Continue reading. - DoReadAll(); - } - } - catch (Exception x) - { - ReadAllCompleted(x); - } - } - - /// - /// Is called when ReadToEnd has completed. - /// - /// Exception happened during read or null if operation was successfull. - private void ReadAllCompleted(Exception x) - { - // Release read lock. - m_pStreamHelper.m_IsReadActive = false; - - // Log - if (m_pStreamHelper.Logger != null) - { - m_pStreamHelper.Logger.AddRead(m_TotalReadedCount, null); - } - - if (m_pCallback != null) - { - m_pCallback(new ReadToStream_EventArgs(x, m_pStoreStream, m_TotalReadedCount, m_pTag)); - } - } - - #endregion - } - - #endregion - - #region Members - - private readonly bool m_IsReadBuffered; - private readonly byte[] m_LineBreak = new[] {(byte) '\r', (byte) '\n'}; - private readonly int m_MaxLineSize = 4096; - private readonly byte[] m_pLineBuffer; - private readonly byte[] m_pReadBuffer; - private readonly byte[] m_pRLine_ByteBuffer; - private readonly ReadLine_EventArgs m_pRLine_EventArgs; - private readonly Stream m_pStream; - // BeginWrite - private int m_BeginWrite_Count; - private int m_BeginWrite_Readed; - // BeginWriteAll - private int m_BeginWriteAll_MaxSize; - private int m_BeginWriteAll_Readed; - // BeginWritePeriodTerminated - private int m_BeginWritePeriodTerminated_MaxSize; - private int m_BeginWritePeriodTerminated_Readed; - private int m_BeginWritePeriodTerminated_Written; - private bool m_IsReadActive; - private bool m_IsWriteActive; - private byte[] m_pBeginWrite_Buffer; - private WriteStreamCallback m_pBeginWrite_Callback; - private Stream m_pBeginWrite_Stream; - private byte[] m_pBeginWriteAll_Buffer; - private WriteStreamCallback m_pBeginWriteAll_Callback; - private Stream m_pBeginWriteAll_Stream; - private BufferedStream m_pBeginWritePeriodTerminated_BufferedStream; - private WriteStreamCallback m_pBeginWritePeriodTerminated_Callback; - private Stream m_pBeginWritePeriodTerminated_Stream; - private ReadLineCallback m_pRLine_Callback; - private byte[] m_pRLine_LineBuffer; - private object m_pRLine_Tag; - private int m_ReadBufferEndPos; - private int m_ReadBufferOffset; - private SizeExceededAction m_RLine_ExceedAction = SizeExceededAction.ThrowException; - private int m_RLine_LineBufferOffset; - private int m_RLine_LineBufferSize; - private bool m_RLine_Log; - private int m_RLine_TotalReadedCount; - private bool m_RLine_UnlockRead = true; - - #endregion - - #region Properties - - /// - /// Gets underlying stream. - /// - public Stream Stream - { - get { return m_pStream; } - } - - /// - /// Gets maximum allowed line size in bytes. - /// - public int MaximumLineSize - { - get { return m_MaxLineSize; } - } - - /// - /// Gets if source stream reads are buffered. - /// - public bool IsReadBuffered - { - get { return m_IsReadBuffered; } - } - - /// - /// Gets or sets logger to use for logging. - /// - public Logger Logger { get; set; } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Source stream. - public StreamHelper(Stream stream) : this(stream, 4096, true) {} - - /// - /// Default constructor. - /// - /// Source stream. - /// Specifies maximum line size in bytes. - /// Specifies if source stream reads are buffered.. - public StreamHelper(Stream stream, int maxLineSize, bool bufferRead) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - if (maxLineSize < 1 || maxLineSize > Workaround.Definitions.MaxStreamLineLength) - { - throw new ArgumentException("Parameter maxLineSize value must be >= 1 and <= Workaround.Definitions.MaxStreamLineLength !"); - } - - m_IsReadBuffered = bufferRead; - if (m_IsReadBuffered) - { - m_pReadBuffer = new byte[Workaround.Definitions.MaxStreamLineLength]; - } - - m_pStream = stream; - m_pLineBuffer = new byte[maxLineSize]; - m_MaxLineSize = maxLineSize; - - m_pRLine_EventArgs = new ReadLine_EventArgs(); - m_pRLine_ByteBuffer = new byte[1]; - } - - #endregion - - #region Methods - - /// - /// Reades byte from source stream. Returns -1 if end of stream reached and no more data. - /// - /// Returns readed byte or -1 if end of stream reached. - public int ReadByte() - { - if (m_IsReadBuffered) - { - if (m_ReadBufferOffset >= m_ReadBufferEndPos) - { - m_ReadBufferEndPos = m_pStream.Read(m_pReadBuffer, 0, Workaround.Definitions.MaxStreamLineLength); - m_ReadBufferOffset = 0; - - // We reached end of stream. - if (m_ReadBufferEndPos == 0) - { - return -1; - } - } - - m_ReadBufferOffset++; - return m_pReadBuffer[m_ReadBufferOffset - 1]; - } - else - { - return m_pStream.ReadByte(); - } - } - - /// - /// Starts reading specified amount data and storing to the specified store stream. - /// - /// Stream where to store readed data. - /// Number of bytes to read from source stream and write to store stream. - /// Callback to be called if asynchronous reading completes. - /// User data. - /// Raised when storeStream is null. - /// Raised when count less than 1. - public void BeginRead(Stream storeStream, int count, ReadToStreamCallback callback, object tag) - { - if (storeStream == null) - { - throw new ArgumentNullException("storeStream"); - } - if (count < 1) - { - throw new ArgumentException("Parameter count value must be >= 1 !"); - } - lock (this) - { - if (m_IsReadActive) - { - throw new InvalidOperationException( - "There is pending read operation, multiple read operations not allowed !"); - } - m_IsReadActive = true; - } - - _ToStreamReader reader = new _ToStreamReader(this, - storeStream, - 0, - SizeExceededAction.ThrowException, - callback, - tag); - reader.BeginRead(count); - } - - /// - /// Reads specified amount of data from source stream and stores to specified store stream. - /// - /// Stream where to store readed data. - /// Number of bytes to read from source stream and write to store stream. - /// Raised when storeStream is null. - /// Raised when count less than 1. - /// Raised when there already is pending read operation. - /// Raised source stream has reached end of stream and doesn't have so much data as specified by count argument. - public void Read(Stream storeStream, int count) - { - if (storeStream == null) - { - throw new ArgumentNullException("storeStream"); - } - if (count < 1) - { - throw new ArgumentException("Parameter count value must be >= 1 !"); - } - lock (this) - { - if (m_IsReadActive) - { - throw new InvalidOperationException( - "There is pending read operation, multiple read operations not allowed !"); - } - m_IsReadActive = true; - } - - try - { - byte[] buffer = new byte[Workaround.Definitions.MaxStreamLineLength]; - int totalReadedCount = 0; - int readedCount = 0; - while (totalReadedCount < count) - { - // We have data in read buffer, we must consume it first ! - if (m_ReadBufferOffset < m_ReadBufferEndPos) - { - int countReadedFromBuffer = Math.Min(m_ReadBufferEndPos - m_ReadBufferOffset, - count - totalReadedCount); - Array.Copy(m_pReadBuffer, m_ReadBufferOffset, buffer, 0, countReadedFromBuffer); - m_ReadBufferOffset += countReadedFromBuffer; - } - // Just get read next data block. - else - { - readedCount = m_pStream.Read(buffer, - 0, - Math.Min(buffer.Length, count - totalReadedCount)); - } - - // We have reached end of stream, no more data. - if (readedCount == 0) - { - throw new IncompleteDataException( - "Underlaying stream don't have so much data than requested, end of stream reached !"); - } - totalReadedCount += readedCount; - - // Write readed data to store stream. - storeStream.Write(buffer, 0, readedCount); - } - - // Log - if (Logger != null) - { - Logger.AddRead(totalReadedCount, null); - } - } - finally - { - m_IsReadActive = false; - } - } - - /// - /// Starts reading line from source stream. - /// - /// Buffer where to store line data. - /// User data. - /// Callback to be called whan asynchronous operation completes. - /// Raised when buffer is null. - /// Raised when there already is pending read operation. - public void BeginReadLine(byte[] buffer, object tag, ReadLineCallback callback) - { - BeginReadLine(buffer, SizeExceededAction.ThrowException, tag, callback); - } - - /// - /// Starts reading line from source stream. - /// - /// Buffer where to store line data. - /// Specifies how this method behaves when maximum line size exceeded. - /// User data. - /// Callback to be called whan asynchronous operation completes. - /// Raised when buffer is null. - /// Raised when there already is pending read operation. - public void BeginReadLine(byte[] buffer, - SizeExceededAction exceededAction, - object tag, - ReadLineCallback callback) - { - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - lock (this) - { - if (m_IsReadActive) - { - throw new InvalidOperationException( - "There is pending read operation, multiple read operations not allowed !"); - } - m_IsReadActive = false; - } - - BeginReadLineInternal(buffer, exceededAction, tag, callback, true, true); - } - - /// - /// Reads line from source stream and stores to specified buffer. This method accepts LF or CRLF lines. - /// - /// Buffer where to store line data. - /// Returns number of bytes stored to buffer, returns -1 if end of stream reached and no more data. - /// Raised when buffer is null. - /// Raised when there already is pending read operation. - /// Raised when maximum allowed line size has exceeded. - public int ReadLine(byte[] buffer) - { - return ReadLine(buffer, SizeExceededAction.ThrowException); - } - - /// - /// Reads line from source stream and stores to specified buffer. This method accepts LF or CRLF lines. - /// - /// Buffer where to store line data. - /// Specifies how this method behaves when maximum line size exceeded. - /// Returns number of bytes stored to buffer, returns -1 if end of stream reached and no more data. - /// Raised when buffer is null. - /// Raised when there already is pending read operation. - /// Raised when maximum allowed line size has exceeded. - public int ReadLine(byte[] buffer, SizeExceededAction exceededAction) - { - if (buffer == null) - { - throw new ArgumentNullException("buffer"); - } - lock (this) - { - if (m_IsReadActive) - { - throw new InvalidOperationException( - "There is pending read operation, multiple read operations not allowed !"); - } - else - { - m_IsReadActive = true; - } - } - - try - { - int readedCount = 0; - return ReadLineInternal(buffer, exceededAction, out readedCount, true); - } - finally - { - m_IsReadActive = false; - } - } - - /// - /// Reads line from source stream. - /// - /// Encoding to use to decode line. - /// Returns readed line with specified encoding or null if end of stream reached and no more data. - /// Raised when encoding is null. - /// Raised when there already is pending read operation. - /// Raised when maximum allowed line size has exceeded. - public string ReadLine(Encoding encoding) - { - return ReadLine(encoding, SizeExceededAction.ThrowException); - } - - /// - /// Reads line from source stream. - /// - /// Encoding to use to decode line. - /// Specifies how this method behaves when maximum line size exceeded. - /// Returns readed line with specified encoding or null if end of stream reached and no more data. - /// Raised when encoding is null. - /// Raised when there already is pending read operation. - /// Raised when maximum allowed line size has exceeded. - public string ReadLine(Encoding encoding, SizeExceededAction exceededAction) - { - if (encoding == null) - { - throw new ArgumentNullException("encoding"); - } - - int readedCount = ReadLine(m_pLineBuffer, exceededAction); - if (readedCount == -1) - { - return null; - } - else - { - return encoding.GetString(m_pLineBuffer, 0, readedCount); - } - } - - /// - /// Reads line from source stream and stores to specified buffer. This method accepts LF or CRLF lines. - /// - /// Buffer where to store line data. - /// Specifies how this method behaves when maximum line size exceeded. - /// Returns how many bytes this method actually readed form source stream. - /// Specifies if read line is logged. - /// Returns number of bytes stored to buffer, returns -1 if end of stream reached and no more data. - /// Raised when there already is pending read operation. - /// Raised when maximum allowed line size has exceeded. - public int ReadLineInternal(byte[] buffer, - SizeExceededAction exceededAction, - out int readedCount, - bool log) - { - readedCount = 0; - int bufferSize = buffer.Length; - int posInBuffer = 0; - int currentByte = 0; - - /* Because performance gain we need todo,2 while, buffered and non buffered read. - Each if clause in this while adds about 5% cpu. - */ - - #region Buffered - - if (m_IsReadBuffered) - { - while (true) - { - //--- Read byte ----------------------------------------------------- - if (m_ReadBufferOffset >= m_ReadBufferEndPos) - { - m_ReadBufferEndPos = m_pStream.Read(m_pReadBuffer, 0, Workaround.Definitions.MaxStreamLineLength); - m_ReadBufferOffset = 0; - - // We reached end of stream. - if (m_ReadBufferEndPos == 0) - { - break; - } - } - - currentByte = m_pReadBuffer[m_ReadBufferOffset]; - m_ReadBufferOffset++; - readedCount++; - //------------------------------------------------------------------- - - // We have LF. - if (currentByte == '\n') - { - break; - } - // We just skip all CR. - else if (currentByte == '\r') {} - // Maximum allowed line size exceeded. - else if (readedCount > bufferSize) - { - if (exceededAction == SizeExceededAction.ThrowException) - { - throw new LineSizeExceededException(); - } - } - // Store readed byte. - else - { - buffer[posInBuffer] = (byte) currentByte; - posInBuffer++; - } - } - } - - #endregion - - #region No-buffered - - else - { - while (true) - { - // Read byte - currentByte = m_pStream.ReadByte(); - // We reached end of stream, no more data. - if (currentByte == -1) - { - break; - } - readedCount++; - - // We have LF. - if (currentByte == '\n') - { - break; - } - // We just skip all CR. - else if (currentByte == '\r') {} - // Maximum allowed line size exceeded. - else if (readedCount > bufferSize) - { - if (exceededAction == SizeExceededAction.ThrowException) - { - throw new LineSizeExceededException(); - } - } - // Store readed byte. - else - { - buffer[posInBuffer] = (byte) currentByte; - posInBuffer++; - } - } - } - - #endregion - - // We are end of stream, no more data. - if (readedCount == 0) - { - return -1; - } - // Maximum allowed line size exceeded. - if (readedCount > m_MaxLineSize) - { - throw new LineSizeExceededException(); - } - - // Log - if (log && Logger != null) - { - Logger.AddRead(readedCount, Encoding.Default.GetString(buffer, 0, readedCount)); - } - - return posInBuffer; - } - - /// - /// Starts reading all source stream data. - /// - /// Stream where to store data. - /// Maximum muber of bytes to read. - /// Specifies how this method behaves when maximum size exceeded. - /// Callback to be called if asynchronous reading completes. - /// User data. - /// Raised when storeStream is null. - /// Raised when there already is pending read operation. - public void BeginReadAll(Stream storeStream, - int maxSize, - SizeExceededAction exceededAction, - ReadToStreamCallback callback, - object tag) - { - if (storeStream == null) - { - throw new ArgumentNullException("storeStream"); - } - lock (this) - { - if (m_IsReadActive) - { - throw new InvalidOperationException( - "There is pending read operation, multiple read operations not allowed !"); - } - m_IsReadActive = false; - } - - _ToStreamReader reader = new _ToStreamReader(this, - storeStream, - maxSize, - exceededAction, - callback, - tag); - reader.BeginReadAll(); - } - - /// - /// Reads all source stream data and stores to the specified store stream. - /// - /// Stream where to store readed data. - /// Maximum muber of bytes to read. - /// Returns number of bytes written to storeStream. - /// Raised when storeStream is null. - /// Raised when maxSize less than 1. - /// Raised when there already is pending read operation. - /// Raised when maximum allowed data size has exceeded. - public int ReadAll(Stream storeStream, int maxSize) - { - return ReadAll(storeStream, maxSize, SizeExceededAction.ThrowException); - } - - /// - /// Reads all source stream data and stores to the specified store stream. - /// - /// Stream where to store readed data. - /// Maximum muber of bytes to read. - /// Specifies how this method behaves when maximum size exceeded. - /// Returns number of bytes written to storeStream. - /// Raised when storeStream is null. - /// Raised when maxSize less than 1. - /// Raised when there already is pending read operation. - /// Raised when maximum allowed data size has exceeded. - public int ReadAll(Stream storeStream, int maxSize, SizeExceededAction exceededAction) - { - if (storeStream == null) - { - throw new ArgumentNullException("storeStream"); - } - if (maxSize < 1) - { - throw new ArgumentException("Parameter maxSize value must be >= 1 !"); - } - lock (this) - { - if (m_IsReadActive) - { - throw new InvalidOperationException( - "There is pending read operation, multiple read operations not allowed !"); - } - m_IsReadActive = true; - } - - try - { - byte[] buffer = new byte[Workaround.Definitions.MaxStreamLineLength]; - int totalReadedCount = 0; - int readedCount = 0; - while (true) - { - // We have data in read buffer, we must consume it first ! - if (m_ReadBufferOffset < m_ReadBufferEndPos) - { - Array.Copy(m_pLineBuffer, - m_ReadBufferOffset, - buffer, - 0, - m_ReadBufferEndPos - m_ReadBufferOffset); - m_ReadBufferOffset = 0; - m_ReadBufferEndPos = 0; - } - // Just get read next data block. - else - { - readedCount = m_pStream.Read(buffer, 0, buffer.Length); - } - - // End of stream reached, no more data. - if (readedCount == 0) - { - break; - } - totalReadedCount += readedCount; - - // Maximum allowed data size exceeded. - if (totalReadedCount > maxSize) - { - if (exceededAction == SizeExceededAction.ThrowException) - { - throw new DataSizeExceededException(); - } - } - else - { - storeStream.Write(buffer, 0, readedCount); - } - } - - // Maximum allowed data size exceeded, some data junked. - if (totalReadedCount > maxSize) - { - throw new DataSizeExceededException(); - } - - // Log - if (Logger != null) - { - Logger.AddRead(totalReadedCount, null); - } - - return totalReadedCount; - } - finally - { - m_IsReadActive = false; - } - } - - /// - /// Starts reading header from source stream. Reads header data while gets blank line, what is - /// header terminator. For example this method can be used for reading mail,http,sip, ... headers. - /// - /// Stream where to store data. - /// Maximum muber of bytes to read. - /// Specifies how this method behaves when maximum size exceeded. - /// Callback to be called if asynchronous reading completes. - /// User data. - /// Raised when storeStream is null. - /// Raised when there already is pending read operation. - public void BeginReadHeader(Stream storeStream, - int maxSize, - SizeExceededAction exceededAction, - ReadToStreamCallback callback, - object tag) - { - if (storeStream == null) - { - throw new ArgumentNullException("storeStream"); - } - lock (this) - { - if (m_IsReadActive) - { - throw new InvalidOperationException( - "There is pending read operation, multiple read operations not allowed !"); - } - m_IsReadActive = false; - } - - _ToStreamReader reader = new _ToStreamReader(this, - storeStream, - maxSize, - exceededAction, - callback, - tag); - reader.BeginReadHeader(); - } - - /// - /// Reads header from source stream and stores to the specified stream. Reads header data while - /// gets blank line, what is header terminator. For example this method can be used for reading - /// mail,http,sip, ... headers. - /// - /// Stream where to store readed data. - /// Maximum number of bytes to read. - /// Specifies how this method behaves when maximum line or data size exceeded. - /// Returns number of bytes written to storeStream. - /// Raised when storeStream is null. - /// Raised when maxSize less than 1. - /// Raised when there already is pending read operation. - /// Raised when maximum allowed line size has exceeded. - /// Raised when maximum allowed data size has exceeded. - public int ReadHeader(Stream storeStream, int maxSize, SizeExceededAction exceededAction) - { - if (storeStream == null) - { - throw new ArgumentNullException("storeStream"); - } - lock (this) - { - if (m_IsReadActive) - { - throw new InvalidOperationException( - "There is pending read operation, multiple read operations not allowed !"); - } - else - { - m_IsReadActive = true; - } - } - - try - { - BufferedStream bufferedStoreStream = new BufferedStream(storeStream, Workaround.Definitions.MaxStreamLineLength); - bool lineSizeExceeded = false; - int totalReadedCount = 0; - int readedCount = 0; - int rawReadedCount = 0; - while (true) - { - // Read line. - readedCount = ReadLineInternal(m_pLineBuffer, - SizeExceededAction.ThrowException, - out rawReadedCount, - false); - - // We have reached end of stream, no more data. - if (rawReadedCount == 0) - { - break; - } - totalReadedCount += rawReadedCount; - - // We got header terminator. - if (readedCount == 0) - { - break; - } - else - { - // Maximum allowed data size exceeded. - if (totalReadedCount > maxSize) - { - if (exceededAction == SizeExceededAction.ThrowException) - { - throw new DataSizeExceededException(); - } - } - // Write readed bytes to store stream. - else - { - bufferedStoreStream.Write(m_pLineBuffer, 0, readedCount); - bufferedStoreStream.Write(m_LineBreak, 0, m_LineBreak.Length); - } - } - } - bufferedStoreStream.Flush(); - - // Maximum allowed line size exceeded, some data is junked. - if (lineSizeExceeded) - { - throw new LineSizeExceededException(); - } - // Maximum allowed data size exceeded, some data is junked. - if (totalReadedCount > maxSize) - { - throw new DataSizeExceededException(); - } - - // Log - if (Logger != null) - { - Logger.AddRead(totalReadedCount, null); - } - - return totalReadedCount; - } - finally - { - m_IsReadActive = false; - } - } - - /// - /// Begins reading period terminated data from source stream. Reads data while gets single period on line, - /// what is data terminator. - /// - /// Stream where to store data. - /// Maximum muber of bytes to read. - /// Specifies how this method behaves when maximum size exceeded. - /// Callback to be called if asynchronous reading completes. - /// User data. - /// Raised when storeStream is null. - /// Raised when there already is pending read operation. - public void BeginReadPeriodTerminated(Stream storeStream, - int maxSize, - SizeExceededAction exceededAction, - ReadToStreamCallback callback, - object tag) - { - if (storeStream == null) - { - throw new ArgumentNullException("storeStream"); - } - lock (this) - { - if (m_IsReadActive) - { - throw new InvalidOperationException( - "There is pending read operation, multiple read operations not allowed !"); - } - m_IsReadActive = false; - } - - _ToStreamReader reader = new _ToStreamReader(this, - storeStream, - maxSize, - exceededAction, - callback, - tag); - reader.BeginReadPeriodTerminated(); - } - - /// - /// Reads period terminated data from source stream. Reads data while gets single period on line, - /// what is data terminator. - /// - /// Stream where to store readed data. - /// Maximum number of bytes to read. - /// Specifies how this method behaves when maximum size exceeded. - /// Returns number of bytes written to storeStream. - /// Raised when storeStream is null. - /// Raised when maxSize less than 1. - /// Raised when there already is pending read operation. - /// Raised when maximum allowed line size has exceeded. - /// Raised when maximum allowed data size has exceeded. - /// Raised when source stream was reached end of stream and data is not period terminated. - public int ReadPeriodTerminated(Stream storeStream, int maxSize, SizeExceededAction exceededAction) - { - if (storeStream == null) - { - throw new ArgumentNullException("storeStream"); - } - lock (this) - { - if (m_IsReadActive) - { - throw new InvalidOperationException( - "There is pending read operation, multiple read operations not allowed !"); - } - else - { - m_IsReadActive = true; - } - } - - try - { - BufferedStream bufferedStoreStream = new BufferedStream(storeStream, Workaround.Definitions.MaxStreamLineLength); - bool lineSizeExceeded = false; - bool isPeriodTerminated = false; - int totalReadedCount = 0; - int readedCount = 0; - int rawReadedCount = 0; - - // Just break reading at once if maximum allowed line or data size exceeded. - if (exceededAction == SizeExceededAction.ThrowException) - { - try - { - // Read first line. - readedCount = ReadLineInternal(m_pLineBuffer, - SizeExceededAction.JunkAndThrowException, - out rawReadedCount, - false); - } - catch (LineSizeExceededException x) - { - string dummy = x.Message; - lineSizeExceeded = true; - } - while (rawReadedCount != 0) - { - totalReadedCount += rawReadedCount; - - // We have data terminator ".". - if (readedCount == 1 && m_pLineBuffer[0] == '.') - { - isPeriodTerminated = true; - break; - } - // If line starts with period(.), first period is removed. - else if (m_pLineBuffer[0] == '.') - { - // Maximum allowed line or data size exceeded. - if (lineSizeExceeded || totalReadedCount > maxSize) - { - // Junk data - } - // Write readed line to store stream. - else - { - bufferedStoreStream.Write(m_pLineBuffer, 1, readedCount - 1); - bufferedStoreStream.Write(m_LineBreak, 0, m_LineBreak.Length); - } - } - // Normal line. - else - { - // Maximum allowed line or data size exceeded. - if (lineSizeExceeded || totalReadedCount > maxSize) - { - // Junk data - } - // Write readed line to store stream. - else - { - bufferedStoreStream.Write(m_pLineBuffer, 0, readedCount); - bufferedStoreStream.Write(m_LineBreak, 0, m_LineBreak.Length); - } - } - - try - { - // Read next line. - readedCount = ReadLineInternal(m_pLineBuffer, - SizeExceededAction.JunkAndThrowException, - out rawReadedCount, - false); - } - catch (LineSizeExceededException x) - { - string dummy = x.Message; - lineSizeExceeded = true; - } - } - } - // Read and junk all data if maximum allowed line or data size exceeded. - else - { - // Read first line. - readedCount = ReadLineInternal(m_pLineBuffer, - SizeExceededAction.JunkAndThrowException, - out rawReadedCount, - false); - while (rawReadedCount != 0) - { - totalReadedCount += rawReadedCount; - - // We have data terminator ".". - if (readedCount == 1 && m_pLineBuffer[0] == '.') - { - isPeriodTerminated = true; - break; - } - // If line starts with period(.), first period is removed. - else if (m_pLineBuffer[0] == '.') - { - // Maximum allowed size exceeded. - if (totalReadedCount > maxSize) - { - throw new DataSizeExceededException(); - } - - // Write readed line to store stream. - bufferedStoreStream.Write(m_pLineBuffer, 1, readedCount - 1); - bufferedStoreStream.Write(m_LineBreak, 0, m_LineBreak.Length); - } - // Normal line. - else - { - // Maximum allowed size exceeded. - if (totalReadedCount > maxSize) - { - throw new DataSizeExceededException(); - } - - // Write readed line to store stream. - bufferedStoreStream.Write(m_pLineBuffer, 0, readedCount); - bufferedStoreStream.Write(m_LineBreak, 0, m_LineBreak.Length); - } - - // Read next line. - readedCount = ReadLineInternal(m_pLineBuffer, - SizeExceededAction.JunkAndThrowException, - out rawReadedCount, - false); - } - } - bufferedStoreStream.Flush(); - - // Log - if (Logger != null) - { - Logger.AddRead(totalReadedCount, null); - } - - if (lineSizeExceeded) - { - throw new LineSizeExceededException(); - } - if (!isPeriodTerminated) - { - throw new IncompleteDataException( - "Source stream was reached end of stream and data is not period terminated !"); - } - if (totalReadedCount > maxSize) - { - throw new DataSizeExceededException(); - } - - return totalReadedCount; - } - finally - { - m_IsReadActive = false; - } - } - - /// - /// Starts writing specified data to source stream. - /// - /// Data what to write to source stream. - /// Callback to be callled if write completes. - /// Raised when data is null. - /// Raised when there already is pending write operation. - public void BeginWrite(byte[] data, WriteCallback callback) - { - if (data == null) - { - throw new ArgumentNullException("data"); - } - - BeginWrite(data, 0, data.Length, callback); - } - - /// - /// Starts writing specified data to source stream. - /// - /// Data what to write to source stream. - /// The zero-based byte offset in buffer at which to begin copying bytes to the source stream. - /// The number of bytes to be written to the source stream. - /// Callback to be callled if write completes. - /// Raised when data is null. - /// Raised when there already is pending write operation. - public void BeginWrite(byte[] data, int offset, int count, WriteCallback callback) - { - if (data == null) - { - throw new ArgumentNullException("data"); - } - lock (this) - { - if (m_IsWriteActive) - { - throw new InvalidOperationException( - "There is pending write operation, multiple write operations not allowed !"); - } - m_IsWriteActive = true; - } - - // Start writing data block. - m_pStream.BeginWrite(data, - offset, - count, - InternalBeginWriteCallback, - new object[] {count, callback}); - } - - /// - /// Strats writing specified amount of data from stream to source stream. - /// - /// Stream which data to wite to source stream. - /// Number of bytes read from stream and write to source stream. - /// Callback to be called if asynchronous write completes. - /// Raised when stream is null. - /// Raised when there already is pending write operation. - public void BeginWrite(Stream stream, int count, WriteStreamCallback callback) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - lock (this) - { - if (m_IsWriteActive) - { - throw new InvalidOperationException( - "There is pending write operation, multiple write operations not allowed !"); - } - m_IsWriteActive = true; - } - - m_pBeginWrite_Buffer = new byte[Workaround.Definitions.MaxStreamLineLength]; - m_pBeginWrite_Stream = stream; - m_BeginWrite_Count = count; - m_pBeginWrite_Callback = callback; - m_BeginWrite_Readed = 0; - - // Start reading data block. - m_pBeginWrite_Stream.BeginRead(m_pBeginWrite_Buffer, - 0, - Math.Min(m_pBeginWrite_Buffer.Length, m_BeginWrite_Count), - InternalBeginWriteStreamCallback, - null); - } - - /// - /// Writes specified buffer data to source stream. - /// - /// Data buffer. - /// Raised when data is null. - /// Raised when there already is pending write operation. - public void Write(byte[] data) - { - if (data == null) - { - throw new ArgumentNullException("data"); - } - - Write(data, 0, data.Length); - } - - /// - /// Writes specified buffer data to source stream. - /// - /// Data buffer. - /// The zero-based byte offset in buffer at which to begin copying bytes to the source stream. - /// The number of bytes to be written to the source stream. - /// Raised when data is null. - /// Raised when there already is pending write operation. - public void Write(byte[] data, int offset, int count) - { - if (data == null) - { - throw new ArgumentNullException("data"); - } - lock (this) - { - if (m_IsWriteActive) - { - throw new InvalidOperationException( - "There is pending write operation, multiple write operations not allowed !"); - } - m_IsWriteActive = true; - } - - try - { - m_pStream.Write(data, offset, count); - - // Log - if (Logger != null) - { - Logger.AddWrite(count, null); - } - } - finally - { - m_IsWriteActive = false; - } - } - - /// - /// Reads specified amount of data for the specified stream and writes it to source stream. - /// - /// Stream from where to read data. - /// Number of bytes to read and write. - /// Raised when stream is null. - /// Raised when argument count is less than 1. - /// Raised when there already is pending write operation. - /// Raised stream has reached end of stream and doesn't have so much data as specified by count argument. - public void Write(Stream stream, int count) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - if (count < 1) - { - throw new ArgumentException("Parameter count value must be >= 1 !"); - } - lock (this) - { - if (m_IsWriteActive) - { - throw new InvalidOperationException( - "There is pending write operation, multiple write operations not allowed !"); - } - m_IsWriteActive = true; - } - - try - { - byte[] buffer = new byte[Workaround.Definitions.MaxStreamLineLength]; - int totalReadedCount = 0; - int readedCount = 0; - while (totalReadedCount < count) - { - // Read data block - readedCount = stream.Read(buffer, 0, Math.Min(buffer.Length, count - totalReadedCount)); - - // We reached end of stream, no more data. That means we didn't get so much data than requested. - if (readedCount == 0) - { - throw new IncompleteDataException( - "Stream reached end of stream before we got requested count of data !"); - } - totalReadedCount += readedCount; - - // Write readed data to source stream. - m_pStream.Write(buffer, 0, readedCount); - } - m_pStream.Flush(); - - // Log - if (Logger != null) - { - Logger.AddWrite(count, null); - } - } - finally - { - m_IsWriteActive = false; - } - } - - /// - /// Starts writing all stream data to source stream. - /// - /// Stream which data to write. - /// Maximum number of bytes to read from stream. - /// User data. - /// Callback to be called if asynchronous write completes. - /// Raised when stream is null. - /// Raised when there already is pending write operation. - public void BeginWriteAll(Stream stream, int maxSize, object tag, WriteStreamCallback callback) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - lock (this) - { - if (m_IsWriteActive) - { - throw new InvalidOperationException( - "There is pending write operation, multiple write operations not allowed !"); - } - m_IsWriteActive = true; - } - - m_pBeginWriteAll_Buffer = new byte[Workaround.Definitions.MaxStreamLineLength]; - m_pBeginWriteAll_Stream = stream; - m_BeginWriteAll_MaxSize = maxSize; - m_pBeginWriteAll_Callback = callback; - m_BeginWriteAll_Readed = 0; - - // Start reading data block. - m_pBeginWriteAll_Stream.BeginRead(m_pBeginWriteAll_Buffer, - 0, - m_pBeginWriteAll_Buffer.Length, - InternalBeginWriteAllCallback, - null); - } - - /// - /// Writes all stream data to source stream. - /// - /// Stream which data to write. - /// Returns number of bytes written to source stream. - /// Raised when stream is null. - /// Raised when there already is pending write operation. - public int WriteAll(Stream stream) - { - return WriteAll(stream, int.MaxValue); - } - - /// - /// Writes all stream data to source stream. - /// - /// Stream which data to write. - /// Maximum muber of bytes to read from stream and write source stream. - /// Returns number of bytes written to source stream. - /// Raised when stream is null. - /// Raised when there already is pending write operation. - /// Raised when stream stream has more data than specified by maxSize. - public int WriteAll(Stream stream, int maxSize) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - lock (this) - { - if (m_IsWriteActive) - { - throw new InvalidOperationException( - "There is pending write operation, multiple write operations not allowed !"); - } - m_IsWriteActive = true; - } - - try - { - byte[] buffer = new byte[Workaround.Definitions.MaxStreamLineLength]; - int totalReadedCount = 0; - int readedCount = 0; - while (readedCount > 0) - { - // Read data block - readedCount = stream.Read(buffer, 0, buffer.Length); - - // We reached end of stream, no more data. - if (readedCount == 0) - { - break; - } - // We have exceeded maximum allowed data size. - else if ((totalReadedCount + readedCount) > maxSize) - { - throw new DataSizeExceededException(); - } - totalReadedCount += readedCount; - - // Write readed data to source stream. - m_pStream.Write(buffer, 0, readedCount); - } - - // Log - if (Logger != null) - { - Logger.AddWrite(totalReadedCount, null); - } - - return totalReadedCount; - } - finally - { - m_IsWriteActive = false; - } - } - - /// - /// Starts writing stream data to source stream. Data will be period handled and terminated as needed. - /// - /// Stream which data to write to source stream. - /// Maximum muber of bytes to read from stream and write source stream. - /// User data. - /// Callback to be called if asynchronous write completes. - /// Raised when stream is null. - /// Raised when there already is pending write operation. - public void BeginWritePeriodTerminated(Stream stream, - int maxSize, - object tag, - WriteStreamCallback callback) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - lock (this) - { - if (m_IsWriteActive) - { - throw new InvalidOperationException( - "There is pending write operation, multiple write operations not allowed !"); - } - m_IsWriteActive = true; - } - - m_pBeginWritePeriodTerminated_Stream = stream; - m_BeginWritePeriodTerminated_MaxSize = maxSize; - m_pBeginWritePeriodTerminated_Callback = callback; - m_pBeginWritePeriodTerminated_BufferedStream = new BufferedStream(m_pStream); - } - - /// - /// Reades all data from the specified stream and writes it to source stream. Period handlign and period terminator is added as required. - /// - /// Stream which data to write to source stream. - /// Returns number of bytes written to source stream. Note this value differs from - /// stream readed bytes count because of period handling and period terminator. - /// - /// Raised when stream is null. - /// Raised when there already is pending write operation. - /// Raised when stream contains line with bigger line size than allowed. - /// Raised when stream has more data than maxSize allows.. - public int WritePeriodTerminated(Stream stream) - { - return WritePeriodTerminated(stream, int.MaxValue); - } - - /// - /// Reades all data from the specified stream and writes it to source stream. Period handlign and period terminator is added as required. - /// - /// Stream which data to write to source stream. - /// Maximum muber of bytes to read from stream and write source stream. - /// Returns number of bytes written to source stream. Note this value differs from - /// stream readed bytes count because of period handling and period terminator. - /// - /// Raised when stream is null. - /// Raised when there already is pending write operation. - /// Raised when stream contains line with bigger line size than allowed. - /// Raised when stream has more data than maxSize allows.. - public int WritePeriodTerminated(Stream stream, int maxSize) - { - if (stream == null) - { - throw new ArgumentNullException("stream"); - } - lock (this) - { - if (m_IsWriteActive) - { - throw new InvalidOperationException( - "There is pending write operation, multiple write operations not allowed !"); - } - m_IsWriteActive = true; - } - - try - { - BufferedStream bufferedStoreStream = new BufferedStream(m_pStream, Workaround.Definitions.MaxStreamLineLength); - StreamHelper reader = new StreamHelper(stream); - int totalWrittenCount = 0; - int readedCount = 0; - int rawReadedCount = 0; - while (true) - { - // Read data block. - readedCount = ReadLineInternal(m_pLineBuffer, - SizeExceededAction.ThrowException, - out rawReadedCount, - false); - - // We reached end of stream, no more data. - if (readedCount == 0) - { - break; - } - - // Maximum allowed data size exceeded. - if ((totalWrittenCount + rawReadedCount) > maxSize) - { - throw new DataSizeExceededException(); - } - - // If line starts with period(.), additional period is added. - if (m_pLineBuffer[0] == '.') - { - bufferedStoreStream.WriteByte((byte) '.'); - totalWrittenCount++; - } - - // Write readed line to buffered stream. - bufferedStoreStream.Write(m_pLineBuffer, 0, readedCount); - bufferedStoreStream.Write(m_LineBreak, 0, m_LineBreak.Length); - totalWrittenCount += (readedCount + m_LineBreak.Length); - } - - // Write terminator ".". We have start already in stream. - bufferedStoreStream.Write(new[] {(byte) '.', (byte) '\r', (byte) '\n'}, 0, 3); - bufferedStoreStream.Flush(); - m_pStream.Flush(); - - // Log - if (Logger != null) - { - Logger.AddWrite(totalWrittenCount, null); - } - - return totalWrittenCount; - } - finally - { - m_IsWriteActive = false; - } - } - - #endregion - - #region Utility methods - - /// - /// Starts reading line from source stream. This method does not do any checks and read locks. - /// - /// Buffer where to store line data. - /// Specifies how this method behaves when maximum line size exceeded. - /// User data. - /// Callback to be called whan asynchronous operation completes. - /// Specifies if read lock is released. - /// User data. - private void BeginReadLineInternal(byte[] buffer, - SizeExceededAction exceededAction, - object tag, - ReadLineCallback callback, - bool unlockRead, - bool log) - { - m_pRLine_LineBuffer = buffer; - m_RLine_ExceedAction = exceededAction; - m_pRLine_Tag = tag; - m_pRLine_Callback = callback; - m_RLine_LineBufferOffset = 0; - m_RLine_TotalReadedCount = 0; - m_RLine_LineBufferSize = buffer.Length; - m_RLine_UnlockRead = unlockRead; - m_RLine_Log = log; - - if (IsReadBuffered) - { - DoReadLine_Buffered(); - } - else - { - m_pStream.BeginRead(m_pRLine_ByteBuffer, 0, 1, OnReadByte_Completed, null); - } - } - - /// - /// Is called when BeginWrite(byte[] data,int offset,int count) has completed. - /// - /// - private void InternalBeginWriteCallback(IAsyncResult result) - { - int count = (int) ((object[]) result.AsyncState)[0]; - WriteCallback callback = (WriteCallback) ((object[]) result.AsyncState)[1]; - - try - { - m_pStream.EndWrite(result); - - // Log - if (Logger != null) - { - Logger.AddWrite(count, null); - } - - if (callback != null) - { - callback(new Write_EventArgs(null)); - } - } - catch (Exception x) - { - if (callback != null) - { - callback(new Write_EventArgs(x)); - } - } - finally - { - m_IsWriteActive = false; - } - } - - /// - /// This method is called when BeginWrite has readed new data block. - /// - /// - private void InternalBeginWriteStreamCallback(IAsyncResult result) - { - try - { - int readedCount = m_pBeginWrite_Stream.EndRead(result); - - // We have reached end of stream, we din't get so much data as requested. - if (readedCount == 0) - { - InternalBeginWriteStreamCompleted( - new IncompleteDataException("Read stream didn't have so much data than requested !")); - } - else - { - m_BeginWrite_Readed += readedCount; - - // Write readed bytes to source stream. - m_pStream.Write(m_pBeginWrite_Buffer, 0, readedCount); - - // We have readed all requested data. - if (m_BeginWrite_Count == m_BeginWrite_Readed) - { - InternalBeginWriteStreamCompleted(null); - } - // We need read more data. - else - { - // Start reading data block. - m_pBeginWrite_Stream.BeginRead(m_pBeginWrite_Buffer, - 0, - Math.Min(m_pBeginWrite_Buffer.Length, - m_BeginWrite_Count - m_BeginWrite_Readed), - InternalBeginWriteStreamCallback, - null); - } - } - } - catch (Exception x) - { - InternalBeginWriteStreamCompleted(x); - } - } - - /// - /// Is called when BeginWrite has completed. - /// - /// Exception happened during write or null if operation was successfull. - private void InternalBeginWriteStreamCompleted(Exception exception) - { - // Release write lock. - m_IsWriteActive = false; - - // Log - if (Logger != null) - { - Logger.AddWrite(m_BeginWrite_Readed, null); - } - - if (m_pBeginWrite_Callback != null) - { - m_pBeginWrite_Callback(new WriteStream_EventArgs(exception, - m_pBeginWrite_Stream, - m_BeginWrite_Readed, - m_BeginWrite_Readed)); - } - } - - /// - /// This method is called when BeginWriteAll has readed new data block. - /// - /// - private void InternalBeginWriteAllCallback(IAsyncResult result) - { - try - { - int readedCount = m_pBeginWriteAll_Stream.EndRead(result); - - // We have reached end of stream, no more data. - if (readedCount == 0) - { - InternalBeginWriteAllCompleted(null); - } - else - { - m_BeginWriteAll_Readed += readedCount; - - // Maximum allowed data size exceeded. - if (m_BeginWriteAll_Readed > m_BeginWriteAll_MaxSize) - { - throw new DataSizeExceededException(); - } - - // Write readed bytes to source stream. - m_pStream.Write(m_pBeginWriteAll_Buffer, 0, readedCount); - - // Start reading next data block. - m_pBeginWriteAll_Stream.BeginRead(m_pBeginWriteAll_Buffer, - 0, - m_pBeginWriteAll_Buffer.Length, - InternalBeginWriteAllCallback, - null); - return; - } - } - catch (Exception x) - { - InternalBeginWriteAllCompleted(x); - } - } - - /// - /// Is called when BeginWriteAll has completed. - /// - /// Exception happened during write or null if operation was successfull. - private void InternalBeginWriteAllCompleted(Exception exception) - { - // Release write lock. - m_IsWriteActive = false; - - // Log - if (Logger != null) - { - Logger.AddWrite(m_BeginWriteAll_Readed, null); - } - - if (m_pBeginWriteAll_Callback != null) - { - m_pBeginWriteAll_Callback(new WriteStream_EventArgs(exception, - m_pBeginWriteAll_Stream, - m_BeginWriteAll_Readed, - m_BeginWriteAll_Readed)); - } - } - - /// - /// Is called when BeginWritePeriodTerminated stream.BeginReadLine has completed. - /// - /// Callback data. - private void InternalBeginWritePeriodTerminatedReadLineCompleted(ReadLine_EventArgs e) - { - try - { - // Error happened during read. - if (e.Exception != null) - { - InternalBeginWritePeriodTerminatedCompleted(e.Exception); - } - // We reached end of stream, no more data. - else if (e.ReadedCount == 0) - { - InternalBeginWritePeriodTerminatedCompleted(null); - } - else - { - m_BeginWritePeriodTerminated_Readed += e.ReadedCount; - - // Maximum allowed data size exceeded. - if (m_BeginWritePeriodTerminated_Readed > m_BeginWritePeriodTerminated_MaxSize) - { - throw new DataSizeExceededException(); - } - - // If line starts with period, addtional period is added. - if (e.LineBuffer[0] == '.') - { - m_pBeginWritePeriodTerminated_BufferedStream.WriteByte((int) '.'); - m_BeginWritePeriodTerminated_Written++; - } - - // Write readed line to buffered stream. - m_pBeginWritePeriodTerminated_BufferedStream.Write(e.LineBuffer, 0, e.Count); - m_pBeginWritePeriodTerminated_BufferedStream.Write(m_LineBreak, 0, m_LineBreak.Length); - m_BeginWritePeriodTerminated_Written += e.Count + m_LineBreak.Length; - } - } - catch (Exception x) - { - InternalBeginWritePeriodTerminatedCompleted(x); - } - } - - /// - /// Is called when asynchronous write period terminated has completed. - /// - /// Exception happened during write or null if operation was successfull. - private void InternalBeginWritePeriodTerminatedCompleted(Exception exception) - { - // Release write lock. - m_IsWriteActive = false; - - // Force to write buffer to source stream, if any. - try - { - m_pBeginWritePeriodTerminated_BufferedStream.Flush(); - } - catch {} - - // Log - if (Logger != null) - { - Logger.AddWrite(m_BeginWritePeriodTerminated_Written, null); - } - - if (m_pBeginWritePeriodTerminated_Callback != null) - { - m_pBeginWritePeriodTerminated_Callback(new WriteStream_EventArgs(exception, - m_pBeginWritePeriodTerminated_Stream, - m_BeginWritePeriodTerminated_Readed, - m_BeginWritePeriodTerminated_Written)); - } - } - - /// - /// Is called when asynchronous read byte operation has completed. - /// - /// - private void OnReadByte_Completed(IAsyncResult result) - { - try - { - int readedCount = m_pStream.EndRead(result); - if (readedCount == 1) - { - m_RLine_TotalReadedCount++; - - // We have LF. - if (m_pRLine_ByteBuffer[0] == '\n') - { - // Line size eceeded and some data junked. - if (m_RLine_LineBufferOffset > m_RLine_LineBufferSize) - { - OnReadLineCompleted(new LineSizeExceededException()); - } - // Read line completed sucessfully. - else - { - OnReadLineCompleted(null); - } - return; - } - // We just skip all CR. - else if (m_pRLine_ByteBuffer[0] == '\r') {} - else - { - // Maximum allowed line size exceeded. - if (m_RLine_LineBufferOffset >= m_RLine_LineBufferSize) - { - if (m_RLine_ExceedAction == SizeExceededAction.ThrowException) - { - OnReadLineCompleted(new LineSizeExceededException()); - return; - } - } - // Write readed byte to line buffer. - else - { - m_pRLine_LineBuffer[m_RLine_LineBufferOffset] = m_pRLine_ByteBuffer[0]; - m_RLine_LineBufferOffset++; - } - } - - // Get next byte. - m_pStream.BeginRead(m_pRLine_ByteBuffer, 0, 1, OnReadByte_Completed, null); - } - // We have no more data. - else - { - // Line size eceeded and some data junked. - if (m_RLine_LineBufferOffset >= m_RLine_LineBufferSize) - { - OnReadLineCompleted(new LineSizeExceededException()); - } - // Read line completed sucessfully. - else - { - OnReadLineCompleted(null); - } - } - } - catch (Exception x) - { - OnReadLineCompleted(x); - } - } - - /// - /// Tries to read line from data buffer, if no line in buffer, new data buffer will be readed(buffered). - /// - private void DoReadLine_Buffered() - { - try - { - byte currentByte = 0; - while (m_ReadBufferOffset < m_ReadBufferEndPos) - { - currentByte = m_pReadBuffer[m_ReadBufferOffset]; - m_ReadBufferOffset++; - m_RLine_TotalReadedCount++; - - // We have LF. - if (currentByte == '\n') - { - // Line size eceeded and some data junked. - if (m_RLine_TotalReadedCount > m_RLine_LineBufferSize) - { - OnReadLineCompleted(new LineSizeExceededException()); - } - // Read line completed sucessfully. - else - { - OnReadLineCompleted(null); - } - return; - } - // We just skip all CR. - else if (currentByte == '\r') {} - else - { - // Maximum allowed line size exceeded. - if (m_RLine_TotalReadedCount >= m_RLine_LineBufferSize) - { - if (m_RLine_ExceedAction == SizeExceededAction.ThrowException) - { - OnReadLineCompleted(new LineSizeExceededException()); - return; - } - } - // Write readed byte to line buffer. - else - { - m_pRLine_LineBuffer[m_RLine_LineBufferOffset] = currentByte; - m_RLine_LineBufferOffset++; - } - } - } - - // If we reach here, that means we consumed all read buffer and no line was in it. - // Just get new data buffer block. - m_pStream.BeginRead(m_pReadBuffer, 0, m_pReadBuffer.Length, OnReadBuffer_Completed, null); - } - catch (Exception x) - { - OnReadLineCompleted(x); - } - } - - /// - /// Is called when asynchronous data buffering has completed. - /// - /// - private void OnReadBuffer_Completed(IAsyncResult result) - { - try - { - m_ReadBufferEndPos = m_pStream.EndRead(result); - m_ReadBufferOffset = 0; - - // We have reached end of stream, no more data. - if (m_ReadBufferEndPos == 0) - { - // Line size eceeded and some data junked. - if (m_RLine_TotalReadedCount > m_RLine_LineBufferSize) - { - OnReadLineCompleted(new LineSizeExceededException()); - } - // Read line completed sucessfully. - else - { - OnReadLineCompleted(null); - } - } - // Continue line reading. - else - { - DoReadLine_Buffered(); - } - } - catch (Exception x) - { - OnReadLineCompleted(x); - } - } - - /// - /// Is called when read line has completed. - /// - /// Excheption what happened during line reading or null if read line was completed sucessfully. - private void OnReadLineCompleted(Exception x) - { - if (m_RLine_UnlockRead) - { - // Release reader lock. - m_IsReadActive = false; - } - - if (m_RLine_Log && Logger != null) - { - Logger.AddRead(m_RLine_TotalReadedCount, null); - } - - if (m_pRLine_Callback != null) - { - m_pRLine_EventArgs.Reuse(x, - m_RLine_TotalReadedCount, - m_pRLine_LineBuffer, - m_RLine_LineBufferOffset, - m_pRLine_Tag); - m_pRLine_Callback(m_pRLine_EventArgs); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/StreamLineReader.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/StreamLineReader.cs deleted file mode 100644 index a789a71e9..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/StreamLineReader.cs +++ /dev/null @@ -1,171 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net -{ - #region usings - - using System; - using System.IO; - - #endregion - - /// - /// Stream line reader. - /// - //[Obsolete("Use StreamHelper instead !")] - public class StreamLineReader - { - #region Members - - private readonly byte[] m_Buffer = new byte[1024]; - private readonly Stream m_StrmSource; - private bool m_CRLF_LinesOnly = true; - private string m_Encoding = ""; - private int m_ReadBufferSize = 1024; - - #endregion - - #region Properties - - /// - /// Gets or sets charset encoding to use for string based methods. Default("") encoding is system default encoding. - /// - public string Encoding - { - get { return m_Encoding; } - - set - { - // Check if encoding is valid - System.Text.Encoding.GetEncoding(value); - - m_Encoding = value; - } - } - - /// - /// Gets or sets if lines must be CRLF terminated or may be only LF terminated too. - /// - public bool CRLF_LinesOnly - { - get { return m_CRLF_LinesOnly; } - - set { m_CRLF_LinesOnly = value; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Source stream from where to read data. Reading begins from stream current position. - public StreamLineReader(Stream strmSource) - { - m_StrmSource = strmSource; - } - - #endregion - - #region Methods - - /// - /// Reads byte[] line from stream. NOTE: Returns null if end of stream reached. - /// - /// Return null if end of stream reached. - public byte[] ReadLine() - { - // TODO: Allow to buffer source stream reads - - byte[] buffer = m_Buffer; - int posInBuffer = 0; - - int prevByte = m_StrmSource.ReadByte(); - int currByteInt = m_StrmSource.ReadByte(); - while (prevByte > -1) - { - // CRLF line found - if ((prevByte == (byte) '\r' && (byte) currByteInt == (byte) '\n')) - { - byte[] retVal = new byte[posInBuffer]; - Array.Copy(buffer, retVal, posInBuffer); - return retVal; - } - // LF line found and only LF lines allowed - else if (!m_CRLF_LinesOnly && currByteInt == '\n') - { - byte[] retVal = new byte[posInBuffer + 1]; - Array.Copy(buffer, retVal, posInBuffer + 1); - retVal[posInBuffer] = (byte) prevByte; - return retVal; - } - - // Buffer is full, add addition m_ReadBufferSize bytes - if (posInBuffer == buffer.Length) - { - byte[] newBuffer = new byte[buffer.Length + m_ReadBufferSize]; - Array.Copy(buffer, newBuffer, buffer.Length); - buffer = newBuffer; - } - buffer[posInBuffer] = (byte) prevByte; - posInBuffer++; - prevByte = currByteInt; - - // Read next byte - currByteInt = m_StrmSource.ReadByte(); - } - - // Line isn't terminated with and has some bytes left, return them. - if (posInBuffer > 0) - { - byte[] retVal = new byte[posInBuffer]; - Array.Copy(buffer, retVal, posInBuffer); - return retVal; - } - - return null; - } - - /// - /// Reads string line from stream. String is converted with specified Encoding property from byte[] line. NOTE: Returns null if end of stream reached. - /// - /// - public string ReadLineString() - { - byte[] line = ReadLine(); - if (line != null) - { - if (m_Encoding == null || m_Encoding == "") - { - return System.Text.Encoding.Default.GetString(line); - } - else - { - return System.Text.Encoding.GetEncoding(m_Encoding).GetString(line); - } - } - else - { - return null; - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/WriteStream_EventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/WriteStream_EventArgs.cs deleted file mode 100644 index 0af62f2a5..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/WriteStream_EventArgs.cs +++ /dev/null @@ -1,96 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IO -{ - #region usings - - using System; - using System.IO; - - #endregion - - /// - /// This class provides data to asynchronous write from stream methods callback. - /// - public class WriteStream_EventArgs - { - #region Members - - private readonly int m_CountReaded; - private readonly int m_CountWritten; - private readonly Exception m_pException; - private readonly Stream m_pStream; - - #endregion - - #region Properties - - /// - /// Gets exception happened during write or null if operation was successfull. - /// - public Exception Exception - { - get { return m_pException; } - } - - /// - /// Gets stream what data was written. - /// - public Stream Stream - { - get { return m_pStream; } - } - - /// - /// Gets number of bytes readed from Stream. - /// - public int CountReaded - { - get { return m_CountReaded; } - } - - /// - /// Gets number of bytes written to source stream. - /// - public int CountWritten - { - get { return m_CountWritten; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Exception happened during write or null if operation was successfull. - /// Stream which data was written. - /// Number of bytes readed from stream. - /// Number of bytes written to source stream. - internal WriteStream_EventArgs(Exception exception, Stream stream, int countReaded, int countWritten) - { - m_pException = exception; - m_pStream = stream; - m_CountReaded = countReaded; - m_CountWritten = countWritten; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/Write_EventArgs.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/Write_EventArgs.cs deleted file mode 100644 index 75a8f11a5..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/_Obsolete/Write_EventArgs.cs +++ /dev/null @@ -1,62 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.IO -{ - #region usings - - using System; - - #endregion - - /// - /// This class provides data for BeginWriteCallback delegate. - /// - public class Write_EventArgs - { - #region Members - - private readonly Exception m_pException; - - #endregion - - #region Properties - - /// - /// Gets exception happened during write or null if operation was successfull. - /// - public Exception Exception - { - get { return m_pException; } - } - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Exception happened during write or null if operation was successfull. - internal Write_EventArgs(Exception exception) - { - m_pException = exception; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/DeliveryAddress.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/DeliveryAddress.cs deleted file mode 100644 index 8b94623dc..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/DeliveryAddress.cs +++ /dev/null @@ -1,317 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime.vCard -{ - /// - /// vCard delivery address implementation. - /// - public class DeliveryAddress - { - #region Members - - private readonly Item m_pItem; - private string m_Country = ""; - - private string m_ExtendedAddress = ""; - private string m_Locality = ""; - private string m_PostalCode = ""; - private string m_PostOfficeAddress = ""; - private string m_Region = ""; - private string m_Street = ""; - - private DeliveryAddressType_enum m_Type = DeliveryAddressType_enum.Ineternational | - DeliveryAddressType_enum.Postal | - DeliveryAddressType_enum.Parcel | - DeliveryAddressType_enum.Work; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner vCard item. - /// Address type. Note: This value can be flagged value ! - /// Post office address. - /// Extended address. - /// Street name. - /// Locality(city). - /// Region. - /// Postal code. - /// Country. - internal DeliveryAddress(Item item, - DeliveryAddressType_enum addressType, - string postOfficeAddress, - string extendedAddress, - string street, - string locality, - string region, - string postalCode, - string country) - { - m_pItem = item; - m_Type = addressType; - m_PostOfficeAddress = postOfficeAddress; - m_ExtendedAddress = extendedAddress; - m_Street = street; - m_Locality = locality; - m_Region = region; - m_PostalCode = postalCode; - m_Country = country; - } - - #endregion - - #region Properties - - /// - /// Gets or sets address type. Note: This property can be flagged value ! - /// - public DeliveryAddressType_enum AddressType - { - get { return m_Type; } - - set - { - m_Type = value; - Changed(); - } - } - - /// - /// Gets or sets country. - /// - public string Country - { - get { return m_Country; } - - set - { - m_Country = value; - Changed(); - } - } - - /// - /// Gests or sets extended address. - /// - public string ExtendedAddress - { - get { return m_ExtendedAddress; } - - set - { - m_ExtendedAddress = value; - Changed(); - } - } - - /// - /// Gets underlaying vCrad item. - /// - public Item Item - { - get { return m_pItem; } - } - - /// - /// Gets or sets locality(city). - /// - public string Locality - { - get { return m_Locality; } - - set - { - m_Locality = value; - Changed(); - } - } - - /// - /// Gets or sets postal code. - /// - public string PostalCode - { - get { return m_PostalCode; } - - set - { - m_PostalCode = value; - Changed(); - } - } - - /// - /// Gets or sets post office address. - /// - public string PostOfficeAddress - { - get { return m_PostOfficeAddress; } - - set - { - m_PostOfficeAddress = value; - Changed(); - } - } - - /// - /// Gets or sets region. - /// - public string Region - { - get { return m_Region; } - - set - { - m_Region = value; - Changed(); - } - } - - /// - /// Gets or sets street. - /// - public string Street - { - get { return m_Street; } - - set - { - m_Street = value; - Changed(); - } - } - - #endregion - - #region Internal methods - - /// - /// Parses delivery address from vCard ADR structure string. - /// - /// vCard ADR item. - internal static DeliveryAddress Parse(Item item) - { - DeliveryAddressType_enum type = DeliveryAddressType_enum.NotSpecified; - if (item.ParametersString.ToUpper().IndexOf("PREF") != -1) - { - type |= DeliveryAddressType_enum.Preferred; - } - if (item.ParametersString.ToUpper().IndexOf("DOM") != -1) - { - type |= DeliveryAddressType_enum.Domestic; - } - if (item.ParametersString.ToUpper().IndexOf("INTL") != -1) - { - type |= DeliveryAddressType_enum.Ineternational; - } - if (item.ParametersString.ToUpper().IndexOf("POSTAL") != -1) - { - type |= DeliveryAddressType_enum.Postal; - } - if (item.ParametersString.ToUpper().IndexOf("PARCEL") != -1) - { - type |= DeliveryAddressType_enum.Parcel; - } - if (item.ParametersString.ToUpper().IndexOf("HOME") != -1) - { - type |= DeliveryAddressType_enum.Home; - } - if (item.ParametersString.ToUpper().IndexOf("WORK") != -1) - { - type |= DeliveryAddressType_enum.Work; - } - - string[] items = item.DecodedValue.Split(';'); - return new DeliveryAddress(item, - type, - items.Length >= 1 ? items[0] : "", - items.Length >= 2 ? items[1] : "", - items.Length >= 3 ? items[2] : "", - items.Length >= 4 ? items[3] : "", - items.Length >= 5 ? items[4] : "", - items.Length >= 6 ? items[5] : "", - items.Length >= 7 ? items[6] : ""); - } - - /// - /// Converts DeliveryAddressType_enum to vCard item parameters string. - /// - /// Value to convert. - /// - internal static string AddressTypeToString(DeliveryAddressType_enum type) - { - string retVal = ""; - if ((type & DeliveryAddressType_enum.Domestic) != 0) - { - retVal += "DOM,"; - } - if ((type & DeliveryAddressType_enum.Home) != 0) - { - retVal += "HOME,"; - } - if ((type & DeliveryAddressType_enum.Ineternational) != 0) - { - retVal += "INTL,"; - } - if ((type & DeliveryAddressType_enum.Parcel) != 0) - { - retVal += "PARCEL,"; - } - if ((type & DeliveryAddressType_enum.Postal) != 0) - { - retVal += "POSTAL,"; - } - if ((type & DeliveryAddressType_enum.Preferred) != 0) - { - retVal += "Preferred,"; - } - if ((type & DeliveryAddressType_enum.Work) != 0) - { - retVal += "Work,"; - } - if (retVal.EndsWith(",")) - { - retVal = retVal.Substring(0, retVal.Length - 1); - } - - return retVal; - } - - #endregion - - #region Utility methods - - /// - /// This method is called when some property has changed, we need to update underlaying vCard item. - /// - private void Changed() - { - string value = "" + m_PostOfficeAddress + ";" + m_ExtendedAddress + ";" + m_Street + ";" + - m_Locality + ";" + m_Region + ";" + m_PostalCode + ";" + m_Country; - - m_pItem.ParametersString = AddressTypeToString(m_Type); - m_pItem.SetDecodedValue(value); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/DeliveryAddressCollection.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/DeliveryAddressCollection.cs deleted file mode 100644 index 8c287c194..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/DeliveryAddressCollection.cs +++ /dev/null @@ -1,151 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime.vCard -{ - #region usings - - using System.Collections; - using System.Collections.Generic; - - #endregion - - /// - /// vCard delivery address collection implementation. - /// - public class DeliveryAddressCollection : IEnumerable - { - #region Members - - private readonly List m_pCollection; - private readonly vCard m_pOwner; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner vCard. - internal DeliveryAddressCollection(vCard owner) - { - m_pOwner = owner; - m_pCollection = new List(); - - foreach (Item item in owner.Items.Get("ADR")) - { - m_pCollection.Add(DeliveryAddress.Parse(item)); - } - } - - #endregion - - #region Properties - - /// - /// Gets number of items in the collection. - /// - public int Count - { - get { return m_pCollection.Count; } - } - - /// - /// Gets item at the specified index. - /// - /// Index of item which to get. - /// - public DeliveryAddress this[int index] - { - get { return m_pCollection[index]; } - } - - #endregion - - #region Methods - - /// - /// Add new delivery address to the collection. - /// - /// Delivery address type. Note: This value can be flagged value ! - /// Post office address. - /// Extended address. - /// Street name. - /// Locality(city). - /// Region. - /// Postal code. - /// Country. - public void Add(DeliveryAddressType_enum type, - string postOfficeAddress, - string extendedAddress, - string street, - string locality, - string region, - string postalCode, - string country) - { - string value = "" + postOfficeAddress + ";" + extendedAddress + ";" + street + ";" + locality + - ";" + region + ";" + postalCode + ";" + country; - - Item item = m_pOwner.Items.Add("ADR", DeliveryAddress.AddressTypeToString(type), ""); - item.SetDecodedValue(value); - m_pCollection.Add(new DeliveryAddress(item, - type, - postOfficeAddress, - extendedAddress, - street, - locality, - region, - postalCode, - country)); - } - - /// - /// Removes specified item from the collection. - /// - /// Item to remove. - public void Remove(DeliveryAddress item) - { - m_pOwner.Items.Remove(item.Item); - m_pCollection.Remove(item); - } - - /// - /// Removes all items from the collection. - /// - public void Clear() - { - foreach (DeliveryAddress email in m_pCollection) - { - m_pOwner.Items.Remove(email.Item); - } - m_pCollection.Clear(); - } - - /// - /// Gets enumerator. - /// - /// - public IEnumerator GetEnumerator() - { - return m_pCollection.GetEnumerator(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/DeliveryAddressType_enum.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/DeliveryAddressType_enum.cs deleted file mode 100644 index 489b34e2b..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/DeliveryAddressType_enum.cs +++ /dev/null @@ -1,65 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime.vCard -{ - /// - /// vCal delivery address type. Note this values may be flagged ! - /// - public enum DeliveryAddressType_enum - { - /// - /// Delivery address type not specified. - /// - NotSpecified = 0, - - /// - /// Preferred delivery address. - /// - Preferred = 1, - - /// - /// Domestic delivery address. - /// - Domestic = 2, - - /// - /// International delivery address. - /// - Ineternational = 4, - - /// - /// Postal delivery address. - /// - Postal = 8, - - /// - /// Parcel delivery address. - /// - Parcel = 16, - - /// - /// Delivery address for a residence. - /// - Home = 32, - - /// - /// Address for a place of work. - /// - Work = 64, - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/EmailAddress.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/EmailAddress.cs deleted file mode 100644 index 8d7ab2578..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/EmailAddress.cs +++ /dev/null @@ -1,158 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime.vCard -{ - /// - /// vCard email address implementation. - /// - public class EmailAddress - { - #region Members - - private readonly Item m_pItem; - private string m_EmailAddress = ""; - private EmailAddressType_enum m_Type = EmailAddressType_enum.Internet; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner vCard item. - /// Email type. Note: This value can be flagged value ! - /// Email address. - internal EmailAddress(Item item, EmailAddressType_enum type, string emailAddress) - { - m_pItem = item; - m_Type = type; - m_EmailAddress = emailAddress; - } - - #endregion - - #region Properties - - /// - /// Gets or sets email address. - /// - public string Email - { - get { return m_EmailAddress; } - - set - { - m_EmailAddress = value; - Changed(); - } - } - - /// - /// Gets or sets email type. Note: This property can be flagged value ! - /// - public EmailAddressType_enum EmailType - { - get { return m_Type; } - - set - { - m_Type = value; - Changed(); - } - } - - /// - /// Gets underlaying vCrad item. - /// - public Item Item - { - get { return m_pItem; } - } - - #endregion - - #region Internal methods - - /// - /// Parses email address from vCard EMAIL structure string. - /// - /// vCard EMAIL item. - internal static EmailAddress Parse(Item item) - { - EmailAddressType_enum type = EmailAddressType_enum.NotSpecified; - if (item.ParametersString.ToUpper().IndexOf("PREF") != -1) - { - type |= EmailAddressType_enum.Preferred; - } - if (item.ParametersString.ToUpper().IndexOf("INTERNET") != -1) - { - type |= EmailAddressType_enum.Internet; - } - if (item.ParametersString.ToUpper().IndexOf("X400") != -1) - { - type |= EmailAddressType_enum.X400; - } - - return new EmailAddress(item, type, item.DecodedValue); - } - - /// - /// Converts EmailAddressType_enum to vCard item parameters string. - /// - /// Value to convert. - /// - internal static string EmailTypeToString(EmailAddressType_enum type) - { - string retVal = ""; - if ((type & EmailAddressType_enum.Internet) != 0) - { - retVal += "INTERNET,"; - } - if ((type & EmailAddressType_enum.Preferred) != 0) - { - retVal += "PREF,"; - } - if ((type & EmailAddressType_enum.X400) != 0) - { - retVal += "X400,"; - } - if (retVal.EndsWith(",")) - { - retVal = retVal.Substring(0, retVal.Length - 1); - } - - return retVal; - } - - #endregion - - #region Utility methods - - /// - /// This method is called when some property has changed, wee need to update underlaying vCard item. - /// - private void Changed() - { - m_pItem.ParametersString = EmailTypeToString(m_Type); - m_pItem.SetDecodedValue(m_EmailAddress); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/EmailAddressCollection.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/EmailAddressCollection.cs deleted file mode 100644 index 3d0dbfeae..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/EmailAddressCollection.cs +++ /dev/null @@ -1,130 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime.vCard -{ - #region usings - - using System.Collections; - using System.Collections.Generic; - - #endregion - - /// - /// vCard email address collection implementation. - /// - public class EmailAddressCollection : IEnumerable - { - #region Members - - private readonly List m_pCollection; - private readonly vCard m_pOwner; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner vCard. - internal EmailAddressCollection(vCard owner) - { - m_pOwner = owner; - m_pCollection = new List(); - - foreach (Item item in owner.Items.Get("EMAIL")) - { - m_pCollection.Add(EmailAddress.Parse(item)); - } - } - - #endregion - - #region Properties - - /// - /// Gets number of items in the collection. - /// - public int Count - { - get { return m_pCollection.Count; } - } - - /// - /// Gets item at the specified index. - /// - /// Index of item which to get. - /// - public EmailAddress this[int index] - { - get { return m_pCollection[index]; } - } - - #endregion - - #region Methods - - /// - /// Add new email address to the collection. - /// - /// Email address type. Note: This value can be flagged value ! - /// Email address. - public EmailAddress Add(EmailAddressType_enum type, string email) - { - Item item = m_pOwner.Items.Add("EMAIL", EmailAddress.EmailTypeToString(type), ""); - item.SetDecodedValue(email); - EmailAddress emailAddress = new EmailAddress(item, type, email); - m_pCollection.Add(emailAddress); - - return emailAddress; - } - - /// - /// Removes specified item from the collection. - /// - /// Item to remove. - public void Remove(EmailAddress item) - { - m_pOwner.Items.Remove(item.Item); - m_pCollection.Remove(item); - } - - /// - /// Removes all items from the collection. - /// - public void Clear() - { - foreach (EmailAddress email in m_pCollection) - { - m_pOwner.Items.Remove(email.Item); - } - m_pCollection.Clear(); - } - - /// - /// Gets enumerator. - /// - /// - public IEnumerator GetEnumerator() - { - return m_pCollection.GetEnumerator(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/EmailAddressType_enum.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/EmailAddressType_enum.cs deleted file mode 100644 index 23fb29096..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/EmailAddressType_enum.cs +++ /dev/null @@ -1,45 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime.vCard -{ - /// - /// vCal email address type. Note this values may be flagged ! - /// - public enum EmailAddressType_enum - { - /// - /// Email address type not specified. - /// - NotSpecified = 0, - - /// - /// Preferred email address. - /// - Preferred = 1, - - /// - /// Internet addressing type. - /// - Internet = 2, - - /// - /// X.400 addressing type. - /// - X400 = 4, - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/Item.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/Item.cs deleted file mode 100644 index 7ca315538..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/Item.cs +++ /dev/null @@ -1,231 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime.vCard -{ - #region usings - - using System; - using System.Text; - - #endregion - - /// - /// vCard structure item. - /// - public class Item - { - #region Members - - private readonly string m_Name = ""; - private string m_Parameters = ""; - private string m_Value = ""; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Item name. - /// Item parameters. - /// Item encoded value value. - internal Item(string name, string parameters, string value) - { - m_Name = name; - m_Parameters = parameters; - m_Value = value; - } - - #endregion - - #region Properties - - /// - /// Gets item decoded value. If param string specifies Encoding and/or Charset, - /// item.Value will be decoded accordingly. - /// - public string DecodedValue - { - /* RFC 2426 vCrad 5. Differences From vCard v2.1 - The CRLF character sequence in a text type value is specified - with the backslash character sequence "\n" or "\N". - - Any COMMA or SEMICOLON in a text type value must be backslash escaped. - */ - - get - { - string data = m_Value; - string encoding = null; - string charset = null; - string[] parameters = m_Parameters.ToLower().Split(';'); - foreach (string parameter in parameters) - { - string[] name_value = parameter.Split('='); - if (name_value[0] == "encoding" && name_value.Length > 1) - { - encoding = name_value[1]; - } - else if (name_value[0] == "charset" && name_value.Length > 1) - { - charset = name_value[1]; - } - } - - // Encoding specified, decode data. - if (encoding != null) - { - if (encoding == "quoted-printable") - { - data = Core.QuotedPrintableDecode(data,Encoding.Default); - } - else if (encoding == "b") - { - data = Encoding.Default.GetString(Core.Base64Decode(Encoding.Default.GetBytes(data))); - } - else - { - throw new Exception("Unknown data encoding '" + encoding + "' !"); - } - } - // Charset specified, convert data to specified charset. - if (charset != null) - { - data = Encoding.GetEncoding(charset).GetString(Encoding.Default.GetBytes(data)); - } - - // FIX ME: this must be done with structured fields - //data = data.Replace("\\n","\r\n"); - //data = TextUtils.UnEscapeString(data); Messes up structured fields - - return data; - } - } - - /// - /// Gest item name. - /// - public string Name - { - get { return m_Name; } - } - - /// - /// Gets or sets item parameters. - /// - public string ParametersString - { - get { return m_Parameters; } - - set { m_Parameters = value; } - } - - /// - /// Gets or sets item encoded value. NOTE: If you set this property value, you must encode data - /// by yourself and also set right ENCODING=encoding; and CHARSET=charset; prameter in item.ParametersString !!! - /// Normally use method item.SetDecodedStringValue method instead, this does all you need. - /// - public string Value - { - get { return m_Value; } - - set { m_Value = value; } - } - - #endregion - - #region Methods - - /// - /// Sets item decoded value. Value will be encoded as needed and stored to item.Value property. - /// Also property item.ParametersString is updated to reflect right encoding(always base64, required by rfc) and charset (utf-8). - /// - /// - public void SetDecodedValue(string value) - { - /* RFC 2426 vCrad 5. Differences From vCard v2.1 - The QUOTED-PRINTABLE inline encoding has been eliminated. - Only the "B" encoding of [RFC 2047] is an allowed value for - the ENCODING parameter. - - The CRLF character sequence in a text type value is specified - with the backslash character sequence "\n" or "\N". - - Any COMMA or SEMICOLON in a text type value must be backslash escaped. - */ - - // FIX ME: this must be done with structured fields - //value = value.Replace("\r\n","\n").Replace("\n","\\n"); - //value = TextUtils.EscapeString(value,new char[]{',',';'}); - - bool needEncode = false; - if (!Core.IsAscii(value)) - { - needEncode = true; - } - - if (needEncode) - { - // Remove encoding and charset parameters - string newParmString = ""; - string[] parameters = m_Parameters.ToLower().Split(';'); - foreach (string parameter in parameters) - { - string[] name_value = parameter.Split('='); - if (name_value[0] == "encoding" || name_value[0] == "charset") {} - else if (parameter.Length > 0) - { - newParmString += parameter + ";"; - } - } - // Add encoding parameter - newParmString += "ENCODING=b;CHARSET=utf-8"; - - ParametersString = newParmString; - Value = Convert.ToBase64String(Encoding.UTF8.GetBytes(value)); - } - else - { - Value = value; - } - } - - #endregion - - #region Internal methods - - /// - /// Converts item to vCal item string. - /// - /// - internal string ToItemString() - { - if (m_Parameters.Length > 0) - { - return m_Name + ";" + m_Parameters + ":" + MimeUtils.FoldData(m_Value); - } - else - { - return m_Name + ":" + MimeUtils.FoldData(m_Value); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/ItemCollection.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/ItemCollection.cs deleted file mode 100644 index 8d307877c..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/ItemCollection.cs +++ /dev/null @@ -1,224 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime.vCard -{ - #region usings - - using System.Collections; - using System.Collections.Generic; - - #endregion - - /// - /// vCard item collection. - /// - public class ItemCollection : IEnumerable - { - #region Members - - private readonly List m_pItems; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - internal ItemCollection() - { - m_pItems = new List(); - } - - #endregion - - #region Properties - - /// - /// Gets number of vCard items in the collection. - /// - public int Count - { - get { return m_pItems.Count; } - } - - #endregion - - #region Methods - - /// - /// Adds new vCard item to the collection. - /// - /// Item name. - /// Item parameters. - /// Item value. - public Item Add(string name, string parametes, string value) - { - Item item = new Item(name, parametes, value); - m_pItems.Add(item); - - return item; - } - - /// - /// Removes all items with the specified name. - /// - /// Item name. - public void Remove(string name) - { - for (int i = 0; i < m_pItems.Count; i++) - { - if (m_pItems[i].Name.ToLower() == name.ToLower()) - { - m_pItems.RemoveAt(i); - i--; - } - } - } - - /// - /// Removes specified item from the collection. - /// - /// Item to remove. - public void Remove(Item item) - { - m_pItems.Remove(item); - } - - /// - /// Clears all items in the collection. - /// - public void Clear() - { - m_pItems.Clear(); - } - - /// - /// Gets first item with specified name. Returns null if specified item doesn't exists. - /// - /// Item name. Name compare is case-insensitive. - /// Returns first item with specified name or null if specified item doesn't exists. - public Item GetFirst(string name) - { - foreach (Item item in m_pItems) - { - if (item.Name.ToLower() == name.ToLower()) - { - return item; - } - } - - return null; - } - - /// - /// Gets items with specified name. - /// - /// Item name. - /// - public Item[] Get(string name) - { - List retVal = new List(); - foreach (Item item in m_pItems) - { - if (item.Name.ToLower() == name.ToLower()) - { - retVal.Add(item); - } - } - - return retVal.ToArray(); - } - - /// - /// Sets first item with specified value. If item doesn't exist, item will be appended to the end. - /// If value is null, all items with specified name will be removed. - /// Value is encoed as needed and specified item.ParametersString will be updated accordingly. - /// - /// Item name. - /// Item value. - public void SetDecodedValue(string name, string value) - { - if (value == null) - { - Remove(name); - return; - } - - Item item = GetFirst(name); - if (item != null) - { - item.SetDecodedValue(value); - } - else - { - item = new Item(name, "", ""); - m_pItems.Add(item); - item.SetDecodedValue(value); - } - } - - /// - /// Sets first item with specified encoded value. If item doesn't exist, item will be appended to the end. - /// If value is null, all items with specified name will be removed. - /// - /// Item name. - /// Item encoded value. - public void SetValue(string name, string value) - { - SetValue(name, "", value); - } - - /// - /// Sets first item with specified name encoded value. If item doesn't exist, item will be appended to the end. - /// If value is null, all items with specified name will be removed. - /// - /// Item name. - /// Item parameters. - /// Item encoded value. - public void SetValue(string name, string parametes, string value) - { - if (value == null) - { - Remove(name); - return; - } - - Item item = GetFirst(name); - if (item != null) - { - item.Value = value; - } - else - { - m_pItems.Add(new Item(name, parametes, value)); - } - } - - /// - /// Gets enumerator. - /// - /// - public IEnumerator GetEnumerator() - { - return m_pItems.GetEnumerator(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/Name.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/Name.cs deleted file mode 100644 index 3023701ed..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/Name.cs +++ /dev/null @@ -1,158 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime.vCard -{ - /// - /// vCard name implementation. - /// - public class Name - { - #region Members - - private string m_AdditionalNames = ""; - private string m_FirstName = ""; - private string m_HonorificPrefix = ""; - private string m_HonorificSuffix = ""; - private string m_LastName = ""; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Last name. - /// First name. - /// Comma separated additional names. - /// Honorific prefix. - /// Honorific suffix. - public Name(string lastName, - string firstName, - string additionalNames, - string honorificPrefix, - string honorificSuffix) - { - m_LastName = lastName; - m_FirstName = firstName; - m_AdditionalNames = additionalNames; - m_HonorificPrefix = honorificPrefix; - m_HonorificSuffix = honorificSuffix; - } - - /// - /// Internal parse constructor. - /// - internal Name() {} - - #endregion - - #region Properties - - /// - /// Gets comma separated additional names. - /// - public string AdditionalNames - { - get { return m_AdditionalNames; } - } - - /// - /// Gets first name. - /// - public string FirstName - { - get { return m_FirstName; } - } - - /// - /// Gets honorific prefix. - /// - public string HonorificPerfix - { - get { return m_HonorificPrefix; } - } - - /// - /// Gets honorific suffix. - /// - public string HonorificSuffix - { - get { return m_HonorificSuffix; } - } - - /// - /// Gets last name. - /// - public string LastName - { - get { return m_LastName; } - } - - #endregion - - #region Methods - - /// - /// Converts item to vCard N structure string. - /// - /// - public string ToValueString() - { - return m_LastName + ";" + m_FirstName + ";" + m_AdditionalNames + ";" + m_HonorificPrefix + ";" + - m_HonorificSuffix; - } - - #endregion - - #region Internal methods - - /// - /// Parses name info from vCard N item. - /// - /// vCard N item. - internal static Name Parse(Item item) - { - string[] items = item.DecodedValue.Split(';'); - Name name = new Name(); - if (items.Length >= 1) - { - name.m_LastName = items[0]; - } - if (items.Length >= 2) - { - name.m_FirstName = items[1]; - } - if (items.Length >= 3) - { - name.m_AdditionalNames = items[2]; - } - if (items.Length >= 4) - { - name.m_HonorificPrefix = items[3]; - } - if (items.Length >= 5) - { - name.m_HonorificSuffix = items[4]; - } - return name; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/PhoneNumber.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/PhoneNumber.cs deleted file mode 100644 index 75c378816..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/PhoneNumber.cs +++ /dev/null @@ -1,246 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime.vCard -{ - /// - /// vCard phone number implementation. - /// - public class PhoneNumber - { - #region Members - - private readonly Item m_pItem; - private string m_Number = ""; - private PhoneNumberType_enum m_Type = PhoneNumberType_enum.Voice; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner vCard item. - /// Phone number type. Note: This value can be flagged value ! - /// Phone number. - internal PhoneNumber(Item item, PhoneNumberType_enum type, string number) - { - m_pItem = item; - m_Type = type; - m_Number = number; - } - - #endregion - - #region Properties - - /// - /// Gets underlaying vCrad item. - /// - public Item Item - { - get { return m_pItem; } - } - - /// - /// Gets or sets phone number. - /// - public string Number - { - get { return m_Number; } - - set - { - m_Number = value; - Changed(); - } - } - - /// - /// Gets or sets phone number type. Note: This property can be flagged value ! - /// - public PhoneNumberType_enum NumberType - { - get { return m_Type; } - - set - { - m_Type = value; - Changed(); - } - } - - #endregion - - #region Internal methods - - /// - /// Parses phone from vCard TEL structure string. - /// - /// vCard TEL item. - internal static PhoneNumber Parse(Item item) - { - PhoneNumberType_enum type = PhoneNumberType_enum.NotSpecified; - if (item.ParametersString.ToUpper().IndexOf("PREF") != -1) - { - type |= PhoneNumberType_enum.Preferred; - } - if (item.ParametersString.ToUpper().IndexOf("HOME") != -1) - { - type |= PhoneNumberType_enum.Home; - } - if (item.ParametersString.ToUpper().IndexOf("MSG") != -1) - { - type |= PhoneNumberType_enum.Msg; - } - if (item.ParametersString.ToUpper().IndexOf("WORK") != -1) - { - type |= PhoneNumberType_enum.Work; - } - if (item.ParametersString.ToUpper().IndexOf("VOICE") != -1) - { - type |= PhoneNumberType_enum.Voice; - } - if (item.ParametersString.ToUpper().IndexOf("FAX") != -1) - { - type |= PhoneNumberType_enum.Fax; - } - if (item.ParametersString.ToUpper().IndexOf("CELL") != -1) - { - type |= PhoneNumberType_enum.Cellular; - } - if (item.ParametersString.ToUpper().IndexOf("VIDEO") != -1) - { - type |= PhoneNumberType_enum.Video; - } - if (item.ParametersString.ToUpper().IndexOf("PAGER") != -1) - { - type |= PhoneNumberType_enum.Pager; - } - if (item.ParametersString.ToUpper().IndexOf("BBS") != -1) - { - type |= PhoneNumberType_enum.BBS; - } - if (item.ParametersString.ToUpper().IndexOf("MODEM") != -1) - { - type |= PhoneNumberType_enum.Modem; - } - if (item.ParametersString.ToUpper().IndexOf("CAR") != -1) - { - type |= PhoneNumberType_enum.Car; - } - if (item.ParametersString.ToUpper().IndexOf("ISDN") != -1) - { - type |= PhoneNumberType_enum.ISDN; - } - if (item.ParametersString.ToUpper().IndexOf("PCS") != -1) - { - type |= PhoneNumberType_enum.PCS; - } - - return new PhoneNumber(item, type, item.Value); - } - - /// - /// Converts PhoneNumberType_enum to vCard item parameters string. - /// - /// Value to convert. - /// - internal static string PhoneTypeToString(PhoneNumberType_enum type) - { - string retVal = ""; - if ((type & PhoneNumberType_enum.BBS) != 0) - { - retVal += "BBS,"; - } - if ((type & PhoneNumberType_enum.Car) != 0) - { - retVal += "CAR,"; - } - if ((type & PhoneNumberType_enum.Cellular) != 0) - { - retVal += "CELL,"; - } - if ((type & PhoneNumberType_enum.Fax) != 0) - { - retVal += "FAX,"; - } - if ((type & PhoneNumberType_enum.Home) != 0) - { - retVal += "HOME,"; - } - if ((type & PhoneNumberType_enum.ISDN) != 0) - { - retVal += "ISDN,"; - } - if ((type & PhoneNumberType_enum.Modem) != 0) - { - retVal += "MODEM,"; - } - if ((type & PhoneNumberType_enum.Msg) != 0) - { - retVal += "MSG,"; - } - if ((type & PhoneNumberType_enum.Pager) != 0) - { - retVal += "PAGER,"; - } - if ((type & PhoneNumberType_enum.PCS) != 0) - { - retVal += "PCS,"; - } - if ((type & PhoneNumberType_enum.Preferred) != 0) - { - retVal += "PREF,"; - } - if ((type & PhoneNumberType_enum.Video) != 0) - { - retVal += "VIDEO,"; - } - if ((type & PhoneNumberType_enum.Voice) != 0) - { - retVal += "VOICE,"; - } - if ((type & PhoneNumberType_enum.Work) != 0) - { - retVal += "WORK,"; - } - if (retVal.EndsWith(",")) - { - retVal = retVal.Substring(0, retVal.Length - 1); - } - - return retVal; - } - - #endregion - - #region Utility methods - - /// - /// This method is called when some property has changed, wee need to update underlaying vCard item. - /// - private void Changed() - { - m_pItem.ParametersString = PhoneTypeToString(m_Type); - m_pItem.Value = m_Number; - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/PhoneNumberCollection.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/PhoneNumberCollection.cs deleted file mode 100644 index 64f171fd4..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/PhoneNumberCollection.cs +++ /dev/null @@ -1,116 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime.vCard -{ - #region usings - - using System.Collections; - using System.Collections.Generic; - - #endregion - - /// - /// vCard phone number collection implementation. - /// - public class PhoneNumberCollection : IEnumerable - { - #region Members - - private readonly List m_pCollection; - private readonly vCard m_pOwner; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - /// Owner vCard. - internal PhoneNumberCollection(vCard owner) - { - m_pOwner = owner; - m_pCollection = new List(); - - foreach (Item item in owner.Items.Get("TEL")) - { - m_pCollection.Add(PhoneNumber.Parse(item)); - } - } - - #endregion - - #region Properties - - /// - /// Gets number of items in the collection. - /// - public int Count - { - get { return m_pCollection.Count; } - } - - #endregion - - #region Methods - - /// - /// Add new phone number to the collection. - /// - /// Phone number type. Note: This value can be flagged value ! - /// Phone number. - public void Add(PhoneNumberType_enum type, string number) - { - Item item = m_pOwner.Items.Add("TEL", PhoneNumber.PhoneTypeToString(type), number); - m_pCollection.Add(new PhoneNumber(item, type, number)); - } - - /// - /// Removes specified item from the collection. - /// - /// Item to remove. - public void Remove(PhoneNumber item) - { - m_pOwner.Items.Remove(item.Item); - m_pCollection.Remove(item); - } - - /// - /// Removes all items from the collection. - /// - public void Clear() - { - foreach (PhoneNumber number in m_pCollection) - { - m_pOwner.Items.Remove(number.Item); - } - m_pCollection.Clear(); - } - - /// - /// Gets enumerator. - /// - /// - public IEnumerator GetEnumerator() - { - return m_pCollection.GetEnumerator(); - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/PhoneNumberType_enum.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/PhoneNumberType_enum.cs deleted file mode 100644 index e525fb448..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/PhoneNumberType_enum.cs +++ /dev/null @@ -1,107 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime.vCard -{ - #region usings - - using System; - - #endregion - - /// - /// vCal phone number type. Note this values may be flagged ! - /// - [Flags] - public enum PhoneNumberType_enum - { - /// - /// Phone number type not specified. - /// - NotSpecified = 0, - - /// - /// Preferred phone number. - /// - Preferred = 1, - - /// - /// Telephone number associated with a residence. - /// - Home = 2, - - /// - /// Telephone number has voice messaging support. - /// - Msg = 4, - - /// - /// Telephone number associated with a place of work. - /// - Work = 8, - - /// - /// Voice telephone number. - /// - Voice = 16, - - /// - /// Fax number. - /// - Fax = 32, - - /// - /// Cellular phone number. - /// - Cellular = 64, - - /// - /// Video conferencing telephone number. - /// - Video = 128, - - /// - /// Paging device telephone number. - /// - Pager = 256, - - /// - /// Bulletin board system telephone number. - /// - BBS = 512, - - /// - /// Modem connected telephone number. - /// - Modem = 1024, - - /// - /// Car-phone telephone number. - /// - Car = 2048, - - /// - /// ISDN service telephone number. - /// - ISDN = 4096, - - /// - /// Personal communication services telephone number. - /// - PCS = 8192, - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/vCard.cs b/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/vCard.cs deleted file mode 100644 index ee611481c..000000000 --- a/module/ASC.Mail.Autoreply/ASC.Mail.Core/Net/vCard/vCard.cs +++ /dev/null @@ -1,596 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -namespace ASC.Mail.Net.Mime.vCard -{ - #region usings - - using System; - using System.Drawing; - using System.Drawing.Imaging; - using System.Globalization; - using System.IO; - using System.Text; - - #endregion - - /// - /// Rfc 2426 vCard implementation. - /// - public class vCard - { - #region Members - - private readonly ItemCollection m_pItems; - private DeliveryAddressCollection m_pAddresses; - private EmailAddressCollection m_pEmailAddresses; - private PhoneNumberCollection m_pPhoneNumbers; - - #endregion - - #region Constructor - - /// - /// Default constructor. - /// - public vCard() - { - m_pItems = new ItemCollection(); - Version = "3.0"; - UID = Guid.NewGuid().ToString(); - } - - #endregion - - #region Properties - - /// - /// Gets addresses collection. - /// - public DeliveryAddressCollection Addresses - { - get - { - // Delay collection creation, create it when needed. - if (m_pAddresses == null) - { - m_pAddresses = new DeliveryAddressCollection(this); - } - - return m_pAddresses; - } - } - - /// - /// Gets or sets birth date. Returns DateTime.MinValue if not set. - /// - public DateTime BirthDate - { - get - { - Item item = m_pItems.GetFirst("BDAY"); - if (item != null) - { - string date = item.DecodedValue.Replace("-", ""); - string[] dateFormats = new[] {"yyyyMMdd", "yyyyMMddz"}; - return DateTime.ParseExact(date, - dateFormats, - DateTimeFormatInfo.InvariantInfo, - DateTimeStyles.None); - } - else - { - return DateTime.MinValue; - } - } - - set - { - if (value != DateTime.MinValue) - { - m_pItems.SetValue("BDAY", value.ToString("yyyyMMdd")); - } - else - { - m_pItems.SetValue("BDAY", null); - } - } - } - - /// - /// Gets email addresses collection. - /// - public EmailAddressCollection EmailAddresses - { - get - { - // Delay collection creation, create it when needed. - if (m_pEmailAddresses == null) - { - m_pEmailAddresses = new EmailAddressCollection(this); - } - - return m_pEmailAddresses; - } - } - - /// - /// Gets or sets formatted(Display name) name. Returns null if FN: item doesn't exist. - /// - public string FormattedName - { - get - { - Item item = m_pItems.GetFirst("FN"); - if (item != null) - { - return item.DecodedValue; - } - else - { - return null; - } - } - - set { m_pItems.SetDecodedValue("FN", value); } - } - - /// - /// Gets or sets vCard home URL. - /// - public string HomeURL - { - get - { - Item[] items = m_pItems.Get("URL"); - foreach (Item item in items) - { - if (item.ParametersString == "" || item.ParametersString.ToUpper().IndexOf("HOME") > -1) - { - return item.DecodedValue; - } - } - - return null; - } - - set - { - Item[] items = m_pItems.Get("URL"); - foreach (Item item in items) - { - if (item.ParametersString.ToUpper().IndexOf("HOME") > -1) - { - if (value != null) - { - item.Value = value; - } - else - { - m_pItems.Remove(item); - } - return; - } - } - - if (value != null) - { - // If we reach here, URL;Work doesn't exist, add it. - m_pItems.Add("URL", "HOME", value); - } - } - } - - /// - /// Gets reference to vCard items. - /// - public ItemCollection Items - { - get { return m_pItems; } - } - - /// - /// Gets or sets name info. Returns null if N: item doesn't exist. - /// - public Name Name - { - get - { - Item item = m_pItems.GetFirst("N"); - if (item != null) - { - return Name.Parse(item); - } - else - { - return null; - } - } - - set - { - if (value != null) - { - m_pItems.SetDecodedValue("N", value.ToValueString()); - } - else - { - m_pItems.SetDecodedValue("N", null); - } - } - } - - /// - /// Gets or sets nick name. Returns null if NICKNAME: item doesn't exist. - /// - public string NickName - { - get - { - Item item = m_pItems.GetFirst("NICKNAME"); - if (item != null) - { - return item.DecodedValue; - } - else - { - return null; - } - } - - set { m_pItems.SetDecodedValue("NICKNAME", value); } - } - - /// - /// Gets or sets note text. Returns null if NOTE: item doesn't exist. - /// - public string NoteText - { - get - { - Item item = m_pItems.GetFirst("NOTE"); - if (item != null) - { - return item.DecodedValue; - } - else - { - return null; - } - } - - set { m_pItems.SetDecodedValue("NOTE", value); } - } - - /// - /// Gets or sets organization name. Usually this value is: comapny;department;office. Returns null if ORG: item doesn't exist. - /// - public string Organization - { - get - { - Item item = m_pItems.GetFirst("ORG"); - if (item != null) - { - return item.DecodedValue; - } - else - { - return null; - } - } - - set { m_pItems.SetDecodedValue("ORG", value); } - } - - /// - /// Gets phone number collection. - /// - public PhoneNumberCollection PhoneNumbers - { - get - { - // Delay collection creation, create it when needed. - if (m_pPhoneNumbers == null) - { - m_pPhoneNumbers = new PhoneNumberCollection(this); - } - - return m_pPhoneNumbers; - } - } - - /// - /// Gets or sets person photo. Returns null if PHOTO: item doesn't exist. - /// - public Image Photo - { - get - { - Item item = m_pItems.GetFirst("PHOTO"); - if (item != null) - { - return Image.FromStream(new MemoryStream(Encoding.Default.GetBytes(item.DecodedValue))); - } - else - { - return null; - } - } - - set - { - if (value != null) - { - MemoryStream ms = new MemoryStream(); - value.Save(ms, ImageFormat.Jpeg); - - m_pItems.SetValue("PHOTO", "ENCODING=b;TYPE=JPEG", Convert.ToBase64String(ms.ToArray())); - } - else - { - m_pItems.SetValue("PHOTO", null); - } - } - } - - /// - /// Gets or sets role. Returns null if ROLE: item doesn't exist. - /// - public string Role - { - get - { - Item item = m_pItems.GetFirst("ROLE"); - if (item != null) - { - return item.DecodedValue; - } - else - { - return null; - } - } - - set { m_pItems.SetDecodedValue("ROLE", value); } - } - - /// - /// Gets or sets job title. Returns null if TITLE: item doesn't exist. - /// - public string Title - { - get - { - Item item = m_pItems.GetFirst("TITLE"); - if (item != null) - { - return item.DecodedValue; - } - else - { - return null; - } - } - - set { m_pItems.SetDecodedValue("TITLE", value); } - } - - /// - /// Gets or sets vCard unique ID. Returns null if UID: item doesn't exist. - /// - public string UID - { - get - { - Item item = m_pItems.GetFirst("UID"); - if (item != null) - { - return item.DecodedValue; - } - else - { - return null; - } - } - - set { m_pItems.SetDecodedValue("UID", value); } - } - - /// - /// Gets or sets vCard version. Returns null if VERSION: item doesn't exist. - /// - public string Version - { - get - { - Item item = m_pItems.GetFirst("VERSION"); - if (item != null) - { - return item.DecodedValue; - } - else - { - return null; - } - } - - set { m_pItems.SetDecodedValue("VERSION", value); } - } - - /// - /// Gets or sets vCard Work URL. - /// - public string WorkURL - { - get - { - Item[] items = m_pItems.Get("URL"); - foreach (Item item in items) - { - if (item.ParametersString.ToUpper().IndexOf("WORK") > -1) - { - return item.DecodedValue; - } - } - - return null; - } - - set - { - Item[] items = m_pItems.Get("URL"); - foreach (Item item in items) - { - if (item.ParametersString.ToUpper().IndexOf("WORK") > -1) - { - if (value != null) - { - item.Value = value; - } - else - { - m_pItems.Remove(item); - } - return; - } - } - - if (value != null) - { - // If we reach here, URL;Work doesn't exist, add it. - m_pItems.Add("URL", "WORK", value); - } - } - } - - #endregion - - #region Methods - - /// - /// Stores vCard structure to byte[]. - /// - /// - public byte[] ToByte() - { - MemoryStream ms = new MemoryStream(); - ToStream(ms); - return ms.ToArray(); - } - - /// - /// Stores vCard to the specified file. - /// - /// File name with path where to store vCard. - public void ToFile(string file) - { - using (FileStream fs = File.Create(file)) - { - ToStream(fs); - } - } - - /// - /// Stores vCard structure to the specified stream. - /// - /// Stream where to store vCard structure. - public void ToStream(Stream stream) - { - /* - BEGIN:VCARD - .... - END:VCARD - */ - - StringBuilder retVal = new StringBuilder(); - retVal.Append("BEGIN:VCARD\r\n"); - foreach (Item item in m_pItems) - { - retVal.Append(item.ToItemString() + "\r\n"); - } - retVal.Append("END:VCARD\r\n"); - - byte[] data = Encoding.UTF8.GetBytes(retVal.ToString()); - stream.Write(data, 0, data.Length); - } - - /// - /// Parses vCard from the specified file. - /// - /// vCard file with path. - public void Parse(string file) - { - using (FileStream fs = File.OpenRead(file)) - { - Parse(fs); - } - } - - /// - /// Parses vCard from the specified stream. - /// - /// Stream what contains vCard. - public void Parse(Stream stream) - { - m_pItems.Clear(); - m_pPhoneNumbers = null; - m_pEmailAddresses = null; - - TextReader r = new StreamReader(stream, Encoding.Default); - string line = r.ReadLine(); - // Find row BEGIN:VCARD - while (line != null && line.ToUpper() != "BEGIN:VCARD") - { - line = r.ReadLine(); - } - // Read frist vCard line after BEGIN:VCARD - line = r.ReadLine(); - while (line != null && line.ToUpper() != "END:VCARD") - { - StringBuilder item = new StringBuilder(); - item.Append(line); - // Get next line, see if item continues (folded line). - line = r.ReadLine(); - while (line != null && (line.StartsWith("\t") || line.StartsWith(" "))) - { - item.Append(line.Substring(1)); - line = r.ReadLine(); - } - - string[] name_value = item.ToString().Split(new[] {':'}, 2); - - // Item syntax: name[*(;parameter)]:value - string[] name_params = name_value[0].Split(new[] {';'}, 2); - string name = name_params[0]; - string parameters = ""; - if (name_params.Length == 2) - { - parameters = name_params[1]; - } - string value = ""; - if (name_value.Length == 2) - { - value = name_value[1]; - } - m_pItems.Add(name, parameters, value); - } - } - - #endregion - } -} \ No newline at end of file diff --git a/module/ASC.Mail.Server/ASC.Mail.Server/ASC.Mail.Server.csproj b/module/ASC.Mail.Server/ASC.Mail.Server/ASC.Mail.Server.csproj index e3039ff79..6882e93df 100644 --- a/module/ASC.Mail.Server/ASC.Mail.Server/ASC.Mail.Server.csproj +++ b/module/ASC.Mail.Server/ASC.Mail.Server/ASC.Mail.Server.csproj @@ -73,10 +73,10 @@ - 12.0.3 + 13.0.1 - 106.1.0 + 106.12.0 diff --git a/module/ASC.Mail/ASC.Mail/ASC.Mail.csproj b/module/ASC.Mail/ASC.Mail/ASC.Mail.csproj index f2a5af822..578a90be9 100644 --- a/module/ASC.Mail/ASC.Mail/ASC.Mail.csproj +++ b/module/ASC.Mail/ASC.Mail/ASC.Mail.csproj @@ -59,7 +59,6 @@ - @@ -165,6 +164,8 @@ + + @@ -493,6 +494,9 @@ + + MailCoreResource.resx + MailCoreResource.resx @@ -542,16 +546,10 @@ - 2.9.17.2 + 21.12.22.2 - 5.1.2 - - - 4.1.0.12182 - - - 1.13.7 + 6.2.0 1.6.0.1 @@ -560,22 +558,19 @@ 4.1.11 - 2.11.1 + 2.15.0 4.0.20505 - 8.0.25 + 8.0.29 - 12.0.3 + 13.0.1 - 106.1.0 - - - 2.4.1 + 106.12.0 1.1.0 diff --git a/module/ASC.Mail/ASC.Mail/Authorization/AuthorizationTracker.cs b/module/ASC.Mail/ASC.Mail/Authorization/AuthorizationTracker.cs deleted file mode 100644 index edd636705..000000000 --- a/module/ASC.Mail/ASC.Mail/Authorization/AuthorizationTracker.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; - -using DotNetOpenAuth.OAuth2; - -namespace ASC.Mail.Authorization -{ - public class AuthorizationTracker : IClientAuthorizationTracker - { - private readonly List _scope; - - public AuthorizationTracker(List scope) - { - _scope = scope; - } - - public IAuthorizationState GetAuthorizationState( - Uri callbackUrl, string clientState) - { - return new AuthorizationState(_scope) - { - Callback = callbackUrl - }; - } - } -} diff --git a/module/ASC.Mail/ASC.Mail/Core/Dao/AccountDao.cs b/module/ASC.Mail/ASC.Mail/Core/Dao/AccountDao.cs index 554fc673a..720944d11 100644 --- a/module/ASC.Mail/ASC.Mail/Core/Dao/AccountDao.cs +++ b/module/ASC.Mail/ASC.Mail/Core/Dao/AccountDao.cs @@ -73,6 +73,7 @@ namespace ASC.Mail.Core.Dao .Select(MailboxTable.Columns.OAuthToken.Prefix(mailbox_alias)) .Select(MailboxTable.Columns.IsServerMailbox.Prefix(mailbox_alias)) .Select(MailboxTable.Columns.EmailInFolder.Prefix(mailbox_alias)) + .Select(MailboxTable.Columns.DateCreated.Prefix(mailbox_alias)) .Select(ServerAddressTable.Columns.Id.Prefix(server_address)) .Select(ServerAddressTable.Columns.AddressName.Prefix(server_address)) .Select(ServerAddressTable.Columns.IsAlias.Prefix(server_address)) @@ -103,14 +104,15 @@ namespace ASC.Mail.Core.Dao MailboxOAuthToken = Convert.ToString(r[6]), MailboxIsTeamlabMailbox = Convert.ToBoolean(r[7]), MailboxEmailInFolder = Convert.ToString(r[8]), - ServerAddressId = Convert.ToInt32(r[9]), - ServerAddressName = Convert.ToString(r[10]), - ServerAddressIsAlias = Convert.ToBoolean(r[11]), - ServerDomainId = Convert.ToInt32(r[12]), - ServerDomainName = Convert.ToString(r[13]), - ServerMailGroupId = Convert.ToInt32(r[14]), - ServerMailGroupAddress = Convert.ToString(r[15]), - ServerDomainTenant = Convert.ToInt32(r[16]) + DateCreated = r[9] != null ? Convert.ToDateTime(r[9]) : (DateTime?)null, + ServerAddressId = Convert.ToInt32(r[10]), + ServerAddressName = Convert.ToString(r[11]), + ServerAddressIsAlias = Convert.ToBoolean(r[12]), + ServerDomainId = Convert.ToInt32(r[13]), + ServerDomainName = Convert.ToString(r[14]), + ServerMailGroupId = Convert.ToInt32(r[15]), + ServerMailGroupAddress = Convert.ToString(r[16]), + ServerDomainTenant = Convert.ToInt32(r[17]) }; return a; diff --git a/module/ASC.Mail/ASC.Mail/Core/Dao/ContactCardDao.cs b/module/ASC.Mail/ASC.Mail/Core/Dao/ContactCardDao.cs index 8958c0dd3..0722ed847 100644 --- a/module/ASC.Mail/ASC.Mail/Core/Dao/ContactCardDao.cs +++ b/module/ASC.Mail/ASC.Mail/Core/Dao/ContactCardDao.cs @@ -96,7 +96,7 @@ namespace ASC.Mail.Core.Dao if (exp.OrderAsc.HasValue) { - query.OrderBy(ContactsTable.Columns.ContactName.Prefix(MAIL_CONTACTS), exp.OrderAsc.HasValue); + query.OrderBy(ContactsTable.Columns.ContactName.Prefix(MAIL_CONTACTS), exp.OrderAsc.Value); } if (exp.StartIndex.HasValue) diff --git a/module/ASC.Mail/ASC.Mail/Core/Dao/CrmContactDao.cs b/module/ASC.Mail/ASC.Mail/Core/Dao/CrmContactDao.cs index 90f4ea13c..1fb4db60d 100644 --- a/module/ASC.Mail/ASC.Mail/Core/Dao/CrmContactDao.cs +++ b/module/ASC.Mail/ASC.Mail/Core/Dao/CrmContactDao.cs @@ -88,7 +88,7 @@ namespace ASC.Mail.Core.Dao return ids; CoreContext.TenantManager.SetCurrentTenant(Tenant); - SecurityContext.AuthenticateMe(new Guid(CurrentUserId)); + SecurityContext.CurrentUser = new Guid(CurrentUserId); foreach (var info in contactList) { diff --git a/module/ASC.Mail/ASC.Mail/Core/Dao/FolderDao.cs b/module/ASC.Mail/ASC.Mail/Core/Dao/FolderDao.cs index 616b05149..6e5ca3020 100644 --- a/module/ASC.Mail/ASC.Mail/Core/Dao/FolderDao.cs +++ b/module/ASC.Mail/ASC.Mail/Core/Dao/FolderDao.cs @@ -78,7 +78,7 @@ namespace ASC.Mail.Core.Dao return Db.ExecuteNonQuery(query); } - private const string INCR_VALUE_FORMAT = "{0}={0}+({1})"; + private const string INCR_VALUE_FORMAT = "{0}=IF((-{0}<=({1})), {0}+({1}),(0))"; private const string SET_VALUE_FORMAT = "{0}={1}"; public int ChangeFolderCounters( diff --git a/module/ASC.Mail/ASC.Mail/Core/Dao/MailDao.cs b/module/ASC.Mail/ASC.Mail/Core/Dao/MailDao.cs index bba7aada0..9571b37b7 100644 --- a/module/ASC.Mail/ASC.Mail/Core/Dao/MailDao.cs +++ b/module/ASC.Mail/ASC.Mail/Core/Dao/MailDao.cs @@ -22,13 +22,13 @@ using System.Linq; using ASC.Common.Data; using ASC.Common.Data.Sql; using ASC.Common.Data.Sql.Expressions; +using ASC.Common.Utils; using ASC.Mail.Core.Dao.Expressions.Message; using ASC.Mail.Core.Dao.Interfaces; using ASC.Mail.Core.DbSchema; using ASC.Mail.Core.DbSchema.Interfaces; using ASC.Mail.Core.DbSchema.Tables; using ASC.Mail.Enums; -using ASC.Mail.Utils; namespace ASC.Mail.Core.Dao { @@ -54,12 +54,12 @@ namespace ASC.Mail.Core.Dao .InColumnValue(MailTable.Columns.Address, mail.Address) .InColumnValue(MailTable.Columns.Uidl, mail.Uidl) .InColumnValue(MailTable.Columns.Md5, mail.Md5) - .InColumnValue(MailTable.Columns.From, MailUtil.NormalizeStringForMySql(mail.From)) - .InColumnValue(MailTable.Columns.To, MailUtil.NormalizeStringForMySql(mail.To)) + .InColumnValue(MailTable.Columns.From, StringUtils.NormalizeStringForMySql(mail.From)) + .InColumnValue(MailTable.Columns.To, StringUtils.NormalizeStringForMySql(mail.To)) .InColumnValue(MailTable.Columns.Reply, mail.Reply) - .InColumnValue(MailTable.Columns.Subject, MailUtil.NormalizeStringForMySql(mail.Subject)) - .InColumnValue(MailTable.Columns.Cc, MailUtil.NormalizeStringForMySql(mail.Cc)) - .InColumnValue(MailTable.Columns.Bcc, MailUtil.NormalizeStringForMySql(mail.Bcc)) + .InColumnValue(MailTable.Columns.Subject, StringUtils.NormalizeStringForMySql(mail.Subject)) + .InColumnValue(MailTable.Columns.Cc, StringUtils.NormalizeStringForMySql(mail.Cc)) + .InColumnValue(MailTable.Columns.Bcc, StringUtils.NormalizeStringForMySql(mail.Bcc)) .InColumnValue(MailTable.Columns.Importance, mail.Importance) .InColumnValue(MailTable.Columns.DateReceived, mail.DateReceived) .InColumnValue(MailTable.Columns.DateSent, mail.DateSent) @@ -75,7 +75,7 @@ namespace ASC.Mail.Core.Dao .InColumnValue(MailTable.Columns.MimeMessageId, mail.MimeMessageId) .InColumnValue(MailTable.Columns.MimeInReplyTo, mail.MimeInReplyTo) .InColumnValue(MailTable.Columns.ChainId, mail.ChainId) - .InColumnValue(MailTable.Columns.Introduction, MailUtil.NormalizeStringForMySql(mail.Introduction)) + .InColumnValue(MailTable.Columns.Introduction, StringUtils.NormalizeStringForMySql(mail.Introduction)) .InColumnValue(MailTable.Columns.ChainDate, mail.DateSent) .InColumnValue(MailTable.Columns.IsTextBodyOnly, mail.IsTextBodyOnly) .Identity(0, 0, true); @@ -178,7 +178,8 @@ namespace ASC.Mail.Core.Dao ChainDate = Convert.ToDateTime(r[31]), IsTextBodyOnly = Convert.ToBoolean(r[32]), HasParseError = Convert.ToBoolean(r[33]), - CalendarUid = Convert.ToString(r[34]) + CalendarUid = Convert.ToString(r[34]), + ReadRequestStatus = Convert.ToBoolean(r[35]) }; return mail; diff --git a/module/ASC.Mail/ASC.Mail/Core/Dao/MailInfoDao.cs b/module/ASC.Mail/ASC.Mail/Core/Dao/MailInfoDao.cs index 10a13383e..b6d32fecc 100644 --- a/module/ASC.Mail/ASC.Mail/Core/Dao/MailInfoDao.cs +++ b/module/ASC.Mail/ASC.Mail/Core/Dao/MailInfoDao.cs @@ -87,7 +87,9 @@ namespace ASC.Mail.Core.Dao MailTable.Columns.Stream.Prefix(MM_ALIAS), MailTable.Columns.Uidl.Prefix(MM_ALIAS), MailTable.Columns.IsRemoved.Prefix(MM_ALIAS), - MailTable.Columns.Introduction.Prefix(MM_ALIAS)); + MailTable.Columns.Introduction.Prefix(MM_ALIAS), + MailTable.Columns.ReadRequestStatus.Prefix(MM_ALIAS)); + if (exp.TagIds != null && exp.TagIds.Any()) { @@ -348,7 +350,8 @@ namespace ASC.Mail.Core.Dao Stream = Convert.ToString(r[20]), Uidl = Convert.ToString(r[21]), IsRemoved = Convert.ToBoolean(r[22]), - Intoduction = Convert.ToString(r[23]) + Intoduction = Convert.ToString(r[23]), + ReadRequestStatus = Convert.ToBoolean(r[24]) }; return mailInfo; diff --git a/module/ASC.Mail/ASC.Mail/Core/Dao/UserFolderDao.cs b/module/ASC.Mail/ASC.Mail/Core/Dao/UserFolderDao.cs index 220494ebc..311eca19c 100644 --- a/module/ASC.Mail/ASC.Mail/Core/Dao/UserFolderDao.cs +++ b/module/ASC.Mail/ASC.Mail/Core/Dao/UserFolderDao.cs @@ -227,7 +227,7 @@ namespace ASC.Mail.Core.Dao var result = Db.ExecuteNonQuery(query); } - private const string INCR_VALUE_FORMAT = "{0}={0}+({1})"; + private const string INCR_VALUE_FORMAT = "{0}=IF((-{0}<=({1})), {0}+({1}),(0))"; private const string SET_VALUE_FORMAT = "{0}={1}"; public int SetFolderCounters(uint folderId, int? unreadMess = null, int? totalMess = null, diff --git a/module/ASC.Mail/ASC.Mail/Core/DbSchema/Tables/MailTable.cs b/module/ASC.Mail/ASC.Mail/Core/DbSchema/Tables/MailTable.cs index f361a49f5..263c46c13 100644 --- a/module/ASC.Mail/ASC.Mail/Core/DbSchema/Tables/MailTable.cs +++ b/module/ASC.Mail/ASC.Mail/Core/DbSchema/Tables/MailTable.cs @@ -65,6 +65,7 @@ namespace ASC.Mail.Core.DbSchema.Tables public const string ChainId = "chain_id"; public const string ChainDate = "chain_date"; public const string LastModified = "time_modified"; + public const string ReadRequestStatus = "read_request_status"; } public string Name @@ -112,7 +113,8 @@ namespace ASC.Mail.Core.DbSchema.Tables Columns.ChainDate, Columns.IsTextBodyOnly, Columns.HasParseError, - Columns.CalendarUid + Columns.CalendarUid, + Columns.ReadRequestStatus }; } } diff --git a/module/ASC.Mail/ASC.Mail/Core/Engine/AccountEngine.cs b/module/ASC.Mail/ASC.Mail/Core/Engine/AccountEngine.cs index 2c66cab3b..dd955ab30 100644 --- a/module/ASC.Mail/ASC.Mail/Core/Engine/AccountEngine.cs +++ b/module/ASC.Mail/ASC.Mail/Core/Engine/AccountEngine.cs @@ -528,7 +528,11 @@ namespace ASC.Mail.Core.Engine !string.IsNullOrEmpty(account.MailboxOAuthToken), account.MailboxEmailInFolder, account.MailboxIsTeamlabMailbox, - account.ServerDomainTenant == Defines.SHARED_TENANT_ID); + account.ServerDomainTenant == Defines.SHARED_TENANT_ID + ) + { + DateCreated = account.DateCreated.HasValue ? account.DateCreated.Value : (DateTime?)null + }; if (group != null) accountInfo.Groups.Add(group); diff --git a/module/ASC.Mail/ASC.Mail/Core/Engine/AttachmentEngine.cs b/module/ASC.Mail/ASC.Mail/Core/Engine/AttachmentEngine.cs index 8918094cd..70dd8e289 100644 --- a/module/ASC.Mail/ASC.Mail/Core/Engine/AttachmentEngine.cs +++ b/module/ASC.Mail/ASC.Mail/Core/Engine/AttachmentEngine.cs @@ -117,7 +117,7 @@ namespace ASC.Mail.Core.Engine if (file == null) throw new AttachmentsException(AttachmentsException.Types.DocumentNotFound, "File not found."); - if (!FilesIntegration.GetFileSecurity().CanRead(file)) + if (!FilesIntegration.GetFileSecurity().CanDownload(file)) throw new AttachmentsException(AttachmentsException.Types.DocumentAccessDenied, "Access denied."); diff --git a/module/ASC.Mail/ASC.Mail/Core/Engine/CalendarEngine.cs b/module/ASC.Mail/ASC.Mail/Core/Engine/CalendarEngine.cs index 387a22463..c7d54fc18 100644 --- a/module/ASC.Mail/ASC.Mail/Core/Engine/CalendarEngine.cs +++ b/module/ASC.Mail/ASC.Mail/Core/Engine/CalendarEngine.cs @@ -16,6 +16,7 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; @@ -24,6 +25,8 @@ using ASC.Core; using ASC.Mail.Data.Contracts; using ASC.Mail.Utils; +using MimeKit; + namespace ASC.Mail.Core.Engine { public class CalendarEngine @@ -36,7 +39,7 @@ namespace ASC.Mail.Core.Engine } public void UploadIcsToCalendar(MailBoxData mailBoxData, int calendarId, string calendarEventUid, string calendarIcs, - string calendarCharset, string calendarContentType, string calendarEventReceiveEmail, string httpContextScheme) + string calendarCharset, string calendarContentType, List mailAttachments, IEnumerable mimeAttachments, string calendarEventReceiveEmail, string httpContextScheme) { try { @@ -50,13 +53,14 @@ namespace ASC.Mail.Core.Engine if (calendar == null) return; + var eventObj = calendar.Events[0]; var alienEvent = true; - var organizer = calendar.Events[0].Organizer; + var organizer = eventObj.Organizer; if (organizer != null) { - var orgEmail = calendar.Events[0].Organizer.Value.ToString() + var orgEmail = eventObj.Organizer.Value.ToString() .ToLowerInvariant() .Replace("mailto:", ""); @@ -70,7 +74,7 @@ namespace ASC.Mail.Core.Engine if (alienEvent) { - if (calendar.Events[0].Attendees.Any( + if (eventObj.Attendees.Any( a => a.Value.ToString() .ToLowerInvariant() @@ -85,12 +89,12 @@ namespace ASC.Mail.Core.Engine return; CoreContext.TenantManager.SetCurrentTenant(mailBoxData.TenantId); - SecurityContext.AuthenticateMe(new Guid(mailBoxData.UserId)); + SecurityContext.CurrentUser = new Guid(mailBoxData.UserId); using (var ms = new MemoryStream(EncodingTools.GetEncodingByCodepageName(calendarCharset).GetBytes(calendarIcs))) { var apiHelper = new ApiHelper(httpContextScheme, Log); - apiHelper.UploadIcsToCalendar(calendarId, ms, "calendar.ics", calendarContentType); + apiHelper.UploadIcsToCalendar(calendarId, ms, "calendar.ics", calendarContentType, eventObj, mimeAttachments, mailAttachments); } Log.Info("CalendarEngine->UploadIcsToCalendar() has been succeeded"); diff --git a/module/ASC.Mail/ASC.Mail/Core/Engine/ContactEngine.cs b/module/ASC.Mail/ASC.Mail/Core/Engine/ContactEngine.cs index 6a7a79d0b..d0bded3c5 100644 --- a/module/ASC.Mail/ASC.Mail/Core/Engine/ContactEngine.cs +++ b/module/ASC.Mail/ASC.Mail/Core/Engine/ContactEngine.cs @@ -254,7 +254,7 @@ namespace ASC.Mail.Core.Engine Task.Run(() => { CoreContext.TenantManager.SetCurrentTenant(tenant); - SecurityContext.AuthenticateMe(userGuid); + SecurityContext.CurrentUser = userGuid; var engine = new EngineFactory(tenant, userName); @@ -274,7 +274,7 @@ namespace ASC.Mail.Core.Engine Task.Run(() => { CoreContext.TenantManager.SetCurrentTenant(tenant); - SecurityContext.AuthenticateMe(userGuid); + SecurityContext.CurrentUser = userGuid; var engine = new EngineFactory(tenant, userGuid.ToString()); return engine.AccountEngine.SearchAccountEmails(term); @@ -283,7 +283,7 @@ namespace ASC.Mail.Core.Engine Task.Run(() => { CoreContext.TenantManager.SetCurrentTenant(tenant); - SecurityContext.AuthenticateMe(userGuid); + SecurityContext.CurrentUser = userGuid; return WebItemSecurity.IsAvailableForMe(WebItemManager.CRMProductID) ? apiHelper.SearchCrmEmails(term, maxCountPerSystem) @@ -293,7 +293,7 @@ namespace ASC.Mail.Core.Engine Task.Run(() => { CoreContext.TenantManager.SetCurrentTenant(tenant); - SecurityContext.AuthenticateMe(userGuid); + SecurityContext.CurrentUser = userGuid; return WebItemSecurity.IsAvailableForMe(WebItemManager.PeopleProductID) ? apiHelper.SearchPeopleEmails(term, 0, maxCountPerSystem) diff --git a/module/ASC.Mail/ASC.Mail/Core/Engine/DocumentsEngine.cs b/module/ASC.Mail/ASC.Mail/Core/Engine/DocumentsEngine.cs index 5cd7fa78f..37782ac80 100644 --- a/module/ASC.Mail/ASC.Mail/Core/Engine/DocumentsEngine.cs +++ b/module/ASC.Mail/ASC.Mail/Core/Engine/DocumentsEngine.cs @@ -55,7 +55,7 @@ namespace ASC.Mail.Core.Engine if (SecurityContext.IsAuthenticated) return; CoreContext.TenantManager.SetCurrentTenant(Tenant); - SecurityContext.AuthenticateMe(new Guid(_userId)); + SecurityContext.CurrentUser = new Guid(_userId); } public List StoreAttachmentsToMyDocuments(int messageId) diff --git a/module/ASC.Mail/ASC.Mail/Core/Engine/DraftEngine.cs b/module/ASC.Mail/ASC.Mail/Core/Engine/DraftEngine.cs index 38078cca5..f99497e12 100644 --- a/module/ASC.Mail/ASC.Mail/Core/Engine/DraftEngine.cs +++ b/module/ASC.Mail/ASC.Mail/Core/Engine/DraftEngine.cs @@ -29,6 +29,7 @@ using System.Web; using ASC.Common.Logging; using ASC.Core; +using ASC.Core.Common.Notify.Signalr; using ASC.Mail.Clients; using ASC.Mail.Core.Dao.Expressions.Contact; using ASC.Mail.Core.Dao.Expressions.Mailbox; @@ -167,6 +168,111 @@ namespace ASC.Mail.Core.Engine return Send(draft); } + public bool SimpleSend( + string from, + List to, + string subject, + string body, + bool isReceipt, + DeliveryFailureMessageTranslates translates = null) + { + + if (!to.Any()) + throw new ArgumentNullException("to"); + + if (string.IsNullOrEmpty(from)) + throw new ArgumentNullException("from"); + + var mailAddress = new MailAddress(from); + + var engine = new EngineFactory(Tenant, User); + + var accounts = engine.AccountEngine.GetAccountInfoList().ToAccountData(); + + var account = accounts.FirstOrDefault(a => a.Email.ToLower().Equals(mailAddress.Address)); + + if (account == null) + throw new ArgumentException("Mailbox not found"); + + if (account.IsGroup) + throw new InvalidOperationException("Sending emails from a group address is forbidden"); + + var mbox = engine.MailboxEngine.GetMailboxData( + new СoncreteUserMailboxExp(account.MailboxId, Tenant, User)); + + if (mbox == null) + throw new ArgumentException("No such mailbox"); + + if (!mbox.Enabled) + throw new InvalidOperationException("Sending emails from a disabled account is forbidden"); + + var previousMailboxId = mbox.MailBoxId; + + var mimeMessageId = MailUtil.CreateMessageId(); + var streamId = MailUtil.CreateStreamId(); + + var fromAddress = MailUtil.CreateFullEmail(mbox.Name, mailAddress.Address); + + var draft = new MailDraftData(0, mbox, fromAddress, to, new List(), new List(), subject, mimeMessageId, string.Empty, + false, new List(), body, streamId, new List(), string.Empty) + { + FileLinksShareMode = (Files.Core.Security.FileShare)FileShare.None, + PreviousMailboxId = previousMailboxId, + RequestReceipt = false, + RequestRead = false, + IsAutogenerated = false, + IsAutoreplied = false, + IsReceipt = isReceipt + }; + + DaemonLabels = translates ?? DeliveryFailureMessageTranslates.Defauilt; + + var scheme = HttpContext.Current == null + ? Uri.UriSchemeHttp + : HttpContext.Current.Request.GetUrlRewriter().Scheme; + + var currentUrl = HttpContext.Current != null ? HttpContext.Current.Request.GetUrlRewriter().ToString() : null; + Task.Run(() => + { + try + { + if (HttpContext.Current == null && !WorkContext.IsMono) + { + HttpContext.Current = new HttpContext( + new HttpRequest("hack", currentUrl, string.Empty), + new HttpResponse(new StringWriter())); + } + + CoreContext.TenantManager.SetCurrentTenant(draft.Mailbox.TenantId); + + SecurityContext.CurrentUser = new Guid(draft.Mailbox.UserId); + + draft.ChangeEmbeddedAttachmentLinks(Log); + + var mimeMessage = draft.ToMimeMessage(); + + using (var mc = new MailClient(draft.Mailbox, CancellationToken.None, + certificatePermit: draft.Mailbox.IsTeamlab || _sslCertificatePermit, log: Log, + enableDsn: draft.RequestReceipt)) + { + mc.Send(mimeMessage, false); + } + + Log.Info($"User {draft.Mailbox.UserId} sent a read confirmation to {string.Join(",", draft.To)} from {draft.From}"); + + SendMailNotification(draft); + } + catch (Exception ex) + { + Log.ErrorFormat("Mail->Send failed: Exception: {0}", ex.ToString()); + + SendMailErrorNotification(draft, MailNotificationState.SendReceiptError); + } + }); + + return true; + } + public long Send(MailDraftData draft) { if (string.IsNullOrEmpty(draft.HtmlBody)) @@ -204,7 +310,7 @@ namespace ASC.Mail.Core.Engine CoreContext.TenantManager.SetCurrentTenant(draft.Mailbox.TenantId); - SecurityContext.AuthenticateMe(new Guid(draft.Mailbox.UserId)); + SecurityContext.CurrentUser = new Guid(draft.Mailbox.UserId); draft.ChangeEmbeddedAttachmentLinks(Log); @@ -438,12 +544,12 @@ namespace ASC.Mail.Core.Engine } } - private void SendMailErrorNotification(MailDraftData draft) + private void SendMailErrorNotification(MailDraftData draft, MailNotificationState state = MailNotificationState.SendMessageError) { try { // send success notification - _signalrServiceClient.SendMailNotification(draft.Mailbox.TenantId, draft.Mailbox.UserId, -1); + _signalrServiceClient.SendMailNotification(draft.Mailbox.TenantId, draft.Mailbox.UserId, state); } catch (Exception ex) { @@ -455,22 +561,27 @@ namespace ASC.Mail.Core.Engine { try { - var state = 0; + MailNotificationState state = 0; + if (!string.IsNullOrEmpty(draft.CalendarIcs)) { switch (draft.CalendarMethod) { case Defines.ICAL_REQUEST: - state = 1; + state = MailNotificationState.SentIcalRequest; break; case Defines.ICAL_REPLY: - state = 2; + state = MailNotificationState.SentIcalResponse; break; case Defines.ICAL_CANCEL: - state = 3; + state = MailNotificationState.SentIcalCancel; break; } } + if (draft.IsReceipt) + { + state = MailNotificationState.ReadingConfirmed; + } // send success notification _signalrServiceClient.SendMailNotification(draft.Mailbox.TenantId, draft.Mailbox.UserId, state); diff --git a/module/ASC.Mail/ASC.Mail/Core/Engine/MailGarbageEngine.cs b/module/ASC.Mail/ASC.Mail/Core/Engine/MailGarbageEngine.cs index 2619de397..a64883804 100644 --- a/module/ASC.Mail/ASC.Mail/Core/Engine/MailGarbageEngine.cs +++ b/module/ASC.Mail/ASC.Mail/Core/Engine/MailGarbageEngine.cs @@ -388,7 +388,7 @@ namespace ASC.Mail.Core.Engine log.Info("RemoveTeamlabMailbox()"); CoreContext.TenantManager.SetCurrentTenant(mailbox.TenantId); - SecurityContext.AuthenticateMe(ASC.Core.Configuration.Constants.CoreSystem); + SecurityContext.CurrentAccount = ASC.Core.Configuration.Constants.CoreSystem; RemoveTeamlabMailbox(mailbox, log); } diff --git a/module/ASC.Mail/ASC.Mail/Core/Engine/MessageEngine.cs b/module/ASC.Mail/ASC.Mail/Core/Engine/MessageEngine.cs index 9fa3cb463..e63bed4ea 100644 --- a/module/ASC.Mail/ASC.Mail/Core/Engine/MessageEngine.cs +++ b/module/ASC.Mail/ASC.Mail/Core/Engine/MessageEngine.cs @@ -505,6 +505,28 @@ namespace ASC.Mail.Core.Engine return true; } + public bool ReceiptStatus(List ids, bool receipt = false) + { + using (var daoFactory = new DaoFactory()) + { + using (var tx = daoFactory.DbManager.BeginTransaction(IsolationLevel.ReadUncommitted)) + { + var daoMailInfo = daoFactory.CreateMailInfoDao(Tenant, User); + + daoMailInfo.SetFieldValue( + SimpleMessagesExp.CreateBuilder(Tenant, User) + .SetMessageIds(ids) + .Build(), + MailTable.Columns.ReadRequestStatus, + receipt); + + tx.Commit(); + } + } + + return true; + } + public void Restore(List ids) { List mailInfoList; @@ -1830,7 +1852,8 @@ namespace ASC.Mail.Core.Engine IsYesterday = isYesterday, MailboxId = mailInfo.MailboxId, CalendarUid = mailInfo.CalendarUid, - Introduction = mailInfo.Intoduction + Introduction = mailInfo.Intoduction, + ReadRequestStatus = mailInfo.ReadRequestStatus }; } @@ -1876,7 +1899,8 @@ namespace ASC.Mail.Core.Engine MimeMessageId = mail.MimeMessageId, MimeReplyToId = mail.MimeInReplyTo, CalendarUid = mail.CalendarUid, - Uidl = mail.Uidl + Uidl = mail.Uidl, + ReadRequestStatus = mail.ReadRequestStatus }; //Reassemble paths diff --git a/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/ApplyFilterOperation.cs b/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/ApplyFilterOperation.cs index 0ae7b7d50..704dd3700 100644 --- a/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/ApplyFilterOperation.cs +++ b/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/ApplyFilterOperation.cs @@ -66,7 +66,7 @@ namespace ASC.Mail.Core.Engine.Operations CoreContext.TenantManager.SetCurrentTenant(CurrentTenant); - SecurityContext.AuthenticateMe(CurrentUser); + SecurityContext.CurrentAccount = CurrentUser; SetProgress((int?)MailOperationApplyFilterProgress.Filtering, "Filtering"); diff --git a/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/ApplyFiltersOperation.cs b/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/ApplyFiltersOperation.cs index 74b64d5f2..bbfa3527e 100644 --- a/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/ApplyFiltersOperation.cs +++ b/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/ApplyFiltersOperation.cs @@ -64,7 +64,7 @@ namespace ASC.Mail.Core.Engine.Operations CoreContext.TenantManager.SetCurrentTenant(CurrentTenant); - SecurityContext.AuthenticateMe(CurrentUser); + SecurityContext.CurrentAccount = CurrentUser; SetProgress((int?)MailOperationApplyFilterProgress.Filtering, "Filtering"); diff --git a/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/Base/MailOperation.cs b/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/Base/MailOperation.cs index 86083cd2e..dabd3df40 100644 --- a/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/Base/MailOperation.cs +++ b/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/Base/MailOperation.cs @@ -89,7 +89,7 @@ namespace ASC.Mail.Core.Engine.Operations.Base CoreContext.TenantManager.SetCurrentTenant(CurrentTenant); - SecurityContext.AuthenticateMe(ASC.Core.Configuration.Constants.CoreSystem); + SecurityContext.CurrentAccount = ASC.Core.Configuration.Constants.CoreSystem; Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(_culture); Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(_culture); diff --git a/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/MailCheckMailserverDomainsDnsOperation.cs b/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/MailCheckMailserverDomainsDnsOperation.cs index 13f3af645..b10ed400a 100644 --- a/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/MailCheckMailserverDomainsDnsOperation.cs +++ b/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/MailCheckMailserverDomainsDnsOperation.cs @@ -56,12 +56,12 @@ namespace ASC.Mail.Core.Engine.Operations try { - SecurityContext.AuthenticateMe(CurrentUser); + SecurityContext.CurrentAccount = CurrentUser; } catch { // User was removed - SecurityContext.AuthenticateMe(ASC.Core.Configuration.Constants.CoreSystem); + SecurityContext.CurrentAccount = ASC.Core.Configuration.Constants.CoreSystem; } ServerDomain domain; diff --git a/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/MailDownloadAllAttachmentsOperation.cs b/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/MailDownloadAllAttachmentsOperation.cs index e33216c77..1b85d81cb 100644 --- a/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/MailDownloadAllAttachmentsOperation.cs +++ b/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/MailDownloadAllAttachmentsOperation.cs @@ -19,7 +19,6 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; using System.Threading; using ASC.Common; @@ -37,8 +36,7 @@ using ASC.Web.Core.Files; using ASC.Web.Files.Classes; using ASC.Web.Studio.PublicResources; -using ICSharpCode.SharpZipLib.GZip; -using ICSharpCode.SharpZipLib.Tar; +using ICSharpCode.SharpZipLib.Zip; namespace ASC.Mail.Core.Engine.Operations { @@ -71,7 +69,7 @@ namespace ASC.Mail.Core.Engine.Operations try { - SecurityContext.AuthenticateMe(CurrentUser); + SecurityContext.CurrentAccount = CurrentUser; } catch { @@ -100,10 +98,12 @@ namespace ASC.Mail.Core.Engine.Operations using (var stream = TempStream.Create()) { - using (var gzoStream = new GZipOutputStream(stream)) - using (var gzip = new TarOutputStream(gzoStream, Encoding.UTF8)) + using (var zip = new ZipOutputStream(stream)) { - gzoStream.IsStreamOwner = false; + zip.IsStreamOwner = false; + zip.SetLevel(3); + zip.UseZip64 = UseZip64.Dynamic; + ZipStrings.UseUnicode = true; var attachmentsCount = attachments.Count; var progressMaxValue = (int)MailOperationDownloadAllAttachmentsProgress.ArchivePreparation; @@ -141,7 +141,7 @@ namespace ASC.Mail.Core.Engine.Operations { using (var file = attachment.ToAttachmentStream()) { - ZipFile(gzip, filename, file.FileStream); + ZipFile(zip, filename, file.FileStream); } } catch (Exception ex) @@ -153,7 +153,7 @@ namespace ASC.Mail.Core.Engine.Operations damagedAttachments++; using (var emptyStream = new MemoryStream()) { - ZipFile(gzip, filename, emptyStream); // Zip empty file + ZipFile(zip, filename, emptyStream); // Zip empty file } } @@ -192,7 +192,7 @@ namespace ASC.Mail.Core.Engine.Operations var source = string.Format("{0}?{1}=bulk&{2}", "/Products/Files/HttpHandlers/filehandler.ashx", - FilesLinkUtility.Action, "ext=.tar.gz"); + FilesLinkUtility.Action, "ext=.zip"); if (damagedAttachments > 1) Error = string.Format(MailCoreResource.FilesNotFound, damagedAttachments); @@ -208,10 +208,10 @@ namespace ASC.Mail.Core.Engine.Operations } } - private static void ZipFile(TarOutputStream zip, string filename, Stream fileStream = null) + private static void ZipFile(ZipOutputStream zip, string filename, Stream fileStream = null) { filename = filename ?? "file"; - var entry = TarEntry.CreateTarEntry(filename); + var entry = new ZipEntry(Path.GetFileName(filename)); entry.Size = fileStream.Length; zip.PutNextEntry(entry); fileStream.CopyTo(zip); diff --git a/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/MailRecalculateFoldersOperation.cs b/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/MailRecalculateFoldersOperation.cs index e228feb75..ba817367f 100644 --- a/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/MailRecalculateFoldersOperation.cs +++ b/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/MailRecalculateFoldersOperation.cs @@ -44,7 +44,7 @@ namespace ASC.Mail.Core.Engine.Operations CoreContext.TenantManager.SetCurrentTenant(CurrentTenant); - SecurityContext.AuthenticateMe(CurrentUser); + SecurityContext.CurrentAccount = CurrentUser; var engine = new EngineFactory(CurrentTenant.TenantId, CurrentUser.ID.ToString()); diff --git a/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/MailRemoveMailboxOperation.cs b/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/MailRemoveMailboxOperation.cs index 0a3e10f07..f1cbdbfdf 100644 --- a/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/MailRemoveMailboxOperation.cs +++ b/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/MailRemoveMailboxOperation.cs @@ -55,7 +55,7 @@ namespace ASC.Mail.Core.Engine.Operations CoreContext.TenantManager.SetCurrentTenant(CurrentTenant); - SecurityContext.AuthenticateMe(CurrentUser); + SecurityContext.CurrentAccount = CurrentUser; var engine = new EngineFactory(_mailBoxData.TenantId, _mailBoxData.UserId); diff --git a/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/MailRemoveMailserverDomainOperation.cs b/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/MailRemoveMailserverDomainOperation.cs index 048756acd..d599589f7 100644 --- a/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/MailRemoveMailserverDomainOperation.cs +++ b/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/MailRemoveMailserverDomainOperation.cs @@ -57,12 +57,12 @@ namespace ASC.Mail.Core.Engine.Operations try { - SecurityContext.AuthenticateMe(CurrentUser); + SecurityContext.CurrentAccount = CurrentUser; } catch { // User was removed - SecurityContext.AuthenticateMe(ASC.Core.Configuration.Constants.CoreSystem); + SecurityContext.CurrentAccount = ASC.Core.Configuration.Constants.CoreSystem; } SetProgress((int?)MailOperationRemoveDomainProgress.RemoveFromDb, "Remove domain from Db"); diff --git a/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/MailRemoveMailserverMailboxOperation.cs b/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/MailRemoveMailserverMailboxOperation.cs index 6d0be62f2..259e65e86 100644 --- a/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/MailRemoveMailserverMailboxOperation.cs +++ b/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/MailRemoveMailserverMailboxOperation.cs @@ -54,12 +54,12 @@ namespace ASC.Mail.Core.Engine.Operations try { - SecurityContext.AuthenticateMe(new Guid(user)); + SecurityContext.CurrentUser = new Guid(user); } catch { // User was removed - SecurityContext.AuthenticateMe(ASC.Core.Configuration.Constants.CoreSystem); + SecurityContext.CurrentAccount = ASC.Core.Configuration.Constants.CoreSystem; } SetProgress((int?)MailOperationRemoveMailboxProgress.RemoveFromDb, "Remove mailbox from Db"); diff --git a/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/MailRemoveUserFolderOperation.cs b/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/MailRemoveUserFolderOperation.cs index 988f17bce..26adce177 100644 --- a/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/MailRemoveUserFolderOperation.cs +++ b/module/ASC.Mail/ASC.Mail/Core/Engine/Operations/MailRemoveUserFolderOperation.cs @@ -57,7 +57,7 @@ namespace ASC.Mail.Core.Engine.Operations CoreContext.TenantManager.SetCurrentTenant(CurrentTenant); - SecurityContext.AuthenticateMe(CurrentUser); + SecurityContext.CurrentAccount = CurrentUser; SetProgress((int?)MailOperationRemoveUserFolderProgress.DeleteFolders, "Delete folders"); diff --git a/module/ASC.Mail/ASC.Mail/Core/Entities/Account.cs b/module/ASC.Mail/ASC.Mail/Core/Entities/Account.cs index 8f5879b5d..48b4d74e2 100644 --- a/module/ASC.Mail/ASC.Mail/Core/Entities/Account.cs +++ b/module/ASC.Mail/ASC.Mail/Core/Entities/Account.cs @@ -38,5 +38,6 @@ namespace ASC.Mail.Core.Entities public int ServerMailGroupId { get; set; } public string ServerMailGroupAddress { get; set; } public int ServerDomainTenant { get; set; } + public DateTime? DateCreated { get; set; } } } diff --git a/module/ASC.Mail/ASC.Mail/Core/Entities/CashedMailUserAction.cs b/module/ASC.Mail/ASC.Mail/Core/Entities/CashedMailUserAction.cs new file mode 100644 index 000000000..a52f721cc --- /dev/null +++ b/module/ASC.Mail/ASC.Mail/Core/Entities/CashedMailUserAction.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; + +namespace ASC.Mail.Core.Entities +{ + public class CashedMailUserAction + { + public string UserName { get; set; } + public int Tenant { get; set; } + public List Uds { get; set; } + public MailUserAction Action { get; set; } + public int Destination { get; set; } + } + + public enum MailUserAction + { + Nothing, + SetAsRead, + SetAsUnread, + SetAsImportant, + SetAsNotImpotant, + SetAsDeleted, + StartImapClient, + MoveTo, + ReceiptStatusChanged + } +} diff --git a/module/ASC.Mail/ASC.Mail/Core/Entities/CashedTenantUserMailBox.cs b/module/ASC.Mail/ASC.Mail/Core/Entities/CashedTenantUserMailBox.cs new file mode 100644 index 000000000..106851465 --- /dev/null +++ b/module/ASC.Mail/ASC.Mail/Core/Entities/CashedTenantUserMailBox.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace ASC.Mail.Core.Entities +{ + public class CachedTenantUserMailBox + { + public string UserName { get; set; } + public int Tenant { get; set; } + public int MailBoxId { get; set; } + public int Folder { get; set; } + public IEnumerable Tags { get; set; } + } +} diff --git a/module/ASC.Mail/ASC.Mail/Core/Entities/Mail.cs b/module/ASC.Mail/ASC.Mail/Core/Entities/Mail.cs index 91f16e4c0..9036ef16b 100644 --- a/module/ASC.Mail/ASC.Mail/Core/Entities/Mail.cs +++ b/module/ASC.Mail/ASC.Mail/Core/Entities/Mail.cs @@ -58,5 +58,6 @@ namespace ASC.Mail.Core.Entities public bool IsTextBodyOnly { get; set; } public bool HasParseError { get; set; } public string CalendarUid { get; set; } + public bool ReadRequestStatus { get; set; } } } diff --git a/module/ASC.Mail/ASC.Mail/Core/Entities/MailInfo.cs b/module/ASC.Mail/ASC.Mail/Core/Entities/MailInfo.cs index f4921f039..f3ef5575f 100644 --- a/module/ASC.Mail/ASC.Mail/Core/Entities/MailInfo.cs +++ b/module/ASC.Mail/ASC.Mail/Core/Entities/MailInfo.cs @@ -46,6 +46,7 @@ namespace ASC.Mail.Core.Entities public string Stream { get; set; } public string Uidl { get; set; } public bool IsRemoved { get; set; } + public bool ReadRequestStatus { get; set; } public string Intoduction { get; set; } } } diff --git a/module/ASC.Mail/ASC.Mail/Data/Contracts/AccountInfo.cs b/module/ASC.Mail/ASC.Mail/Data/Contracts/AccountInfo.cs index fea20d7ef..02c6d92c8 100644 --- a/module/ASC.Mail/ASC.Mail/Data/Contracts/AccountInfo.cs +++ b/module/ASC.Mail/ASC.Mail/Data/Contracts/AccountInfo.cs @@ -15,6 +15,7 @@ */ +using System; using System.Collections.Generic; namespace ASC.Mail.Data.Contracts @@ -49,6 +50,8 @@ namespace ASC.Mail.Data.Contracts public bool IsSharedDomainMailbox { get; set; } + public DateTime? DateCreated { get; set; } + public override string ToString() { return Name + " <" + Email + ">"; diff --git a/module/ASC.Mail/ASC.Mail/Data/Contracts/MailAccountData.cs b/module/ASC.Mail/ASC.Mail/Data/Contracts/MailAccountData.cs index 8468a9d12..9ffdc298a 100644 --- a/module/ASC.Mail/ASC.Mail/Data/Contracts/MailAccountData.cs +++ b/module/ASC.Mail/ASC.Mail/Data/Contracts/MailAccountData.cs @@ -68,5 +68,8 @@ namespace ASC.Mail.Data.Contracts [DataMember(IsRequired = true, Name = "isSharedDomainMailbox")] public bool IsSharedDomainMailbox { get; set; } + + [DataMember(IsRequired = true, Name = "dateCreated")] + public DateTime? DateCreated { get; set; } } } diff --git a/module/ASC.Mail/ASC.Mail/Data/Contracts/MailDraftData.cs b/module/ASC.Mail/ASC.Mail/Data/Contracts/MailDraftData.cs index ff2ed8f88..df0ac25c1 100644 --- a/module/ASC.Mail/ASC.Mail/Data/Contracts/MailDraftData.cs +++ b/module/ASC.Mail/ASC.Mail/Data/Contracts/MailDraftData.cs @@ -28,6 +28,7 @@ namespace ASC.Mail.Data.Contracts public bool IsAutoreplied { get; set; } public bool RequestReceipt { get; set; } public bool RequestRead { get; set; } + public bool IsReceipt { get; set; } public MailDraftData(int id, MailBoxData mailBoxData, string from, List to, List cc, List bcc, diff --git a/module/ASC.Mail/ASC.Mail/Data/Contracts/MailMessageData.cs b/module/ASC.Mail/ASC.Mail/Data/Contracts/MailMessageData.cs index 3d76ba4c6..19b0fec6b 100644 --- a/module/ASC.Mail/ASC.Mail/Data/Contracts/MailMessageData.cs +++ b/module/ASC.Mail/ASC.Mail/Data/Contracts/MailMessageData.cs @@ -233,6 +233,9 @@ namespace ASC.Mail.Data.Contracts [DataMember] public string CalendarUid { get; set; } + [DataMember] + public bool ReadRequestStatus { get; set; } + [IgnoreDataMember] public int CalendarId { get; set; } diff --git a/module/ASC.Mail/ASC.Mail/Defines.cs b/module/ASC.Mail/ASC.Mail/Defines.cs index 6fccbfeaa..f3630799b 100644 --- a/module/ASC.Mail/ASC.Mail/Defines.cs +++ b/module/ASC.Mail/ASC.Mail/Defines.cs @@ -199,7 +199,7 @@ namespace ASC.Mail public const string ORDER_BY_SUBJECT = "subject"; public const string CONNECTION_STRING_NAME = "mail"; public const string DNS_DEFAULT_ORIGIN = "@"; - public const string ARCHIVE_NAME = "download.tar.gz"; + public const string ARCHIVE_NAME = "download.zip"; public static readonly DateTime BaseJsDateTime = new DateTime(1970, 1, 1); public enum TariffType diff --git a/module/ASC.Mail/ASC.Mail/Exceptions/LimitMessageException.cs b/module/ASC.Mail/ASC.Mail/Exceptions/LimitMessageException.cs index 1d52c4220..b3a6e7f78 100644 --- a/module/ASC.Mail/ASC.Mail/Exceptions/LimitMessageException.cs +++ b/module/ASC.Mail/ASC.Mail/Exceptions/LimitMessageException.cs @@ -1,4 +1,21 @@ -using System; +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System; namespace ASC.Mail.Exceptions { diff --git a/module/ASC.Mail/ASC.Mail/Extensions/DataContractsExtensions.cs b/module/ASC.Mail/ASC.Mail/Extensions/DataContractsExtensions.cs index 223334436..e4f1925f9 100644 --- a/module/ASC.Mail/ASC.Mail/Extensions/DataContractsExtensions.cs +++ b/module/ASC.Mail/ASC.Mail/Extensions/DataContractsExtensions.cs @@ -52,7 +52,8 @@ namespace ASC.Mail.Extensions IsGroup = false, IsTeamlabMailbox = account.IsTeamlabMailbox, IsDefault = mailBoxAccountSettings.DefaultEmail == account.Email, - IsSharedDomainMailbox = account.IsSharedDomainMailbox + IsSharedDomainMailbox = account.IsSharedDomainMailbox, + DateCreated = account.DateCreated }; fromEmailList.Add(emailData); diff --git a/module/ASC.Mail/ASC.Mail/Extensions/DateTimeExtensions.cs b/module/ASC.Mail/ASC.Mail/Extensions/DateTimeExtensions.cs index c316bb6d2..426c69d01 100644 --- a/module/ASC.Mail/ASC.Mail/Extensions/DateTimeExtensions.cs +++ b/module/ASC.Mail/ASC.Mail/Extensions/DateTimeExtensions.cs @@ -23,6 +23,16 @@ namespace ASC.Mail.Extensions { public static class DateTimeExtensions { + /// + /// Cuts off precision beyond a second on a DateTime value. + /// + /// The value. + /// A DateTime with a 0 millisecond component. + public static DateTime CutToSecond(this DateTime value) + { + return value - TimeSpan.FromMilliseconds(value.Millisecond); + } + public static string ToVerbString(this DateTime dateTime) { try diff --git a/module/ASC.Mail/ASC.Mail/Extensions/MailBoxExtensions.cs b/module/ASC.Mail/ASC.Mail/Extensions/MailBoxExtensions.cs index 7b6f153e2..48c8320dc 100644 --- a/module/ASC.Mail/ASC.Mail/Extensions/MailBoxExtensions.cs +++ b/module/ASC.Mail/ASC.Mail/Extensions/MailBoxExtensions.cs @@ -96,11 +96,11 @@ namespace ASC.Mail.Extensions try { - SecurityContext.AuthenticateMe(tenantInfo.OwnerId); + SecurityContext.CurrentUser = tenantInfo.OwnerId; } catch (InvalidCredentialException) { - SecurityContext.AuthenticateMe(new Guid(mailbox.UserId)); + SecurityContext.CurrentUser = new Guid(mailbox.UserId); } var apiHelper = new ApiHelper(httpContextScheme, log); @@ -155,7 +155,7 @@ namespace ASC.Mail.Extensions if (tenantInfo.Status == TenantStatus.RemovePending) return false; - SecurityContext.AuthenticateMe(new Guid(mailbox.UserId)); + SecurityContext.CurrentUser = new Guid(mailbox.UserId); var apiHelper = new ApiHelper(httpContextScheme, log); return apiHelper.IsCrmModuleAvailable(); diff --git a/module/ASC.Mail/ASC.Mail/Extensions/MailDraftExtensions.cs b/module/ASC.Mail/ASC.Mail/Extensions/MailDraftExtensions.cs index da7a7fcb6..cea3e60cc 100644 --- a/module/ASC.Mail/ASC.Mail/Extensions/MailDraftExtensions.cs +++ b/module/ASC.Mail/ASC.Mail/Extensions/MailDraftExtensions.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net; using System.Text; using System.Web; @@ -123,7 +124,10 @@ namespace ASC.Mail.Extensions mimeMessage.Bcc.AddRange(draft.Bcc.ConvertAll(MailboxAddress.Parse)); if (draft.Important) + { mimeMessage.Importance = MessageImportance.High; + mimeMessage.XPriority = XMessagePriority.Highest; + } if (!string.IsNullOrEmpty(draft.MimeReplyToId)) mimeMessage.InReplyTo = draft.MimeReplyToId; @@ -450,17 +454,39 @@ namespace ASC.Mail.Extensions var doc = new HtmlDocument(); doc.LoadHtml(draft.HtmlBody); - var linkNodes = doc.DocumentNode.SelectNodes("//img[@src and (contains(@src,'" + baseAttachmentFolder + "'))]"); + var linkNodes = doc.DocumentNode.SelectNodes("//img[@src and (contains(@src,'" + baseAttachmentFolder + "') or @x-mail-embedded)]"); if (linkNodes == null) return; foreach (var linkNode in linkNodes) { var link = linkNode.Attributes["src"].Value; log.InfoFormat("ChangeEmbededAttachmentLinks() Embeded attachment link for changing to cid: {0}", link); - var fileLink = HttpUtility.UrlDecode(link.Substring(baseAttachmentFolder.Length)); + + var isExternal = link.IndexOf(baseAttachmentFolder) == -1; + + var fileLink = isExternal + ? link + : HttpUtility.UrlDecode(link.Substring(baseAttachmentFolder.Length)); var fileName = Path.GetFileName(fileLink); var attach = CreateEmbbededAttachment(fileName, link, fileLink, draft.Mailbox.UserId, draft.Mailbox.TenantId, draft.Mailbox.MailBoxId, draft.StreamId); + + if (isExternal) + { + using (var webClient = new WebClient()) + { + //webClient.Headers.Add("Authorization", GetPartnerAuthHeader(actionUrl)); + try + { + attach.data = webClient.DownloadData(fileLink); + } + catch (WebException we) + { + log.Error(we); + continue; + } + } + } draft.AttachmentsEmbedded.Add(attach); linkNode.SetAttributeValue("src", "cid:" + attach.contentId); log.InfoFormat("ChangeEmbededAttachmentLinks() Attachment cid: {0}", attach.contentId); diff --git a/module/ASC.Mail/ASC.Mail/Resources/MailCoreResource.az-Latn-AZ.resx b/module/ASC.Mail/ASC.Mail/Resources/MailCoreResource.az-Latn-AZ.resx new file mode 100644 index 000000000..df6b1dc2f --- /dev/null +++ b/module/ASC.Mail/ASC.Mail/Resources/MailCoreResource.az-Latn-AZ.resx @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + '{0}' faylı tapılmadı + + + {0} fayl tapılmadı + + + Poçt mesajlarının məzmununa görə axtarın + + + Mesajda qoşma yoxdur + + diff --git a/module/ASC.Mail/ASC.Mail/Utils/ApiHelper.cs b/module/ASC.Mail/ASC.Mail/Utils/ApiHelper.cs index 72400f7c4..5555f2aaf 100644 --- a/module/ASC.Mail/ASC.Mail/Utils/ApiHelper.cs +++ b/module/ASC.Mail/ASC.Mail/Utils/ApiHelper.cs @@ -34,7 +34,9 @@ using ASC.Mail.Extensions; using ASC.Specific; using ASC.Web.Core; -using DotNetOpenAuth.Messaging; +using Ical.Net.CalendarComponents; + +using MimeKit; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -330,11 +332,11 @@ namespace ASC.Mail.Utils public List SearchPeopleEmails(string term, int startIndex, int count) { - var request = new RestRequest("people/filter.json?filterValue={FilterValue}&StartIndex={StartIndex}&Count={Count}", Method.GET); + var request = new RestRequest("people/filter.json", Method.GET); - request.AddParameter("FilterValue", term, ParameterType.UrlSegment) - .AddParameter("StartIndex", startIndex.ToString(), ParameterType.UrlSegment) - .AddParameter("Count", count.ToString(), ParameterType.UrlSegment); + request.AddParameter("FilterValue", term) + .AddParameter("StartIndex", startIndex) + .AddParameter("Count", count); var response = Execute(request); @@ -511,14 +513,34 @@ namespace ASC.Mail.Utils } } - public void UploadIcsToCalendar(int calendarId, Stream fileStream, string filename, string contentType) + public void UploadIcsToCalendar(int calendarId, Stream fileStream, string filename, string contentType, CalendarEvent eventObj, IEnumerable mimeAttachments, List mailAttachments) { - var request = new RestRequest("calendar/import.json", Method.POST); + var request = new RestRequest("calendar/importFromAggregator.json", Method.POST); request.AddParameter("calendarId", calendarId); request.AddFile(filename, fileStream.CopyTo, filename, fileStream.Length, contentType); + foreach (var attachment in eventObj.Attachments) + { + if (attachment.Uri.AbsoluteUri.StartsWith("cid:", StringComparison.OrdinalIgnoreCase)) + { + var contentId = attachment.Uri.AbsoluteUri.Replace("cid:", ""); + var mimeEntity = mimeAttachments.FirstOrDefault(a => a.ContentId == contentId); + + if (mimeEntity != null) + { + var file = mailAttachments.FirstOrDefault(a => a.fileName == mimeEntity.ContentDisposition.FileName); + + if (file != null) + { + file.dataStream.Position = 0; + request.AddFile(contentId, file.dataStream.CopyTo, string.Format("{0}/{1}", contentId, file.fileName), file.dataStream.Length, file.contentType); + } + } + } + } + var response = Execute(request); if (response.ResponseStatus != ResponseStatus.Completed || diff --git a/module/ASC.Mail/ASC.Mail/Utils/MailUtil.cs b/module/ASC.Mail/ASC.Mail/Utils/MailUtil.cs index a279fc30d..aefef6261 100644 --- a/module/ASC.Mail/ASC.Mail/Utils/MailUtil.cs +++ b/module/ASC.Mail/ASC.Mail/Utils/MailUtil.cs @@ -25,7 +25,6 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Web; -using System.Xml; using ASC.Common.Logging; using ASC.Core; @@ -157,24 +156,6 @@ namespace ASC.Mail.Utils return subject.Trim(); } - /// - /// Removes control characters and other non-UTF-8 characters - /// - /// The string to process - /// A string with no control characters or entities above 0x00FD - public static string NormalizeStringForMySql(string inString) - { - if (string.IsNullOrEmpty(inString)) - return inString; - - var newString = new StringBuilder(inString.Length); - - foreach (var ch in inString.Where(XmlConvert.IsXmlChar)) - newString.Append(ch); - - return newString.ToString(); - } - public static T[] SubArray(this T[] data, int index, int length) { var result = new T[length]; diff --git a/module/ASC.Mail/ASC.Mail/Utils/Parser.cs b/module/ASC.Mail/ASC.Mail/Utils/Parser.cs index 78f6e326e..64e1d4b0d 100644 --- a/module/ASC.Mail/ASC.Mail/Utils/Parser.cs +++ b/module/ASC.Mail/ASC.Mail/Utils/Parser.cs @@ -18,11 +18,11 @@ using System; using System.Collections.Generic; using System.Net.Mail; -using System.Text; using System.Text.RegularExpressions; using ASC.Mail.Data.Contracts; using ASC.Web.Core.Utility; +using ASC.Web.Studio.Core.Users; using ASC.Web.Studio.PublicResources; using MimeKit; @@ -31,12 +31,8 @@ namespace ASC.Mail.Utils { public static class Parser { - private static readonly Regex RegxWhiteSpaces = new Regex(@"\s+", RegexOptions.Compiled | RegexOptions.CultureInvariant); - private static readonly Regex RegxClean = new Regex(@"(\(((\\\))|[^)])*\))", RegexOptions.Compiled | RegexOptions.CultureInvariant); - //private static readonly Regex RegxEmail = new Regex("<(.|[.])*?>", RegexOptions.Compiled | RegexOptions.CultureInvariant); private static readonly Regex RegxDomain = new Regex(@"(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{1,63}(? /// Parses the address. @@ -124,26 +120,6 @@ namespace ASC.Mail.Utils } } - /// - /// Removes the white spaces. - /// - /// The input. - /// - internal static string RemoveWhiteSpaces(string input) - { - return RegxWhiteSpaces.Replace(input, ""); - } - - /// - /// Cleans the specified input. - /// - /// The input. - /// - internal static string Clean(string input) - { - return RegxClean.Replace(input, "").Trim(' '); - } - /// /// Get valid password or throw exception. /// @@ -158,36 +134,14 @@ namespace ASC.Mail.Utils var pwdSettings = PasswordSettings.Load(); - if (!PasswordSettings.CheckPasswordRegex(pwdSettings, trimPwd) - || RegxNoneAscii.IsMatch(trimPwd) - || RegxWhiteSpaces.IsMatch(trimPwd)) + if (!PasswordSettings.CheckPasswordRegex(pwdSettings, trimPwd)) { - throw new ArgumentException(GeneratePasswordErrorMessage(pwdSettings)); + throw new ArgumentException(UserManagerWrapper.GetPasswordHelpMessage(pwdSettings)); } return trimPwd; } - internal static string GeneratePasswordErrorMessage(PasswordSettings passwordSettings) - { - var error = new StringBuilder(); - - error.AppendFormat("{0} ", Resource.ErrorPasswordMessage); - error.AppendFormat(Resource.ErrorPasswordLength, passwordSettings.MinLength, PasswordSettings.MaxLength); - - error.AppendFormat(", {0}", Resource.ErrorPasswordOnlyLatinLetters); - error.AppendFormat(", {0}", Resource.ErrorPasswordNoSpaces); - - if (passwordSettings.UpperCase) - error.AppendFormat(", {0}", Resource.ErrorPasswordNoUpperCase); - if (passwordSettings.Digits) - error.AppendFormat(", {0}", Resource.ErrorPasswordNoDigits); - if (passwordSettings.SpecSymbols) - error.AppendFormat(", {0}", Resource.ErrorPasswordNoSpecialSymbols); - - return error.ToString(); - } - public static List ToMailAddresses(this List addresses) { var toList = new List(); diff --git a/module/ASC.Mail/Services/ASC.Mail.Aggregator.CollectionService/ASC.Mail.Aggregator.CollectionService.csproj b/module/ASC.Mail/Services/ASC.Mail.Aggregator.CollectionService/ASC.Mail.Aggregator.CollectionService.csproj index 3643a5c21..7d8853c3b 100644 --- a/module/ASC.Mail/Services/ASC.Mail.Aggregator.CollectionService/ASC.Mail.Aggregator.CollectionService.csproj +++ b/module/ASC.Mail/Services/ASC.Mail.Aggregator.CollectionService/ASC.Mail.Aggregator.CollectionService.csproj @@ -121,16 +121,16 @@ - 1.9.71 + 2.8.0 - 3.1.0 + 5.0.11 - 2.0.8 + 2.0.9 - 2.11.1 + 2.15.0 0.8.5 diff --git a/module/ASC.Mail/Services/ASC.Mail.Aggregator.CollectionService/AggregatorService.cs b/module/ASC.Mail/Services/ASC.Mail.Aggregator.CollectionService/AggregatorService.cs index 92dade92c..b53690fea 100644 --- a/module/ASC.Mail/Services/ASC.Mail.Aggregator.CollectionService/AggregatorService.cs +++ b/module/ASC.Mail/Services/ASC.Mail.Aggregator.CollectionService/AggregatorService.cs @@ -800,7 +800,7 @@ namespace ASC.Mail.Aggregator.CollectionService uidl, mailbox.MailBoxId, mailbox.EMail); CoreContext.TenantManager.SetCurrentTenant(mailbox.TenantId); - SecurityContext.AuthenticateMe(new Guid(mailbox.UserId)); + SecurityContext.CurrentUser = new Guid(mailbox.UserId); var message = MessageEngine.Save(mailbox, mimeMessage, uidl, folder, null, unread, log); @@ -1005,7 +1005,7 @@ namespace ASC.Mail.Aggregator.CollectionService factory .CalendarEngine .UploadIcsToCalendar(mailbox, message.CalendarId, message.CalendarUid, message.CalendarEventIcs, - message.CalendarEventCharset, message.CalendarEventMimeType, mailbox.EMail.Address, + message.CalendarEventCharset, message.CalendarEventMimeType, message.Attachments, mimeMessage.Attachments, mailbox.EMail.Address, _tasksConfig.DefaultApiSchema); } diff --git a/module/ASC.Mail/Services/ASC.Mail.Aggregator.CollectionService/App.config b/module/ASC.Mail/Services/ASC.Mail.Aggregator.CollectionService/App.config index dc9da573c..1a5cdd77d 100644 --- a/module/ASC.Mail/Services/ASC.Mail.Aggregator.CollectionService/App.config +++ b/module/ASC.Mail/Services/ASC.Mail.Aggregator.CollectionService/App.config @@ -64,6 +64,14 @@ + + + + + + + + diff --git a/module/ASC.Mail/Services/ASC.Mail.Aggregator.CollectionService/Program.Options.cs b/module/ASC.Mail/Services/ASC.Mail.Aggregator.CollectionService/Program.Options.cs index e5856c9a0..3253c379d 100644 --- a/module/ASC.Mail/Services/ASC.Mail.Aggregator.CollectionService/Program.Options.cs +++ b/module/ASC.Mail/Services/ASC.Mail.Aggregator.CollectionService/Program.Options.cs @@ -24,7 +24,7 @@ namespace ASC.Mail.Aggregator.CollectionService { public class Options { - [OptionList('u', "users", MetaValue = "STRING ARRAY", Required = false, HelpText = "An array of users for which the aggregator will take tasks. " + + [Option('u', "users", MetaValue = "STRING ARRAY", Required = false, HelpText = "An array of users for which the aggregator will take tasks. " + "Separator = ';' " + "Example: -u\"{tl_userId_1}\";\"{tl_userId_2}\";\"{tl_userId_3}\"", Separator = ';')] public IList OnlyUsers { get; set; } @@ -34,11 +34,5 @@ namespace ASC.Mail.Aggregator.CollectionService [Option("unlimit", Required = false, HelpText = "Unlimit messages per mailbox session")] public bool NoMessagesLimit { get; set; } - - [HelpOption] - public string GetUsage() - { - return HelpText.AutoBuild(this, current => HelpText.DefaultParsingErrorsHandler(this, current)); - } } } diff --git a/module/ASC.Mail/Services/ASC.Mail.Aggregator.CollectionService/Program.cs b/module/ASC.Mail/Services/ASC.Mail.Aggregator.CollectionService/Program.cs index c94293c5d..8e51d67c1 100644 --- a/module/ASC.Mail/Services/ASC.Mail.Aggregator.CollectionService/Program.cs +++ b/module/ASC.Mail/Services/ASC.Mail.Aggregator.CollectionService/Program.cs @@ -35,9 +35,10 @@ namespace ASC.Mail.Aggregator.CollectionService if (args.Any()) { - Parser.Default.ParseArgumentsStrict(args, options, - () => - Console.WriteLine(@"Bad command line parameters.")); + + Parser.Default.ParseArguments(args) + .WithNotParsed(x => Console.WriteLine(@"Bad command line parameters.")) + .WithParsed(x => options = x); } if (Environment.UserInteractive || options.IsConsole) diff --git a/module/ASC.Mail/Services/ASC.Mail.Aggregator.CollectionService/Queue/QueueManager.cs b/module/ASC.Mail/Services/ASC.Mail.Aggregator.CollectionService/Queue/QueueManager.cs index 3665604f0..8cf53bf01 100644 --- a/module/ASC.Mail/Services/ASC.Mail.Aggregator.CollectionService/Queue/QueueManager.cs +++ b/module/ASC.Mail/Services/ASC.Mail.Aggregator.CollectionService/Queue/QueueManager.cs @@ -55,8 +55,8 @@ namespace ASC.Mail.Aggregator.CollectionService.Queue private readonly object _locker = new object(); private LiteDatabase _db; - private LiteCollection _mailboxes; - private LiteCollection _tenants; + private ILiteCollection _mailboxes; + private ILiteCollection _tenants; public ManualResetEvent CancelHandler { get; set; } @@ -313,7 +313,7 @@ namespace ASC.Mail.Aggregator.CollectionService.Queue if (mailbox == null) return; - _mailboxes.Delete(Query.EQ("MailboxId", mailBoxId)); + _mailboxes.DeleteMany(Query.EQ("MailboxId", mailBoxId)); } } catch (Exception ex) @@ -392,7 +392,7 @@ namespace ASC.Mail.Aggregator.CollectionService.Queue if (tenant == null) return; - _tenants.Delete(Query.EQ("Tenant", tenantId)); + _tenants.DeleteMany(Query.EQ("Tenant", tenantId)); } } catch (Exception ex) diff --git a/module/ASC.Mail/Services/ASC.Mail.Aggregator.CollectionService/mail.agg.nlog.config b/module/ASC.Mail/Services/ASC.Mail.Aggregator.CollectionService/mail.agg.nlog.config index df8a1befd..151899312 100644 --- a/module/ASC.Mail/Services/ASC.Mail.Aggregator.CollectionService/mail.agg.nlog.config +++ b/module/ASC.Mail/Services/ASC.Mail.Aggregator.CollectionService/mail.agg.nlog.config @@ -1,18 +1,15 @@  - - - - + - - - - + + + + diff --git a/module/ASC.Mail/Services/ASC.Mail.EmlDownloader/ASC.Mail.EmlDownloader.csproj b/module/ASC.Mail/Services/ASC.Mail.EmlDownloader/ASC.Mail.EmlDownloader.csproj index eb95d351b..67f7180f2 100644 --- a/module/ASC.Mail/Services/ASC.Mail.EmlDownloader/ASC.Mail.EmlDownloader.csproj +++ b/module/ASC.Mail/Services/ASC.Mail.EmlDownloader/ASC.Mail.EmlDownloader.csproj @@ -75,25 +75,19 @@ - 1.9.71 - - - 4.1.0.12182 + 2.8.0 - 2.0.8 + 2.0.9 - 2.11.1 + 2.15.0 - 8.0.25 + 8.0.29 - 4.7.0 - - - 2.4.1 + 4.7.11 diff --git a/module/ASC.Mail/Services/ASC.Mail.EmlDownloader/App.config b/module/ASC.Mail/Services/ASC.Mail.EmlDownloader/App.config index 7bbef3e34..9c798d54a 100644 --- a/module/ASC.Mail/Services/ASC.Mail.EmlDownloader/App.config +++ b/module/ASC.Mail/Services/ASC.Mail.EmlDownloader/App.config @@ -42,6 +42,14 @@ + + + + + + + + diff --git a/module/ASC.Mail/Services/ASC.Mail.EmlDownloader/Program.Options.cs b/module/ASC.Mail/Services/ASC.Mail.EmlDownloader/Program.Options.cs index 54a621d14..bbee3de0d 100644 --- a/module/ASC.Mail/Services/ASC.Mail.EmlDownloader/Program.Options.cs +++ b/module/ASC.Mail/Services/ASC.Mail.EmlDownloader/Program.Options.cs @@ -29,15 +29,6 @@ namespace ASC.Mail.EmlDownloader [Option('u', "uid", Required = false, HelpText = "Message uid for download.")] public string MessageUid { get; set; } - - [ParserState] - public IParserState LastParserState { get; set; } - - [HelpOption] - public string GetUsage() - { - return HelpText.AutoBuild(this, current => HelpText.DefaultParsingErrorsHandler(this, current)); - } } } } diff --git a/module/ASC.Mail/Services/ASC.Mail.EmlDownloader/Program.cs b/module/ASC.Mail/Services/ASC.Mail.EmlDownloader/Program.cs index 202045084..9786290e0 100644 --- a/module/ASC.Mail/Services/ASC.Mail.EmlDownloader/Program.cs +++ b/module/ASC.Mail/Services/ASC.Mail.EmlDownloader/Program.cs @@ -27,77 +27,77 @@ using ASC.Mail.Core.Dao.Expressions.Mailbox; using ASC.Mail.Data.Contracts; using ASC.Mail.Enums; +using CommandLine; + namespace ASC.Mail.EmlDownloader { internal partial class Program { private static void Main(string[] args) - { - var options = new Options(); - - if (CommandLine.Parser.Default.ParseArgumentsStrict(args, options, - () => Console.WriteLine(@"Bad command line parameters."))) - { - try - { - Console.WriteLine(@"Searching account with id {0}", options.MailboxId); - - if (string.IsNullOrEmpty(options.MessageUid)) + { + CommandLine.Parser.Default.ParseArguments(args) + .WithNotParsed(options => Console.WriteLine(@"Bad command line parameters.")) + .WithParsed(options => { + try { - Console.WriteLine(@"MessageUid not setup."); - ShowAnyKey(); - return; - } + Console.WriteLine(@"Searching account with id {0}", options.MailboxId); - var mailbox = GetMailBox(options.MailboxId); + if (string.IsNullOrEmpty(options.MessageUid)) + { + Console.WriteLine(@"MessageUid not setup."); + ShowAnyKey(); + return; + } - if (mailbox == null) - { - Console.WriteLine(@"Account not found."); - ShowAnyKey(); - return; - } + var mailbox = GetMailBox(options.MailboxId); - if (mailbox.Imap) - { - var uidlStucture = ParserImapUidl(options.MessageUid); - if (uidlStucture.folderId != FolderType.Inbox) - throw new FormatException("Only inbox messages are supported for downloading."); + if (mailbox == null) + { + Console.WriteLine(@"Account not found."); + ShowAnyKey(); + return; + } + + if (mailbox.Imap) + { + var uidlStucture = ParserImapUidl(options.MessageUid); + if (uidlStucture.folderId != FolderType.Inbox) + throw new FormatException("Only inbox messages are supported for downloading."); + + } + + var certificatePermit = ConfigurationManagerExtension.AppSettings["mail.certificate-permit"] != null && + Convert.ToBoolean( + ConfigurationManagerExtension.AppSettings["mail.certificate-permit"]); + + string messageEml; + using (var client = new MailClient(mailbox, CancellationToken.None, certificatePermit: mailbox.IsTeamlab || certificatePermit)) + { + var message = client.GetInboxMessage(options.MessageUid); + messageEml = message.ToString(); + } + + Console.WriteLine(@"Try StoreToFile"); + + var now = DateTime.UtcNow; + + var path = Path.Combine(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Downloads"), + string.Format("uid_{0}_{1}.eml", options.MessageUid, + now.ToString("dd_MM_yyyy_hh_mm"))); + + + var pathFile = StoreToFile(messageEml, path, true); + + Console.WriteLine(@"[SUCCESS] File was stored into path ""{0}""", pathFile); } - - var certificatePermit = ConfigurationManagerExtension.AppSettings["mail.certificate-permit"] != null && - Convert.ToBoolean( - ConfigurationManagerExtension.AppSettings["mail.certificate-permit"]); - - string messageEml; - using (var client = new MailClient(mailbox, CancellationToken.None, certificatePermit: mailbox.IsTeamlab || certificatePermit)) + catch (Exception ex) { - var message = client.GetInboxMessage(options.MessageUid); - messageEml = message.ToString(); + Console.WriteLine(ex.ToString()); } - Console.WriteLine(@"Try StoreToFile"); - var now = DateTime.UtcNow; - - var path = Path.Combine(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Downloads"), - string.Format("uid_{0}_{1}.eml", options.MessageUid, - now.ToString("dd_MM_yyyy_hh_mm"))); - - - var pathFile = StoreToFile(messageEml, path, true); - - Console.WriteLine(@"[SUCCESS] File was stored into path ""{0}""", pathFile); - - } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - } - } - - ShowAnyKey(); + }); } private static void ShowAnyKey() diff --git a/module/ASC.Mail/Services/ASC.Mail.Reloader/Program.cs b/module/ASC.Mail/Services/ASC.Mail.Reloader/Program.cs index 720ca0619..b81bba656 100644 --- a/module/ASC.Mail/Services/ASC.Mail.Reloader/Program.cs +++ b/module/ASC.Mail/Services/ASC.Mail.Reloader/Program.cs @@ -21,7 +21,9 @@ using System.Configuration; using System.IO; using System.Linq; using System.Threading; + using ASC.Common.Logging; +using ASC.Common.Utils; using ASC.Mail.Clients; using ASC.Mail.Core; using ASC.Mail.Core.Dao.Expressions.Mailbox; @@ -194,21 +196,21 @@ namespace ASC.Mail.Reloader continue; } - if (!storedMessage.From.Equals(MailUtil.NormalizeStringForMySql(message.From)) || - !storedMessage.To.Equals(MailUtil.NormalizeStringForMySql(message.To)) || - !storedMessage.Subject.Equals(MailUtil.NormalizeStringForMySql(message.Subject))) + if (!storedMessage.From.Equals(StringUtils.NormalizeStringForMySql(message.From)) || + !storedMessage.To.Equals(StringUtils.NormalizeStringForMySql(message.To)) || + !storedMessage.Subject.Equals(StringUtils.NormalizeStringForMySql(message.Subject))) { Console.WriteLine(@"storedMessage.From = '{0}'", storedMessage.From); Console.WriteLine(@"message.From = '{0}'", - MailUtil.NormalizeStringForMySql(message.From)); + StringUtils.NormalizeStringForMySql(message.From)); Console.WriteLine(@"storedMessage.To = '{0}'", storedMessage.To); Console.WriteLine(@"message.To = '{0}'", - MailUtil.NormalizeStringForMySql(message.To)); + StringUtils.NormalizeStringForMySql(message.To)); Console.WriteLine(@"storedMessage.Subject = '{0}'", storedMessage.Subject); Console.WriteLine(@"message.Subject = '{0}'", - MailUtil.NormalizeStringForMySql(message.Subject)); + StringUtils.NormalizeStringForMySql(message.Subject)); Console.WriteLine(@"[ERROR] Stored message not equals to server message"); continue; diff --git a/module/ASC.Mail/Services/ASC.Mail.StorageCleaner/ASC.Mail.StorageCleaner.Service.csproj b/module/ASC.Mail/Services/ASC.Mail.StorageCleaner/ASC.Mail.StorageCleaner.Service.csproj index 0738c320d..d63230c0f 100644 --- a/module/ASC.Mail/Services/ASC.Mail.StorageCleaner/ASC.Mail.StorageCleaner.Service.csproj +++ b/module/ASC.Mail/Services/ASC.Mail.StorageCleaner/ASC.Mail.StorageCleaner.Service.csproj @@ -111,20 +111,17 @@ - 2.0.8 + 2.0.9 - 8.0.25 + 8.0.29 - 4.7.0 + 4.7.11 0.8.5 - - 2.4.1 - diff --git a/module/ASC.Mail/Services/ASC.Mail.StorageCleaner/App.config b/module/ASC.Mail/Services/ASC.Mail.StorageCleaner/App.config index ba6242f7e..ac67345d9 100644 --- a/module/ASC.Mail/Services/ASC.Mail.StorageCleaner/App.config +++ b/module/ASC.Mail/Services/ASC.Mail.StorageCleaner/App.config @@ -38,6 +38,14 @@ + + + + + + + + diff --git a/module/ASC.Mail/Services/ASC.Mail.StorageCleaner/mail.cln.nlog.config b/module/ASC.Mail/Services/ASC.Mail.StorageCleaner/mail.cln.nlog.config index ed6985106..5c0949462 100644 --- a/module/ASC.Mail/Services/ASC.Mail.StorageCleaner/mail.cln.nlog.config +++ b/module/ASC.Mail/Services/ASC.Mail.StorageCleaner/mail.cln.nlog.config @@ -1,17 +1,14 @@  - - - - + - - - + + + diff --git a/module/ASC.Mail/Services/ASC.Mail.Watchdog.Service/ASC.Mail.Watchdog.Service.csproj b/module/ASC.Mail/Services/ASC.Mail.Watchdog.Service/ASC.Mail.Watchdog.Service.csproj index 1fa6deb00..86aa8cce4 100644 --- a/module/ASC.Mail/Services/ASC.Mail.Watchdog.Service/ASC.Mail.Watchdog.Service.csproj +++ b/module/ASC.Mail/Services/ASC.Mail.Watchdog.Service/ASC.Mail.Watchdog.Service.csproj @@ -128,16 +128,13 @@ - 1.9.71 + 2.8.0 - 2.0.8 + 2.0.9 - 8.0.25 - - - 2.4.1 + 8.0.29 diff --git a/module/ASC.Mail/Services/ASC.Mail.Watchdog.Service/App.config b/module/ASC.Mail/Services/ASC.Mail.Watchdog.Service/App.config index f1ba5acdc..5e1d2ab53 100644 --- a/module/ASC.Mail/Services/ASC.Mail.Watchdog.Service/App.config +++ b/module/ASC.Mail/Services/ASC.Mail.Watchdog.Service/App.config @@ -24,6 +24,14 @@ + + + + + + + + diff --git a/module/ASC.Mail/Services/ASC.Mail.Watchdog.Service/Program.Options.cs b/module/ASC.Mail/Services/ASC.Mail.Watchdog.Service/Program.Options.cs index 3f6500860..91eb1bab9 100644 --- a/module/ASC.Mail/Services/ASC.Mail.Watchdog.Service/Program.Options.cs +++ b/module/ASC.Mail/Services/ASC.Mail.Watchdog.Service/Program.Options.cs @@ -26,12 +26,6 @@ namespace ASC.Mail.Watchdog.Service { [Option("console", Required = false, HelpText = "Console state")] public bool IsConsole { get; set; } - - [HelpOption] - public string GetUsage() - { - return HelpText.AutoBuild(this, current => HelpText.DefaultParsingErrorsHandler(this, current)); - } } } } diff --git a/module/ASC.Mail/Services/ASC.Mail.Watchdog.Service/mail.dog.nlog.config b/module/ASC.Mail/Services/ASC.Mail.Watchdog.Service/mail.dog.nlog.config index 567557cbe..10be53f09 100644 --- a/module/ASC.Mail/Services/ASC.Mail.Watchdog.Service/mail.dog.nlog.config +++ b/module/ASC.Mail/Services/ASC.Mail.Watchdog.Service/mail.dog.nlog.config @@ -1,17 +1,14 @@  - - - - + - - - + + + diff --git a/module/ASC.MessagingSystem/ASC.MessagingSystem.csproj b/module/ASC.MessagingSystem/ASC.MessagingSystem.csproj index 2ed1da1f6..6f15fb86e 100644 --- a/module/ASC.MessagingSystem/ASC.MessagingSystem.csproj +++ b/module/ASC.MessagingSystem/ASC.MessagingSystem.csproj @@ -32,16 +32,16 @@ - - + + @@ -59,14 +59,11 @@ - - 2.0.3 - - 12.0.3 + 13.0.1 - 3.1.44 + 3.1.47 diff --git a/module/ASC.MessagingSystem/DbSender/DbMessageSender.cs b/module/ASC.MessagingSystem/DbSender/DbMessageSender.cs index 853459a10..d267ad4e4 100644 --- a/module/ASC.MessagingSystem/DbSender/DbMessageSender.cs +++ b/module/ASC.MessagingSystem/DbSender/DbMessageSender.cs @@ -25,6 +25,12 @@ namespace ASC.MessagingSystem.DbSender public class DbMessageSender : IMessageSender { private readonly ILog log = LogManager.GetLogger("ASC.Messaging"); + private MessagesRepository messagesRepository; + + public DbMessageSender() + { + messagesRepository = new MessagesRepository(); + } private static bool MessagingEnabled { @@ -36,19 +42,21 @@ namespace ASC.MessagingSystem.DbSender } - public void Send(EventMessage message) + public int Send(EventMessage message) { try { - if (!MessagingEnabled) return; + if (!MessagingEnabled) return 0; - if (message == null) return; + if (message == null) return 0; - MessagesRepository.Add(message); + var id = messagesRepository.Add(message); + return id; } catch (Exception ex) { log.Error("Failed to send a message", ex); + return 0; } } } diff --git a/module/ASC.MessagingSystem/DbSender/MessagesRepository.cs b/module/ASC.MessagingSystem/DbSender/MessagesRepository.cs index 89045a200..27477818c 100644 --- a/module/ASC.MessagingSystem/DbSender/MessagesRepository.cs +++ b/module/ASC.MessagingSystem/DbSender/MessagesRepository.cs @@ -17,6 +17,7 @@ using System; using System.Collections.Generic; +using System.Configuration; using System.Linq; using System.Threading; @@ -37,39 +38,75 @@ namespace ASC.MessagingSystem.DbSender internal class MessagesRepository { private const string MessagesDbId = "default"; - private static DateTime lastSave = DateTime.UtcNow; - private static readonly TimeSpan CacheTime; - private static readonly IDictionary Cache; - private static Parser Parser { get; set; } - private static readonly Timer Timer; - private static bool timerStarted; + private DateTime lastSave = DateTime.UtcNow; + private readonly TimeSpan CacheTime; + private readonly int CacheLimit; + private readonly IDictionary Cache; + private readonly Timer Timer; + private bool timerStarted; private const string LoginEventsTable = "login_events"; private const string AuditEventsTable = "audit_events"; - private static readonly Timer ClearTimer; + private readonly Timer ClearTimer; - static MessagesRepository() + public MessagesRepository() { - CacheTime = TimeSpan.FromMinutes(1); Cache = new Dictionary(); - Parser = Parser.GetDefault(); Timer = new Timer(FlushCache); timerStarted = false; ClearTimer = new Timer(DeleteOldEvents); ClearTimer.Change(new TimeSpan(0), TimeSpan.FromDays(1)); + + var minutes = ConfigurationManagerExtension.AppSettings["messaging.CacheTimeFromMinutes"]; + var limit = ConfigurationManagerExtension.AppSettings["messaging.CacheLimit"]; + + CacheTime = Int32.TryParse(minutes, out var cacheTime) ? TimeSpan.FromMinutes(cacheTime) : TimeSpan.FromMinutes(1); + CacheLimit = Int32.TryParse(limit, out var cacheLimit) ? cacheLimit : 100; } - public static void Add(EventMessage message) + ~MessagesRepository() + { + FlushCache(true); + } + + private bool ForseSave(EventMessage message) { // messages with action code < 2000 are related to login-history - if ((int)message.Action < 2000) + if ((int)message.Action < 2000) return true; + + return message.Action == MessageAction.UserSentPasswordChangeInstructions; + } + + + public int Add(EventMessage message) + { + if (ForseSave(message)) { + int id = 0; + if (!string.IsNullOrEmpty(message.UAHeader)) + { + try + { + MessageSettings.AddInfoMessage(message); + } + catch (Exception e) + { + LogManager.GetLogger("ASC").Error("Add " + message.Id, e); + } + } using (var db = DbManager.FromHttpContext(MessagesDbId)) { - AddLoginEvent(message, db); + if ((int)message.Action < 2000) + { + id = AddLoginEvent(message, db); + } + else + { + id = AddAuditEvent(message, db); + } } - return; + return id; } var now = DateTime.UtcNow; @@ -85,14 +122,19 @@ namespace ASC.MessagingSystem.DbSender timerStarted = true; } } - + return 0; } - private static void FlushCache(object state) + private void FlushCache(object state) + { + FlushCache(false); + } + + private void FlushCache(bool isDisposed = false) { List events = null; - if (CacheTime < DateTime.UtcNow - lastSave || Cache.Count > 100) + if (DateTime.UtcNow > lastSave.Add(CacheTime) || Cache.Count > CacheLimit || isDisposed) { lock (Cache) { @@ -118,24 +160,7 @@ namespace ASC.MessagingSystem.DbSender { try { - - ClientInfo clientInfo; - - if (dict.ContainsKey(message.UAHeader)) - { - clientInfo = dict[message.UAHeader]; - } - else - { - clientInfo = Parser.Parse(message.UAHeader); - dict.Add(message.UAHeader, clientInfo); - } - - if (clientInfo != null) - { - message.Browser = GetBrowser(clientInfo); - message.Platform = GetPlatform(clientInfo); - } + MessageSettings.AddInfoMessage(message, dict); } catch (Exception e) { @@ -143,10 +168,13 @@ namespace ASC.MessagingSystem.DbSender } } - // messages with action code < 2000 are related to login-history - if ((int)message.Action >= 2000) + if (!ForseSave(message)) { - AddAuditEvent(message, db); + // messages with action code < 2000 are related to login-history + if ((int)message.Action < 2000) + AddLoginEvent(message, db); + else + AddAuditEvent(message, db); } } @@ -154,9 +182,10 @@ namespace ASC.MessagingSystem.DbSender } } - private static void AddLoginEvent(EventMessage message, IDbManager dbManager) + private int AddLoginEvent(EventMessage message, IDbManager dbManager) { var i = new SqlInsert("login_events") + .InColumnValue("id", 0) .InColumnValue("ip", message.IP) .InColumnValue("login", message.Initiator) .InColumnValue("browser", message.Browser) @@ -165,7 +194,8 @@ namespace ASC.MessagingSystem.DbSender .InColumnValue("tenant_id", message.TenantId) .InColumnValue("user_id", message.UserId) .InColumnValue("page", message.Page) - .InColumnValue("action", message.Action); + .InColumnValue("action", message.Action) + .InColumnValue("active", message.Active); if (message.Description != null && message.Description.Any()) { @@ -176,12 +206,16 @@ namespace ASC.MessagingSystem.DbSender })); } - dbManager.ExecuteNonQuery(i); + i = i.Identity(0, 0, true); + + var id = dbManager.ExecuteScalar(i); + return id; } - private static void AddAuditEvent(EventMessage message, IDbManager dbManager) + private int AddAuditEvent(EventMessage message, IDbManager dbManager) { var i = new SqlInsert("audit_events") + .InColumnValue("id", 0) .InColumnValue("ip", message.IP) .InColumnValue("initiator", message.Initiator) .InColumnValue("browser", message.Browser) @@ -203,10 +237,13 @@ namespace ASC.MessagingSystem.DbSender i.InColumnValue("target", message.Target == null ? null : message.Target.ToString()); - dbManager.ExecuteNonQuery(i); + i = i.Identity(0, 0, true); + + var id = dbManager.ExecuteScalar(i); + return id; } - private static IList GetSafeDescription(IEnumerable description) + private IList GetSafeDescription(IEnumerable description) { const int maxLength = 15000; @@ -230,22 +267,7 @@ namespace ASC.MessagingSystem.DbSender return safe; } - private static string GetBrowser(ClientInfo clientInfo) - { - return clientInfo == null - ? null - : string.Format("{0} {1}", clientInfo.UA.Family, clientInfo.UA.Major); - } - - private static string GetPlatform(ClientInfo clientInfo) - { - return clientInfo == null - ? null - : string.Format("{0} {1}", clientInfo.OS.Family, clientInfo.OS.Major); - } - - - private static void DeleteOldEvents(object state) + private void DeleteOldEvents(object state) { try { @@ -258,9 +280,9 @@ namespace ASC.MessagingSystem.DbSender } } - private static void GetOldEvents(string table, string settings) + private void GetOldEvents(string table, string settings) { - var sqlQueryLimit = string.Format("(IFNULL((SELECT JSON_EXTRACT(`Data`, '$.{0}') from webstudio_settings where tt.id = TenantID and id='{1}'), {2})) as tout", settings, TenantAuditSettings.LoadForDefaultTenant().ID, TenantAuditSettings.MaxLifeTime); + var sqlQueryLimit = string.Format("(IFNULL((SELECT JSON_EXTRACT(`Data`, '$.{0}') from webstudio_settings where tt.id = TenantID and id='{1}'), {2})) as tout", settings, new TenantAuditSettings().ID, TenantAuditSettings.MaxLifeTime); var query = new SqlQuery(table + " t1") .Select("t1.id") .Select(sqlQueryLimit) diff --git a/module/ASC.MessagingSystem/EventMessage.cs b/module/ASC.MessagingSystem/EventMessage.cs index c6942978f..4cd65140c 100644 --- a/module/ASC.MessagingSystem/EventMessage.cs +++ b/module/ASC.MessagingSystem/EventMessage.cs @@ -1,51 +1,53 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; - -namespace ASC.MessagingSystem -{ - public class EventMessage - { - public int Id { get; set; } - - public string IP { get; set; } - - public string Initiator { get; set; } - - public string Browser { get; set; } - - public string Platform { get; set; } - - public DateTime Date { get; set; } - - public int TenantId { get; set; } - - public Guid UserId { get; set; } - - public string Page { get; set; } - - public MessageAction Action { get; set; } - - public IList Description { get; set; } - - public MessageTarget Target { get; set; } - - public string UAHeader { get; set; } - } +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System; +using System.Collections.Generic; + +namespace ASC.MessagingSystem +{ + public class EventMessage + { + public int Id { get; set; } + + public string IP { get; set; } + + public string Initiator { get; set; } + + public string Browser { get; set; } + + public string Platform { get; set; } + + public DateTime Date { get; set; } + + public int TenantId { get; set; } + + public Guid UserId { get; set; } + + public string Page { get; set; } + + public MessageAction Action { get; set; } + + public IList Description { get; set; } + + public MessageTarget Target { get; set; } + + public string UAHeader { get; set; } + + public bool Active { get; set; } + } } \ No newline at end of file diff --git a/module/ASC.MessagingSystem/IMessageSender.cs b/module/ASC.MessagingSystem/IMessageSender.cs index 1f727e0e5..e5e4f8d64 100644 --- a/module/ASC.MessagingSystem/IMessageSender.cs +++ b/module/ASC.MessagingSystem/IMessageSender.cs @@ -19,6 +19,6 @@ namespace ASC.MessagingSystem { public interface IMessageSender { - void Send(EventMessage message); + int Send(EventMessage message); } } \ No newline at end of file diff --git a/module/ASC.MessagingSystem/MessageFactory.cs b/module/ASC.MessagingSystem/MessageFactory.cs index 2a87432dc..a4f4dd08f 100644 --- a/module/ASC.MessagingSystem/MessageFactory.cs +++ b/module/ASC.MessagingSystem/MessageFactory.cs @@ -27,28 +27,23 @@ namespace ASC.MessagingSystem static class MessageFactory { private static readonly ILog log = LogManager.GetLogger("ASC.Messaging"); - private const string userAgentHeader = "User-Agent"; - private const string forwardedHeader = "X-Forwarded-For"; - private const string hostHeader = "Host"; - private const string refererHeader = "Referer"; - - public static EventMessage Create(HttpRequest request, string initiator, MessageAction action, MessageTarget target, params string[] description) + public static EventMessage Create(HttpRequest request, string initiator, DateTime? dateTime, MessageAction action, MessageTarget target, params string[] description) { try { return new EventMessage { - IP = request != null ? request.Headers[forwardedHeader] ?? request.UserHostAddress : null, + IP = MessageSettings.GetIP(request), Initiator = initiator, - Date = DateTime.UtcNow, + Date = dateTime.HasValue ? dateTime.Value : DateTime.UtcNow, TenantId = CoreContext.TenantManager.GetCurrentTenant().TenantId, UserId = SecurityContext.CurrentAccount.ID, - Page = request != null && request.UrlReferrer != null ? request.UrlReferrer.ToString() : null, + Page = MessageSettings.GetUrlReferer(request), Action = action, Description = description, Target = target, - UAHeader = request != null ? request.Headers[userAgentHeader] : null + UAHeader = MessageSettings.GetUAHeader(request) }; } catch (Exception ex) @@ -74,12 +69,11 @@ namespace ASC.MessagingSystem if (headers != null) { - var userAgent = headers.ContainsKey(userAgentHeader) ? headers[userAgentHeader] : null; - var forwarded = headers.ContainsKey(forwardedHeader) ? headers[forwardedHeader] : null; - var host = headers.ContainsKey(hostHeader) ? headers[hostHeader] : null; - var referer = headers.ContainsKey(refererHeader) ? headers[refererHeader] : null; + var ip = MessageSettings.GetIP(headers); + var userAgent = MessageSettings.GetUAHeader(headers); + var referer = MessageSettings.GetReferer(headers); - message.IP = forwarded ?? host; + message.IP = ip; message.UAHeader = userAgent; message.Page = referer; } @@ -114,6 +108,37 @@ namespace ASC.MessagingSystem } } + public static EventMessage Create(HttpRequest request, MessageUserData userData, MessageAction action) + { + try + { + var message = new EventMessage + { + Date = DateTime.UtcNow, + TenantId = userData == null ? CoreContext.TenantManager.GetCurrentTenant().TenantId : userData.TenantId, + UserId = userData == null ? SecurityContext.CurrentAccount.ID : userData.UserId, + Action = action, + Active = true + }; + if (request != null) + { + var ip = MessageSettings.GetIP(request); + var userAgent = MessageSettings.GetUAHeader(request); + var referer = MessageSettings.GetReferer(request); + + message.IP = ip; + message.UAHeader = userAgent; + message.Page = referer; + } + + return message; + } + catch (Exception ex) + { + log.Error(string.Format("Error while parse Initiator Message for \"{0}\" type of event: {1}", action, ex)); + return null; + } + } } } \ No newline at end of file diff --git a/module/ASC.MessagingSystem/MessageInitiator.cs b/module/ASC.MessagingSystem/MessageInitiator.cs index b26f7647e..7b6fc3f59 100644 --- a/module/ASC.MessagingSystem/MessageInitiator.cs +++ b/module/ASC.MessagingSystem/MessageInitiator.cs @@ -22,5 +22,6 @@ namespace ASC.MessagingSystem System, DocsService, ThirdPartyProvider, + AutoCleanUp } } \ No newline at end of file diff --git a/module/ASC.MessagingSystem/MessageService.cs b/module/ASC.MessagingSystem/MessageService.cs index e621db4ec..597530330 100644 --- a/module/ASC.MessagingSystem/MessageService.cs +++ b/module/ASC.MessagingSystem/MessageService.cs @@ -15,12 +15,14 @@ */ +using System; using System.Collections.Generic; using System.Configuration; using System.Linq; using System.Web; using ASC.Common.Logging; +using ASC.Core; using ASC.MessagingSystem.DbSender; @@ -46,57 +48,57 @@ namespace ASC.MessagingSystem public static void Send(HttpRequest request, MessageAction action) { - SendRequestMessage(request, null, action, null); + SendRequestMessage(request, null, null, action, null); } public static void Send(HttpRequest request, MessageAction action, string d1) { - SendRequestMessage(request, null, action, null, d1); + SendRequestMessage(request, null, null, action, null, d1); } public static void Send(HttpRequest request, MessageAction action, string d1, string d2) { - SendRequestMessage(request, null, action, null, d1, d2); + SendRequestMessage(request, null, null, action, null, d1, d2); } public static void Send(HttpRequest request, MessageAction action, string d1, string d2, string d3) { - SendRequestMessage(request, null, action, null, d1, d2, d3); + SendRequestMessage(request, null, null, action, null, d1, d2, d3); } public static void Send(HttpRequest request, MessageAction action, string d1, string d2, string d3, string d4) { - SendRequestMessage(request, null, action, null, d1, d2, d3, d4); + SendRequestMessage(request, null, null, action, null, d1, d2, d3, d4); } public static void Send(HttpRequest request, MessageAction action, IEnumerable d1, string d2) { - SendRequestMessage(request, null, action, null, string.Join(", ", d1), d2); + SendRequestMessage(request, null, null, action, null, string.Join(", ", d1), d2); } public static void Send(HttpRequest request, MessageAction action, string d1, IEnumerable d2) { - SendRequestMessage(request, null, action, null, d1, string.Join(", ", d2)); + SendRequestMessage(request, null, null, action, null, d1, string.Join(", ", d2)); } public static void Send(HttpRequest request, MessageAction action, string d1, string d2, IEnumerable d3) { - SendRequestMessage(request, null, action, null, d1, d2, string.Join(", ", d3)); + SendRequestMessage(request, null, null, action, null, d1, d2, string.Join(", ", d3)); } public static void Send(HttpRequest request, MessageAction action, IEnumerable d1) { - SendRequestMessage(request, null, action, null, string.Join(", ", d1)); + SendRequestMessage(request, null, null, action, null, string.Join(", ", d1)); } public static void Send(HttpRequest request, string loginName, MessageAction action) { - SendRequestMessage(request, loginName, action, null); + SendRequestMessage(request, loginName, null, action, null); } public static void Send(HttpRequest request, string loginName, MessageAction action, string d1) { - SendRequestMessage(request, loginName, action, null, d1); + SendRequestMessage(request, loginName, null, action, null, d1); } #endregion @@ -105,62 +107,67 @@ namespace ASC.MessagingSystem public static void Send(HttpRequest request, MessageAction action, MessageTarget target) { - SendRequestMessage(request, null, action, target); + SendRequestMessage(request, null, null, action, target); + } + + public static void Send(HttpRequest request, DateTime? dateTime, MessageAction action, MessageTarget target, string d1) + { + SendRequestMessage(request, null, dateTime, action, target, d1); } public static void Send(HttpRequest request, MessageAction action, MessageTarget target, string d1) { - SendRequestMessage(request, null, action, target, d1); + SendRequestMessage(request, null, null, action, target, d1); } public static void Send(HttpRequest request, MessageAction action, MessageTarget target, string d1, string d2) { - SendRequestMessage(request, null, action, target, d1, d2); + SendRequestMessage(request, null, null, action, target, d1, d2); } public static void Send(HttpRequest request, MessageAction action, MessageTarget target, string d1, string d2, string d3) { - SendRequestMessage(request, null, action, target, d1, d2, d3); + SendRequestMessage(request, null, null, action, target, d1, d2, d3); } public static void Send(HttpRequest request, MessageAction action, MessageTarget target, string d1, string d2, string d3, string d4) { - SendRequestMessage(request, null, action, target, d1, d2, d3, d4); + SendRequestMessage(request, null, null, action, target, d1, d2, d3, d4); } public static void Send(HttpRequest request, MessageAction action, MessageTarget target, IEnumerable d1, string d2) { - SendRequestMessage(request, null, action, target, string.Join(", ", d1), d2); + SendRequestMessage(request, null,null, action, target, string.Join(", ", d1), d2); } public static void Send(HttpRequest request, MessageAction action, MessageTarget target, string d1, IEnumerable d2) { - SendRequestMessage(request, null, action, target, d1, string.Join(", ", d2)); + SendRequestMessage(request, null, null, action, target, d1, string.Join(", ", d2)); } public static void Send(HttpRequest request, MessageAction action, MessageTarget target, string d1, string d2, IEnumerable d3) { - SendRequestMessage(request, null, action, target, d1, d2, string.Join(", ", d3)); + SendRequestMessage(request, null, null, action, target, d1, d2, string.Join(", ", d3)); } public static void Send(HttpRequest request, MessageAction action, MessageTarget target, IEnumerable d1) { - SendRequestMessage(request, null, action, target, string.Join(", ", d1)); + SendRequestMessage(request, null, null, action, target, string.Join(", ", d1)); } public static void Send(HttpRequest request, string loginName, MessageAction action, MessageTarget target) { - SendRequestMessage(request, loginName, action, target); + SendRequestMessage(request, loginName, null, action, target); } public static void Send(HttpRequest request, string loginName, MessageAction action, MessageTarget target, string d1) { - SendRequestMessage(request, loginName, action, target, d1); + SendRequestMessage(request, loginName, null, action, target, d1); } #endregion - private static void SendRequestMessage(HttpRequest request, string loginName, MessageAction action, MessageTarget target, params string[] description) + private static void SendRequestMessage(HttpRequest request, string loginName, DateTime? dateTime, MessageAction action, MessageTarget target, params string[] description) { if (sender == null) return; @@ -170,7 +177,7 @@ namespace ASC.MessagingSystem return; } - var message = MessageFactory.Create(request, loginName, action, target, description); + var message = MessageFactory.Create(request, loginName, dateTime, action, target, description); if (!MessagePolicy.Check(message)) return; sender.Send(message); @@ -266,10 +273,21 @@ namespace ASC.MessagingSystem { if (sender == null) return; - var message = MessageFactory.Create(request, initiator, action, target, description); + var message = MessageFactory.Create(request, initiator, null, action, target, description); if (!MessagePolicy.Check(message)) return; sender.Send(message); } + + public static int SendLoginMessage(HttpRequest request, MessageUserData userData, MessageAction action) + { + if (sender == null) return 0; + + var message = MessageFactory.Create(request, userData, action); + if (!MessagePolicy.Check(message)) return 0; + + var id = sender.Send(message); + return id; + } } } \ No newline at end of file diff --git a/module/ASC.MessagingSystem/MessageSettings.cs b/module/ASC.MessagingSystem/MessageSettings.cs new file mode 100644 index 000000000..449d01d9a --- /dev/null +++ b/module/ASC.MessagingSystem/MessageSettings.cs @@ -0,0 +1,127 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System.Collections.Generic; +using System.Web; + +using UAParser; + +namespace ASC.MessagingSystem +{ + public class MessageSettings + { + private const string userAgentHeader = "User-Agent"; + private const string forwardedHeader = "X-Forwarded-For"; + private const string hostHeader = "Host"; + private const string refererHeader = "Referer"; + + static MessageSettings() + { + Parser = Parser.GetDefault(); + } + + private static Parser Parser { get; set; } + + public static ClientInfo GetClientInfo(string uaHeader) + { + return Parser.Parse(uaHeader); + } + + public static string GetUAHeader(HttpRequest request) + { + return request != null ? request.Headers[userAgentHeader] : null; + } + + public static string GetUAHeader(Dictionary headers) + { + return headers.ContainsKey(userAgentHeader) ? headers[userAgentHeader] : null; + } + + public static string GetReferer(HttpRequest request) + { + return request != null ? request.Headers[refererHeader] : null; + } + + public static string GetReferer(Dictionary headers) + { + return headers.ContainsKey(refererHeader) ? headers[refererHeader] : null; + } + + public static string GetUrlReferer(HttpRequest request) + { + return request != null && request.UrlReferrer != null ? request.UrlReferrer.ToString() : null; + } + + public static string GetIP(Dictionary headers) + { + var forwarded = headers.ContainsKey(forwardedHeader) ? headers[forwardedHeader] : null; + var host = headers.ContainsKey(hostHeader) ? headers[hostHeader] : null; + return forwarded ?? host; + } + + public static string GetIP(HttpRequest request) + { + if (request != null) + { + string str = request.Headers[forwardedHeader] ?? request.UserHostAddress; + return str.Substring(0, str.IndexOf(':') != -1 ? str.IndexOf(':') : str.Length); + } + return null; + } + + public static void AddInfoMessage(EventMessage message, Dictionary dict = null) + { + ClientInfo clientInfo; + if (dict != null) + { + if (!dict.TryGetValue(message.UAHeader, out clientInfo)) + { + clientInfo = GetClientInfo(message.UAHeader); + dict.Add(message.UAHeader, clientInfo); + } + } + else + { + clientInfo = GetClientInfo(message.UAHeader); + } + if (clientInfo != null) + { + message.Browser = GetBrowser(clientInfo); + message.Platform = GetPlatformAndDevice(clientInfo); + } + } + + public static string GetBrowser(ClientInfo clientInfo) + { + return clientInfo == null + ? null + : string.Format("{0} {1}", clientInfo.UA.Family, clientInfo.UA.Major).Trim(); + } + + public static string GetPlatformAndDevice(ClientInfo clientInfo) + { + return clientInfo == null + ? null + : string.Format("{0} {1} {2} {3}", + clientInfo.OS.Family, + clientInfo.OS.Major, + clientInfo.Device.Brand, + clientInfo.Device.Model) + .Trim(); + } + } +} diff --git a/module/ASC.MessagingSystem/MessageTarget.cs b/module/ASC.MessagingSystem/MessageTarget.cs index 7a61dfcc0..a63ae28cc 100644 --- a/module/ASC.MessagingSystem/MessageTarget.cs +++ b/module/ASC.MessagingSystem/MessageTarget.cs @@ -1,4 +1,4 @@ -/* +/* * * (c) Copyright Ascensio System Limited 2010-2021 * @@ -15,12 +15,9 @@ */ -using System; using System.Collections.Generic; using System.Linq; -using ASC.Common.Logging; - namespace ASC.MessagingSystem { public class MessageTarget @@ -29,33 +26,34 @@ namespace ASC.MessagingSystem public static MessageTarget Create(T value) { - try + var res = new List(1); + if (value != null) { - var res = new List(); - var ids = value as System.Collections.IEnumerable; - - if (ids != null) - { - res.AddRange(from object id in ids select id.ToString()); - } - else - { - res.Add(value.ToString()); - } - - return new MessageTarget - { - _items = res.Distinct() - }; - } - catch (Exception e) - { - LogManager.GetLogger("ASC.Messaging").Error("EventMessageTarget exception", e); - return null; + res.Add(value.ToString()); } - } + return new MessageTarget + { + _items = res + }; + } + public static MessageTarget Create(IEnumerable value) + { + var res = new MessageTarget + { + _items = new List() + }; + + if (value != null) + { + res._items = value.Select(r => r.ToString()).ToList(); + } + + return res; + } + + public static MessageTarget Parse(string value) { if (string.IsNullOrEmpty(value)) return null; @@ -69,6 +67,8 @@ namespace ASC.MessagingSystem _items = items }; } + + public IEnumerable GetItems() { return _items.ToList(); } public override string ToString() { diff --git a/module/ASC.MessagingSystem/MessageUserData.cs b/module/ASC.MessagingSystem/MessageUserData.cs index 9ee8b685f..ba79f9661 100644 --- a/module/ASC.MessagingSystem/MessageUserData.cs +++ b/module/ASC.MessagingSystem/MessageUserData.cs @@ -1,33 +1,33 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; - -namespace ASC.MessagingSystem -{ - public class MessageUserData - { - public int TenantId { get; private set; } - public Guid UserId { get; private set; } - - public MessageUserData(int tenentId, Guid userId) - { - TenantId = tenentId; - UserId = userId; - } - } +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System; + +namespace ASC.MessagingSystem +{ + public class MessageUserData + { + public int TenantId { get; private set; } + public Guid UserId { get; private set; } + + public MessageUserData(int tenentId, Guid userId) + { + TenantId = tenentId; + UserId = userId; + } + } } \ No newline at end of file diff --git a/module/ASC.Migration/ASC.Migration.csproj b/module/ASC.Migration/ASC.Migration.csproj new file mode 100644 index 000000000..5cd9c1c56 --- /dev/null +++ b/module/ASC.Migration/ASC.Migration.csproj @@ -0,0 +1,166 @@ + + + + + Debug + AnyCPU + {72D10CCA-CCF1-4E2F-A17D-443917B154CB} + Library + ..\..\web\studio\ASC.Web.Studio\bin\ + ASC.Migration + ASC.Migration + v4.8 + 512 + true + true + + + AnyCPU + true + full + false + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + MigrationResource.resx + + + + + + + + {76DE7717-3D4B-4A5B-B740-15B8913DF0CB} + ASC.Common + + + {A51D0454-4AFA-46DE-89D4-B03D37E1816C} + ASC.Core.Common + + + {8c534af7-5696-4e68-9ff4-ffc311893c10} + ASC.Web.Files + + + {5f7dc7bd-d831-449a-908d-5a419e4dfe71} + ASC.Api.Calendar + + + {49F07FFF-98A5-47D2-A9E9-A46B98C41245} + ASC.Api.Core + + + {7080343D-FBE7-423A-9405-4F883F6E235B} + ASC.Mail + + + + + 2.2.0 + + + 1.6.0.1 + + + + + ResXFileCodeGenerator + MigrationResource.Designer.cs + + + + \ No newline at end of file diff --git a/module/ASC.Migration/App.config b/module/ASC.Migration/App.config new file mode 100644 index 000000000..193aecc67 --- /dev/null +++ b/module/ASC.Migration/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/module/ASC.Migration/Core/AbstractMigration.cs b/module/ASC.Migration/Core/AbstractMigration.cs new file mode 100644 index 000000000..9e938e36b --- /dev/null +++ b/module/ASC.Migration/Core/AbstractMigration.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +using ASC.Migration.Core.Models; +using ASC.Migration.Core.Models.Api; + +namespace ASC.Migration.Core +{ + public abstract class AbstractMigration : IMigration + where TMigrationInfo : IMigrationInfo + { + private MigrationLogger logger; + protected CancellationToken cancellationToken; + protected TMigrationInfo migrationInfo; + private double lastProgressUpdate; + private string lastStatusUpdate; + protected List importedUsers; + + public event Action OnProgressUpdate; + + public AbstractMigration() + { + logger = new MigrationLogger(); + } + + protected void ReportProgress(double value, string status) + { + lastProgressUpdate = value; + lastStatusUpdate = status; + OnProgressUpdate?.Invoke(value, status); + logger.Log($"{value:0.00} progress: {status}"); + } + + public double GetProgress() => lastProgressUpdate; + public string GetProgressStatus() => lastStatusUpdate; + + public abstract void Init(string path, CancellationToken cancellationToken); + + public abstract Task Parse(); + + public abstract Task Migrate(MigrationApiInfo migrationInfo); + + public void Log(string msg, Exception exception = null) + { + logger.Log(msg, exception); + } + public virtual void Dispose() + { + logger.Dispose(); + } + + public Stream GetLogs() + { + return logger.GetStream(); + } + + public virtual List GetGuidImportedUsers() + { + return importedUsers; + } + } +} diff --git a/module/ASC.Migration/Core/IMigration.cs b/module/ASC.Migration/Core/IMigration.cs new file mode 100644 index 000000000..4380f66e4 --- /dev/null +++ b/module/ASC.Migration/Core/IMigration.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +using ASC.Migration.Core.Models.Api; + +namespace ASC.Migration.Core +{ + public interface IMigration : IDisposable + { + event Action OnProgressUpdate; + + double GetProgress(); + string GetProgressStatus(); + + + void Init(string path, CancellationToken token); + + Task Parse(); + + Task Migrate(MigrationApiInfo migrationInfo); + + Stream GetLogs(); + + List GetGuidImportedUsers(); + } +} diff --git a/module/ASC.Migration/Core/ImportableEntity.cs b/module/ASC.Migration/Core/ImportableEntity.cs new file mode 100644 index 000000000..14ccb4f72 --- /dev/null +++ b/module/ASC.Migration/Core/ImportableEntity.cs @@ -0,0 +1,20 @@ +using System; + +namespace ASC.Migration.Core +{ + public abstract class ImportableEntity + { + public bool ShouldImport { get; set; } = true; + + public abstract void Parse(); + + public abstract void Migrate(); + + protected Action Log { get; set; } + + public ImportableEntity(Action log) + { + Log = log; + } + } +} diff --git a/module/ASC.Migration/Core/MigrationLogger.cs b/module/ASC.Migration/Core/MigrationLogger.cs new file mode 100644 index 000000000..04334017d --- /dev/null +++ b/module/ASC.Migration/Core/MigrationLogger.cs @@ -0,0 +1,61 @@ +using System; +using System.IO; + +using ASC.Common.Logging; + +namespace ASC.Migration.Core +{ + public class MigrationLogger + { + private ILog logger; + private string migrationLogPath; + private Stream migration; + private StreamWriter migrationLog; + + public MigrationLogger() + { + migrationLogPath = TempPath.GetTempFileName(); + migration = new FileStream(migrationLogPath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read, 4096, FileOptions.DeleteOnClose); + migrationLog = new StreamWriter(migration); + logger = LogManager.GetLogger("ASC.Migration"); + } + + public void Log(string msg, Exception exception = null) + { + try + { + if (exception != null) + { + logger.Warn(msg, exception); + } + else + { + logger.Info(msg); + } + migrationLog.WriteLine($"{DateTime.Now.ToString("s")}: {msg}"); + if (exception != null) + { + migrationLog.WriteLine($"{exception.Message}"); + } + migrationLog.Flush(); + } + catch { } + } + + public void Dispose() + { + try + { + migrationLog.Dispose(); + File.Delete(migrationLogPath); + } + catch { } + } + + public Stream GetStream() + { + migration.Position = 0; + return migration; + } + } +} diff --git a/module/ASC.Migration/Core/Models/Api/ApiMigratorAttribute.cs b/module/ASC.Migration/Core/Models/Api/ApiMigratorAttribute.cs new file mode 100644 index 000000000..34e1aabaa --- /dev/null +++ b/module/ASC.Migration/Core/Models/Api/ApiMigratorAttribute.cs @@ -0,0 +1,24 @@ +using System; + +namespace ASC.Migration.Core.Models.Api +{ + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public class ApiMigratorAttribute : Attribute + { + public string Name { get; private set; } + public bool RequiresFolder { get; private set; } + + public string[] RequiredFileTypes { get; private set; } + + public ApiMigratorAttribute(string name, string[] fileTypes) + { + Name = name; + RequiredFileTypes = fileTypes; + } + + public ApiMigratorAttribute(string name) + { + Name = name; + } + } +} diff --git a/module/ASC.Migration/Core/Models/Api/ImportableApiEntity.cs b/module/ASC.Migration/Core/Models/Api/ImportableApiEntity.cs new file mode 100644 index 000000000..af1444505 --- /dev/null +++ b/module/ASC.Migration/Core/Models/Api/ImportableApiEntity.cs @@ -0,0 +1,13 @@ +using System.Runtime.Serialization; + +using Newtonsoft.Json; + +namespace ASC.Migration.Core.Models.Api +{ + public abstract class ImportableApiEntity + { + [JsonRequired] + [DataMember(Name = "shouldImport", IsRequired = true)] + public bool ShouldImport { get; set; } = true; + } +} diff --git a/module/ASC.Migration/Core/Models/Api/MigratingApiCalendar.cs b/module/ASC.Migration/Core/Models/Api/MigratingApiCalendar.cs new file mode 100644 index 000000000..41a3d5fdf --- /dev/null +++ b/module/ASC.Migration/Core/Models/Api/MigratingApiCalendar.cs @@ -0,0 +1,18 @@ +using System.Runtime.Serialization; + +using Newtonsoft.Json; + +namespace ASC.Migration.Core.Models.Api +{ + [DataContract(Name = "migratingApiCalendar", Namespace = "")] + public class MigratingApiCalendar : ImportableApiEntity + { + [DataMember(Name = "calendarsCount")] + public int CalendarsCount { get; set; } + [DataMember(Name = "eventsCount")] + public int EventsCount { get; set; } + [JsonRequired] + [DataMember(Name = "moduleName", IsRequired = true)] + public string ModuleName { get; set; } + } +} diff --git a/module/ASC.Migration/Core/Models/Api/MigratingApiContacts.cs b/module/ASC.Migration/Core/Models/Api/MigratingApiContacts.cs new file mode 100644 index 000000000..45d49232b --- /dev/null +++ b/module/ASC.Migration/Core/Models/Api/MigratingApiContacts.cs @@ -0,0 +1,16 @@ +using System.Runtime.Serialization; + +using Newtonsoft.Json; + +namespace ASC.Migration.Core.Models.Api +{ + [DataContract(Name = "migratingApiContacts", Namespace = "")] + public class MigratingApiContacts : ImportableApiEntity + { + [DataMember(Name = "contactsCount")] + public int ContactsCount { get; set; } + [JsonRequired] + [DataMember(Name = "moduleName", IsRequired = true)] + public string ModuleName { get; set; } + } +} diff --git a/module/ASC.Migration/Core/Models/Api/MigratingApiFiles.cs b/module/ASC.Migration/Core/Models/Api/MigratingApiFiles.cs new file mode 100644 index 000000000..54a1aeae7 --- /dev/null +++ b/module/ASC.Migration/Core/Models/Api/MigratingApiFiles.cs @@ -0,0 +1,20 @@ +using System.Runtime.Serialization; + +using Newtonsoft.Json; + +namespace ASC.Migration.Core.Models.Api +{ + [DataContract(Name = "migratingApiFiles", Namespace = "")] + public class MigratingApiFiles : ImportableApiEntity + { + [DataMember(Name = "foldersCount")] + public int FoldersCount { get; set; } + [DataMember(Name = "filesCount")] + public int FilesCount { get; set; } + [DataMember(Name = "bytesTotal")] + public long BytesTotal { get; set; } + [JsonRequired] + [DataMember(Name = "moduleName", IsRequired = true)] + public string ModuleName { get; set; } + } +} diff --git a/module/ASC.Migration/Core/Models/Api/MigratingApiGroup.cs b/module/ASC.Migration/Core/Models/Api/MigratingApiGroup.cs new file mode 100644 index 000000000..2efa0eb2f --- /dev/null +++ b/module/ASC.Migration/Core/Models/Api/MigratingApiGroup.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; + +using Newtonsoft.Json; + +namespace ASC.Migration.Core.Models.Api +{ + [DataContract(Name = "migratingApiGroup", Namespace = "")] + public class MigratingApiGroup : ImportableApiEntity + { + [DataMember(Name = "groupName")] + public string GroupName { get; set; } = default; + [JsonRequired] + [DataMember(Name = "moduleName", IsRequired = true)] + public string ModuleName { get; set; } + [DataMember(Name ="userUidList")] + public List UserUidList { get; set; } + } +} diff --git a/module/ASC.Migration/Core/Models/Api/MigratingApiMail.cs b/module/ASC.Migration/Core/Models/Api/MigratingApiMail.cs new file mode 100644 index 000000000..4a2146d3e --- /dev/null +++ b/module/ASC.Migration/Core/Models/Api/MigratingApiMail.cs @@ -0,0 +1,18 @@ +using System.Runtime.Serialization; + +using Newtonsoft.Json; + +namespace ASC.Migration.Core.Models.Api +{ + [DataContract(Name = "migratingApiMail", Namespace = "")] + public class MigratingApiMail : ImportableApiEntity + { + // mail count? + [DataMember(Name = "messagesCount")] + public int MessagesCount { get; set; } = default; + [JsonRequired] + [DataMember(Name = "moduleName", IsRequired = true)] + public string ModuleName { get; set; } + } +} + diff --git a/module/ASC.Migration/Core/Models/Api/MigratingApiUser.cs b/module/ASC.Migration/Core/Models/Api/MigratingApiUser.cs new file mode 100644 index 000000000..5ded2703c --- /dev/null +++ b/module/ASC.Migration/Core/Models/Api/MigratingApiUser.cs @@ -0,0 +1,35 @@ +using System.Runtime.Serialization; + +using Newtonsoft.Json; + +namespace ASC.Migration.Core.Models.Api +{ + [DataContract(Name = "migratingApiUser", Namespace = "")] + public class MigratingApiUser : ImportableApiEntity + { + [JsonRequired] + [DataMember(Name = "key", IsRequired = true)] + public string Key { get; set; } + + [DataMember(Name = "email")] + public string Email { get; set; } + [JsonRequired] + [DataMember(Name = "displayName", IsRequired = true)] + public string DisplayName { get; set; } + [JsonRequired] + [DataMember(Name = "moduleName", IsRequired = true)] + public string ModuleName { get; set; } + + [DataMember(Name = "migratingContacts")] + public MigratingApiContacts MigratingContacts { get; set; } = default; + + [DataMember(Name = "migratingCalendar")] + public MigratingApiCalendar MigratingCalendar { get; set; } = default; + + [DataMember(Name = "migratingFiles")] + public MigratingApiFiles MigratingFiles { get; set; } = default; + + [DataMember(Name = "migratingMail")] + public MigratingApiMail MigratingMail { get; set; } = default; + } +} diff --git a/module/ASC.Migration/Core/Models/Api/MigrationApiInfo.cs b/module/ASC.Migration/Core/Models/Api/MigrationApiInfo.cs new file mode 100644 index 000000000..fa7dca418 --- /dev/null +++ b/module/ASC.Migration/Core/Models/Api/MigrationApiInfo.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace ASC.Migration.Core.Models.Api +{ + [DataContract(Name = "migrationApiInfo", Namespace = "")] + public class MigrationApiInfo + { + [DataMember(Name = "users")] + public List Users { get; set; } = new List(); + + [DataMember(Name = "migratorName")] + public string MigratorName { get; set; } + + [DataMember(Name = "modules")] + public List Modules { get; set; } = new List(); + + [DataMember(Name = "failedArchives")] + public List FailedArchives { get; set; } = new List(); + [DataMember(Name = "groups")] + public List Groups { get; set; } = new List(); + } +} diff --git a/module/ASC.Migration/Core/Models/Api/MigrationCore.cs b/module/ASC.Migration/Core/Models/Api/MigrationCore.cs new file mode 100644 index 000000000..6d300060c --- /dev/null +++ b/module/ASC.Migration/Core/Models/Api/MigrationCore.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace ASC.Migration.Core.Models.Api +{ + public static class MigrationCore + { + + private static Dictionary _migrators = null; + private static Dictionary Migrators + { + get + { + if (_migrators != null) return _migrators; + + _migrators = new Dictionary(StringComparer.OrdinalIgnoreCase); + + var migratorTypes = Assembly.GetExecutingAssembly() + .GetExportedTypes() + .Where(t => !t.IsAbstract && !t.IsInterface + && typeof(IMigration).IsAssignableFrom(t)); + + foreach (var type in migratorTypes) + { + var attr = type.GetCustomAttribute(); + if (attr == null) continue; + + _migrators.Add(attr.Name, new MigratorMeta(type)); + } + + return _migrators; + } + } + + public static string[] GetAvailableMigrations() => Migrators.Keys.ToArray(); + + public static MigratorMeta GetMigrator(string migrator) => Migrators.TryGetValue(migrator, out var meta) ? meta : null; + } +} diff --git a/module/ASC.Migration/Core/Models/Api/MigrationLogApiContentResponce.cs b/module/ASC.Migration/Core/Models/Api/MigrationLogApiContentResponce.cs new file mode 100644 index 000000000..57214b05c --- /dev/null +++ b/module/ASC.Migration/Core/Models/Api/MigrationLogApiContentResponce.cs @@ -0,0 +1,41 @@ +using System.IO; +using System.Net.Mime; +using System.Text; + +using ASC.Api.Interfaces.ResponseTypes; + +namespace ASC.Migration.Core.Models.Api +{ + public class MigrationLogApiContentResponce : IApiContentResponce + { + private readonly Stream _stream; + private readonly string _fileName; + + + public MigrationLogApiContentResponce(Stream stream, string fileName) + { + _stream = stream; + _fileName = fileName; + } + + public Encoding ContentEncoding + { + get { return Encoding.UTF8; } + } + + public Stream ContentStream + { + get { return _stream; } + } + + public ContentType ContentType + { + get { return new ContentType("text/plain; charset=UTF-8"); } + } + + public ContentDisposition ContentDisposition + { + get { return new ContentDisposition { Inline = false, FileName = _fileName }; } + } + } +} diff --git a/module/ASC.Migration/Core/Models/Api/MigratorMeta.cs b/module/ASC.Migration/Core/Models/Api/MigratorMeta.cs new file mode 100644 index 000000000..e58185325 --- /dev/null +++ b/module/ASC.Migration/Core/Models/Api/MigratorMeta.cs @@ -0,0 +1,17 @@ +using System; +using System.Reflection; + +namespace ASC.Migration.Core.Models.Api +{ + public class MigratorMeta + { + public Type MigratorType { get; private set; } + + public ApiMigratorAttribute MigratorInfo { get => MigratorType.GetCustomAttribute(); } + + public MigratorMeta(Type type) + { + MigratorType = type; + } + } +} diff --git a/module/ASC.Migration/Core/Models/IMigrationInfo.cs b/module/ASC.Migration/Core/Models/IMigrationInfo.cs new file mode 100644 index 000000000..313f0e532 --- /dev/null +++ b/module/ASC.Migration/Core/Models/IMigrationInfo.cs @@ -0,0 +1,11 @@ +using ASC.Migration.Core.Models.Api; + +namespace ASC.Migration.Core.Models +{ + public interface IMigrationInfo + { + MigrationApiInfo ToApiInfo(); + + void Merge(MigrationApiInfo apiInfo); + } +} diff --git a/module/ASC.Migration/Core/Models/MigratingCalendar.cs b/module/ASC.Migration/Core/Models/MigratingCalendar.cs new file mode 100644 index 000000000..65c47e08c --- /dev/null +++ b/module/ASC.Migration/Core/Models/MigratingCalendar.cs @@ -0,0 +1,25 @@ +using System; + +using ASC.Migration.Core.Models.Api; + +namespace ASC.Migration.Core.Models +{ + public abstract class MigratingCalendar : ImportableEntity + { + public abstract int CalendarsCount { get; } + public abstract int EventsCount { get; } + public abstract string ModuleName { get; } + + public virtual MigratingApiCalendar ToApiInfo() + { + return new MigratingApiCalendar() + { + CalendarsCount = CalendarsCount, + ModuleName = ModuleName, + EventsCount = EventsCount + }; + } + + protected MigratingCalendar(Action log) : base(log) { } + } +} diff --git a/module/ASC.Migration/Core/Models/MigratingContacts.cs b/module/ASC.Migration/Core/Models/MigratingContacts.cs new file mode 100644 index 000000000..fee55ba15 --- /dev/null +++ b/module/ASC.Migration/Core/Models/MigratingContacts.cs @@ -0,0 +1,23 @@ +using System; + +using ASC.Migration.Core.Models.Api; + +namespace ASC.Migration.Core.Models +{ + public abstract class MigratingContacts : ImportableEntity + { + public abstract int ContactsCount { get; } + public abstract string ModuleName { get; } + + public virtual MigratingApiContacts ToApiInfo() + { + return new MigratingApiContacts() + { + ContactsCount = ContactsCount, + ModuleName = ModuleName + }; + } + + protected MigratingContacts(Action log) : base(log) { } + } +} diff --git a/module/ASC.Migration/Core/Models/MigratingFiles.cs b/module/ASC.Migration/Core/Models/MigratingFiles.cs new file mode 100644 index 000000000..d5cd835c2 --- /dev/null +++ b/module/ASC.Migration/Core/Models/MigratingFiles.cs @@ -0,0 +1,27 @@ +using System; + +using ASC.Migration.Core.Models.Api; + +namespace ASC.Migration.Core.Models +{ + public abstract class MigratingFiles : ImportableEntity + { + public abstract int FoldersCount { get; } + public abstract int FilesCount { get; } + public abstract long BytesTotal { get; } + public abstract string ModuleName { get; } + + public virtual MigratingApiFiles ToApiInfo() + { + return new MigratingApiFiles() + { + BytesTotal = BytesTotal, + FilesCount = FilesCount, + FoldersCount = FoldersCount, + ModuleName = ModuleName + }; + } + + protected MigratingFiles(Action log) : base(log) { } + } +} diff --git a/module/ASC.Migration/Core/Models/MigratingGroup.cs b/module/ASC.Migration/Core/Models/MigratingGroup.cs new file mode 100644 index 000000000..f2cb4f6ae --- /dev/null +++ b/module/ASC.Migration/Core/Models/MigratingGroup.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using ASC.Migration.Core.Models.Api; + +namespace ASC.Migration.Core.Models +{ + public abstract class MigratingGroup : ImportableEntity + { + public abstract string GroupName { get; } + public abstract string ModuleName { get; } + public abstract List UserUidList { get; } + public virtual MigratingApiGroup ToApiInfo() + { + return new MigratingApiGroup() + { + GroupName = GroupName, + ModuleName = ModuleName, + UserUidList = UserUidList + }; + } + + protected MigratingGroup(Action log) : base(log) { } + } +} diff --git a/module/ASC.Migration/Core/Models/MigratingMail.cs b/module/ASC.Migration/Core/Models/MigratingMail.cs new file mode 100644 index 000000000..42cc1db07 --- /dev/null +++ b/module/ASC.Migration/Core/Models/MigratingMail.cs @@ -0,0 +1,22 @@ +using System; + +using ASC.Migration.Core.Models.Api; + +namespace ASC.Migration.Core.Models +{ + public abstract class MigratingMail : ImportableEntity + { + public abstract int MessagesCount { get; } + public abstract string ModuleName { get; } + public virtual MigratingApiMail ToApiInfo() + { + return new MigratingApiMail() + { + MessagesCount = MessagesCount, + ModuleName = ModuleName + }; + } + + protected MigratingMail(Action log) : base(log) { } + } +} diff --git a/module/ASC.Migration/Core/Models/MigratingUser.cs b/module/ASC.Migration/Core/Models/MigratingUser.cs new file mode 100644 index 000000000..776d063f9 --- /dev/null +++ b/module/ASC.Migration/Core/Models/MigratingUser.cs @@ -0,0 +1,44 @@ +using System; + +using ASC.Migration.Core.Models.Api; + +namespace ASC.Migration.Core.Models +{ + public abstract class MigratingUser : ImportableEntity + where TContacts : MigratingContacts + where TCalendar : MigratingCalendar + where TFiles : MigratingFiles + where TMail : MigratingMail + { + public string Key { get; set; } + + public abstract string Email { get; } + public abstract string DisplayName { get; } + public abstract string ModuleName { get; } + + public TContacts MigratingContacts { get; set; } = default; + + public TCalendar MigratingCalendar { get; set; } = default; + + public TFiles MigratingFiles { get; set; } = default; + + public TMail MigratingMail { get; set; } = default; + + public virtual MigratingApiUser ToApiInfo() + { + return new MigratingApiUser() + { + Key = Key, + Email = Email, + DisplayName = DisplayName, + ModuleName = ModuleName, + MigratingCalendar = MigratingCalendar.ToApiInfo(), + MigratingContacts = MigratingContacts.ToApiInfo(), + MigratingFiles = MigratingFiles.ToApiInfo(), + MigratingMail = MigratingMail.ToApiInfo() + }; + } + + protected MigratingUser(Action log) : base(log) { } + } +} diff --git a/module/ASC.Migration/Core/Models/MigrationInfo.cs b/module/ASC.Migration/Core/Models/MigrationInfo.cs new file mode 100644 index 000000000..8add68122 --- /dev/null +++ b/module/ASC.Migration/Core/Models/MigrationInfo.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using System.Linq; + +using ASC.Migration.Core.Models.Api; + +namespace ASC.Migration.Core.Models +{ + public abstract class MigrationInfo : IMigrationInfo + where TUser : MigratingUser + where TContacts : MigratingContacts + where TCalendar : MigratingCalendar + where TFiles : MigratingFiles + where TMail : MigratingMail + where TGroup: MigratingGroup + { + public Dictionary Users = new Dictionary(); + public string MigratorName { get; set; } + public List Modules = new List(); + public List failedArchives = new List(); + public List Groups = new List(); + + public virtual MigrationApiInfo ToApiInfo() + { + return new MigrationApiInfo() + { + Users = Users.Values.Select(u => u.ToApiInfo()).ToList(), + MigratorName = MigratorName, + Modules = Modules, + FailedArchives = failedArchives, + Groups = Groups.Select(g => g.ToApiInfo()).ToList() + }; + } + + public virtual void Merge(MigrationApiInfo apiInfo) + { + foreach(var apiUser in apiInfo.Users) + { + if (!Users.ContainsKey(apiUser.Key)) continue; + + var user = Users[apiUser.Key]; + user.ShouldImport = apiUser.ShouldImport; + + user.MigratingCalendar.ShouldImport = apiUser.MigratingCalendar.ShouldImport; + user.MigratingContacts.ShouldImport = apiUser.MigratingContacts.ShouldImport; + user.MigratingFiles.ShouldImport = apiUser.MigratingFiles.ShouldImport; + user.MigratingMail.ShouldImport = apiUser.MigratingMail.ShouldImport; + } + foreach(var apiGroup in apiInfo.Groups) + { + if (!Groups.Exists(g => apiGroup.GroupName == g.GroupName)) continue; + var group = Groups.Find(g => apiGroup.GroupName == g.GroupName); + group.ShouldImport = apiGroup.ShouldImport; + } + } + } +} diff --git a/module/ASC.Migration/Core/Models/MigrationModules.cs b/module/ASC.Migration/Core/Models/MigrationModules.cs new file mode 100644 index 000000000..04baaca3f --- /dev/null +++ b/module/ASC.Migration/Core/Models/MigrationModules.cs @@ -0,0 +1,19 @@ +using System.Runtime.Serialization; + +namespace ASC.Migration.Core.Models +{ + [DataContract(Name = "migrationModules", Namespace = "")] + public class MigrationModules + { + public MigrationModules(){ } + public MigrationModules(string migrationModule, string module) + { + MigrationModule = migrationModule; + Module = module; + } + [DataMember(Name = "migrationModule")] + public string MigrationModule { get; set; } + [DataMember(Name = "module")] + public string Module { get; set; } + } +} diff --git a/module/ASC.Migration/GoogleWorkspace/GoogleWorkspaceMigration.cs b/module/ASC.Migration/GoogleWorkspace/GoogleWorkspaceMigration.cs new file mode 100644 index 000000000..5d384cb0b --- /dev/null +++ b/module/ASC.Migration/GoogleWorkspace/GoogleWorkspaceMigration.cs @@ -0,0 +1,234 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +using ASC.Core; +using ASC.Migration.Core; +using ASC.Migration.Core.Models; +using ASC.Migration.Core.Models.Api; +using ASC.Migration.GoogleWorkspace.Models; +using ASC.Migration.Resources; + +namespace ASC.Migration.GoogleWorkspace +{ + [ApiMigrator("GoogleWorkspace")] + public class GoogleWorkspaceMigration : AbstractMigration + { + private string[] takeouts; + + public override void Init(string path, CancellationToken cancellationToken) + { + this.cancellationToken = cancellationToken; + List tempTakeouts = new List(); + var files = Directory.GetFiles(path); + if (!files.Any() || !files.Any(f => f.EndsWith(".zip"))) + { + throw new Exception("Folder must not be empty and should contain .zip files."); + } + foreach(var item in files) + { + if(item.EndsWith(".zip")) + { + tempTakeouts.Add(item); + } + } + takeouts = tempTakeouts.ToArray(); + + migrationInfo = new GwsMigrationInfo(); + migrationInfo.MigratorName = this.GetType().CustomAttributes.First().ConstructorArguments.First().Value.ToString(); + } + + public override Task Parse() + { + ReportProgress(0, MigrationResource.StartOfDataProcessing); + + var progressStep = 100 / takeouts.Length; + var i = 1; + foreach (var takeout in takeouts) + { + if (cancellationToken.IsCancellationRequested) { ReportProgress(100, MigrationResource.MigrationCanceled); return null; } + ReportProgress(GetProgress() + progressStep, MigrationResource.DataProcessing + $" {takeout} ({i++}/{takeouts.Length})"); + var tmpFolder = Path.Combine(TempPath.GetTempPath(), Path.GetFileNameWithoutExtension(takeout)); + try + { + ZipFile.ExtractToDirectory(takeout, tmpFolder); + + var rootFolder = Path.Combine(tmpFolder, "Takeout"); + + if (!Directory.Exists(rootFolder)) + { + throw new Exception("Takeout zip does not contain root 'Takeout' folder."); + } + var directories = Directory.GetDirectories(rootFolder); + if (directories.Length == 1 && directories[0].Split(Path.DirectorySeparatorChar).Last() == "Groups") + { + var group = new GWSMigratingGroups(rootFolder, Log); + group.Parse(); + if(group.Module.MigrationModule != null) + { + migrationInfo.Groups.Add(group); + if (!migrationInfo.Modules.Exists(x => x.MigrationModule == group.Module.MigrationModule)) + { + migrationInfo.Modules.Add(new MigrationModules(group.Module.MigrationModule, group.Module.Module)); + } + } + } + else + { + var user = new GwsMigratingUser(takeout, rootFolder, Log); + user.Parse(); + foreach (var element in user.ModulesList) + { + if (!migrationInfo.Modules.Exists(x => x.MigrationModule == element.MigrationModule)) + { + migrationInfo.Modules.Add(new MigrationModules(element.MigrationModule, element.Module)); + } + } + migrationInfo.Users.Add(takeout, user); + } + } + catch (Exception ex) + { + migrationInfo.failedArchives.Add(Path.GetFileName(takeout)); + Log($"Couldn't parse user from {Path.GetFileNameWithoutExtension(takeout)} archive", ex); + } + finally + { + if (Directory.Exists(tmpFolder)) + { + Directory.Delete(tmpFolder, true); + } + } + } + ReportProgress(100, MigrationResource.DataProcessingCompleted); + return Task.FromResult(migrationInfo.ToApiInfo()); + } + + public override Task Migrate(MigrationApiInfo migrationApiInfo) + { + ReportProgress(0, MigrationResource.PreparingForMigration); + migrationInfo.Merge(migrationApiInfo); + + var usersForImport = migrationInfo.Users + .Where(u => u.Value.ShouldImport) + .Select(u => u.Value); + + importedUsers = new List(); + var failedUsers = new List(); + var usersCount = usersForImport.Count(); + var progressStep = 25 / usersCount; + // Add all users first + var i = 1; + foreach (var user in usersForImport) + { + if (cancellationToken.IsCancellationRequested) { ReportProgress(100, MigrationResource.MigrationCanceled); return null; } + ReportProgress(GetProgress() + progressStep, String.Format(MigrationResource.UserMigration, user.DisplayName, i++, usersCount)); + try + { + user.dataСhange(migrationApiInfo.Users.Find(element => element.Key == user.Key)); + user.Migrate(); + importedUsers.Add(user.Guid); + } + catch (Exception ex) + { + failedUsers.Add(user); + Log($"Couldn't migrate user {user.DisplayName} ({user.Email})", ex); + } + } + var groupsForImport = migrationInfo.Groups + .Where(g => g.ShouldImport) + .Select(g => g); + var groupsCount = groupsForImport.Count(); + if (groupsCount != 0) + { + progressStep = 25 / groupsForImport.Count(); + //Create all groups + i = 1; + foreach (var group in groupsForImport) + { + if (cancellationToken.IsCancellationRequested) { ReportProgress(100, MigrationResource.MigrationCanceled); return null; } + ReportProgress(GetProgress() + progressStep, String.Format(MigrationResource.GroupMigration, group.GroupName, i++, groupsCount)); + try + { + group.Migrate(); + } + catch (Exception ex) + { + Log($"Couldn't migrate group {group.GroupName} ", ex); + } + } + } + + // Add files, contacts and other stuff + i = 1; + foreach (var user in usersForImport) + { + if (cancellationToken.IsCancellationRequested) { ReportProgress(100, MigrationResource.MigrationCanceled); return null; } + if (failedUsers.Contains(user)) + { + ReportProgress(GetProgress() + progressStep, String.Format(MigrationResource.UserSkipped, user.DisplayName, i, usersCount)); + continue; + } + + var smallStep = progressStep / 4; + + try + { + user.MigratingContacts.Migrate(); + } + catch (Exception ex) + { + Log($"Couldn't migrate user {user.DisplayName} ({user.Email}) contacts", ex); + } + finally + { + ReportProgress(GetProgress() + smallStep, String.Format(MigrationResource.MigratingUserContacts, user.DisplayName, i, usersCount)); + } + + /*try + { + user.MigratingCalendar.Migrate(); + } + catch (Exception ex) + { + Log($"Couldn't migrate user {user.DisplayName} ({user.Email}) calendar", ex); + } + finally + { + ReportProgress(GetProgress() + smallStep, String.Format(MigrationResource.UserCalendarMigration, user.DisplayName, i, usersCount)); + }*/ + + try + { + var currentUser = SecurityContext.CurrentAccount; + SecurityContext.AuthenticateMe(user.Guid); + user.MigratingFiles.SetUsersDict(usersForImport.Except(failedUsers)); + user.MigratingFiles.SetGroupsDict(groupsForImport); + user.MigratingFiles.Migrate(); + SecurityContext.AuthenticateMe(currentUser.ID); + } + catch (Exception ex) + { + Log($"Couldn't migrate user {user.DisplayName} ({user.Email}) files", ex); + } + finally + { + ReportProgress(GetProgress() + smallStep, String.Format(MigrationResource.MigratingUserFiles, user.DisplayName, i, usersCount)); + } + i++; + } + + foreach (var item in takeouts) + { + File.Delete(item); + } + + ReportProgress(100, MigrationResource.MigrationCompleted); + return Task.CompletedTask; + } + } +} diff --git a/module/ASC.Migration/GoogleWorkspace/Models/GWSMigratingCalendar.cs b/module/ASC.Migration/GoogleWorkspace/Models/GWSMigratingCalendar.cs new file mode 100644 index 000000000..66c9d141b --- /dev/null +++ b/module/ASC.Migration/GoogleWorkspace/Models/GWSMigratingCalendar.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Migration.Core.Models; +using ASC.Migration.Resources; + +using Ical.Net; + +namespace ASC.Migration.GoogleWorkspace.Models +{ + public class GwsMigratingCalendar : MigratingCalendar + { + public override int CalendarsCount => calendars.Count; + + public override int EventsCount => calendars.Values.SelectMany(c => c.SelectMany(x=>x.Events)).Count(); + public override string ModuleName => MigrationResource.ModuleNameCalendar; + + public override void Parse() + { + //var calPath = Path.Combine(rootFolder, "Calendar"); + //if (!Directory.Exists(calPath)) return; + + //var calFiles = Directory.GetFiles(calPath, "*.ics"); + //if (!calFiles.Any()) return; + + //foreach (var calFile in calFiles) + //{ + // var events = DDayICalParser.DeserializeCalendar(File.ReadAllText(calFile)); + // calendars.Add(Path.GetFileNameWithoutExtension(calFile), events); + //} + } + + public override void Migrate() + { + if (!ShouldImport) return; + + // \portals\module\ASC.Api\ASC.Api.Calendar\CalendarApi.cs#L2311 + throw new NotImplementedException(); + } + + public GwsMigratingCalendar(string rootFolder, Action log) : base(log) + { + this.rootFolder = rootFolder; + } + + private string rootFolder; + private Dictionary calendars = new Dictionary(); + } +} diff --git a/module/ASC.Migration/GoogleWorkspace/Models/GWSMigratingContacts.cs b/module/ASC.Migration/GoogleWorkspace/Models/GWSMigratingContacts.cs new file mode 100644 index 000000000..ede11a20b --- /dev/null +++ b/module/ASC.Migration/GoogleWorkspace/Models/GWSMigratingContacts.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using ASC.Core; +using ASC.Mail.Core.Engine; +using ASC.Mail.Core.Entities; +using ASC.Mail.Enums; +using ASC.Migration.Core.Models; +using ASC.Migration.GoogleWorkspace.Models.Parse; +using ASC.Migration.Resources; + +using FolkerKinzel.VCards; + +namespace ASC.Migration.GoogleWorkspace.Models +{ + public class GwsMigratingContacts : MigratingContacts + { + public override int ContactsCount => contacts.Count; + public override string ModuleName => MigrationResource.GoogleModuleNameContacts; + + public override void Parse() + { + var vcfPath = Path.Combine(rootFolder, "Contacts", "All Contacts", "All Contacts.vcf"); + if (!File.Exists(vcfPath)) return; + + var vcards = VCard.Load(vcfPath); + foreach (var vcard in vcards) + { + if (vcard.EmailAddresses == null || !vcard.EmailAddresses.Any()) continue; // We can't import contacts without email + + var contact = new GwsContact() + { + Emails = vcard.EmailAddresses.Select(v => v.Value).Distinct().ToList() + }; + + if (vcard.Addresses != null && vcard.Addresses.Any()) contact.Address = vcard.Addresses.First().Value.ToString(); + if (vcard.DisplayNames != null && vcard.DisplayNames.Any()) contact.ContactName = vcard.DisplayNames.First().Value.ToString(); + if (vcard.Notes != null && vcard.Notes.Any()) contact.Description = string.Join("\n", vcard.Notes.Select(v => v.Value)); + if (vcard.PhoneNumbers != null && vcard.PhoneNumbers.Any()) contact.Phones = vcard.PhoneNumbers.Select(v => v.Value).Distinct().ToList(); + + contacts.Add(contact); + } + } + + public override void Migrate() + { + if (!ShouldImport) return; + + var engine = new ContactEngine(CoreContext.TenantManager.GetCurrentTenant().TenantId, user.Guid.ToString()); + foreach (var card in GetContactCards()) + { + try + { + engine.SaveContactCard(card); + } + catch (Exception ex) + { + Log($"Couldn't save contactCard {card.ContactInfo.ContactName}", ex); + } + } + } + + public GwsMigratingContacts(string rootFolder, GwsMigratingUser user, Action log) : base(log) + { + this.rootFolder = rootFolder; + this.user = user; + } + + private List GetContactCards() + { + var tenantId = CoreContext.TenantManager.GetCurrentTenant().TenantId; + var userId = user.Guid.ToString(); + + var portalContacts = new List(); + foreach (var gwsContact in contacts) + { + var portalContact = new Contact() + { + Type = ContactType.Personal, + ContactName = gwsContact.ContactName, + Address = gwsContact.Address, + Tenant = tenantId, + User = userId + }; + + var infos = new List(); + if (gwsContact.Emails != null) + { + infos.AddRange(gwsContact + .Emails.Select(e => new ContactInfo() { Data = e, Type = (int)ContactInfoType.Email, Tenant = tenantId, User = userId })); + } + if (gwsContact.Phones != null) + { + infos.AddRange(gwsContact + .Phones.Select(p => new ContactInfo() { Data = p, Type = (int)ContactInfoType.Phone, Tenant = tenantId, User = userId })); + } + + try + { + portalContacts.Add(new ContactCard(portalContact, infos)); + } + catch (Exception ex) + { + Log($"Couldn't create contactCard {gwsContact.ContactName}", ex); + } + } + + return portalContacts; + } + + private string rootFolder; + + private List contacts = new List(); + private GwsMigratingUser user; + } +} diff --git a/module/ASC.Migration/GoogleWorkspace/Models/GWSMigratingFiles.cs b/module/ASC.Migration/GoogleWorkspace/Models/GWSMigratingFiles.cs new file mode 100644 index 000000000..02ae2373c --- /dev/null +++ b/module/ASC.Migration/GoogleWorkspace/Models/GWSMigratingFiles.cs @@ -0,0 +1,410 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Text.RegularExpressions; + +using ASC.Files.Core; +using ASC.Files.Core.Security; +using ASC.Migration.Core.Models; +using ASC.Migration.GoogleWorkspace.Models.Parse; +using ASC.Migration.Resources; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Resources; +using ASC.Web.Files.Services.WCFService; + +using Newtonsoft.Json; + +using ASCFile = ASC.Files.Core.File; +using ASCShare = ASC.Files.Core.Security.FileShare; +using File = System.IO.File; + +namespace ASC.Migration.GoogleWorkspace.Models +{ + public class GwsMigratingFiles : MigratingFiles + { + public override int FoldersCount => foldersCount; + + public override int FilesCount => filesCount; + + public override long BytesTotal => bytesTotal; + public override string ModuleName => MigrationResource.GoogleModuleNameDocuments; + + private string newParentFolder; + + public override void Parse() + { + var drivePath = Path.Combine(rootFolder, "Drive"); + if (!Directory.Exists(drivePath)) return; + + var entries = Directory.GetFileSystemEntries(drivePath, "*", SearchOption.AllDirectories); + + List filteredEntries = new List(); + files = new List(); + folders = new List(); + folderCreation = folderCreation != null ? folderCreation : DateTime.Now.ToString("dd.MM.yyyy"); + newParentFolder = ModuleName + " " + folderCreation; + + foreach (var entry in entries) + { + if (ShouldIgnoreFile(entry, entries)) continue; + + filteredEntries.Add(entry); + } + + foreach (var entry in filteredEntries) + { + var attr = File.GetAttributes(entry); + if (attr.HasFlag(FileAttributes.Directory)) + { + foldersCount++; + folders.Add(newParentFolder + Path.DirectorySeparatorChar.ToString() + entry.Substring(drivePath.Length + 1)); + } + else + { + filesCount++; + var fi = new FileInfo(entry); + bytesTotal += fi.Length; + files.Add(newParentFolder + Path.DirectorySeparatorChar.ToString() + entry.Substring(drivePath.Length + 1)); + } + } + } + + public void SetUsersDict(IEnumerable users) + { + this.users = users.ToDictionary(user => user.Email, user => user); + } + public void SetGroupsDict(IEnumerable groups) + { + this.groups = groups.ToDictionary(group => group.GroupName, group => group); + } + + public override void Migrate() + { + if (!ShouldImport) return; + + var tmpFolder = Path.Combine(Path.GetTempPath(), Path.GetFileNameWithoutExtension(user.Key)); + try + { + ZipFile.ExtractToDirectory(user.Key, tmpFolder); + var drivePath = Path.Combine(tmpFolder, "Takeout", "Drive"); + + // Create all folders first + var foldersDict = new Dictionary(); + if (folders != null && folders.Count != 0) + { + foreach (var folder in folders) + { + var split = folder.Split(Path.DirectorySeparatorChar); // recursivly create all the folders + for (var i = 0; i < split.Length; i++) + { + var path = string.Join(Path.DirectorySeparatorChar.ToString(), split.Take(i + 1)); + if (foldersDict.ContainsKey(path)) continue; // skip folder if it was already created as a part of another path + var parentId = i == 0 ? Global.FolderMy : foldersDict[string.Join(Path.DirectorySeparatorChar.ToString(), split.Take(i))].ID; + try + { + var createdFolder = Global.FileStorageService.CreateNewFolder(parentId.ToString(), split[i]); + path = path.Contains(newParentFolder + Path.DirectorySeparatorChar.ToString()) ? path.Replace(newParentFolder + Path.DirectorySeparatorChar.ToString(), "") : path; + foldersDict.Add(path, createdFolder); + } + catch (Exception ex) + { + Log($"Couldn't create folder {path}", ex); + } + } + } + } + //create default folder + if((folders == null || folders.Count == 0) && (files != null && files.Count != 0)) + { + var parentId = Global.FolderMy; + var createdFolder = Global.FileStorageService.CreateNewFolder(parentId.ToString(), newParentFolder); + foldersDict.Add(newParentFolder, createdFolder); + } + + // Copy all files + var filesDict = new Dictionary(); + if (files != null && files.Count != 0) + { + foreach (var file in files) + { + var maskFile = file.Replace(ModuleName + " " + folderCreation + Path.DirectorySeparatorChar.ToString(), ""); + var maskParentPath = Path.GetDirectoryName(maskFile); + + // ToDo: maybe we should upload to root, if required folder wasn't created + + try + { + var realPath = Path.Combine(drivePath, maskFile); + using (var fs = new FileStream(realPath, FileMode.Open)) + using (var fileDao = Global.DaoFactory.GetFileDao()) + using (var folderDao = Global.DaoFactory.GetFolderDao()) + { + var parentFolder = string.IsNullOrWhiteSpace(maskParentPath) ? foldersDict[newParentFolder] : foldersDict[maskParentPath]; + + var newFile = new ASCFile + { + FolderID = parentFolder.ID, + Comment = FilesCommonResource.CommentCreate, + Title = Path.GetFileName(file), + ContentLength = fs.Length + }; + newFile = fileDao.SaveFile(newFile, fs); + realPath = realPath.Contains(Path.DirectorySeparatorChar.ToString() + newParentFolder) ? realPath.Replace(Path.DirectorySeparatorChar.ToString() + newParentFolder, "") : realPath; + filesDict.Add(realPath, newFile); + } + } + catch (Exception ex) + { + Log($"Couldn't create file {maskParentPath}/{Path.GetFileName(file)}", ex); + } + } + } + + var entries = filesDict + .ToDictionary(kv => kv.Key, kv => (FileEntry)kv.Value) + .Concat(foldersDict + .ToDictionary(kv => Path.Combine(drivePath, kv.Key), kv => (FileEntry)kv.Value)) + .OrderBy(kv => kv.Value is ASCFile) + .ThenBy(kv => kv.Key.Count(c => Path.DirectorySeparatorChar.Equals(c))); + + var favFolders = new ItemList(); + var favFiles = new ItemList(); + + var fileSec = new FileSecurity(Global.DaoFactory); + + foreach (var kv in entries) + { + if (TryReadInfoFile(kv.Key, out var info)) + { + if (info.Starred) + { + if (kv.Value is ASCFile) + { + favFiles.Add(kv.Value.ID); + } + else + { + favFolders.Add(kv.Value.ID); + } + } + + var list = new ItemList(); + foreach (var shareInfo in info.Permissions) + { + if (shareInfo.Type == "user" || shareInfo.Type == "group") + { + var shareType = GetPortalShare(shareInfo); + users.TryGetValue(shareInfo.EmailAddress, out var userToShare); + groups.TryGetValue(shareInfo.Name, out var groupToShare); + if (shareType == null || (userToShare == null && groupToShare == null)) continue; + + Func checkRights = null; + switch (shareType) + { + case ASCShare.ReadWrite: + checkRights = fileSec.CanEdit; + break; + case ASCShare.Comment: + checkRights = fileSec.CanComment; + break; + case ASCShare.Read: + checkRights = fileSec.CanRead; + break; + default: // unused + break; + } + var entryGuid = userToShare == null ? groupToShare.Guid : userToShare.Guid; + + if (checkRights != null && checkRights(kv.Value, entryGuid)) continue; // already have rights, skip + + list.Add(new AceWrapper + { + Share = shareType.Value, + SubjectId = entryGuid, + SubjectGroup = false + }); + } + } + + if (!list.Any()) continue; + + var aceCollection = new AceCollection + { + Entries = new ItemList { (kv.Value is ASCFile ? "file_" : "folder_") + kv.Value.ID }, + Aces = list, + Message = null + }; + try + { + Global.FileStorageService.SetAceObject(aceCollection, false); + } + catch (Exception ex) + { + Log($"Couldn't change file permissions for {aceCollection.Entries.First()}", ex); + } + } + } + + if (favFolders.Any() || favFiles.Any()) + { + Global.FileStorageService.AddToFavorites(favFolders, favFiles); + } + } + catch + { + throw; + } + finally + { + if (Directory.Exists(tmpFolder)) + { + Directory.Delete(tmpFolder, true); + } + } + } + + public GwsMigratingFiles(string rootFolder, GwsMigratingUser user, Action log) : base(log) + { + this.rootFolder = rootFolder; + this.user = user; + } + + private static readonly Regex versionRegex = new Regex(@"(\([\d]+\))"); + private string FindInfoFile(string entry) + { + var infoFilePath = entry + infoFile; + if (File.Exists(infoFilePath)) return infoFilePath; // file.docx-info.json + + var ext = Path.GetExtension(entry); + infoFilePath = entry.Substring(0, entry.Length - ext.Length) + infoFile; + if (File.Exists(infoFilePath)) return infoFilePath; // file-info.json + + var versionMatch = versionRegex.Match(entry); + if (!versionMatch.Success) return null; + + var version = versionMatch.Groups[1].Value; + infoFilePath = entry.Replace(version, "") + infoFile.Replace(".", version + "."); + if (File.Exists(infoFilePath)) return infoFilePath; // file.docx-info(1).json + + infoFilePath = entry.Substring(0, entry.Length - ext.Length).Replace(version, "") + infoFile.Replace(".", version + "."); + if (File.Exists(infoFilePath)) return infoFilePath; // file-info(1).json + + return null; + } + + private bool TryReadInfoFile(string entry, out GwsDriveFileInfo info) + { + info = null; + var infoFilePath = FindInfoFile(entry); + + if (infoFilePath == null) return false; + + try + { + info = JsonConvert.DeserializeObject(File.ReadAllText(infoFilePath)); + return true; + } + catch (Exception ex) + { + Log($"Couldn't read info file for {entry}", ex); + } + + return false; + } + + private static readonly Regex workspacesRegex = new Regex(@"Workspaces(\(\d+\))?.json"); + private static readonly Regex pinnedRegex = new Regex(@".*-at-.*-pinned\..*"); + private const string commentsFile = "-comments.html"; + private const string infoFile = "-info.json"; + private static readonly Regex commentsVersionFile = new Regex(@"-comments(\([\d]+\))\.html"); + private static readonly Regex infoVersionFile = new Regex(@"-info(\([\d]+\))\.json"); + private bool ShouldIgnoreFile(string entry, string[] entries) + { + if (workspacesRegex.IsMatch(Path.GetFileName(entry))) return true; // ignore workspaces.json + if (pinnedRegex.IsMatch(Path.GetFileName(entry))) return true; // ignore pinned files + + if (entry.EndsWith(commentsFile) || entry.EndsWith(infoFile)) // check if this really a meta for existing file + { + // folder - folder + // folder-info.json - valid meta + + // file.docx - file + // file.docx-info.json - valid meta + // file-info.json - valid meta + + var baseName = entry.Substring(0, entry.Length - (entry.EndsWith(commentsFile) ? commentsFile.Length : infoFile.Length)); + if (entries.Contains(baseName)) return true; + if (entries + .Where(e => e.StartsWith(baseName + ".")) + .Select(e => e.Substring(0, e.Length - Path.GetExtension(e).Length)) + .Contains(baseName)) return true; + } + + // file(1).docx - file + // file.docx-info(1).json - valid meta + // file-info(1).json - valid meta + var commentsVersionMatch = commentsVersionFile.Match(entry); + if (commentsVersionMatch.Success) + { + var baseName = entry.Substring(0, entry.Length - commentsVersionMatch.Groups[0].Value.Length); + baseName = baseName.Insert(baseName.LastIndexOf("."), commentsVersionMatch.Groups[1].Value); + + if (entries.Contains(baseName)) return true; + if (entries + .Where(e => e.StartsWith(baseName + ".")) + .Select(e => e.Substring(0, e.Length - Path.GetExtension(e).Length)) + .Contains(baseName)) return true; + } + + var infoVersionMatch = infoVersionFile.Match(entry); + if (infoVersionMatch.Success) + { + var baseName = entry.Substring(0, entry.Length - infoVersionMatch.Groups[0].Length); + baseName = baseName.Insert(baseName.LastIndexOf("."), infoVersionMatch.Groups[1].Value); + + if (entries.Contains(baseName)) return true; + if (entries + .Where(e => e.StartsWith(baseName + ".")) + .Select(e => e.Substring(0, e.Length - Path.GetExtension(e).Length)) + .Contains(baseName)) return true; + } + + return false; + } + + private ASCShare? GetPortalShare(GwsDriveFilePermission fileInfo) + { + switch (fileInfo.Role) + { + case "writer": + return ASCShare.ReadWrite; + case "reader": + if (fileInfo.AdditionalRoles == null) return ASCShare.Read; + if (fileInfo.AdditionalRoles.Contains("commenter")) + { + return ASCShare.Comment; + } + else + { + return ASCShare.Read; + } + + default: + return null; + }; + } + + private List files; + private List folders; + private string rootFolder; + private int foldersCount; + private int filesCount; + private long bytesTotal; + private GwsMigratingUser user; + private Dictionary users; + private Dictionary groups; + private string folderCreation; + } +} diff --git a/module/ASC.Migration/GoogleWorkspace/Models/GWSMigratingGroups.cs b/module/ASC.Migration/GoogleWorkspace/Models/GWSMigratingGroups.cs new file mode 100644 index 000000000..d35fa3aaa --- /dev/null +++ b/module/ASC.Migration/GoogleWorkspace/Models/GWSMigratingGroups.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using ASC.Core; +using ASC.Core.Users; +using ASC.Migration.Core.Models; +using ASC.Migration.Resources; + +namespace ASC.Migration.GoogleWorkspace.Models +{ + public class GWSMigratingGroups : MigratingGroup + { + private string groupName; + private List userUidList; + private string rootFolder; + private GroupInfo groupinfo; + public Guid Guid => groupinfo.ID; + public MigrationModules Module = new MigrationModules(); + public override List UserUidList => userUidList; + public override string GroupName => groupName; + public override string ModuleName => MigrationResource.ModuleNameGroups; + public GWSMigratingGroups(string rootFolder, Action log) : base(log) + { + this.rootFolder = rootFolder; + } + + public override void Parse() + { + userUidList = new List(); + var groupsFolder = Path.Combine(rootFolder, "Groups"); + var groupInfo = Path.Combine(groupsFolder, "info.csv"); + using (StreamReader sr = new StreamReader(groupInfo)) + { + string line = sr.ReadLine(); + line = sr.ReadLine(); + if(line!=null) + { + groupName = line.Split(',')[9]; + if(!string.IsNullOrWhiteSpace(groupName)) + { + groupinfo = new GroupInfo() + { + Name = groupName + }; + } + } + } + if(!string.IsNullOrWhiteSpace(groupinfo.Name)) + { + var groupMembers = Path.Combine(groupsFolder, "members.csv"); + using (StreamReader sr = new StreamReader(groupMembers)) + { + string line = sr.ReadLine(); + while ((line = sr.ReadLine()) != null) + { + var b = line.Split(','); + userUidList.Add(line.Split(',')[1]); + } + } + } + if(userUidList.Count > 0) + { + Module = new MigrationModules(ModuleName, MigrationResource.OnlyofficeModuleNamePeople); + } + } + + public override void Migrate() + { + var existingGroups = CoreContext.UserManager.GetGroups().ToList(); + var oldGroup = existingGroups.Find(g => g.Name == groupinfo.Name); + if (oldGroup != null) + { + groupinfo = oldGroup; + } + else + { + groupinfo = CoreContext.UserManager.SaveGroupInfo(groupinfo); + } + foreach (var userEmail in userUidList) + { + UserInfo user; + try + { + user = CoreContext.UserManager.GetUserByEmail(userEmail); + if(user == Constants.LostUser) + { + throw new ArgumentNullException(); + } + if (!CoreContext.UserManager.IsUserInGroup(user.ID, groupinfo.ID)) + { + CoreContext.UserManager.AddUserIntoGroup(user.ID, groupinfo.ID); + } + } + catch(Exception ex) + { + //Think about the text of the error + Log($"Couldn't to add user {userEmail} to group {groupName} {Path.Combine(rootFolder, "Groups")} ", ex); + } + } + } + } +} diff --git a/module/ASC.Migration/GoogleWorkspace/Models/GWSMigratingMail.cs b/module/ASC.Migration/GoogleWorkspace/Models/GWSMigratingMail.cs new file mode 100644 index 000000000..a18a3d419 --- /dev/null +++ b/module/ASC.Migration/GoogleWorkspace/Models/GWSMigratingMail.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.IO; + +using ASC.Migration.Core.Models; +using ASC.Migration.GoogleWorkspace.Models.Parse; +using ASC.Migration.Resources; + +using MimeKit; + +namespace ASC.Migration.GoogleWorkspace.Models +{ + public class GwsMigratingMail : MigratingMail + { + private int messagesCount; + private string rootFolder; + private GwsMigratingUser user; + private List mails = new List(); + + public override int MessagesCount => messagesCount; + public override string ModuleName => MigrationResource.ModuleNameMail; + + public GwsMigratingMail(string rootFolder, GwsMigratingUser user, Action log) : base(log) + { + this.rootFolder = rootFolder; + this.user = user; + } + + public override void Migrate() + { + throw new System.NotImplementedException(); + } + + public override void Parse() + { + var path = Path.Combine(rootFolder, "Mail"); + var foldersName = Directory.GetFiles(path); + foreach(var item in foldersName) + { + var mail = new GwsMail(); + List messagesList = new List(); + using (FileStream sr = File.OpenRead(item)) + { + var parser = new MimeParser(sr, MimeFormat.Mbox); + while (!parser.IsEndOfStream) + { + messagesList.Add(parser.ParseMessage()); + messagesCount++; + } + } + var folder = item.Split(Path.DirectorySeparatorChar); + mail.ParentFolder = folder[folder.Length - 1].Split('.')[0].ToLower() == "All mail Including Spam and Trash".ToLower()? "inbox" : folder[folder.Length - 1].Split('.')[0]; + mail.Message = messagesList; + mails.Add(mail); + } + throw new System.NotImplementedException(); + } + } +} diff --git a/module/ASC.Migration/GoogleWorkspace/Models/GWSMigratingUser.cs b/module/ASC.Migration/GoogleWorkspace/Models/GWSMigratingUser.cs new file mode 100644 index 000000000..ec33d5da0 --- /dev/null +++ b/module/ASC.Migration/GoogleWorkspace/Models/GWSMigratingUser.cs @@ -0,0 +1,261 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Text.RegularExpressions; + +using ASC.Core; +using ASC.Core.Users; +using ASC.Migration.Core.Models; +using ASC.Migration.Core.Models.Api; +using ASC.Migration.GoogleWorkspace.Models.Parse; +using ASC.Migration.Resources; +using ASC.Web.Files.Resources; + +using HtmlAgilityPack; + +using Newtonsoft.Json; + +namespace ASC.Migration.GoogleWorkspace.Models +{ + public class GwsMigratingUser : MigratingUser + { + public override string Email => userInfo.Email; + public Guid Guid => userInfo.ID; + public List ModulesList = new List(); + public override string ModuleName => MigrationResource.ModuleNameUsers; + + public override string DisplayName => $"{userInfo.FirstName} {userInfo.LastName}".Trim(); + + public override void Parse() + { + ModulesList.Add(new MigrationModules(ModuleName, MigrationResource.OnlyofficeModuleNamePeople)); + userInfo = new UserInfo() + { + ID = Guid.NewGuid() + }; + + ParseRootHtml(); + ParseProfile(); + ParseAccount(); + + Action log = (m, e) => { Log($"{DisplayName} ({Email}): {m}", e); }; + + MigratingContacts = new GwsMigratingContacts(rootFolder, this, log); + MigratingContacts.Parse(); + if (MigratingContacts.ContactsCount != 0) + { + ModulesList.Add(new MigrationModules(MigratingContacts.ModuleName, MigrationResource.OnlyofficeModuleNameMail)); + } + + MigratingCalendar = new GwsMigratingCalendar(rootFolder, log); + //MigratingCalendar.Parse(); + if (MigratingCalendar.CalendarsCount != 0) + { + ModulesList.Add(new MigrationModules(MigratingCalendar.ModuleName, MigrationResource.OnlyofficeModuleNameCalendar)); + } + + MigratingFiles = new GwsMigratingFiles(rootFolder, this, log); + MigratingFiles.Parse(); + if (MigratingFiles.FoldersCount != 0 || MigratingFiles.FilesCount != 0) + { + ModulesList.Add(new MigrationModules(MigratingFiles.ModuleName, MigrationResource.OnlyofficeModuleNameDocuments)); + } + + MigratingMail = new GwsMigratingMail(rootFolder, this, log); + //MigratingMail.Parse(); + + userInfo.UserName = userInfo.Email.Split('@').First(); + if (userInfo.FirstName == null || userInfo.FirstName == "") + { + userInfo.FirstName = userInfo.Email.Split('@').First(); + } + userInfo.ActivationStatus = EmployeeActivationStatus.Pending; + } + + public void dataСhange(MigratingApiUser frontUser) + { + if (userInfo.LastName == null) + { + userInfo.LastName = "NOTPROVIDED"; + } + } + + public override void Migrate() + { + if (string.IsNullOrWhiteSpace(userInfo.FirstName)) + { + userInfo.FirstName = FilesCommonResource.UnknownFirstName; + } + if (string.IsNullOrWhiteSpace(userInfo.LastName)) + { + userInfo.LastName = FilesCommonResource.UnknownLastName; + } + + var saved = CoreContext.UserManager.GetUserByEmail(userInfo.Email); + if (saved != Constants.LostUser) + { + saved.Contacts = saved.Contacts.Union(userInfo.Contacts).ToList(); + userInfo.ID = saved.ID; + } + else + { + saved = CoreContext.UserManager.SaveUserInfo(userInfo); + } + if (hasPhoto) + { + using (var fs = File.OpenRead(Key)) + { + using (var zip = new ZipArchive(fs)) + { + using (var ms = new MemoryStream()) + { + using (var imageStream = zip.GetEntry(string.Join("/", "Takeout", "Profile", "ProfilePhoto.jpg")).Open()) + { + imageStream.CopyTo(ms); + } + CoreContext.UserManager.SaveUserPhoto(saved.ID, ms.ToArray()); + } + } + } + } + } + + public GwsMigratingUser(string key, string rootFolder, Action log) : base(log) + { + Key = key; + this.rootFolder = rootFolder; + } + + private Regex emailRegex = new Regex(@"(\S*@\S*\.\S*)"); + private Regex phoneRegex = new Regex(@"(\+?\d+)"); + + private string rootFolder; + private UserInfo userInfo; + private bool hasPhoto; + + private void ParseRootHtml() + { + var htmlFiles = Directory.GetFiles(rootFolder, "*.html"); + if (htmlFiles.Count() != 1) + { + throw new Exception("Incorrect Takeout format."); + } + + var htmlPath = htmlFiles[0]; + + var doc = new HtmlDocument(); + doc.Load(htmlPath); + + var emailNode = doc.DocumentNode.SelectNodes("//body//h1[@class='header_title']")[0]; + var matches = emailRegex.Match(emailNode.InnerText); + if (!matches.Success) + { + throw new Exception("Couldn't parse root html."); + } + + userInfo.Email = matches.Groups[1].Value; + } + + private void ParseProfile() + { + var profilePath = Path.Combine(rootFolder, "Profile", "Profile.json"); + if (!File.Exists(profilePath)) return; + + var googleProfile = JsonConvert.DeserializeObject(File.ReadAllText(profilePath)); + + if (googleProfile.Birthday != null) + { + userInfo.BirthDate = googleProfile.Birthday.Value.DateTime; + } + + if (googleProfile.Gender != null) + { + switch (googleProfile.Gender.Type) + { + case "male": + userInfo.Sex = true; + break; + + case "female": + userInfo.Sex = false; + break; + + default: + userInfo.Sex = null; + break; + } + } + + userInfo.FirstName = googleProfile.Name.GivenName; + userInfo.LastName = googleProfile.Name.FamilyName; + + if (googleProfile.Emails != null) + { + foreach (var email in googleProfile.Emails.Distinct()) + { + AddEmailToUser(userInfo, email.Value); + } + } + + var profilePhotoPath = Path.Combine(rootFolder, "Profile", "ProfilePhoto.jpg"); + hasPhoto = File.Exists(profilePhotoPath); + } + + private void ParseAccount() + { + var accountPath = Path.Combine(rootFolder, "Google Account"); + if (!Directory.Exists(accountPath)) return; + var htmlFiles = Directory.GetFiles(accountPath, "*.SubscriberInfo.html"); + if (htmlFiles.Count() != 1) return; + + var htmlPath = htmlFiles[0]; + + var doc = new HtmlDocument(); + doc.Load(htmlPath); + + var alternateEmails = emailRegex.Matches(doc.DocumentNode.SelectNodes("//div[@class='section'][1]/ul/li[2]")[0].InnerText); + foreach (Match match in alternateEmails) + { + AddEmailToUser(userInfo, match.Value); + } + + var contactEmail = emailRegex.Match(doc.DocumentNode.SelectNodes("//div[@class='section'][3]/ul/li[1]")[0].InnerText); + if (contactEmail.Success) + { + AddEmailToUser(userInfo, contactEmail.Groups[1].Value); + } + + var recoveryEmail = emailRegex.Match(doc.DocumentNode.SelectNodes("//div[@class='section'][3]/ul/li[2]")[0].InnerText); + if (recoveryEmail.Success) + { + AddEmailToUser(userInfo, recoveryEmail.Groups[1].Value); + } + + var recoverySms = phoneRegex.Match(doc.DocumentNode.SelectNodes("//div[@class='section'][3]/ul/li[3]")[0].InnerText); + if (recoverySms.Success) + { + AddPhoneToUser(userInfo, recoverySms.Groups[1].Value); + } + } + + private void AddEmailToUser(UserInfo userInfo, string email) + { + if (userInfo.Email != email && !userInfo.Contacts.Contains(email)) + { + userInfo.Contacts.Add(email.EndsWith("@gmail.com") ? "gmail" : "mail"); // SocialContactsManager.ContactType_gmail in ASC.WebStudio + userInfo.Contacts.Add(email); + } + } + + private void AddPhoneToUser(UserInfo userInfo, string phone) + { + if (userInfo.MobilePhone != phone && !userInfo.Contacts.Contains(phone)) + { + userInfo.Contacts.Add("mobphone"); // SocialContactsManager.ContactType_mobphone in ASC.WebStudio + userInfo.Contacts.Add(phone); + } + } + } +} diff --git a/module/ASC.Migration/GoogleWorkspace/Models/GWSMigrationInfo.cs b/module/ASC.Migration/GoogleWorkspace/Models/GWSMigrationInfo.cs new file mode 100644 index 000000000..86d5b9d3e --- /dev/null +++ b/module/ASC.Migration/GoogleWorkspace/Models/GWSMigrationInfo.cs @@ -0,0 +1,8 @@ +using ASC.Migration.Core.Models; + +namespace ASC.Migration.GoogleWorkspace.Models +{ + public class GwsMigrationInfo : MigrationInfo + { + } +} diff --git a/module/ASC.Migration/GoogleWorkspace/Models/Parse/GWSContact.cs b/module/ASC.Migration/GoogleWorkspace/Models/Parse/GWSContact.cs new file mode 100644 index 000000000..4040c0b2e --- /dev/null +++ b/module/ASC.Migration/GoogleWorkspace/Models/Parse/GWSContact.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace ASC.Migration.GoogleWorkspace.Models.Parse +{ + public class GwsContact + { + public string ContactName { get; set; } + public string Address { get; set; } + public string Description { get; set; } + + public List Emails { get; set; } + public List Phones { get; set; } + } +} diff --git a/module/ASC.Migration/GoogleWorkspace/Models/Parse/GWSDriveFileInfo.cs b/module/ASC.Migration/GoogleWorkspace/Models/Parse/GWSDriveFileInfo.cs new file mode 100644 index 000000000..9bf4d2c4f --- /dev/null +++ b/module/ASC.Migration/GoogleWorkspace/Models/Parse/GWSDriveFileInfo.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; + +using Newtonsoft.Json; + +namespace ASC.Migration.GoogleWorkspace.Models.Parse +{ + public class GwsDriveFileInfo + { + [JsonProperty("starred")] + public bool Starred { get; set; } + + [JsonProperty("viewers_can_download")] + public bool ViewersCanDownload { get; set; } + + [JsonProperty("editors_can_edit_access")] + public bool EditorsCanEditAccess { get; set; } + + [JsonProperty("last_modified_by_any_user")] + public DateTimeOffset LastModifiedByAnyUser { get; set; } + + [JsonProperty("last_modified_by_me")] + public DateTimeOffset LastModifiedByMe { get; set; } + + [JsonProperty("content_last_modified")] + public DateTimeOffset ContentLastModified { get; set; } + + [JsonProperty("created")] + public DateTimeOffset Created { get; set; } + + [JsonProperty("permissions")] + public List Permissions { get; set; } + } + + public class GwsDriveFilePermission + { + [JsonProperty("id")] + public string Id { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } + + [JsonProperty("role")] + public string Role { get; set; } + + [JsonProperty("additional_roles")] + public List AdditionalRoles { get; set; } + + [JsonProperty("kind")] + public string Kind { get; set; } + + [JsonProperty("self_link")] + public Uri SelfLink { get; set; } + + [JsonProperty("email_address")] + public string EmailAddress { get; set; } + + [JsonProperty("domain")] + public string Domain { get; set; } + + [JsonProperty("etag")] + public string Etag { get; set; } + + [JsonProperty("deleted")] + public bool Deleted { get; set; } + } +} diff --git a/module/ASC.Migration/GoogleWorkspace/Models/Parse/GWSMail.cs b/module/ASC.Migration/GoogleWorkspace/Models/Parse/GWSMail.cs new file mode 100644 index 000000000..786ce5072 --- /dev/null +++ b/module/ASC.Migration/GoogleWorkspace/Models/Parse/GWSMail.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; + +using MimeKit; + +namespace ASC.Migration.GoogleWorkspace.Models.Parse +{ + class GwsMail + { + public string ParentFolder { get; set; } + public List Message { get; set; } + } +} diff --git a/module/ASC.Migration/GoogleWorkspace/Models/Parse/GWSProfile.cs b/module/ASC.Migration/GoogleWorkspace/Models/Parse/GWSProfile.cs new file mode 100644 index 000000000..8766b3337 --- /dev/null +++ b/module/ASC.Migration/GoogleWorkspace/Models/Parse/GWSProfile.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; + +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace ASC.Migration.GoogleWorkspace.Models.Parse +{ + public class GwsProfile + { + [JsonProperty("name")] + public GwsName Name { get; set; } + + [JsonProperty("displayName")] + public string DisplayName { get; set; } + + [JsonProperty("emails", NullValueHandling = NullValueHandling.Ignore)] + public List Emails { get; set; } + + [JsonProperty("birthday", NullValueHandling = NullValueHandling.Ignore)] + public DateTimeOffset? Birthday { get; set; } + + [JsonProperty("gender", NullValueHandling = NullValueHandling.Ignore)] + public GwsGender Gender { get; set; } + + [OnError] + internal void OnError(StreamingContext context, ErrorContext errorContext) + { + switch(errorContext.Member) + { + case "birthday": + case "gender": + errorContext.Handled = true; // Ignore those fields if we can't parse them + break; + } + } + } + + public class GwsEmail + { + [JsonProperty("value")] + public string Value { get; set; } + } + + public class GwsGender + { + [JsonProperty("type")] + public string Type { get; set; } + } + + public class GwsName + { + [JsonProperty("givenName")] + public string GivenName { get; set; } + + [JsonProperty("familyName")] + public string FamilyName { get; set; } + + [JsonProperty("formattedName")] + public string FormattedName { get; set; } + } +} diff --git a/module/ASC.Migration/GoogleWorkspace/Models/Parse/GWSTasks.cs b/module/ASC.Migration/GoogleWorkspace/Models/Parse/GWSTasks.cs new file mode 100644 index 000000000..21c1732ad --- /dev/null +++ b/module/ASC.Migration/GoogleWorkspace/Models/Parse/GWSTasks.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; + +using Newtonsoft.Json; + +namespace ASC.Migration.GoogleWorkspace.Models.Parse +{ + public class GwsTasksRoot + { + [JsonProperty("kind")] + public string Kind { get; set; } + + [JsonProperty("items")] + public List Items { get; set; } + } + + public class GwsTaskList + { + [JsonProperty("kind")] + public string Kind { get; set; } + + [JsonProperty("id")] + public string Id { get; set; } + + [JsonProperty("title")] + public string Title { get; set; } + + [JsonProperty("updated")] + public DateTimeOffset Updated { get; set; } + + [JsonProperty("items", NullValueHandling = NullValueHandling.Ignore)] + public List Items { get; set; } + } + + public class GwsTask + { + [JsonProperty("notes")] + public string Notes { get; set; } + + [JsonProperty("kind")] + public string Kind { get; set; } + + [JsonProperty("created")] + public DateTimeOffset Created { get; set; } + + [JsonProperty("id")] + public string Id { get; set; } + + [JsonProperty("completed", NullValueHandling = NullValueHandling.Ignore)] + public DateTimeOffset? Completed { get; set; } + + [JsonProperty("title")] + public string Title { get; set; } + + [JsonProperty("task_type")] + public string TaskType { get; set; } + + [JsonProperty("updated")] + public DateTimeOffset Updated { get; set; } + + [JsonProperty("status")] + public string Status { get; set; } + + [JsonProperty("due", NullValueHandling = NullValueHandling.Ignore)] + public DateTimeOffset? Due { get; set; } + } +} diff --git a/module/ASC.Migration/NextcloudWorkspace/Models/NCMigratingCalendar.cs b/module/ASC.Migration/NextcloudWorkspace/Models/NCMigratingCalendar.cs new file mode 100644 index 000000000..10d2beac8 --- /dev/null +++ b/module/ASC.Migration/NextcloudWorkspace/Models/NCMigratingCalendar.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Migration.Core.Models; +using ASC.Migration.Resources; + +using Ical.Net; + +namespace ASC.Migration.NextcloudWorkspace.Models.Parse +{ + public class NCMigratingCalendar : MigratingCalendar + { + public override int CalendarsCount => calendars.Count; + + public override int EventsCount => calendars.Count>0 && calendars.Values.First()!=null ? calendars.Values.SelectMany(c => c.SelectMany(x => x.Events)).Count(): calendars.Count; + public override string ModuleName => MigrationResource.ModuleNameCalendar; + + private List userCalendars; + private Dictionary calendars = new Dictionary(); + public NCMigratingCalendar(List calendars, Action log) : base(log) + { + this.userCalendars = calendars; + } + + public override void Parse() + { + //foreach(var calendar in this.userCalendars) + //{ + // var calendarString = ""; + // foreach (var calendarEvent in calendar.CalendarObject) + // { + // string calendarEventByteToString = Encoding.Default.GetString(calendarEvent.CalendarData); + // if (calendarString != "") + // { + // int start = calendarEventByteToString.IndexOf("BEGIN:VEVENT"); + // int end = calendarEventByteToString.IndexOf("END:VEVENT\r\n") + "END:VEVENT\r\n".Length; + // string newEvent = calendarEventByteToString.Substring(start, end - start); + + // int insertionPoint = calendarString.LastIndexOf("END:VEVENT\r\n") + "END:VEVENT\r\n".Length; + // calendarString = calendarString.Insert(insertionPoint, newEvent); + // } + // else + // { + // calendarString = calendarEventByteToString; + // } + // } + // var events = DDayICalParser.DeserializeCalendar(calendarString); + // calendars.Add(calendar.DisplayName, events); + //} + } + + public override void Migrate() + { + if (!ShouldImport) return; + throw new NotImplementedException(); + } + } +} diff --git a/module/ASC.Migration/NextcloudWorkspace/Models/NCMigratingContacts.cs b/module/ASC.Migration/NextcloudWorkspace/Models/NCMigratingContacts.cs new file mode 100644 index 000000000..e97649197 --- /dev/null +++ b/module/ASC.Migration/NextcloudWorkspace/Models/NCMigratingContacts.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using ASC.Core; +using ASC.Mail.Core.Engine; +using ASC.Mail.Core.Entities; +using ASC.Mail.Enums; +using ASC.Migration.Core.Models; +using ASC.Migration.Resources; + +using FolkerKinzel.VCards; + +namespace ASC.Migration.NextcloudWorkspace.Models.Parse +{ + public class NCMigratingContacts : MigratingContacts + { + public override int ContactsCount => contacts.Count; + public override string ModuleName => MigrationResource.NextcloudModuleNameContacts; + + private List contacts = new List(); + private NCMigratingUser user; + private NCAddressbooks addressbooks; + + public NCMigratingContacts(NCMigratingUser user, NCAddressbooks addressbooks, Action log) : base(log) + { + this.user = user; + this.addressbooks = addressbooks; + } + + public override void Parse() + { + if(this.addressbooks != null) + { + if (this.addressbooks.Cards != null) + { + foreach (var vcardByte in this.addressbooks.Cards) + { + var vcard = VCard.Parse(Encoding.Default.GetString(vcardByte.CardData)); + foreach (var item in vcard) + { + if (item.EmailAddresses == null || !item.EmailAddresses.Any()) continue; + + var contact = new NCContact() + { + Emails = item.EmailAddresses.Select(v => v.Value).Distinct().ToList() + }; + + if (item.Addresses != null && item.Addresses.Any()) contact.Address = item.Addresses.First().Value.ToString(); + if (item.DisplayNames != null && item.DisplayNames.Any()) contact.ContactName = item.DisplayNames.First().Value.ToString(); + if (item.Notes != null && item.Notes.Any()) contact.Description = string.Join("\n", item.Notes.Select(v => v.Value)); + if (item.PhoneNumbers != null && item.PhoneNumbers.Any()) contact.Phones = item.PhoneNumbers.Select(v => v.Value).Distinct().ToList(); + + contacts.Add(contact); + } + } + } + } + } + + public override void Migrate() + { + if (!ShouldImport) return; + + var engine = new ContactEngine(CoreContext.TenantManager.GetCurrentTenant().TenantId, user.Guid.ToString()); + var cards = GetContactCards(); + foreach (var card in cards) + { + try + { + engine.SaveContactCard(card); + } + catch (Exception ex) + { + Log($"Couldn't save contactCard {card.ContactInfo.ContactName}", ex); + } + } + } + + private List GetContactCards() + { + var tenantId = CoreContext.TenantManager.GetCurrentTenant().TenantId; + var userId = user.Guid.ToString(); + + var portalContacts = new List(); + foreach (var gwsContact in contacts) + { + var portalContact = new Contact() + { + Type = ContactType.Personal, + ContactName = gwsContact.ContactName, + Address = gwsContact.Address, + Tenant = tenantId, + User = userId + }; + + var infos = new List(); + if (gwsContact.Emails != null) + { + infos.AddRange(gwsContact + .Emails.Select(e => new ContactInfo() { Data = e, Type = (int)ContactInfoType.Email, Tenant = tenantId, User = userId })); + } + if (gwsContact.Phones != null) + { + infos.AddRange(gwsContact + .Phones.Select(p => new ContactInfo() { Data = p, Type = (int)ContactInfoType.Phone, Tenant = tenantId, User = userId })); + } + try + { + portalContacts.Add(new ContactCard(portalContact, infos)); + } + catch (Exception ex) + { + Log($"Couldn't create contactCard {gwsContact.ContactName}", ex); + } + } + + return portalContacts; + } + + } +} diff --git a/module/ASC.Migration/NextcloudWorkspace/Models/NCMigratingFiles.cs b/module/ASC.Migration/NextcloudWorkspace/Models/NCMigratingFiles.cs new file mode 100644 index 000000000..0a518adcc --- /dev/null +++ b/module/ASC.Migration/NextcloudWorkspace/Models/NCMigratingFiles.cs @@ -0,0 +1,233 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using ASC.Files.Core; +using ASC.Migration.Core.Models; +using ASC.Migration.Resources; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Resources; +using ASC.Web.Files.Services.WCFService; + +using ASCFile = ASC.Files.Core.File; +using ASCShare = ASC.Files.Core.Security.FileShare; +using File = System.IO.File; + +namespace ASC.Migration.NextcloudWorkspace.Models.Parse +{ + public class NCMigratingFiles : MigratingFiles + { + public override int FoldersCount => foldersCount; + + public override int FilesCount => filesCount; + + public override long BytesTotal => bytesTotal; + + public override string ModuleName => MigrationResource.NextcloudModuleNameDocuments; + + private NCMigratingUser user; + private string rootFolder; + private List files; + private List folders; + private int foldersCount; + private int filesCount; + private long bytesTotal; + private NCStorages storages; + private Dictionary users; + private Dictionary groups; + private Dictionary matchingFileId; + private string folderCreation; + public NCMigratingFiles(NCMigratingUser user, NCStorages storages, string rootFolder, Action log) : base(log) + { + this.user = user; + this.rootFolder = rootFolder; + this.storages = storages; + } + + public override void Parse() + { + var drivePath = Directory.Exists(Path.Combine(rootFolder, "data", user.Key, "files")) ? + Path.Combine(rootFolder, "data", user.Key, "files") : null; + if (drivePath == null) return; + + files = new List(); + folders = new List(); + folderCreation = folderCreation != null ? folderCreation : DateTime.Now.ToString("dd.MM.yyyy"); + foreach (var entry in storages.FileCache) + { + string[] paths = entry.Path.Split('/'); + if (paths[0] != "files") continue; + + paths[0] = "NextCloud’s Files " + folderCreation; + entry.Path = string.Join("/", paths); + + if (paths.Length >= 1) + { + string tmpPath = drivePath; + for (var i = 1; i < paths.Length; i++) + { + tmpPath = Path.Combine(tmpPath, paths[i]); + } + if (Directory.Exists(tmpPath) || File.Exists(tmpPath)) + { + var attr = File.GetAttributes(tmpPath); + if (attr.HasFlag(FileAttributes.Directory)) + { + foldersCount++; + folders.Add(entry); + } + else + { + filesCount++; + var fi = new FileInfo(tmpPath); + bytesTotal += fi.Length; + files.Add(entry); + } + } + } + } + } + + public override void Migrate() + { + + if (!ShouldImport) return; + + var drivePath = Directory.Exists(Path.Combine(rootFolder, "data", user.Key, "files")) ? + Path.Combine(rootFolder, "data", user.Key) : null; + if (drivePath == null) return; + matchingFileId = new Dictionary(); + var foldersDict = new Dictionary(); + if (folders != null) + { + foreach (var folder in folders) + { + var split = folder.Path.Split('/'); + for (var i = 0; i < split.Length; i++) + { + var path = string.Join(Path.DirectorySeparatorChar.ToString(), split.Take(i + 1)); + if (foldersDict.ContainsKey(path)) continue; + var parentId = i == 0 ? Global.FolderMy : foldersDict[string.Join(Path.DirectorySeparatorChar.ToString(), split.Take(i))].ID; + try + { + var newFolder = Global.FileStorageService.CreateNewFolder(parentId.ToString(), split[i]); + foldersDict.Add(path, newFolder); + matchingFileId.Add(newFolder.ID, folder.FileId); + } + catch (Exception ex) + { + Log($"Couldn't create folder {path}", ex); + } + } + } + } + + if (files != null) + { + foreach (var file in files) + { + string[] maskPaths = file.Path.Split('/'); + if (maskPaths[0] == "NextCloud’s Files " + DateTime.Now.ToString("dd.MM.yyyy")) + { + maskPaths[0] = "files"; + } + var maskPath = string.Join(Path.DirectorySeparatorChar.ToString(), maskPaths); + var parentPath = Path.GetDirectoryName(file.Path); + try + { + var realPath = Path.Combine(drivePath, maskPath); + using (var fs = new FileStream(realPath, FileMode.Open)) + using (var fileDao = Global.DaoFactory.GetFileDao()) + using (var folderDao = Global.DaoFactory.GetFolderDao()) + { + var parentFolder = string.IsNullOrWhiteSpace(parentPath) ? folderDao.GetFolder(Global.FolderMy) : foldersDict[parentPath]; + + var newFile = new ASCFile + { + FolderID = parentFolder.ID, + Comment = FilesCommonResource.CommentCreate, + Title = Path.GetFileName(file.Path), + ContentLength = fs.Length + }; + newFile = fileDao.SaveFile(newFile, fs); + matchingFileId.Add(newFile.ID, file.FileId); + } + } + catch(Exception ex) + { + Log($"Couldn't create file {parentPath}/{Path.GetFileName(file.Path)}", ex); + } + } + } + + foreach (var item in matchingFileId) + { + var list = new ItemList(); + var entryIsFile = files.Exists(el => el.FileId == item.Value) ? true : false; + var entry = entryIsFile ? files.Find(el => el.FileId == item.Value) : folders.Find(el => el.FileId == item.Value); + if (entry.Share.Count == 0) continue; + foreach (var shareInfo in entry.Share) + { + if (shareInfo.ShareWith == null) continue; + var shareType = GetPortalShare(shareInfo.Premissions, entryIsFile); + users.TryGetValue(shareInfo.ShareWith, out var userToShare); + groups.TryGetValue(shareInfo.ShareWith, out var groupToShare); + + if(userToShare != null || groupToShare != null) + { + var entryGuid = userToShare == null ? groupToShare.Guid : userToShare.Guid; + list.Add(new AceWrapper + { + Share = shareType.Value, + SubjectId = entryGuid, + SubjectGroup = false + }); + } + } + if (!list.Any()) continue; + var aceCollection = new AceCollection + { + Entries = new ItemList { (entryIsFile ? "file_" : "folder_") + (int)item.Key }, + Aces = list, + Message = null + }; + + try + { + Global.FileStorageService.SetAceObject(aceCollection, false); + } + catch (Exception ex) + { + Log($"Couldn't change file permissions for {aceCollection.Entries.First()}", ex); + } + } + } + + public void SetUsersDict(IEnumerable users) + { + this.users = users.ToDictionary(user => user.Key, user => user); + } + + public void SetGroupsDict(IEnumerable groups) + { + this.groups = groups.ToDictionary(group => group.GroupName, group => group); + } + + private ASCShare? GetPortalShare(int role, bool entryType) + { + if (entryType) + { + if (role == 1 || role == 17) + return ASCShare.Read; + return ASCShare.ReadWrite;//permission = 19 => denySharing = true, permission = 3 => denySharing = false; ASCShare.ReadWrite + } + else + { + if (Array.Exists(new int[] { 1, 17, 9, 25, 5, 21, 13, 29, 3, 19, 11, 27 }, el => el == role)) + return ASCShare.Read; + return ASCShare.ReadWrite;//permission = 19||23 => denySharing = true, permission = 7||15 => denySharing = false; ASCShare.ReadWrite + } + } + } +} diff --git a/module/ASC.Migration/NextcloudWorkspace/Models/NCMigratingGroups.cs b/module/ASC.Migration/NextcloudWorkspace/Models/NCMigratingGroups.cs new file mode 100644 index 000000000..76c1004c6 --- /dev/null +++ b/module/ASC.Migration/NextcloudWorkspace/Models/NCMigratingGroups.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using ASC.Core; +using ASC.Core.Users; +using ASC.Migration.Core.Models; +using ASC.Migration.Resources; + +namespace ASC.Migration.NextcloudWorkspace.Models +{ + public class NCMigratingGroups : MigratingGroup + { + private string groupName; + private List userUidList; + private NCGroup Group; + private GroupInfo groupinfo; + public Guid Guid => groupinfo.ID; + public Dictionary usersGuidList; + public MigrationModules Module = new MigrationModules(); + public override List UserUidList => userUidList; + public override string GroupName => groupName; + public override string ModuleName => MigrationResource.ModuleNameGroups; + public NCMigratingGroups(NCGroup group, Action log) : base(log) + { + Group = group; + } + + public override void Parse() + { + groupName = Group.GroupGid; + groupinfo = new GroupInfo() + { + Name = Group.GroupGid + }; + userUidList = Group.UsersUid; + Module = new MigrationModules(ModuleName, MigrationResource.OnlyofficeModuleNamePeople); + } + + public override void Migrate() + { + var existingGroups = CoreContext.UserManager.GetGroups().ToList(); + var oldGroup = existingGroups.Find(g => g.Name == groupinfo.Name); + if (oldGroup != null) + { + groupinfo = oldGroup; + } + else + { + groupinfo = CoreContext.UserManager.SaveGroupInfo(groupinfo); + } + foreach (var userGuid in usersGuidList) + { + UserInfo user; + try + { + user = CoreContext.UserManager.GetUsers(userGuid.Value); + if (user == Constants.LostUser) + { + throw new ArgumentNullException(); + } + if(!CoreContext.UserManager.IsUserInGroup(user.ID,groupinfo.ID)) + { + CoreContext.UserManager.AddUserIntoGroup(user.ID, groupinfo.ID); + } + } + catch (Exception ex) + { + //Think about the text of the error + Log($"Couldn't to add user {userGuid.Key} to group {groupName} ", ex); + } + } + } + } +} diff --git a/module/ASC.Migration/NextcloudWorkspace/Models/NCMigratingMail.cs b/module/ASC.Migration/NextcloudWorkspace/Models/NCMigratingMail.cs new file mode 100644 index 000000000..801264ff8 --- /dev/null +++ b/module/ASC.Migration/NextcloudWorkspace/Models/NCMigratingMail.cs @@ -0,0 +1,28 @@ + +using System; + +using ASC.Migration.Core.Models; +using ASC.Migration.Resources; + +namespace ASC.Migration.NextcloudWorkspace.Models +{ + public class NCMigratingMail : MigratingMail + { + private int messagesCount; + + public override int MessagesCount => messagesCount; + public override string ModuleName => MigrationResource.ModuleNameMail; + public override void Migrate() + { + throw new System.NotImplementedException(); + } + + public override void Parse() + { + messagesCount++; + throw new System.NotImplementedException(); + } + + public NCMigratingMail(Action log) : base(log) { } + } +} diff --git a/module/ASC.Migration/NextcloudWorkspace/Models/NCMigratingUser.cs b/module/ASC.Migration/NextcloudWorkspace/Models/NCMigratingUser.cs new file mode 100644 index 000000000..e9d501e8b --- /dev/null +++ b/module/ASC.Migration/NextcloudWorkspace/Models/NCMigratingUser.cs @@ -0,0 +1,179 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; + +using ASC.Core; +using ASC.Core.Users; +using ASC.Migration.Core.Models; +using ASC.Migration.Core.Models.Api; +using ASC.Migration.Resources; +using ASC.Web.Files.Resources; + +namespace ASC.Migration.NextcloudWorkspace.Models.Parse +{ + public class NCMigratingUser : MigratingUser + { + public override string Email => userInfo.Email; + + public override string DisplayName => userInfo.ToString(); + + public List ModulesList = new List(); + + public Guid Guid => userInfo.ID; + + public override string ModuleName => MigrationResource.ModuleNameUsers; + + public string ConnectionString { get; set; } + private string rootFolder; + private bool hasPhoto; + private string pathToPhoto; + private UserInfo userInfo; + private NCUser User; + private Regex emailRegex = new Regex(@"(\S*@\S*\.\S*)"); + private Regex phoneRegex = new Regex(@"(\+?\d+)"); + + public NCMigratingUser(string key, NCUser userData, string rootFolder, Action log) : base(log) + { + Key = key; + User = userData; + this.rootFolder = rootFolder; + } + + public override void Parse() + { + ModulesList.Add(new MigrationModules(ModuleName, MigrationResource.OnlyofficeModuleNamePeople)); + userInfo = new UserInfo() + { + ID = Guid.NewGuid() + }; + var drivePath = Directory.Exists(Path.Combine(rootFolder, "data", Key, "cache")) ? + Path.Combine(rootFolder, "data", Key, "cache") : null; + if(drivePath == null) + { + hasPhoto = false; + } + else + { + pathToPhoto = File.Exists(Path.Combine(drivePath, "avatar_upload")) ? Directory.GetFiles(drivePath, "avatar_upload")[0] : null; + hasPhoto = pathToPhoto != null ? true : false; + } + + if (!hasPhoto) + { + var appdataDir = Directory.GetDirectories(Path.Combine(rootFolder, "data")).Where(dir => dir.Split(Path.DirectorySeparatorChar).Last().StartsWith("appdata_")).First(); + if (appdataDir != null) + { + var pathToAvatarDir = Path.Combine(appdataDir, "avatar", Key); + pathToPhoto = File.Exists(Path.Combine(pathToAvatarDir, "generated")) ? null : Path.Combine(pathToAvatarDir, "avatar.jpg"); + hasPhoto = pathToPhoto != null ? true : false; + } + } + string[] userName = User.Data.DisplayName.Split(' '); + userInfo.FirstName = userName[0]; + if(userName.Length > 1) + { + userInfo.LastName = userName[1]; + } + userInfo.Location = User.Data.Address; + if (User.Data.Phone != null) + { + var phone = phoneRegex.Match(User.Data.Phone); + if (phone.Success) + { + userInfo.Contacts.Add(phone.Groups[1].Value); + } + } + if (User.Data.Twitter != null) + { + userInfo.Contacts.Add(User.Data.Twitter); + + } + if (User.Data.Email != null && User.Data.Email != "" && User.Data.Email != "NULL") + { + var email = emailRegex.Match(User.Data.Email); + if (email.Success) + { + userInfo.Email = email.Groups[1].Value; + } + userInfo.UserName = userInfo.Email.Split('@').First(); + } + userInfo.ActivationStatus = EmployeeActivationStatus.Pending; + Action log = (m, e) => { Log($"{DisplayName} ({Email}): {m}", e); }; + + MigratingContacts = new NCMigratingContacts(this, User.Addressbooks, log); + MigratingContacts.Parse(); + if (MigratingContacts.ContactsCount !=0) + { + ModulesList.Add(new MigrationModules(MigratingContacts.ModuleName, MigrationResource.OnlyofficeModuleNameMail)); + } + + MigratingCalendar = new NCMigratingCalendar(User.Calendars, log); + //MigratingCalendar.Parse(); + if(MigratingCalendar.CalendarsCount!=0) + { + ModulesList.Add(new MigrationModules(MigratingCalendar.ModuleName, MigrationResource.OnlyofficeModuleNameCalendar)); + } + + MigratingFiles = new NCMigratingFiles(this, User.Storages, rootFolder, log); + MigratingFiles.Parse(); + if(MigratingFiles.FoldersCount != 0 || MigratingFiles.FilesCount != 0) + { + ModulesList.Add(new MigrationModules(MigratingFiles.ModuleName, MigrationResource.OnlyofficeModuleNameDocuments)); + } + + MigratingMail = new NCMigratingMail(log); + } + + public void dataСhange(MigratingApiUser frontUser) + { + if (userInfo.Email == null) + { + userInfo.Email = frontUser.Email; + if(userInfo.UserName == null) + { + userInfo.UserName = userInfo.Email.Split('@').First(); + } + } + if(userInfo.LastName == null) + { + userInfo.LastName = "NOTPROVIDED"; + } + } + + public override void Migrate() + { + if (string.IsNullOrWhiteSpace(userInfo.FirstName)) + { + userInfo.FirstName = FilesCommonResource.UnknownFirstName; + } + if (string.IsNullOrWhiteSpace(userInfo.LastName)) + { + userInfo.LastName = FilesCommonResource.UnknownLastName; + } + + var saved = CoreContext.UserManager.GetUserByEmail(userInfo.Email); + if (saved != Constants.LostUser) + { + saved.Contacts = saved.Contacts.Union(userInfo.Contacts).ToList(); + userInfo.ID = saved.ID; + } + else + { + saved = CoreContext.UserManager.SaveUserInfo(userInfo); + } + if (hasPhoto) + { + using (var ms = new MemoryStream()) + { + using (var fs = File.OpenRead(pathToPhoto)) + { + fs.CopyTo(ms); + } + CoreContext.UserManager.SaveUserPhoto(saved.ID, ms.ToArray()); + } + } + } + } +} diff --git a/module/ASC.Migration/NextcloudWorkspace/Models/NCMigrationInfo.cs b/module/ASC.Migration/NextcloudWorkspace/Models/NCMigrationInfo.cs new file mode 100644 index 000000000..128c26168 --- /dev/null +++ b/module/ASC.Migration/NextcloudWorkspace/Models/NCMigrationInfo.cs @@ -0,0 +1,9 @@ + +using ASC.Migration.Core.Models; + +namespace ASC.Migration.NextcloudWorkspace.Models.Parse +{ + public class NCMigrationInfo : MigrationInfo + { + } +} diff --git a/module/ASC.Migration/NextcloudWorkspace/Models/Parse/NCAddressbooks.cs b/module/ASC.Migration/NextcloudWorkspace/Models/Parse/NCAddressbooks.cs new file mode 100644 index 000000000..3a704f8bf --- /dev/null +++ b/module/ASC.Migration/NextcloudWorkspace/Models/Parse/NCAddressbooks.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace ASC.Migration.NextcloudWorkspace.Models +{ + public class NCAddressbooks + { + public int Id { get; set; } + public List Cards { get; set; } + } + + public class NCCards + { + public int Id { get; set; } + public byte[] CardData { get; set; } + } +} diff --git a/module/ASC.Migration/NextcloudWorkspace/Models/Parse/NCCalendars.cs b/module/ASC.Migration/NextcloudWorkspace/Models/Parse/NCCalendars.cs new file mode 100644 index 000000000..b8106e255 --- /dev/null +++ b/module/ASC.Migration/NextcloudWorkspace/Models/Parse/NCCalendars.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace ASC.Migration.NextcloudWorkspace.Models +{ + public class NCCalendars + { + public int Id { get; set; } + public List CalendarObject { get; set; } + public string DisplayName { get; set; } + } + + public class NCCalendarObjects + { + public int Id { get; set; } + public byte[] CalendarData { get; set; } + } +} diff --git a/module/ASC.Migration/NextcloudWorkspace/Models/Parse/NCContact.cs b/module/ASC.Migration/NextcloudWorkspace/Models/Parse/NCContact.cs new file mode 100644 index 000000000..c32858914 --- /dev/null +++ b/module/ASC.Migration/NextcloudWorkspace/Models/Parse/NCContact.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace ASC.Migration.NextcloudWorkspace.Models +{ + public class NCContact + { + public string ContactName { get; set; } + public string Address { get; set; } + public string Description { get; set; } + + public List Emails { get; set; } + public List Phones { get; set; } + } +} diff --git a/module/ASC.Migration/NextcloudWorkspace/Models/Parse/NCGroup.cs b/module/ASC.Migration/NextcloudWorkspace/Models/Parse/NCGroup.cs new file mode 100644 index 000000000..a75de90f9 --- /dev/null +++ b/module/ASC.Migration/NextcloudWorkspace/Models/Parse/NCGroup.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ASC.Migration.NextcloudWorkspace.Models +{ + public class NCGroup + { + public string GroupGid { get; set; } + public List UsersUid { get; set; } + } +} diff --git a/module/ASC.Migration/NextcloudWorkspace/Models/Parse/NCStorages.cs b/module/ASC.Migration/NextcloudWorkspace/Models/Parse/NCStorages.cs new file mode 100644 index 000000000..0bcbb115a --- /dev/null +++ b/module/ASC.Migration/NextcloudWorkspace/Models/Parse/NCStorages.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; + +namespace ASC.Migration.NextcloudWorkspace.Models +{ + public class NCStorages + { + public int NumericId { get; set; } + public string Id { get; set; } + public List FileCache { get; set; } + } + + public class NCFileCache + { + public int FileId { get; set; } + public string Path { get; set; } + public List Share { get; set; } + } + + public class NCShare + { + public int Id { get; set; } + public string ShareWith { get; set; } + public int Premissions { get; set; } + } +} diff --git a/module/ASC.Migration/NextcloudWorkspace/Models/Parse/NCUser.cs b/module/ASC.Migration/NextcloudWorkspace/Models/Parse/NCUser.cs new file mode 100644 index 000000000..f0398791d --- /dev/null +++ b/module/ASC.Migration/NextcloudWorkspace/Models/Parse/NCUser.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +namespace ASC.Migration.NextcloudWorkspace.Models +{ + public class NCUser + { + public string Uid { get; set; } + public NCUserData Data { get; set; } + public NCAddressbooks Addressbooks { get; set; } + public List Calendars { get; set; } + public NCStorages Storages { get; set; } + } + + public class NCUserData + { + public string DisplayName { get; set; } + public string Address { get; set; } + public string Email { get; set; } + public string Phone { get; set; } + public string Twitter { get; set; } + } +} diff --git a/module/ASC.Migration/NextcloudWorkspace/NextcloudWorkspaceMigration.cs b/module/ASC.Migration/NextcloudWorkspace/NextcloudWorkspaceMigration.cs new file mode 100644 index 000000000..2e88bf4d5 --- /dev/null +++ b/module/ASC.Migration/NextcloudWorkspace/NextcloudWorkspaceMigration.cs @@ -0,0 +1,511 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; + +using ASC.Core; +using ASC.Migration.Core; +using ASC.Migration.Core.Models; +using ASC.Migration.Core.Models.Api; +using ASC.Migration.NextcloudWorkspace.Models; +using ASC.Migration.NextcloudWorkspace.Models.Parse; +using ASC.Migration.Resources; + +namespace ASC.Migration.NextcloudWorkspace +{ + [ApiMigrator("NextcloudMigrate")] + public class NextcloudWorkspaceMigration : AbstractMigration + { + private string takeouts; + public string[] tempParse; + private string tmpFolder; + + public override void Init(string path, CancellationToken cancellationToken) + { + this.cancellationToken = cancellationToken; + var files = Directory.GetFiles(path); + if (!files.Any() || !files.Any(f => f.EndsWith(".zip"))) + { + throw new Exception("Folder must not be empty and should contain only .zip files."); + } + for (var i=0; i < files.Length; i++) + { + if (files[i].EndsWith(".zip")) + { + DateTime creationTime = File.GetCreationTimeUtc(files[i]); + takeouts = files[i]; + } + } + + migrationInfo = new NCMigrationInfo(); + migrationInfo.MigratorName = this.GetType().CustomAttributes.First().ConstructorArguments.First().Value.ToString(); + tmpFolder = path; + } + public override Task Parse() + { + ReportProgress(0, MigrationResource.Unzipping); + try + { + try + { + ZipFile.ExtractToDirectory(takeouts, tmpFolder); + } + catch(Exception ex) + { + Log($"Couldn't to unzip {takeouts}", ex); + } + if (cancellationToken.IsCancellationRequested) { ReportProgress(100, MigrationResource.MigrationCanceled); return null; } + ReportProgress(30, MigrationResource.UnzippingFinished); + var bdFile = ""; + try + { + bdFile = Directory.GetFiles(Directory.GetDirectories(tmpFolder)[0], "*.bak")[0]; + if (bdFile == null) + { + throw new Exception(); + } + } + catch(Exception ex) + { + migrationInfo.failedArchives.Add(Path.GetFileName(takeouts)); + Log("Archive must not be empty and should contain .bak files.", ex); + } + ReportProgress(40, MigrationResource.DumpParse); + var users = DBExtractUser(bdFile); + var progress = 40; + foreach (var item in users) + { + if (cancellationToken.IsCancellationRequested) { ReportProgress(100, MigrationResource.MigrationCanceled); return null; } + ReportProgress(progress, MigrationResource.DataProcessing); + progress += 30 / users.Count; + if (item.Data.DisplayName != null) + { + try + { + string[] userName = item.Data.DisplayName.Split(' '); + item.Data.DisplayName = userName.Length > 1 ? String.Format("{0} {1}", userName[0], userName[1]).Trim() : userName[0].Trim(); + var user = new NCMigratingUser(item.Uid, item, Directory.GetDirectories(tmpFolder)[0], Log); + user.Parse(); + foreach (var element in user.ModulesList) + { + if (!migrationInfo.Modules.Exists(x => x.MigrationModule == element.MigrationModule)) + { + migrationInfo.Modules.Add(new MigrationModules(element.MigrationModule, element.Module)); + } + } + migrationInfo.Users.Add(item.Uid, user); + } + catch (Exception ex) + { + Log($"Couldn't parse user {item.Data.DisplayName}", ex); + } + } + } + + var groups = DBExtractGroup(bdFile); + progress = 80; + foreach (var item in groups) + { + ReportProgress(progress, MigrationResource.DataProcessing); + progress += 10 / groups.Count; + var group = new NCMigratingGroups(item, Log); + group.Parse(); + if (group.Module.MigrationModule != null) + { + migrationInfo.Groups.Add(group); + if (!migrationInfo.Modules.Exists(x => x.MigrationModule == group.Module.MigrationModule)) + { + migrationInfo.Modules.Add(new MigrationModules(group.Module.MigrationModule, group.Module.Module)); + } + } + } + ReportProgress(90, MigrationResource.ClearingTemporaryData); + } + catch (Exception ex) + { + migrationInfo.failedArchives.Add(Path.GetFileName(takeouts)); + Log($"Couldn't parse users from {Path.GetFileNameWithoutExtension(takeouts)} archive", ex); + } + ReportProgress(100, MigrationResource.DataProcessingCompleted); + return Task.FromResult(migrationInfo.ToApiInfo()); + } + + public List DBExtractGroup(string dbFile) { + var groups = new List(); + + var sqlFile = File.ReadAllText(dbFile); + + var groupList = GetDumpChunk("oc_groups", sqlFile); + if (groupList == null) return groups; + + foreach (var group in groupList) + { + groups.Add(new NCGroup + { + GroupGid = group.Split(',').First().Trim('\''), + UsersUid = new List() + }); + } + + var usersInGroups = GetDumpChunk("oc_group_user", sqlFile); + foreach(var user in usersInGroups) + { + var userGroupGid = user.Split(',').First().Trim('\''); + var userUid = user.Split(',').Last().Trim('\''); + groups.Find(ggid => userGroupGid == ggid.GroupGid).UsersUid.Add(userUid); + } + + return groups; + } + + public List DBExtractUser(string dbFile) + { + var userDataList = new Dictionary(); + + var sqlFile = File.ReadAllText(dbFile); + + var accounts = GetDumpChunk("oc_accounts", sqlFile); + if (accounts == null) return userDataList.Values.ToList(); + + foreach (var account in accounts) + { + var userId = account.Split(',').First().Trim('\''); + + userDataList.Add(userId, new NCUser + { + Uid = userId, + Data = new NCUserData(), + Addressbooks = null, + Calendars = new List(), + Storages = new NCStorages() + }); + } + + var accountsData = GetDumpChunk("oc_accounts_data", sqlFile); + if (accountsData != null) + { + foreach (var accountData in accountsData) + { + var values = accountData.Split(',') + .Select(s => s.Trim('\'')).ToArray(); + userDataList.TryGetValue(values[1], out var user); + if (user == null) continue; + + switch (values[2]) + { + case "displayname": + user.Data.DisplayName = values[3]; + break; + case "address": + user.Data.Address = values[3]; + break; + case "email": + user.Data.Email = values[3]; + break; + case "phone": + user.Data.Phone = values[3]; + break; + case "twitter": + user.Data.Twitter = values[3]; + break; + } + } + } + else + { + throw new Exception(); + } + + var calendarsData = GetDumpChunk("oc_calendars", sqlFile); + if (calendarsData != null) + { + foreach (var calendarData in calendarsData) + { + var values = calendarData.Split(',') + .Select(s => s.Trim('\'')).ToArray(); + var uid = values[1].Split('/').Last(); + userDataList.TryGetValue(uid, out var user); + if (user == null) continue; + + user.Calendars.Add(new NCCalendars() + { + Id = int.Parse(values[0]), + CalendarObject = new List(), + DisplayName = values[2] + }); + } + } + + var calendars = userDataList.Values + .SelectMany(u => u.Calendars) + .ToDictionary(c => c.Id, c => c); + var calendarObjects = GetDumpChunk("oc_calendarobjects", sqlFile); + if (calendarObjects != null) + { + foreach (var calendarObject in calendarObjects) + { + var values = calendarObject.Split(',') + .Select(s => s.Trim('\'')).ToArray(); + var calId = int.Parse(values[3]); + calendars.TryGetValue(calId, out var cal); + if (cal == null) continue; + + cal.CalendarObject.Add(new NCCalendarObjects() + { + Id = int.Parse(values[0]), + CalendarData = Encoding.UTF8.GetBytes(values[1] + .Replace("\\r", "") + .Replace("\\n", "\n")), + }); + } + } + + var addressBooks = GetDumpChunk("oc_addressbooks", sqlFile); + if (addressBooks != null) + { + foreach (var addressBook in addressBooks) + { + var values = addressBook.Split(',') + .Select(s => s.Trim('\'')).ToArray(); + var uid = values[1].Split('/').Last(); + userDataList.TryGetValue(uid, out var user); + if (user == null) continue; + user.Addressbooks = new NCAddressbooks(); + user.Addressbooks.Id = int.Parse(values[0]); + user.Addressbooks.Cards = new List(); + } + } + + var addressBooksDict = userDataList.Values + .Select(u => u.Addressbooks) + .Where(x => x != null) + .ToDictionary(b => b.Id, b => b); + var cards = GetDumpChunk("oc_cards", sqlFile); + if (cards != null) + { + foreach (var card in cards) + { + var values = card.Split(',') + .Select(s => s.Trim('\'')).ToArray(); + var bookId = int.Parse(values[1]); + addressBooksDict.TryGetValue(bookId, out var book); + if (book == null) continue; + + book.Cards.Add(new NCCards() + { + Id = int.Parse(values[0]), + CardData = Encoding.UTF8.GetBytes(values[2] + .Replace("\\r", "") + .Replace("\\n", "\n")), + }); + } + } + + var storages = GetDumpChunk("oc_storages", sqlFile); + if (storages != null) + { + foreach (var storage in storages) + { + var values = storage.Split(',') + .Select(s => s.Trim('\'')).ToArray(); + var uid = values[1].Split(':').Last(); + userDataList.TryGetValue(uid, out var user); + if (user == null) continue; + + user.Storages.NumericId = int.Parse(values[0]); + user.Storages.Id = values[1]; + user.Storages.FileCache = new List(); + } + } + + var storagesDict = userDataList.Values + .Select(u => u.Storages) + .ToDictionary(s => s.NumericId, s => s); + var fileCaches = GetDumpChunk("oc_filecache", sqlFile); + if (fileCaches != null) + { + foreach (var cache in fileCaches) + { + var values = cache.Split(',') + .Select(s => s.Trim('\'')).ToArray(); + var storageId = int.Parse(values[1]); + storagesDict.TryGetValue(storageId, out var storage); + if (storage == null) continue; + + storage.FileCache.Add(new NCFileCache() + { + FileId = int.Parse(values[0]), + Path = values[2], + Share = new List() + }); + } + } + + var files = userDataList.Values + .SelectMany(u => u.Storages.FileCache) + .ToDictionary(f => f.FileId, f => f); + var shares = GetDumpChunk("oc_share", sqlFile); + if (shares != null) + { + foreach (var share in shares) + { + var values = share.Split(',') + .Select(s => s.Trim('\'')).ToArray(); + var fileId = int.Parse(values[10]); + files.TryGetValue(fileId, out var file); + if (file == null) continue; + + file.Share.Add(new NCShare() + { + Id = int.Parse(values[0]), + ShareWith = values[2], + Premissions = int.Parse(values[12]) + }); + } + } + + return userDataList.Values.ToList(); + } + + private IEnumerable GetDumpChunk(string tableName, string dump) + { + var regex = new Regex($"INSERT INTO `{tableName}` VALUES (.*);"); + var match = regex.Match(dump); + if (!match.Success) return null; + + var entryRegex = new Regex(@"(\(.*?\))[,;]"); + var accountDataMatches = entryRegex.Matches(match.Groups[1].Value + ";"); + return accountDataMatches.Cast() + .Select(m => m.Groups[1].Value.Trim(new[] { '(', ')' })); + } + + public override Task Migrate(MigrationApiInfo migrationApiInfo) + { + ReportProgress(0, MigrationResource.PreparingForMigration); + migrationInfo.Merge(migrationApiInfo); + + var usersForImport = migrationInfo.Users + .Where(u => u.Value.ShouldImport) + .Select(u => u.Value); + importedUsers = new List(); + var failedUsers = new List(); + var usersCount = usersForImport.Count(); + var progressStep = 25 / usersCount; + var i = 1; + foreach (var user in usersForImport) + { + if (cancellationToken.IsCancellationRequested) { ReportProgress(100, MigrationResource.MigrationCanceled); return null; } + ReportProgress(GetProgress() + progressStep, String.Format(MigrationResource.UserMigration, user.DisplayName, i++, usersCount)); + try + { + user.dataСhange(migrationApiInfo.Users.Find(element => element.Key == user.Key)); + user.Migrate(); + importedUsers.Add(user.Guid); + } + catch (Exception ex) + { + failedUsers.Add(user); + Log($"Couldn't migrate user {user.DisplayName} ({user.Email})", ex); + } + } + + var groupsForImport = migrationInfo.Groups + .Where(g => g.ShouldImport) + .Select(g => g); + var groupsCount = groupsForImport.Count(); + if (groupsCount != 0) + { + progressStep = 25 / groupsForImport.Count(); + //Create all groups + i = 1; + foreach (var group in groupsForImport) + { + if (cancellationToken.IsCancellationRequested) { ReportProgress(100, MigrationResource.MigrationCanceled); return null; } + ReportProgress(GetProgress() + progressStep, String.Format(MigrationResource.GroupMigration, group.GroupName, i++, groupsCount)); + try + { + group.usersGuidList = migrationInfo.Users + .Where(user => group.UserUidList.Exists(u => user.Key == u)) + .Select(u => u) + .ToDictionary(k => k.Key, v => v.Value.Guid); + group.Migrate(); + } + catch (Exception ex) + { + Log($"Couldn't migrate group {group.GroupName} ", ex); + } + } + } + + i = 1; + foreach (var user in usersForImport) + { + if (cancellationToken.IsCancellationRequested) { ReportProgress(100, MigrationResource.MigrationCanceled); return null; } + if (failedUsers.Contains(user)) + { + ReportProgress(GetProgress() + progressStep, String.Format(MigrationResource.UserSkipped, user.DisplayName, i, usersCount)); + continue; + } + + var smallStep = progressStep / 3; + + try + { + user.MigratingContacts.Migrate(); + } + catch (Exception ex) + { + Log($"Couldn't migrate user {user.DisplayName} ({user.Email}) contacts", ex); + } + finally + { + ReportProgress(GetProgress() + smallStep, String.Format(MigrationResource.MigratingUserContacts, user.DisplayName, i, usersCount)); + } + + /*try + { + user.MigratingCalendar.Migrate(); + } + catch (Exception ex) + { + Log($"Couldn't migrate user {user.DisplayName} ({user.Email}) calendar", ex); + } + finally + { + ReportProgress(GetProgress() + smallStep, String.Format(MigrationResource.UserCalendarMigration, user.DisplayName, i, usersCount)); + }*/ + + try + { + var currentUser = SecurityContext.CurrentAccount; + SecurityContext.AuthenticateMe(user.Guid); + user.MigratingFiles.SetUsersDict(usersForImport.Except(failedUsers)); + user.MigratingFiles.SetGroupsDict(groupsForImport); + user.MigratingFiles.Migrate(); + SecurityContext.AuthenticateMe(currentUser.ID); + } + catch (Exception ex) + { + Log($"Couldn't migrate user {user.DisplayName} ({user.Email}) files", ex); + } + finally + { + ReportProgress(GetProgress() + smallStep, String.Format(MigrationResource.MigratingUserFiles, user.DisplayName, i, usersCount)); + } + i++; + } + + if (Directory.Exists(tmpFolder)) + { + Directory.Delete(tmpFolder, true); + } + ReportProgress(100, MigrationResource.MigrationCompleted); + return Task.CompletedTask; + } + } +} diff --git a/module/ASC.Migration/OwnCloud/Models/OCMigratingCalendar.cs b/module/ASC.Migration/OwnCloud/Models/OCMigratingCalendar.cs new file mode 100644 index 000000000..c7a23acd5 --- /dev/null +++ b/module/ASC.Migration/OwnCloud/Models/OCMigratingCalendar.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Migration.Core.Models; +using ASC.Migration.Resources; + +using Ical.Net; + +namespace ASC.Migration.OwnCloud.Models +{ + public class OCMigratingCalendar : MigratingCalendar + { + public override int CalendarsCount => calendars.Count; + + public override int EventsCount => calendars.Count > 0 && calendars.Values.First() != null ? calendars.Values.SelectMany(c => c.SelectMany(x => x.Events)).Count() : calendars.Count; + public override string ModuleName => MigrationResource.ModuleNameCalendar; + + private List userCalendars; + private Dictionary calendars = new Dictionary(); + public OCMigratingCalendar(List calendars, Action log) : base(log) + { + this.userCalendars = calendars; + } + + public override void Parse() + { + //foreach(var calendar in this.userCalendars) + //{ + // var calendarString = ""; + // foreach (var calendarEvent in calendar.CalendarObject) + // { + // string calendarEventByteToString = Encoding.Default.GetString(calendarEvent.CalendarData); + // if (calendarString != "") + // { + // int start = calendarEventByteToString.IndexOf("BEGIN:VEVENT"); + // int end = calendarEventByteToString.IndexOf("END:VEVENT\r\n") + "END:VEVENT\r\n".Length; + // string newEvent = calendarEventByteToString.Substring(start, end - start); + + // int insertionPoint = calendarString.LastIndexOf("END:VEVENT\r\n") + "END:VEVENT\r\n".Length; + // calendarString = calendarString.Insert(insertionPoint, newEvent); + // } + // else + // { + // calendarString = calendarEventByteToString; + // } + // } + // var events = DDayICalParser.DeserializeCalendar(calendarString); + // calendars.Add(calendar.DisplayName, events); + //} + } + + public override void Migrate() + { + if (!ShouldImport) return; + throw new NotImplementedException(); + } + } +} diff --git a/module/ASC.Migration/OwnCloud/Models/OCMigratingContacts.cs b/module/ASC.Migration/OwnCloud/Models/OCMigratingContacts.cs new file mode 100644 index 000000000..9e1580a00 --- /dev/null +++ b/module/ASC.Migration/OwnCloud/Models/OCMigratingContacts.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using ASC.Core; +using ASC.Mail.Core.Engine; +using ASC.Mail.Core.Entities; +using ASC.Mail.Enums; +using ASC.Migration.Core.Models; +using ASC.Migration.Resources; + +using FolkerKinzel.VCards; + +namespace ASC.Migration.OwnCloud.Models +{ + public class OCMigratingContacts : MigratingContacts + { + public override int ContactsCount => contacts.Count; + public override string ModuleName => MigrationResource.NextcloudModuleNameContacts; + + private List contacts = new List(); + private OCMigratingUser user; + private OCAddressbooks addressbooks; + + public OCMigratingContacts(OCMigratingUser user, OCAddressbooks addressbooks, Action log) : base(log) + { + this.user = user; + this.addressbooks = addressbooks; + } + + public override void Parse() + { + if (this.addressbooks != null) + { + if (this.addressbooks.Cards != null) + { + foreach (var vcardByte in this.addressbooks.Cards) + { + var vcard = VCard.Parse(Encoding.Default.GetString(vcardByte.CardData)); + foreach (var item in vcard) + { + if (item.EmailAddresses == null || !item.EmailAddresses.Any()) continue; + + var contact = new OCContact() + { + Emails = item.EmailAddresses.Select(v => v.Value).Distinct().ToList() + }; + + if (item.Addresses != null && item.Addresses.Any()) contact.Address = item.Addresses.First().Value.ToString(); + if (item.DisplayNames != null && item.DisplayNames.Any()) contact.ContactName = item.DisplayNames.First().Value.ToString(); + if (item.Notes != null && item.Notes.Any()) contact.Description = string.Join("\n", item.Notes.Select(v => v.Value)); + if (item.PhoneNumbers != null && item.PhoneNumbers.Any()) contact.Phones = item.PhoneNumbers.Select(v => v.Value).Distinct().ToList(); + + contacts.Add(contact); + } + } + } + } + } + + public override void Migrate() + { + if (!ShouldImport) return; + + var engine = new ContactEngine(CoreContext.TenantManager.GetCurrentTenant().TenantId, user.Guid.ToString()); + var cards = GetContactCards(); + foreach (var card in cards) + { + try + { + engine.SaveContactCard(card); + } + catch (Exception ex) + { + Log($"Couldn't save contactCard {card.ContactInfo.ContactName}", ex); + } + } + } + + private List GetContactCards() + { + var tenantId = CoreContext.TenantManager.GetCurrentTenant().TenantId; + var userId = user.Guid.ToString(); + + var portalContacts = new List(); + foreach (var gwsContact in contacts) + { + var portalContact = new Contact() + { + Type = ContactType.Personal, + ContactName = gwsContact.ContactName, + Address = gwsContact.Address, + Tenant = tenantId, + User = userId + }; + + var infos = new List(); + if (gwsContact.Emails != null) + { + infos.AddRange(gwsContact + .Emails.Select(e => new ContactInfo() { Data = e, Type = (int)ContactInfoType.Email, Tenant = tenantId, User = userId })); + } + if (gwsContact.Phones != null) + { + infos.AddRange(gwsContact + .Phones.Select(p => new ContactInfo() { Data = p, Type = (int)ContactInfoType.Phone, Tenant = tenantId, User = userId })); + } + try + { + portalContacts.Add(new ContactCard(portalContact, infos)); + } + catch (Exception ex) + { + Log($"Couldn't create contactCard {gwsContact.ContactName}", ex); + } + } + + return portalContacts; + } + } +} diff --git a/module/ASC.Migration/OwnCloud/Models/OCMigratingFiles.cs b/module/ASC.Migration/OwnCloud/Models/OCMigratingFiles.cs new file mode 100644 index 000000000..b4aadfb90 --- /dev/null +++ b/module/ASC.Migration/OwnCloud/Models/OCMigratingFiles.cs @@ -0,0 +1,232 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +using ASC.Files.Core; +using ASC.Migration.Core.Models; +using ASC.Migration.Resources; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Resources; +using ASC.Web.Files.Services.WCFService; + +using ASCFile = ASC.Files.Core.File; +using ASCShare = ASC.Files.Core.Security.FileShare; +using File = System.IO.File; + +namespace ASC.Migration.OwnCloud.Models +{ + public class OCMigratingFiles : MigratingFiles + { + public override int FoldersCount => foldersCount; + + public override int FilesCount => filesCount; + + public override long BytesTotal => bytesTotal; + + public override string ModuleName => MigrationResource.NextcloudModuleNameDocuments; + + private OCMigratingUser user; + private string rootFolder; + private List files; + private List folders; + private int foldersCount; + private int filesCount; + private long bytesTotal; + private OCStorages storages; + private Dictionary users; + private Dictionary groups; + private Dictionary matchingFileId; + private string folderCreation; + public OCMigratingFiles(OCMigratingUser user, OCStorages storages, string rootFolder, Action log) : base(log) + { + this.user = user; + this.rootFolder = rootFolder; + this.storages = storages; + } + + public override void Parse() + { + var drivePath = Directory.Exists(Path.Combine(rootFolder, "data", user.Key, "files")) ? + Path.Combine(rootFolder, "data", user.Key, "files") : null; + if (drivePath == null) return; + + files = new List(); + folders = new List(); + folderCreation = folderCreation != null ? folderCreation : DateTime.Now.ToString("dd.MM.yyyy"); + foreach (var entry in storages.FileCache) + { + string[] paths = entry.Path.Split('/'); + if (paths[0] != "files") continue; + + paths[0] = "OwnCloud’s Files " + folderCreation; + entry.Path = string.Join("/", paths); + + if (paths.Length >= 1) + { + string tmpPath = drivePath; + for (var i = 1; i < paths.Length; i++) + { + tmpPath = Path.Combine(tmpPath, paths[i]); + } + if (Directory.Exists(tmpPath) || File.Exists(tmpPath)) + { + var attr = File.GetAttributes(tmpPath); + if (attr.HasFlag(FileAttributes.Directory)) + { + foldersCount++; + folders.Add(entry); + } + else + { + filesCount++; + var fi = new FileInfo(tmpPath); + bytesTotal += fi.Length; + files.Add(entry); + } + } + } + } + } + + public override void Migrate() + { + + if (!ShouldImport) return; + + var drivePath = Directory.Exists(Path.Combine(rootFolder, "data", user.Key, "files")) ? + Path.Combine(rootFolder, "data", user.Key) : null; + if (drivePath == null) return; + matchingFileId = new Dictionary(); + var foldersDict = new Dictionary(); + if (folders != null) + { + foreach (var folder in folders) + { + var split = folder.Path.Split('/'); + for (var i = 0; i < split.Length; i++) + { + var path = string.Join(Path.DirectorySeparatorChar.ToString(), split.Take(i + 1)); + if (foldersDict.ContainsKey(path)) continue; + var parentId = i == 0 ? Global.FolderMy : foldersDict[string.Join(Path.DirectorySeparatorChar.ToString(), split.Take(i))].ID; + try + { + var newFolder = Global.FileStorageService.CreateNewFolder(parentId.ToString(), split[i]); + foldersDict.Add(path, newFolder); + matchingFileId.Add(newFolder.ID, folder.FileId); + } + catch (Exception ex) + { + Log($"Couldn't create folder {path}", ex); + } + } + } + } + + if (files != null) + { + foreach (var file in files) + { + string[] maskPaths = file.Path.Split('/'); + if (maskPaths[0] == "OwnCloud’s Files " + DateTime.Now.ToString("dd.MM.yyyy")) + { + maskPaths[0] = "files"; + } + var maskPath = string.Join(Path.DirectorySeparatorChar.ToString(), maskPaths); + var parentPath = Path.GetDirectoryName(file.Path); + try + { + var realPath = Path.Combine(drivePath, maskPath); + using (var fs = new FileStream(realPath, FileMode.Open)) + using (var fileDao = Global.DaoFactory.GetFileDao()) + using (var folderDao = Global.DaoFactory.GetFolderDao()) + { + var parentFolder = string.IsNullOrWhiteSpace(parentPath) ? folderDao.GetFolder(Global.FolderMy) : foldersDict[parentPath]; + + var newFile = new ASCFile + { + FolderID = parentFolder.ID, + Comment = FilesCommonResource.CommentCreate, + Title = Path.GetFileName(file.Path), + ContentLength = fs.Length + }; + newFile = fileDao.SaveFile(newFile, fs); + matchingFileId.Add(newFile.ID, file.FileId); + } + } + catch (Exception ex) + { + Log($"Couldn't create file {parentPath}/{Path.GetFileName(file.Path)}", ex); + } + } + } + + foreach (var item in matchingFileId) + { + var list = new ItemList(); + var entryIsFile = files.Exists(el => el.FileId == item.Value) ? true : false; + var entry = entryIsFile ? files.Find(el => el.FileId == item.Value) : folders.Find(el => el.FileId == item.Value); + if (entry.Share.Count == 0) continue; + foreach (var shareInfo in entry.Share) + { + if (shareInfo.ShareWith == null) continue; + var shareType = GetPortalShare(shareInfo.Premissions, entryIsFile); + users.TryGetValue(shareInfo.ShareWith, out var userToShare); + groups.TryGetValue(shareInfo.ShareWith, out var groupToShare); + + if (userToShare != null || groupToShare != null) + { + var entryGuid = userToShare == null ? groupToShare.Guid : userToShare.Guid; + list.Add(new AceWrapper + { + Share = shareType.Value, + SubjectId = entryGuid, + SubjectGroup = false + }); + } + } + if (!list.Any()) continue; + var aceCollection = new AceCollection + { + Entries = new ItemList { (entryIsFile ? "file_" : "folder_") + (int)item.Key }, + Aces = list, + Message = null + }; + + try + { + Global.FileStorageService.SetAceObject(aceCollection, false); + } + catch (Exception ex) + { + Log($"Couldn't change file permissions for {aceCollection.Entries.First()}", ex); + } + } + } + + public void SetUsersDict(IEnumerable users) + { + this.users = users.ToDictionary(user => user.Key, user => user); + } + + public void SetGroupsDict(IEnumerable groups) + { + this.groups = groups.ToDictionary(group => group.GroupName, group => group); + } + private ASCShare? GetPortalShare(int role, bool entryType) + { + if (entryType) + { + if (role == 1 || role == 17) + return ASCShare.Read; + return ASCShare.ReadWrite;//permission = 19 => denySharing = true, permission = 3 => denySharing = false; ASCShare.ReadWrite + } + else + { + if (Array.Exists(new int[] { 1, 17, 9, 25, 5, 21, 13, 29, 3, 19, 11, 27 }, el => el == role)) + return ASCShare.Read; + return ASCShare.ReadWrite;//permission = 19||23 => denySharing = true, permission = 7||15 => denySharing = false; ASCShare.ReadWrite + } + } + } +} diff --git a/module/ASC.Migration/OwnCloud/Models/OCMigratingGroups.cs b/module/ASC.Migration/OwnCloud/Models/OCMigratingGroups.cs new file mode 100644 index 000000000..6342d334a --- /dev/null +++ b/module/ASC.Migration/OwnCloud/Models/OCMigratingGroups.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using ASC.Core; +using ASC.Core.Users; +using ASC.Migration.Core.Models; +using ASC.Migration.Resources; + +namespace ASC.Migration.OwnCloud.Models +{ + public class OCMigratingGroups : MigratingGroup + { + private string groupName; + private List userUidList; + private OCGroup Group; + private GroupInfo groupinfo; + public Guid Guid => groupinfo.ID; + public Dictionary usersGuidList; + public MigrationModules Module = new MigrationModules(); + public override List UserUidList => userUidList; + public override string GroupName => groupName; + public override string ModuleName => MigrationResource.ModuleNameGroups; + public OCMigratingGroups(OCGroup group, Action log) : base(log) + { + Group = group; + } + + public override void Parse() + { + groupName = Group.GroupGid; + groupinfo = new GroupInfo() + { + Name = Group.GroupGid + }; + userUidList = Group.UsersUid; + Module = new MigrationModules(ModuleName, MigrationResource.OnlyofficeModuleNamePeople); + } + + public override void Migrate() + { + var existingGroups = CoreContext.UserManager.GetGroups().ToList(); + var oldGroup = existingGroups.Find(g => g.Name == groupinfo.Name); + if (oldGroup != null) + { + groupinfo = oldGroup; + } + else + { + groupinfo = CoreContext.UserManager.SaveGroupInfo(groupinfo); + } + foreach (var userGuid in usersGuidList) + { + UserInfo user; + try + { + user = CoreContext.UserManager.GetUsers(userGuid.Value); + if (user == Constants.LostUser) + { + throw new ArgumentNullException(); + } + if (!CoreContext.UserManager.IsUserInGroup(user.ID, groupinfo.ID)) + { + CoreContext.UserManager.AddUserIntoGroup(user.ID, groupinfo.ID); + } + } + catch (Exception ex) + { + //Think about the text of the error + Log($"Couldn't to add user {userGuid.Key} to group {groupName} ", ex); + } + } + } + } +} diff --git a/module/ASC.Migration/OwnCloud/Models/OCMigratingMail.cs b/module/ASC.Migration/OwnCloud/Models/OCMigratingMail.cs new file mode 100644 index 000000000..6b446ced7 --- /dev/null +++ b/module/ASC.Migration/OwnCloud/Models/OCMigratingMail.cs @@ -0,0 +1,27 @@ +using System; + +using ASC.Migration.Core.Models; +using ASC.Migration.Resources; + +namespace ASC.Migration.OwnCloud.Models +{ + public class OCMigratingMail : MigratingMail + { + private int messagesCount; + + public override int MessagesCount => messagesCount; + public override string ModuleName => MigrationResource.ModuleNameMail; + public override void Migrate() + { + throw new System.NotImplementedException(); + } + + public override void Parse() + { + messagesCount++; + throw new System.NotImplementedException(); + } + + public OCMigratingMail(Action log) : base(log) { } + } +} diff --git a/module/ASC.Migration/OwnCloud/Models/OCMigratingUser.cs b/module/ASC.Migration/OwnCloud/Models/OCMigratingUser.cs new file mode 100644 index 000000000..03cde27bc --- /dev/null +++ b/module/ASC.Migration/OwnCloud/Models/OCMigratingUser.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; + +using ASC.Core; +using ASC.Core.Users; +using ASC.Migration.Core.Models; +using ASC.Migration.Core.Models.Api; +using ASC.Migration.Resources; +using ASC.Web.Files.Resources; + +namespace ASC.Migration.OwnCloud.Models +{ + public class OCMigratingUser : MigratingUser + { + public override string Email => userInfo.Email; + + public override string DisplayName => userInfo.ToString(); + + public List ModulesList = new List(); + + public Guid Guid => userInfo.ID; + + public override string ModuleName => MigrationResource.ModuleNameUsers; + + public string ConnectionString { get; set; } + private string rootFolder; + private bool hasPhoto; + private string pathToPhoto; + private UserInfo userInfo; + private OCUser User; + private Regex emailRegex = new Regex(@"(\S*@\S*\.\S*)"); + + public OCMigratingUser(string key, OCUser userData, string rootFolder, Action log) : base(log) + { + Key = key; + User = userData; + this.rootFolder = rootFolder; + } + + public override void Parse() + { + ModulesList.Add(new MigrationModules(ModuleName, MigrationResource.OnlyofficeModuleNamePeople)); + userInfo = new UserInfo() + { + ID = Guid.NewGuid() + }; + var drivePath = Directory.Exists(Path.Combine(rootFolder, "data", Key, "cache")) ? + Path.Combine(rootFolder, "data", Key, "cache") : null; + if (drivePath == null) + { + hasPhoto = false; + } + else + { + pathToPhoto = File.Exists(Path.Combine(drivePath, "avatar_upload")) ? Directory.GetFiles(drivePath, "avatar_upload")[0] : null; + hasPhoto = pathToPhoto != null ? true : false; + } + + string[] userName = User.Data.DisplayName.Split(' '); + userInfo.FirstName = userName[0]; + if (userName.Length > 1) + { + userInfo.LastName = userName[1]; + } + if (User.Data.Email != null && User.Data.Email != "" && User.Data.Email != "NULL") + { + var email = emailRegex.Match(User.Data.Email); + if (email.Success) + { + userInfo.Email = email.Groups[1].Value; + } + userInfo.UserName = userInfo.Email.Split('@').First(); + } + userInfo.ActivationStatus = EmployeeActivationStatus.Pending; + Action log = (m, e) => { Log($"{DisplayName} ({Email}): {m}", e); }; + + MigratingContacts = new OCMigratingContacts(this, User.Addressbooks, log); + MigratingContacts.Parse(); + if (MigratingContacts.ContactsCount != 0) + { + ModulesList.Add(new MigrationModules(MigratingContacts.ModuleName, MigrationResource.OnlyofficeModuleNameMail)); + } + + MigratingCalendar = new OCMigratingCalendar(User.Calendars, log); + //MigratingCalendar.Parse(); + if (MigratingCalendar.CalendarsCount != 0) + { + ModulesList.Add(new MigrationModules(MigratingCalendar.ModuleName, MigrationResource.OnlyofficeModuleNameCalendar)); + } + + MigratingFiles = new OCMigratingFiles(this, User.Storages, rootFolder, log); + MigratingFiles.Parse(); + if (MigratingFiles.FoldersCount != 0 || MigratingFiles.FilesCount != 0) + { + ModulesList.Add(new MigrationModules(MigratingFiles.ModuleName, MigrationResource.OnlyofficeModuleNameDocuments)); + } + + MigratingMail = new OCMigratingMail(log); + } + + public void dataСhange(MigratingApiUser frontUser) + { + if (userInfo.Email == null) + { + userInfo.Email = frontUser.Email; + if (userInfo.UserName == null) + { + userInfo.UserName = userInfo.Email.Split('@').First(); + } + } + if (userInfo.LastName == null) + { + userInfo.LastName = "NOTPROVIDED"; + } + } + + public override void Migrate() + { + if (string.IsNullOrWhiteSpace(userInfo.FirstName)) + { + userInfo.FirstName = FilesCommonResource.UnknownFirstName; + } + if (string.IsNullOrWhiteSpace(userInfo.LastName)) + { + userInfo.LastName = FilesCommonResource.UnknownLastName; + } + + var saved = CoreContext.UserManager.GetUserByEmail(userInfo.Email); + if (saved != Constants.LostUser) + { + saved.Contacts = saved.Contacts.Union(userInfo.Contacts).ToList(); + userInfo.ID = saved.ID; + } + else + { + saved = CoreContext.UserManager.SaveUserInfo(userInfo); + } + if (hasPhoto) + { + using (var ms = new MemoryStream()) + { + using (var fs = File.OpenRead(pathToPhoto)) + { + fs.CopyTo(ms); + } + CoreContext.UserManager.SaveUserPhoto(saved.ID, ms.ToArray()); + } + } + } + } +} diff --git a/module/ASC.Migration/OwnCloud/Models/OCMigrationinfo.cs b/module/ASC.Migration/OwnCloud/Models/OCMigrationinfo.cs new file mode 100644 index 000000000..7a2454b5b --- /dev/null +++ b/module/ASC.Migration/OwnCloud/Models/OCMigrationinfo.cs @@ -0,0 +1,8 @@ +using ASC.Migration.Core.Models; + +namespace ASC.Migration.OwnCloud.Models +{ + public class OCMigrationInfo : MigrationInfo + { + } +} diff --git a/module/ASC.Migration/OwnCloud/Models/Parse/OCAddressbooks.cs b/module/ASC.Migration/OwnCloud/Models/Parse/OCAddressbooks.cs new file mode 100644 index 000000000..1fe6e5143 --- /dev/null +++ b/module/ASC.Migration/OwnCloud/Models/Parse/OCAddressbooks.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace ASC.Migration.OwnCloud.Models +{ + public class OCAddressbooks + { + public int Id { get; set; } + public List Cards { get; set; } + } + + public class OCCards + { + public int Id { get; set; } + public byte[] CardData { get; set; } + } +} diff --git a/module/ASC.Migration/OwnCloud/Models/Parse/OCCalendars.cs b/module/ASC.Migration/OwnCloud/Models/Parse/OCCalendars.cs new file mode 100644 index 000000000..07403f745 --- /dev/null +++ b/module/ASC.Migration/OwnCloud/Models/Parse/OCCalendars.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace ASC.Migration.OwnCloud.Models +{ + public class OCCalendars + { + public int Id { get; set; } + public List CalendarObject { get; set; } + public string DisplayName { get; set; } + } + + public class OCCalendarObjects + { + public int Id { get; set; } + public byte[] CalendarData { get; set; } + } +} diff --git a/module/ASC.Migration/OwnCloud/Models/Parse/OCContact.cs b/module/ASC.Migration/OwnCloud/Models/Parse/OCContact.cs new file mode 100644 index 000000000..f491dcf1f --- /dev/null +++ b/module/ASC.Migration/OwnCloud/Models/Parse/OCContact.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace ASC.Migration.OwnCloud.Models +{ + public class OCContact + { + public string ContactName { get; set; } + public string Address { get; set; } + public string Description { get; set; } + + public List Emails { get; set; } + public List Phones { get; set; } + } +} diff --git a/module/ASC.Migration/OwnCloud/Models/Parse/OCGroup.cs b/module/ASC.Migration/OwnCloud/Models/Parse/OCGroup.cs new file mode 100644 index 000000000..e17d77bcb --- /dev/null +++ b/module/ASC.Migration/OwnCloud/Models/Parse/OCGroup.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ASC.Migration.OwnCloud.Models +{ + public class OCGroup + { + public string GroupGid { get; set; } + public List UsersUid { get; set; } + } +} diff --git a/module/ASC.Migration/OwnCloud/Models/Parse/OCStorages.cs b/module/ASC.Migration/OwnCloud/Models/Parse/OCStorages.cs new file mode 100644 index 000000000..ba881b31c --- /dev/null +++ b/module/ASC.Migration/OwnCloud/Models/Parse/OCStorages.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; + +namespace ASC.Migration.OwnCloud.Models +{ + public class OCStorages + { + public int NumericId { get; set; } + public string Id { get; set; } + public List FileCache { get; set; } + } + + public class OCFileCache + { + public int FileId { get; set; } + public string Path { get; set; } + public List Share { get; set; } + } + + public class OCShare + { + public int Id { get; set; } + public string ShareWith { get; set; } + public int Premissions { get; set; } + } +} diff --git a/module/ASC.Migration/OwnCloud/Models/Parse/OCUser.cs b/module/ASC.Migration/OwnCloud/Models/Parse/OCUser.cs new file mode 100644 index 000000000..a06b59089 --- /dev/null +++ b/module/ASC.Migration/OwnCloud/Models/Parse/OCUser.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; + +namespace ASC.Migration.OwnCloud.Models +{ + public class OCUser + { + public string Uid { get; set; } + public OCUserData Data { get; set; } + public OCAddressbooks Addressbooks { get; set; } + public List Calendars { get; set; } + public OCStorages Storages { get; set; } + } + + public class OCUserData + { + public string DisplayName { get; set; } + public string Email { get; set; } + } +} diff --git a/module/ASC.Migration/OwnCloud/OwnCloudMigration.cs b/module/ASC.Migration/OwnCloud/OwnCloudMigration.cs new file mode 100644 index 000000000..27f37805a --- /dev/null +++ b/module/ASC.Migration/OwnCloud/OwnCloudMigration.cs @@ -0,0 +1,488 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; + +using ASC.Core; +using ASC.Migration.Core; +using ASC.Migration.Core.Models; +using ASC.Migration.Core.Models.Api; +using ASC.Migration.OwnCloud.Models; +using ASC.Migration.Resources; + +namespace ASC.Migration.OwnCloud +{ + [ApiMigrator("OwncloudMigrate")] + public class OwnCloudMigration : AbstractMigration + { + private string takeouts; + public string[] tempParse; + private string tmpFolder; + + public override void Init(string path, CancellationToken cancellationToken) + { + this.cancellationToken = cancellationToken; + var files = Directory.GetFiles(path); + if (!files.Any() || !files.Any(f => f.EndsWith(".zip"))) + { + throw new Exception("Folder must not be empty and should contain only .zip files."); + } + for (var i = 0; i < files.Length; i++) + { + if (files[i].EndsWith(".zip")) + { + DateTime creationTime = File.GetCreationTimeUtc(files[i]); + takeouts = files[i]; + } + } + + migrationInfo = new OCMigrationInfo(); + migrationInfo.MigratorName = this.GetType().CustomAttributes.First().ConstructorArguments.First().Value.ToString(); + tmpFolder = path; + } + public override Task Parse() + { + ReportProgress(0, MigrationResource.Unzipping); + try + { + try + { + ZipFile.ExtractToDirectory(takeouts, tmpFolder); + } + catch (Exception ex) + { + Log($"Couldn't to unzip {takeouts}", ex); + } + if (cancellationToken.IsCancellationRequested) { ReportProgress(100, MigrationResource.MigrationCanceled); return null; } + ReportProgress(30, MigrationResource.UnzippingFinished); + var bdFile = ""; + try + { + bdFile = Directory.GetFiles(Directory.GetDirectories(tmpFolder)[0], "*.bak")[0]; + if (bdFile == null) + { + throw new Exception(); + } + } + catch (Exception ex) + { + migrationInfo.failedArchives.Add(Path.GetFileName(takeouts)); + Log("Archive must not be empty and should contain .bak files.", ex); + } + + ReportProgress(40, MigrationResource.DumpParse); + var users = DBExtractUser(bdFile); + var progress = 40; + foreach (var item in users) + { + if (cancellationToken.IsCancellationRequested) { ReportProgress(100, MigrationResource.MigrationCanceled); return null; } + ReportProgress(progress, MigrationResource.DataProcessing); + progress += 30 / users.Count; + if (item.Data.DisplayName != null) + { + try + { + string[] userName = item.Data.DisplayName.Split(' '); + item.Data.DisplayName = userName.Length > 1 ? String.Format("{0} {1}", userName[0], userName[1]).Trim() : userName[0].Trim(); + var user = new OCMigratingUser(item.Uid, item, Directory.GetDirectories(tmpFolder)[0], Log); + user.Parse(); + foreach (var element in user.ModulesList) + { + if (!migrationInfo.Modules.Exists(x => x.MigrationModule == element.MigrationModule)) + { + migrationInfo.Modules.Add(new MigrationModules(element.MigrationModule, element.Module)); + } + } + migrationInfo.Users.Add(item.Uid, user); + } + catch (Exception ex) + { + Log($"Couldn't parse user {item.Data.DisplayName}", ex); + } + } + } + + var groups = DBExtractGroup(bdFile); + progress = 80; + foreach (var item in groups) + { + ReportProgress(progress, MigrationResource.DataProcessing); + progress += 10 / groups.Count; + var group = new OCMigratingGroups(item, Log); + group.Parse(); + if (group.Module.MigrationModule != null) + { + migrationInfo.Groups.Add(group); + if (!migrationInfo.Modules.Exists(x => x.MigrationModule == group.Module.MigrationModule)) + { + migrationInfo.Modules.Add(new MigrationModules(group.Module.MigrationModule, group.Module.Module)); + } + } + } + ReportProgress(90, MigrationResource.ClearingTemporaryData); + } + catch (Exception ex) + { + migrationInfo.failedArchives.Add(Path.GetFileName(takeouts)); + Log($"Couldn't parse users from {Path.GetFileNameWithoutExtension(takeouts)} archive", ex); + } + ReportProgress(100, MigrationResource.DataProcessingCompleted); + return Task.FromResult(migrationInfo.ToApiInfo()); + } + + public List DBExtractGroup(string dbFile) { + var groups = new List(); + + var sqlFile = File.ReadAllText(dbFile); + + var groupList = GetDumpChunk("oc_groups", sqlFile); + if (groupList == null) return groups; + + foreach (var group in groupList) + { + groups.Add(new OCGroup + { + GroupGid = group.Trim('\''), + UsersUid = new List() + }); + } + + var usersInGroups = GetDumpChunk("oc_group_user", sqlFile); + foreach (var user in usersInGroups) + { + var userGroupGid = user.Split(',').First().Trim('\''); + var userUid = user.Split(',').Last().Trim('\''); + groups.Find(ggid => userGroupGid == ggid.GroupGid).UsersUid.Add(userUid); + } + + return groups; + } + + public List DBExtractUser(string dbFile) + { + var userDataList = new Dictionary(); + + var sqlFile = File.ReadAllText(dbFile); + + var accountsData = GetDumpChunk("oc_accounts_data", sqlFile); + if (accountsData != null) + { + throw new Exception(); + } + + var accounts = GetDumpChunk("oc_accounts", sqlFile); + if (accounts == null) return userDataList.Values.ToList(); + + foreach (var account in accounts) + { + + var userId = account.Split(',')[2].Trim('\''); + + userDataList.Add(userId, new OCUser + { + Uid = account.Split(',')[2].Trim('\''), + Data = new OCUserData + { + DisplayName = account.Split(',')[4].Trim('\''), + Email = account.Split(',')[1].Trim('\'') + }, + Addressbooks = null, + Calendars = new List(), + Storages = new OCStorages() + }); + } + + var calendarsData = GetDumpChunk("oc_calendars", sqlFile); + if (calendarsData != null) + { + foreach (var calendarData in calendarsData) + { + var values = calendarData.Split(',') + .Select(s => s.Trim('\'')).ToArray(); + var uid = values[1].Split('/').Last(); + userDataList.TryGetValue(uid, out var user); + if (user == null) continue; + + user.Calendars.Add(new OCCalendars() + { + Id = int.Parse(values[0]), + CalendarObject = new List(), + DisplayName = values[2] + }); + } + } + + var calendars = userDataList.Values + .SelectMany(u => u.Calendars) + .ToDictionary(c => c.Id, c => c); + var calendarObjects = GetDumpChunk("oc_calendarobjects", sqlFile); + if (calendarObjects != null) + { + foreach (var calendarObject in calendarObjects) + { + var values = calendarObject.Split(',') + .Select(s => s.Trim('\'')).ToArray(); + var calId = int.Parse(values[3]); + calendars.TryGetValue(calId, out var cal); + if (cal == null) continue; + + cal.CalendarObject.Add(new OCCalendarObjects() + { + Id = int.Parse(values[0]), + CalendarData = Encoding.UTF8.GetBytes(values[1] + .Replace("\\r", "") + .Replace("\\n", "\n")), + }); + } + } + + var addressBooks = GetDumpChunk("oc_addressbooks", sqlFile); + if (addressBooks != null) + { + foreach (var addressBook in addressBooks) + { + var values = addressBook.Split(',') + .Select(s => s.Trim('\'')).ToArray(); + var uid = values[1].Split('/').Last(); + userDataList.TryGetValue(uid, out var user); + if (user == null) continue; + user.Addressbooks = new OCAddressbooks(); + user.Addressbooks.Id = int.Parse(values[0]); + user.Addressbooks.Cards = new List(); + } + } + + var addressBooksDict = userDataList.Values + .Select(u => u.Addressbooks) + .Where(x => x != null) + .ToDictionary(b => b.Id, b => b); + var cards = GetDumpChunk("oc_cards", sqlFile); + if (cards != null) + { + foreach (var card in cards) + { + var values = card.Split(',') + .Select(s => s.Trim('\'')).ToArray(); + var bookId = int.Parse(values[1]); + addressBooksDict.TryGetValue(bookId, out var book); + if (book == null) continue; + + book.Cards.Add(new OCCards() + { + Id = int.Parse(values[0]), + CardData = Encoding.UTF8.GetBytes(values[2] + .Replace("\\r", "") + .Replace("\\n", "\n")), + }); + } + } + + var storages = GetDumpChunk("oc_storages", sqlFile); + if (storages != null) + { + foreach (var storage in storages) + { + var values = storage.Split(',') + .Select(s => s.Trim('\'')).ToArray(); + var uid = values[0].Split(':').Last(); + userDataList.TryGetValue(uid, out var user); + if (user == null) continue; + + user.Storages.NumericId = int.Parse(values[1]); + user.Storages.Id = values[0]; + user.Storages.FileCache = new List(); + } + } + + var storagesDict = userDataList.Values + .Select(u => u.Storages) + .ToDictionary(s => s.NumericId, s => s); + var fileCaches = GetDumpChunk("oc_filecache", sqlFile); + if (fileCaches != null) + { + foreach (var cache in fileCaches) + { + var values = cache.Split(',') + .Select(s => s.Trim('\'')).ToArray(); + var storageId = int.Parse(values[1]); + storagesDict.TryGetValue(storageId, out var storage); + if (storage == null) continue; + + storage.FileCache.Add(new OCFileCache() + { + FileId = int.Parse(values[0]), + Path = values[2], + Share = new List() + }); + } + } + + var files = userDataList.Values + .SelectMany(u => u.Storages.FileCache) + .ToDictionary(f => f.FileId, f => f); + var shares = GetDumpChunk("oc_share", sqlFile); + if (shares != null) + { + foreach (var share in shares) + { + var values = share.Split(',') + .Select(s => s.Trim('\'')).ToArray(); + var fileId = int.Parse(values[9]); + files.TryGetValue(fileId, out var file); + if (file == null) continue; + + file.Share.Add(new OCShare() + { + Id = int.Parse(values[0]), + ShareWith = values[2], + Premissions = int.Parse(values[11 ]) + }); + } + } + + return userDataList.Values.ToList(); + } + + private IEnumerable GetDumpChunk(string tableName, string dump) + { + var regex = new Regex($"INSERT INTO `{tableName}` VALUES (.*);"); + var match = regex.Match(dump); + if (!match.Success) return null; + + var entryRegex = new Regex(@"(\(.*?\))[,;]"); + var accountDataMatches = entryRegex.Matches(match.Groups[1].Value + ";"); + return accountDataMatches.Cast() + .Select(m => m.Groups[1].Value.Trim(new[] { '(', ')' })); + } + + public override Task Migrate(MigrationApiInfo migrationApiInfo) + { + ReportProgress(0, MigrationResource.PreparingForMigration); + migrationInfo.Merge(migrationApiInfo); + + var usersForImport = migrationInfo.Users + .Where(u => u.Value.ShouldImport) + .Select(u => u.Value); + + importedUsers = new List(); + var failedUsers = new List(); + var usersCount = usersForImport.Count(); + var progressStep = 25 / usersCount; + var i = 1; + foreach (var user in usersForImport) + { + if (cancellationToken.IsCancellationRequested) { ReportProgress(100, MigrationResource.MigrationCanceled); return null; } + ReportProgress(GetProgress() + progressStep, String.Format(MigrationResource.UserMigration, user.DisplayName, i++, usersCount)); + try + { + user.dataСhange(migrationApiInfo.Users.Find(element => element.Key == user.Key)); + user.Migrate(); + importedUsers.Add(user.Guid); + } + catch (Exception ex) + { + failedUsers.Add(user); + Log($"Couldn't migrate user {user.DisplayName} ({user.Email})", ex); + } + } + + var groupsForImport = migrationInfo.Groups + .Where(g => g.ShouldImport) + .Select(g => g); + var groupsCount = groupsForImport.Count(); + if (groupsCount != 0) + { + progressStep = 25 / groupsForImport.Count(); + //Create all groups + i = 1; + foreach (var group in groupsForImport) + { + if (cancellationToken.IsCancellationRequested) { ReportProgress(100, MigrationResource.MigrationCanceled); return null; } + ReportProgress(GetProgress() + progressStep, String.Format(MigrationResource.GroupMigration, group.GroupName, i++, groupsCount)); + try + { + group.usersGuidList = migrationInfo.Users + .Where(user => group.UserUidList.Exists(u => user.Key == u)) + .Select(u => u) + .ToDictionary(k => k.Key, v => v.Value.Guid); + group.Migrate(); + } + catch (Exception ex) + { + Log($"Couldn't migrate group {group.GroupName} ", ex); + } + } + } + + i = 1; + foreach (var user in usersForImport) + { + if (cancellationToken.IsCancellationRequested) { ReportProgress(100, MigrationResource.MigrationCanceled); return null; } + if (failedUsers.Contains(user)) + { + ReportProgress(GetProgress() + progressStep, String.Format(MigrationResource.UserSkipped, user.DisplayName, i, usersCount)); + continue; + } + + var smallStep = progressStep / 3; + + try + { + user.MigratingContacts.Migrate(); + } + catch (Exception ex) + { + Log($"Couldn't migrate user {user.DisplayName} ({user.Email}) contacts", ex); + } + finally + { + ReportProgress(GetProgress() + smallStep, String.Format(MigrationResource.MigratingUserContacts, user.DisplayName, i, usersCount)); + } + + /*try + { + user.MigratingCalendar.Migrate(); + } + catch (Exception ex) + { + Log($"Couldn't migrate user {user.DisplayName} ({user.Email}) calendar", ex); + } + finally + { + ReportProgress(GetProgress() + smallStep, String.Format(MigrationResource.UserCalendarMigration, user.DisplayName, i, usersCount)); + }*/ + + try + { + var currentUser = SecurityContext.CurrentAccount; + SecurityContext.AuthenticateMe(user.Guid); + user.MigratingFiles.SetUsersDict(usersForImport.Except(failedUsers)); + user.MigratingFiles.SetGroupsDict(groupsForImport); + user.MigratingFiles.Migrate(); + SecurityContext.AuthenticateMe(currentUser.ID); + } + catch (Exception ex) + { + Log($"Couldn't migrate user {user.DisplayName} ({user.Email}) files", ex); + } + finally + { + ReportProgress(GetProgress() + smallStep, String.Format(MigrationResource.MigratingUserFiles, user.DisplayName, i, usersCount)); + } + i++; + } + + if (Directory.Exists(tmpFolder)) + { + Directory.Delete(tmpFolder, true); + } + ReportProgress(100, MigrationResource.MigrationCompleted); + return Task.CompletedTask; + } + } +} diff --git a/module/ASC.Migration/Properties/AssemblyInfo.cs b/module/ASC.Migration/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..76c2a9a8b --- /dev/null +++ b/module/ASC.Migration/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ASC.Migration")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Ascensio System SIA")] +[assembly: AssemblyProduct("ASC.Migration")] +[assembly: AssemblyCopyright("(c) Ascensio System SIA. All rights reserved")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("72d10cca-ccf1-4e2f-a17d-443917b154cb")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/module/ASC.Migration/Resources/MigrationResource.Designer.cs b/module/ASC.Migration/Resources/MigrationResource.Designer.cs new file mode 100644 index 000000000..4ba29d7d0 --- /dev/null +++ b/module/ASC.Migration/Resources/MigrationResource.Designer.cs @@ -0,0 +1,315 @@ +//------------------------------------------------------------------------------ +// +// Этот код создан программой. +// Исполняемая версия:4.0.30319.42000 +// +// Изменения в этом файле могут привести к неправильной работе и будут потеряны в случае +// повторной генерации кода. +// +//------------------------------------------------------------------------------ + +namespace ASC.Migration.Resources { + using System; + + + /// + /// Класс ресурса со строгой типизацией для поиска локализованных строк и т.д. + /// + // Этот класс создан автоматически классом StronglyTypedResourceBuilder + // с помощью такого средства, как ResGen или Visual Studio. + // Чтобы добавить или удалить член, измените файл .ResX и снова запустите ResGen + // с параметром /str или перестройте свой проект VS. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class MigrationResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal MigrationResource() { + } + + /// + /// Возвращает кэшированный экземпляр ResourceManager, использованный этим классом. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ASC.Migration.Resources.MigrationResource", typeof(MigrationResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Перезаписывает свойство CurrentUICulture текущего потока для всех + /// обращений к ресурсу с помощью этого класса ресурса со строгой типизацией. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Ищет локализованную строку, похожую на Clearing temporary data. + /// + internal static string ClearingTemporaryData { + get { + return ResourceManager.GetString("ClearingTemporaryData", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на Data processing.... + /// + internal static string DataProcessing { + get { + return ResourceManager.GetString("DataProcessing", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на Data processing completed. + /// + internal static string DataProcessingCompleted { + get { + return ResourceManager.GetString("DataProcessingCompleted", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на Database parsing. + /// + internal static string DumpParse { + get { + return ResourceManager.GetString("DumpParse", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на Mail`s Contacts. + /// + internal static string GoogleModuleNameContacts { + get { + return ResourceManager.GetString("GoogleModuleNameContacts", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на Google Drive`s Files. + /// + internal static string GoogleModuleNameDocuments { + get { + return ResourceManager.GetString("GoogleModuleNameDocuments", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на Group migration {0} ({1}/{2}). + /// + internal static string GroupMigration { + get { + return ResourceManager.GetString("GroupMigration", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на Migrating user contacts {0} ({1}/{2}). + /// + internal static string MigratingUserContacts { + get { + return ResourceManager.GetString("MigratingUserContacts", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на Migrating user files {0} ({1}/{2}). + /// + internal static string MigratingUserFiles { + get { + return ResourceManager.GetString("MigratingUserFiles", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на Migration canceled. + /// + internal static string MigrationCanceled { + get { + return ResourceManager.GetString("MigrationCanceled", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на Migration completed. + /// + internal static string MigrationCompleted { + get { + return ResourceManager.GetString("MigrationCompleted", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на Calendar. + /// + internal static string ModuleNameCalendar { + get { + return ResourceManager.GetString("ModuleNameCalendar", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на Groups. + /// + internal static string ModuleNameGroups { + get { + return ResourceManager.GetString("ModuleNameGroups", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на Mail. + /// + internal static string ModuleNameMail { + get { + return ResourceManager.GetString("ModuleNameMail", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на Users. + /// + internal static string ModuleNameUsers { + get { + return ResourceManager.GetString("ModuleNameUsers", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на Contacts. + /// + internal static string NextcloudModuleNameContacts { + get { + return ResourceManager.GetString("NextcloudModuleNameContacts", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на User`s Files. + /// + internal static string NextcloudModuleNameDocuments { + get { + return ResourceManager.GetString("NextcloudModuleNameDocuments", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на Calendar. + /// + internal static string OnlyofficeModuleNameCalendar { + get { + return ResourceManager.GetString("OnlyofficeModuleNameCalendar", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на Documents. + /// + internal static string OnlyofficeModuleNameDocuments { + get { + return ResourceManager.GetString("OnlyofficeModuleNameDocuments", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на Mail. + /// + internal static string OnlyofficeModuleNameMail { + get { + return ResourceManager.GetString("OnlyofficeModuleNameMail", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на People. + /// + internal static string OnlyofficeModuleNamePeople { + get { + return ResourceManager.GetString("OnlyofficeModuleNamePeople", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на Preparing for migration. + /// + internal static string PreparingForMigration { + get { + return ResourceManager.GetString("PreparingForMigration", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на Starting data processing.... + /// + internal static string StartOfDataProcessing { + get { + return ResourceManager.GetString("StartOfDataProcessing", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на Unzipping.... + /// + internal static string Unzipping { + get { + return ResourceManager.GetString("Unzipping", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на Unzipping completed.... + /// + internal static string UnzippingFinished { + get { + return ResourceManager.GetString("UnzippingFinished", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на User calendar migration {0} ({1}/{2}). + /// + internal static string UserCalendarMigration { + get { + return ResourceManager.GetString("UserCalendarMigration", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на User migration {0} ({1}/{2}). + /// + internal static string UserMigration { + get { + return ResourceManager.GetString("UserMigration", resourceCulture); + } + } + + /// + /// Ищет локализованную строку, похожую на Failed to import user: {0} ({1}/{2}). + /// + internal static string UserSkipped { + get { + return ResourceManager.GetString("UserSkipped", resourceCulture); + } + } + } +} diff --git a/module/ASC.Migration/Resources/MigrationResource.resx b/module/ASC.Migration/Resources/MigrationResource.resx new file mode 100644 index 000000000..b7d7f3021 --- /dev/null +++ b/module/ASC.Migration/Resources/MigrationResource.resx @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Clearing temporary data + + + Data processing... + + + Data processing completed + + + Database parsing + + + Mail`s Contacts + + + Google Drive`s Files + + + Group migration {0} ({1}/{2}) + + + Migrating user contacts {0} ({1}/{2}) + + + Migrating user files {0} ({1}/{2}) + + + Migration canceled + + + Migration completed + + + Calendar + + + Groups + + + Mail + + + Users + + + Contacts + + + User`s Files + + + Calendar + + + Documents + + + Mail + + + People + + + Preparing for migration + + + Starting data processing... + + + Unzipping... + + + Unzipping completed... + + + User calendar migration {0} ({1}/{2}) + + + User migration {0} ({1}/{2}) + + + Failed to import user: {0} ({1}/{2}) + + \ No newline at end of file diff --git a/module/ASC.Notify/ASC.Notify/ASC.Notify.csproj b/module/ASC.Notify/ASC.Notify/ASC.Notify.csproj index 410e676d2..172da4b4a 100644 --- a/module/ASC.Notify/ASC.Notify/ASC.Notify.csproj +++ b/module/ASC.Notify/ASC.Notify/ASC.Notify.csproj @@ -103,13 +103,13 @@ - 3.5.6.3 + 3.7.3.20 - 3.5.7.6 + 3.7.1.29 - 12.0.3 + 13.0.1 diff --git a/module/ASC.Radicale/ASC.Radicale.csproj b/module/ASC.Radicale/ASC.Radicale.csproj index 62a420a6f..baf0ef307 100644 --- a/module/ASC.Radicale/ASC.Radicale.csproj +++ b/module/ASC.Radicale/ASC.Radicale.csproj @@ -66,7 +66,7 @@ - 4.7.0 + 4.7.11 diff --git a/module/ASC.Radicale/Launcher.cs b/module/ASC.Radicale/Launcher.cs index 3f93e622d..93736d968 100644 --- a/module/ASC.Radicale/Launcher.cs +++ b/module/ASC.Radicale/Launcher.cs @@ -20,11 +20,14 @@ using System.Configuration; using System.Diagnostics; using System.IO; using System.Reflection; +using System.Text.RegularExpressions; using ASC.Common.Logging; using ASC.Common.Module; using ASC.Core; +using ASC.Core.Common.Contracts; +using LogManager = ASC.Common.Logging.BaseLogManager; namespace ASC.Radicale { @@ -33,6 +36,8 @@ namespace ASC.Radicale private static Process proc; private static ProcessStartInfo startInfo; private static readonly ILog Logger = LogManager.GetLogger("ASC"); + private const string ResultOfPing = "Radicale works!"; + private HealthCheckSvc HealthCheckSvc; public void Start() { try @@ -57,6 +62,14 @@ namespace ASC.Radicale }; StartRedicale(); + var appSettings = ConfigurationManagerExtension.AppSettings; + HealthCheckSvc = new HealthCheckSvc (cfg.Port, ResultOfPing, Logger); + HealthCheckSvc.StartPing(); + + + var checkRadicaleVersion = CheckRadicaleVersion(); + + if (!checkRadicaleVersion) Stop(); } catch (Exception e) { @@ -64,16 +77,88 @@ namespace ASC.Radicale } } + private static bool CheckRadicaleVersion() + { + ProcessStartInfo start = new ProcessStartInfo(); + + var pythonName = "pip"; + + if (WorkContext.IsMono) + { + pythonName = "pip"; + } + var isSuccess = true; + + start.FileName = pythonName; + start.Arguments = "show radicale app_store_plugin app_auth_plugin app_right_plugin"; + start.UseShellExecute = false; + start.RedirectStandardOutput = true; + try + { + using (Process process = Process.Start(start)) + { + using (StreamReader reader = process.StandardOutput) + { + string result = reader.ReadToEnd(); + if (result != null) + { + string[] plugins = result.Split(new string[] { "---" }, StringSplitOptions.None); + foreach (string plugin in plugins) + { + const string namePattern = "Name: (.*)\n"; + const string versionPattern = "Version: (.*)\n"; + Regex rgxName = new Regex(namePattern); + Regex rgxVersion = new Regex(versionPattern); + Match matchName = rgxName.Match(plugin); + + if (matchName.Groups.Count > 1) + { + Match matchVersion = rgxVersion.Match(plugin); + switch (matchName.Groups[1].Value.Trim()) + { + case "Radicale": + if (matchVersion.Groups[1].Value.Split('.')[0] != "3") + { + Logger.Error("Only Radicale 3 version supported"); + isSuccess = false; + } + break; + case "app-store-plugin": + case "app-auth-plugin": + case "app-right-plugin": + + if (matchVersion.Groups[1].Value.Trim() != "1.0.0") + { + Logger.Error("Required " + matchName.Groups[1].Value.ToLower() + " plugin version 1.0.0"); + isSuccess = false; + } + break; + } + } + } + return isSuccess; + } + return false; + } + } + } + catch (Exception e) + { + Logger.Error(e.Message); + return false; + } + } + public void Stop() { StopRadicale(); + HealthCheckSvc.StopPing(); } private static void StartRedicale() { StopRadicale(); proc = Process.Start(startInfo); - } private static void StopRadicale() diff --git a/module/ASC.Radicale/RadicaleCfgSectionHandler.cs b/module/ASC.Radicale/RadicaleCfgSectionHandler.cs index a748aa8e6..343a1c1b6 100644 --- a/module/ASC.Radicale/RadicaleCfgSectionHandler.cs +++ b/module/ASC.Radicale/RadicaleCfgSectionHandler.cs @@ -26,5 +26,11 @@ namespace ASC.Radicale { get { return (string)base["path"]; } } + + [ConfigurationProperty("port", DefaultValue = "5232")] + public string Port + { + get { return (string)base["port"]; } + } } } diff --git a/module/ASC.Radicale/radicale.rights b/module/ASC.Radicale/radicale.rights index 61d3c10ff..96a591b9b 100644 --- a/module/ASC.Radicale/radicale.rights +++ b/module/ASC.Radicale/radicale.rights @@ -1,11 +1,11 @@ [admin] -user: admin@ascsystem@.+ +user: admin@radicale@.+ collection: .* permissions: RWrw -[allow-shared-read] +[allow-readonly] user: .+ -collection: %(user)s/.+-shared$ +collection: %(user)s/.+-readonly$ permissions: Rr [owner-write] diff --git a/module/ASC.Socket.IO.Svc/Launcher.cs b/module/ASC.Socket.IO.Svc/Launcher.cs index 765258af3..375ab51d8 100644 --- a/module/ASC.Socket.IO.Svc/Launcher.cs +++ b/module/ASC.Socket.IO.Svc/Launcher.cs @@ -30,6 +30,8 @@ using ASC.Core.Notify.Signalr; using WebSocketSharp; +using LogManager = ASC.Common.Logging.BaseLogManager; + namespace ASC.Socket.IO.Svc { public class Launcher : IServiceController @@ -38,7 +40,7 @@ namespace ASC.Socket.IO.Svc private static ProcessStartInfo startInfo; private static WebSocket webSocket; private static CancellationTokenSource cancellationTokenSource; - private const int PingInterval = 10000; + private static int PingInterval = int.Parse(ConfigurationManagerExtension.AppSettings["ping.interval"]); private static readonly ILog Logger = LogManager.GetLogger("ASC"); private static string LogDir; @@ -48,7 +50,7 @@ namespace ASC.Socket.IO.Svc { cancellationTokenSource = new CancellationTokenSource(); - var cfg = (SocketIOCfgSectionHandler)ConfigurationManagerExtension.GetSection("socketio"); + var cfg = (SocketIOCfgSectionHandler)ConfigurationManagerExtension.GetSection("socketio"); startInfo = new ProcessStartInfo { @@ -138,7 +140,7 @@ namespace ASC.Socket.IO.Svc webSocket.Log.Output = (logData, filePath) => { - if (logData.Message.Contains("SocketException")) + if (logData.Message.Contains("SocketException")) { error = true; } @@ -174,11 +176,11 @@ namespace ASC.Socket.IO.Svc pingCancellationTokenSource.Cancel(); pingCancellationTokenSource.Dispose(); - if (cancellationTokenSource.IsCancellationRequested) return; + if (cancellationTokenSource.IsCancellationRequested) return; if (error) { - Process.GetCurrentProcess().Kill(); + Process.GetCurrentProcess().Kill(); } else { @@ -213,7 +215,7 @@ namespace ASC.Socket.IO.Svc if (webSocket.IsAlive) { webSocket.Close(); - webSocket = null; + webSocket = null; } } catch (Exception) diff --git a/module/ASC.Socket.IO/app/hubs/counters.js b/module/ASC.Socket.IO/app/hubs/counters.js index 4d2acd0e3..9a85c3c8b 100644 --- a/module/ASC.Socket.IO/app/hubs/counters.js +++ b/module/ASC.Socket.IO/app/hubs/counters.js @@ -21,6 +21,7 @@ module.exports = (io) => { const counters = io.of('/counters'); const onlineUsers = []; const uaParser = require('ua-parser-js'); + var timeInterval; counters.on('connection', (socket) => { const request = socket.client.request; @@ -44,7 +45,7 @@ module.exports = (io) => { socket.join([tenantId, `${tenantId}-${userId}`, `${tenantId}-${userName}`]); getNewMessagesCount(); - + socket .on('disconnect', () => { if (!onlineUsers[tenantId]) return; @@ -57,10 +58,16 @@ module.exports = (io) => { delete onlineUsers[tenantId][userId].browsers[browserName]; } if (Object.keys(onlineUsers[tenantId][userId].browsers).length === 0) { - delete onlineUsers[tenantId][userId]; - counters.to(tenantId).emit('renderOfflineUser', userId); - updateMailUserActivity(socket.client.request, false); - console.log(`a user ${userName} in portal ${tenantId} disconnected`); + + timeInterval = setTimeout(function(){ + delete onlineUsers[tenantId][userId]; + if(typeof onlineUsers[tenantId][userId] != "undefined") return; + + counters.to(tenantId).emit('renderOfflineUser', userId); + updateMailUserActivity(socket.client.request, false); + timeInterval = undefined; + console.log(`a user ${userName} in portal ${tenantId} disconnected`); + }, 3000); } }) .on('renderOnlineUsers', () => { @@ -85,14 +92,11 @@ module.exports = (io) => { function updateMailUserActivity(request, userOnline = true) { if(!request.mailEnabled) return; + if((!userOnline && typeof onlineUsers[tenantId][userId] != "undefined") || + (userOnline && !onlineUsers[tenantId][userId])) return; - setTimeout(function(){ - if((!userOnline && typeof onlineUsers[tenantId][userId] != "undefined") || - (userOnline && !onlineUsers[tenantId][userId])) return; - - apiRequestManager.put("mail/accounts/updateuseractivity.json", request, { userOnline }); - console.log(`updateuseractivity ${userOnline}`); - }, 3000); + apiRequestManager.put("mail/accounts/updateuseractivity.json", request, { userOnline }); + console.log(`updateuseractivity ${userOnline}`); } function getNewMessagesCount() { @@ -135,13 +139,18 @@ module.exports = (io) => { if (!onlineUsers[tenantId]) { onlineUsers[tenantId] = {}; } - if (!onlineUsers[tenantId][userId]) { - onlineUsers[tenantId][userId] = {browsers: {},FirstConnection: new Date(), LastConnection: new Date() }; - socket.broadcast.to(tenantId).emit('renderOnlineUser', userId); - updateMailUserActivity(socket.client.request); - } - else { - onlineUsers[tenantId][userId].LastConnection = new Date(); + if(timeInterval == undefined){ + if (!onlineUsers[tenantId][userId]) { + onlineUsers[tenantId][userId] = {browsers: {},FirstConnection: new Date(), LastConnection: new Date() }; + socket.broadcast.to(tenantId).emit('renderOnlineUser', userId); + updateMailUserActivity(socket.client.request); + } + else { + onlineUsers[tenantId][userId].LastConnection = new Date(); + } + }else{ + clearTimeout(timeInterval); + timeInterval = undefined; } if (!onlineUsers[tenantId][userId].browsers[browserName]) { diff --git a/module/ASC.SsoAuth.Svc/ASC.SsoAuth.Svc.csproj b/module/ASC.SsoAuth.Svc/ASC.SsoAuth.Svc.csproj index e4c27680a..21153bdf3 100644 --- a/module/ASC.SsoAuth.Svc/ASC.SsoAuth.Svc.csproj +++ b/module/ASC.SsoAuth.Svc/ASC.SsoAuth.Svc.csproj @@ -39,6 +39,10 @@ {76de7717-3d4b-4a5b-b740-15b8913df0cb} ASC.Common + + {A51D0454-4AFA-46DE-89D4-B03D37E1816C} + ASC.Core.Common + @@ -49,7 +53,7 @@ - 4.7.0 + 4.7.11 diff --git a/module/ASC.SsoAuth.Svc/Launcher.cs b/module/ASC.SsoAuth.Svc/Launcher.cs index 2c25bb723..dbc91c75f 100644 --- a/module/ASC.SsoAuth.Svc/Launcher.cs +++ b/module/ASC.SsoAuth.Svc/Launcher.cs @@ -23,6 +23,9 @@ using System.Reflection; using ASC.Common.Logging; using ASC.Common.Module; +using ASC.Core.Common.Contracts; + +using LogManager = ASC.Common.Logging.BaseLogManager; namespace ASC.SsoAuth.Svc { @@ -32,6 +35,10 @@ namespace ASC.SsoAuth.Svc private Process proc; private static readonly ILog Logger = LogManager.GetLogger("ASC"); private static string LogDir; + private const string ResultOfPing = "OK"; + private const string PathToPing = "/isLife"; + private HealthCheckSvc HealthCheckSvc; + public void Start() { try @@ -57,6 +64,8 @@ namespace ASC.SsoAuth.Svc startInfo.EnvironmentVariables.Add("logPath", LogDir); StartNode(); + HealthCheckSvc = new HealthCheckSvc(cfg.Port, ResultOfPing, Logger, PathToPing); + HealthCheckSvc.StartPing(); } catch (Exception e) { @@ -70,6 +79,8 @@ namespace ASC.SsoAuth.Svc { if (proc != null && !proc.HasExited) { + HealthCheckSvc.StopPing(); + proc.Kill(); proc.WaitForExit(10000); diff --git a/module/ASC.SsoAuth/app/middleware/saml.js b/module/ASC.SsoAuth/app/middleware/saml.js index 4d80433ca..08e2b3665 100644 --- a/module/ASC.SsoAuth/app/middleware/saml.js +++ b/module/ASC.SsoAuth/app/middleware/saml.js @@ -33,6 +33,11 @@ module.exports = (app, config, logger) => { }) : []; + + if(req.originalUrl =="/isLife"){ + res.sendStatus(200); + return; + } if (!foundRoutes.length) { logger.error(`invalid route ${req.originalUrl}`); return res.redirect(urlResolver.getPortal404Url(req)); diff --git a/module/ASC.SsoAuth/app/routes.js b/module/ASC.SsoAuth/app/routes.js index 8676f226f..a745fc92b 100644 --- a/module/ASC.SsoAuth/app/routes.js +++ b/module/ASC.SsoAuth/app/routes.js @@ -774,6 +774,8 @@ module.exports = function (app, config, logger) { * @desc Route to load metadata */ app.post(config.routes.loadmetadata, onLoadMetadata); + + app.get('/isLife', (req, res) => { res.sendStatus(200); }); /** * @desc Catch any untracked routes diff --git a/module/ASC.TeamLabSvc/ASC.TeamLabSvc/ASC.TeamLabSvc.csproj b/module/ASC.TeamLabSvc/ASC.TeamLabSvc/ASC.TeamLabSvc.csproj index 9928f9186..0fbc1eef2 100644 --- a/module/ASC.TeamLabSvc/ASC.TeamLabSvc/ASC.TeamLabSvc.csproj +++ b/module/ASC.TeamLabSvc/ASC.TeamLabSvc/ASC.TeamLabSvc.csproj @@ -100,10 +100,5 @@ ASC.Core.Common - - - 4.7.0 - - \ No newline at end of file diff --git a/module/ASC.TelegramService/ASC.TelegramService.csproj b/module/ASC.TelegramService/ASC.TelegramService.csproj index 458d919a9..a6c8329d0 100644 --- a/module/ASC.TelegramService/ASC.TelegramService.csproj +++ b/module/ASC.TelegramService/ASC.TelegramService.csproj @@ -59,10 +59,13 @@ - 12.0.3 + 13.0.1 - 15.7.0 + 16.0.2 + + + 0.2.0 diff --git a/module/ASC.TelegramService/Core/Core.cs b/module/ASC.TelegramService/Core/Core.cs index f5d6cacd9..b29fde2dd 100644 --- a/module/ASC.TelegramService/Core/Core.cs +++ b/module/ASC.TelegramService/Core/Core.cs @@ -121,7 +121,7 @@ namespace ASC.TelegramService.Core return parsedParams.ToArray(); } - public async Task HandleCommand(Message msg, TelegramBotClient client, int tenantId) + public async Task HandleCommand(Message msg, ITelegramBotClient client, int tenantId) { try { diff --git a/module/ASC.TelegramService/Core/TenantTgClient.cs b/module/ASC.TelegramService/Core/TenantTgClient.cs index 5efa158d4..e89ab9aee 100644 --- a/module/ASC.TelegramService/Core/TenantTgClient.cs +++ b/module/ASC.TelegramService/Core/TenantTgClient.cs @@ -15,6 +15,8 @@ */ +using System.Threading; + using Telegram.Bot; namespace ASC.TelegramService.Core @@ -23,6 +25,7 @@ namespace ASC.TelegramService.Core { public string Token { get; set; } public TelegramBotClient Client { get; set; } + public CancellationTokenSource CancellationTokenSource { get; set; } public string Proxy { get; set; } public int TokenLifeSpan { get; set; } public int TenantId { get; set; } diff --git a/module/ASC.TelegramService/TelegramHandler.cs b/module/ASC.TelegramService/TelegramHandler.cs index 845316bda..50f1b7593 100644 --- a/module/ASC.TelegramService/TelegramHandler.cs +++ b/module/ASC.TelegramService/TelegramHandler.cs @@ -18,15 +18,24 @@ using System; using System.Collections.Generic; using System.Net; +using System.Net.Http; using System.Runtime.Caching; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; using ASC.Common.Logging; using ASC.Core.Common.Notify.Telegram; +using ASC.Core.Tenants; using ASC.Notify.Messages; using ASC.TelegramService.Core; using Telegram.Bot; using Telegram.Bot.Args; +using Telegram.Bot.Exceptions; +using Telegram.Bot.Extensions.Polling; +using Telegram.Bot.Types; +using Telegram.Bot.Types.Enums; namespace ASC.TelegramService { @@ -62,7 +71,8 @@ namespace ASC.TelegramService } var chat = await client.GetChatAsync(tgUser.TelegramId); - await client.SendTextMessageAsync(chat, msg.Content, Telegram.Bot.Types.Enums.ParseMode.Markdown); + + await client.SendTextMessageAsync(chat, msg.Content, ParseMode.MarkdownV2); } catch (Exception e) { @@ -75,7 +85,13 @@ namespace ASC.TelegramService if (!clients.ContainsKey(tenantId)) return; var client = clients[tenantId]; - client.Client.StopReceiving(); + + if (client.CancellationTokenSource != null) + { + client.CancellationTokenSource.Cancel(); + client.CancellationTokenSource.Dispose(); + client.CancellationTokenSource = null; + } clients.Remove(tenantId); } @@ -101,8 +117,12 @@ namespace ASC.TelegramService return false; } - - client.Client.StopReceiving(); + if (client.CancellationTokenSource != null) + { + client.CancellationTokenSource.Cancel(); + client.CancellationTokenSource.Dispose(); + client.CancellationTokenSource = null; + } BindClient(newClient, tenantId); @@ -158,21 +178,54 @@ namespace ASC.TelegramService return (string)MemoryCache.Default.Get(UserKey(userId, tenantId)); } - private async void OnMessage(object sender, MessageEventArgs e, TelegramBotClient client, int tenantId) - { - if (string.IsNullOrEmpty(e.Message.Text) || e.Message.Text[0] != '/') return; - await command.HandleCommand(e.Message, client, tenantId); - } - private TelegramBotClient InitClient(string token, string proxy) { - return string.IsNullOrEmpty(proxy) ? new TelegramBotClient(token) : new TelegramBotClient(token, new WebProxy(proxy)); + if (String.IsNullOrEmpty(proxy)) return new TelegramBotClient(token); + + var httpClient = new HttpClient( + new HttpClientHandler { Proxy = new WebProxy(proxy), UseProxy = true } + ); + + return new TelegramBotClient(token, httpClient); } private void BindClient(TelegramBotClient client, int tenantId) { - client.OnMessage += (sender, e) => { OnMessage(sender, e, client, tenantId); }; - client.StartReceiving(); + var cts = new CancellationTokenSource(); + + client.StartReceiving(new DefaultUpdateHandler((botClient, exception, cancellationToken) => HandleUpdateAsync(botClient, exception, cancellationToken, tenantId), HandleErrorAsync), + cts.Token); + } + + async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken cancellationToken, int tenantId) + { + if (update.Type != UpdateType.Message) + return; + + if (update.Message.Type != MessageType.Text) + return; + + if (String.IsNullOrEmpty(update.Message.Text) || update.Message.Text[0] != '/') return; + + await command.HandleCommand(update.Message, botClient, tenantId); + } + + Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, CancellationToken cancellationToken) + { + String errorMessage; + + if (exception is ApiRequestException) + { + errorMessage = String.Format("Telegram API Error:\n[{0}]\n{1}", ((ApiRequestException)exception).ErrorCode, ((ApiRequestException)exception).Message); + } + else + { + errorMessage = exception.ToString(); + } + + log.Error(errorMessage); + + return Task.CompletedTask; } private string UserKey(string userId, int tenantId) diff --git a/module/ASC.Thrdparty/ASC.FederatedLogin/ASC.FederatedLogin.csproj b/module/ASC.Thrdparty/ASC.FederatedLogin/ASC.FederatedLogin.csproj index b8f9f33eb..02f62f3e9 100644 --- a/module/ASC.Thrdparty/ASC.FederatedLogin/ASC.FederatedLogin.csproj +++ b/module/ASC.Thrdparty/ASC.FederatedLogin/ASC.FederatedLogin.csproj @@ -83,12 +83,13 @@ + + - @@ -97,7 +98,6 @@ - @@ -140,14 +140,14 @@ - - 4.1.0.12182 - - 1.3.4 + 8.2.3 - - 2.4.2.2 + + 6.12.2 + + + 5.0.4 diff --git a/module/ASC.Thrdparty/ASC.FederatedLogin/Login.cs b/module/ASC.Thrdparty/ASC.FederatedLogin/Login.cs index c0f67f365..f6bf73761 100644 --- a/module/ASC.Thrdparty/ASC.FederatedLogin/Login.cs +++ b/module/ASC.Thrdparty/ASC.FederatedLogin/Login.cs @@ -24,7 +24,6 @@ using System.Web; using System.Web.Script.Serialization; using System.Web.Security; using System.Web.Services; -using System.Web.SessionState; using ASC.FederatedLogin.Helpers; using ASC.FederatedLogin.LoginProviders; @@ -34,7 +33,7 @@ namespace ASC.FederatedLogin { [WebService(Namespace = "http://tempuri.org/")] [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)] - public class Login : IHttpHandler, IRequiresSessionState + public class Login : IHttpHandler { private Dictionary _params; @@ -172,12 +171,7 @@ namespace ASC.FederatedLogin if (useMinimalProfile) profile = profile.GetMinimalProfile(); //Only id and provider - if (context.Session != null && !useMinimalProfile) - { - //Store in session - context.Response.Redirect(new Uri(ReturnUrl, UriKind.Absolute).AddProfileSession(profile, context).ToString(), true); - } - else if (HttpRuntime.Cache != null && !useMinimalProfile) + if (HttpRuntime.Cache != null && !useMinimalProfile) { context.Response.Redirect(new Uri(ReturnUrl, UriKind.Absolute).AddProfileCache(profile).ToString(), true); } diff --git a/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/AppleIdLoginProvider.cs b/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/AppleIdLoginProvider.cs new file mode 100644 index 000000000..701767511 --- /dev/null +++ b/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/AppleIdLoginProvider.cs @@ -0,0 +1,157 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using ASC.FederatedLogin.Helpers; +using ASC.FederatedLogin.Profile; +using Microsoft.IdentityModel.Tokens; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Security.Cryptography; +using System.Threading; +using System.Web; + +namespace ASC.FederatedLogin.LoginProviders +{ + public class AppleIdLoginProvider : BaseLoginProvider + { + public override string AccessTokenUrl { get { return "https://appleid.apple.com/auth/token"; } } + public override string RedirectUri { get { return this["appleIdRedirectUrl"]; } } + public override string ClientID { get { return (this["appleIdClientIdMobile"] != null && HttpContext.Current != null && HttpContext.Current.Request.MobileApp()) ? this["appleIdClientIdMobile"] : this["appleIdClientId"]; } } + public override string ClientSecret => GenerateSecret(); + public override string CodeUrl { get { return "https://appleid.apple.com/auth/authorize"; } } + public override string Scopes { get { return ""; } } + + public string TeamId { get { return this["appleIdTeamId"]; } } + public string KeyId { get { return this["appleIdKeyId"]; } } + public string PrivateKey { get { return this["appleIdPrivateKey"]; } } + + public AppleIdLoginProvider() { } + public AppleIdLoginProvider(string name, int order, Dictionary props, Dictionary additional = null) : base(name, order, props, additional) { } + + public override LoginProfile ProcessAuthoriztion(HttpContext context, IDictionary @params) + { + try + { + var token = Auth(context, Scopes); + var claims = ValidateIdToken(JObject.Parse(token.OriginJson).Value("id_token")); + return GetProfileFromClaims(claims); + } + catch (ThreadAbortException) + { + throw; + } + catch (Exception ex) + { + return LoginProfile.FromError(ex); + } + } + + public override LoginProfile GetLoginProfile(string authCode) + { + if (string.IsNullOrEmpty(authCode)) + throw new Exception("Login failed"); + + var token = OAuth20TokenHelper.GetAccessToken(authCode); + var claims = ValidateIdToken(JObject.Parse(token.OriginJson).Value("id_token")); + return GetProfileFromClaims(claims); + } + + public override LoginProfile GetLoginProfile(OAuth20Token token) + { + if (token == null) + throw new Exception("Login failed"); + + var claims = ValidateIdToken(JObject.Parse(token.OriginJson).Value("id_token")); + return GetProfileFromClaims(claims); + } + + private LoginProfile GetProfileFromClaims(ClaimsPrincipal claims) + { + return new LoginProfile() + { + Id = claims.FindFirst(ClaimTypes.NameIdentifier).Value, + EMail = claims.FindFirst(ClaimTypes.Email)?.Value, + Provider = ProviderConstants.AppleId, + }; + } + + private string GenerateSecret() + { + using (var cngKey = CngKey.Import(Convert.FromBase64String(PrivateKey), CngKeyBlobFormat.Pkcs8PrivateBlob)) + { + var handler = new JwtSecurityTokenHandler(); + var token = handler.CreateJwtSecurityToken( + issuer: TeamId, + audience: "https://appleid.apple.com", + subject: new ClaimsIdentity(new List { new Claim("sub", ClientID) }), + issuedAt: DateTime.UtcNow, + notBefore: DateTime.UtcNow, + expires: DateTime.UtcNow.AddMinutes(5), + signingCredentials: new SigningCredentials(new ECDsaSecurityKey(new ECDsaCng(cngKey)), SecurityAlgorithms.EcdsaSha256) + ); + token.Header.Add("kid", KeyId); + + return handler.WriteToken(token); + } + } + + private ClaimsPrincipal ValidateIdToken(string idToken) + { + var handler = new JwtSecurityTokenHandler(); + var claims = handler.ValidateToken(idToken, new TokenValidationParameters() + { + ValidateIssuerSigningKey = true, + IssuerSigningKeys = GetApplePublicKeys(), + + ValidateIssuer = true, + ValidIssuer = "https://appleid.apple.com", + + ValidateAudience = true, + ValidAudience = ClientID, + + ValidateLifetime = true + + }, out var _); + + return claims; + } + + private IEnumerable GetApplePublicKeys() + { + var appplePublicKeys = RequestHelper.PerformRequest("https://appleid.apple.com/auth/keys"); + + var keys = new List(); + foreach (var webKey in JObject.Parse(appplePublicKeys).Value("keys")) + { + var e = Base64UrlEncoder.DecodeBytes(webKey.Value("e")); + var n = Base64UrlEncoder.DecodeBytes(webKey.Value("n")); + + var key = new RsaSecurityKey(new RSAParameters { Exponent = e, Modulus = n }) + { + KeyId = webKey.Value("kid") + }; + + keys.Add(key); + } + + return keys; + } + } +} \ No newline at end of file diff --git a/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/BaseLoginProvider.cs b/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/BaseLoginProvider.cs index 6e538d240..f6a4edecd 100644 --- a/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/BaseLoginProvider.cs +++ b/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/BaseLoginProvider.cs @@ -104,5 +104,15 @@ namespace ASC.FederatedLogin.LoginProviders } public abstract LoginProfile GetLoginProfile(string accessToken); + + public virtual LoginProfile GetLoginProfile(OAuth20Token token) + { + return GetLoginProfile(token.AccessToken); + } + + public OAuth20Token GetToken(string codeOAuth) + { + return OAuth20TokenHelper.GetAccessToken(codeOAuth); + } } } diff --git a/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/BitlyLoginProvider.cs b/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/BitlyLoginProvider.cs index 7b87b54b9..380e04bc2 100644 --- a/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/BitlyLoginProvider.cs +++ b/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/BitlyLoginProvider.cs @@ -17,31 +17,22 @@ using System; using System.Collections.Generic; -using System.Globalization; -using System.Net; -using System.Xml.Linq; -using System.Xml.XPath; using ASC.Core.Common.Configuration; +using ASC.FederatedLogin.Helpers; + +using Newtonsoft.Json.Linq; namespace ASC.FederatedLogin.LoginProviders { public class BitlyLoginProvider : Consumer, IValidateKeysProvider { - private static string BitlyClientId + private static string BitlyToken { - get { return Instance["bitlyClientId"]; } + get { return Instance["bitlyToken"]; } } - private static string BitlyClientSecret - { - get { return Instance["bitlyClientSecret"]; } - } - - private static string BitlyUrl - { - get { return Instance["bitlyUrl"]; } - } + private readonly static string BitlyUrl = "https://api-ssl.bitly.com/v4/shorten"; private static BitlyLoginProvider Instance { @@ -71,36 +62,26 @@ namespace ASC.FederatedLogin.LoginProviders { get { - return !String.IsNullOrEmpty(BitlyClientId) && - !String.IsNullOrEmpty(BitlyClientSecret) && - !String.IsNullOrEmpty(BitlyUrl); + return !String.IsNullOrEmpty(BitlyToken); } } public static String GetShortenLink(String shareLink) { - var uri = new Uri(shareLink); - - var bitly = string.Format(BitlyUrl, BitlyClientId, BitlyClientSecret, Uri.EscapeDataString(uri.ToString())); - XDocument response; - try + var data = string.Format("{{\"long_url\":\"{0}\"}}", shareLink); + var headers = new Dictionary { - response = XDocument.Load(bitly); - } - catch (Exception e) - { - throw new InvalidOperationException(e.Message, e); - } + {"Authorization" ,"Bearer " + BitlyToken} + }; - var status = response.XPathSelectElement("/response/status_code").Value; - if (status != ((int)HttpStatusCode.OK).ToString(CultureInfo.InvariantCulture)) - { - throw new InvalidOperationException(status); - } + var response = RequestHelper.PerformRequest(BitlyUrl, "application/json", "POST", data, headers); - var data = response.XPathSelectElement("/response/data/url"); + var parser = JObject.Parse(response); + if (parser == null) return null; - return data.Value; + var link = parser.Value("link"); + + return link; } } } diff --git a/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/EncryptionLoginProvider.cs b/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/EncryptionLoginProvider.cs index 50ab9aafe..4c2ec6c41 100644 --- a/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/EncryptionLoginProvider.cs +++ b/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/EncryptionLoginProvider.cs @@ -18,6 +18,7 @@ using System; using System.Linq; +using ASC.Common.Logging; using ASC.FederatedLogin; using ASC.FederatedLogin.Profile; using ASC.Security.Cryptography; @@ -53,9 +54,18 @@ namespace ASC.Web.Studio.Core var linker = new AccountLinker("webstudio"); var profile = linker.GetLinkedProfiles(userId.ToString(), ProviderConstants.Encryption).FirstOrDefault(); if (profile == null) return null; - - var keys = InstanceCrypto.Decrypt(profile.Name); - return keys; + + try + { + var keys = InstanceCrypto.Decrypt(profile.Name); + return keys; + } + catch (Exception ex) + { + var message = string.Format("Can not decrypt {0} keys for {1}", ProviderConstants.Encryption, userId); + LogManager.GetLogger("ASC").Error(message, ex); + return null; + } } } } \ No newline at end of file diff --git a/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/GosUslugiLoginProvider.cs b/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/GosUslugiLoginProvider.cs index 2e5677ee9..8e631cb27 100644 --- a/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/GosUslugiLoginProvider.cs +++ b/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/GosUslugiLoginProvider.cs @@ -28,6 +28,9 @@ using ASC.FederatedLogin.Helpers; using ASC.FederatedLogin.Profile; using JWT; +using JWT.Algorithms; +using JWT.Builder; +using JWT.Serializers; using Newtonsoft.Json.Linq; @@ -185,8 +188,11 @@ namespace ASC.FederatedLogin.LoginProviders } public override LoginProfile GetLoginProfile(string accessToken) - { - var tokenPayloadString = JsonWebToken.Decode(accessToken, string.Empty, false); + { + var tokenPayloadString = JwtBuilder.Create() + .WithAlgorithm(new HMACSHA256Algorithm()) + .Decode(accessToken); + var tokenPayload = JObject.Parse(tokenPayloadString); if (tokenPayload == null) { diff --git a/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/ILoginProvider.cs b/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/ILoginProvider.cs index 8b874f783..15c9bf360 100644 --- a/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/ILoginProvider.cs +++ b/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/ILoginProvider.cs @@ -27,6 +27,10 @@ namespace ASC.FederatedLogin.LoginProviders LoginProfile ProcessAuthoriztion(HttpContext context, IDictionary @params); LoginProfile GetLoginProfile(string accessToken); + + LoginProfile GetLoginProfile(OAuth20Token token); + + OAuth20Token GetToken(string codeOAuth); } public interface IOAuthProvider diff --git a/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/MicrosoftLoginProvider.cs b/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/MicrosoftLoginProvider.cs new file mode 100644 index 000000000..00e39d826 --- /dev/null +++ b/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/MicrosoftLoginProvider.cs @@ -0,0 +1,72 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System; +using System.Collections.Generic; +using ASC.FederatedLogin.Helpers; +using ASC.FederatedLogin.Profile; +using Newtonsoft.Json.Linq; + +namespace ASC.FederatedLogin.LoginProviders +{ + public class MicrosoftLoginProvider : BaseLoginProvider + { + private const string MicrosoftProfileUrl = "https://graph.microsoft.com/oidc/userinfo"; + + public override string AccessTokenUrl { get { return "https://login.microsoftonline.com/consumers/oauth2/v2.0/token"; } } + public override string RedirectUri { get { return this["microsoftRedirectUrl"]; } } + public override string ClientID { get { return this["microsoftClientId"]; } } + public override string ClientSecret { get { return this["microsoftClientSecret"]; } } + public override string CodeUrl { get { return "https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize"; } } + public override string Scopes { get { return "openid,email,profile"; } } + + public MicrosoftLoginProvider() { } + public MicrosoftLoginProvider(string name, int order, Dictionary props, Dictionary additional = null) : base(name, order, props, additional) {} + + public override LoginProfile GetLoginProfile(string accessToken) + { + if (string.IsNullOrEmpty(accessToken)) + throw new Exception("Login failed"); + + return RequestProfile(accessToken); + } + + private static LoginProfile RequestProfile(string accessToken) + { + var openidProfile = RequestHelper.PerformRequest(MicrosoftProfileUrl, headers: new Dictionary() { { "Authorization", "Bearer " + accessToken } }); + var loginProfile = ProfileFromMicrosoft(openidProfile); + return loginProfile; + } + + internal static LoginProfile ProfileFromMicrosoft(string openidProfile) + { + var jProfile = JObject.Parse(openidProfile); + if (jProfile == null) throw new Exception("Failed to correctly process the response"); + + var profile = new LoginProfile + { + FirstName = jProfile.Value("given_name"), + LastName = jProfile.Value("family_name"), + EMail = jProfile.Value("email"), + Id = jProfile.Value("sub"), + Provider = ProviderConstants.Microsoft + }; + + return profile; + } + } +} \ No newline at end of file diff --git a/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/OpenIdLoginProvider.cs b/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/OpenIdLoginProvider.cs deleted file mode 100644 index 4d0ff9f74..000000000 --- a/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/OpenIdLoginProvider.cs +++ /dev/null @@ -1,171 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.Web; - -using ASC.FederatedLogin.Profile; - -using DotNetOpenAuth.Messaging; -using DotNetOpenAuth.OpenId; -using DotNetOpenAuth.OpenId.Extensions.AttributeExchange; -using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration; -using DotNetOpenAuth.OpenId.RelyingParty; - -namespace ASC.FederatedLogin.LoginProviders -{ - class OpenIdLoginProvider : ILoginProvider - { - private static readonly OpenIdRelyingParty Openid = new OpenIdRelyingParty(); - - - public LoginProfile ProcessAuthoriztion(HttpContext context, IDictionary @params) - { - var response = Openid.GetResponse(); - if (response == null) - { - Identifier id; - if (Identifier.TryParse(@params["oid"], out id)) - { - try - { - IAuthenticationRequest request; - - var realmUrlString = String.Empty; - - if (@params.ContainsKey("realmUrl")) - realmUrlString = @params["realmUrl"]; - - if (!String.IsNullOrEmpty(realmUrlString)) - request = Openid.CreateRequest(id, new Realm(realmUrlString)); - else - request = Openid.CreateRequest(id); - - request.AddExtension(new ClaimsRequest - { - Email = DemandLevel.Require, - Nickname = DemandLevel.Require, - Country = DemandLevel.Request, - Gender = DemandLevel.Request, - PostalCode = DemandLevel.Request, - TimeZone = DemandLevel.Request, - FullName = DemandLevel.Request, - - - }); - var fetch = new FetchRequest(); - fetch.Attributes.AddRequired(WellKnownAttributes.Contact.Email); - //Duplicating attributes - fetch.Attributes.AddRequired("http://schema.openid.net/contact/email");//Add two more - fetch.Attributes.AddRequired("http://openid.net/schema/contact/email"); - fetch.Attributes.AddRequired(WellKnownAttributes.Name.Alias); - fetch.Attributes.AddRequired(WellKnownAttributes.Name.First); - fetch.Attributes.AddRequired(WellKnownAttributes.Media.Images.Default); - fetch.Attributes.AddRequired(WellKnownAttributes.Name.Last); - fetch.Attributes.AddRequired(WellKnownAttributes.Name.Middle); - fetch.Attributes.AddRequired(WellKnownAttributes.Person.Gender); - fetch.Attributes.AddRequired(WellKnownAttributes.BirthDate.WholeBirthDate); - request.AddExtension(fetch); - request.RedirectToProvider(); - context.Response.End();//This will throw thread abort - - } - catch (ProtocolException ex) - { - return LoginProfile.FromError(ex); - } - } - else - { - return LoginProfile.FromError(new Exception("invalid OpenID identifier")); - } - } - else - { - // Stage 3: OpenID Provider sending assertion response - switch (response.Status) - { - case AuthenticationStatus.Authenticated: - var spprofile = response.GetExtension(); - var fetchprofile = response.GetExtension(); - - var realmUrlString = String.Empty; - if (@params.ContainsKey("realmUrl")) - realmUrlString = @params["realmUrl"]; - - var profile = ProfileFromOpenId(spprofile, fetchprofile, response.ClaimedIdentifier.ToString(), realmUrlString); - return profile; - case AuthenticationStatus.Canceled: - return LoginProfile.FromError(new Exception("Canceled at provider")); - case AuthenticationStatus.Failed: - return LoginProfile.FromError(response.Exception); - } - } - return null; - } - - public LoginProfile GetLoginProfile(string accessToken) - { - throw new NotImplementedException(); - } - - public string Scopes { get { return ""; } } - public string CodeUrl { get { return ""; } } - public string AccessTokenUrl { get { return ""; } } - public string RedirectUri { get { return ""; } } - public string ClientID { get { return ""; } } - public string ClientSecret { get { return ""; } } - public bool IsEnabled { get { return GoogleLoginProvider.Instance.IsEnabled; } } - - internal static LoginProfile ProfileFromOpenId(ClaimsResponse spprofile, FetchResponse fetchprofile, string claimedId, string realmUrlString) - { - var profile = new LoginProfile - { - Link = claimedId, - Id = claimedId, - Provider = ProviderConstants.OpenId, - }; - if (spprofile != null) - { - //Fill - profile.BirthDay = spprofile.BirthDateRaw; - profile.DisplayName = spprofile.FullName; - profile.EMail = spprofile.Email; - profile.Name = spprofile.Nickname; - profile.Gender = spprofile.Gender.HasValue ? spprofile.Gender.Value.ToString() : ""; - profile.TimeZone = spprofile.TimeZone; - profile.Locale = spprofile.Language; - } - if (fetchprofile != null) - { - profile.Name = fetchprofile.GetAttributeValue(WellKnownAttributes.Name.Alias); - profile.LastName = fetchprofile.GetAttributeValue(WellKnownAttributes.Name.Last); - profile.FirstName = fetchprofile.GetAttributeValue(WellKnownAttributes.Name.First); - profile.DisplayName = fetchprofile.GetAttributeValue(WellKnownAttributes.Name.FullName); - profile.MiddleName = fetchprofile.GetAttributeValue(WellKnownAttributes.Name.Middle); - profile.Salutation = fetchprofile.GetAttributeValue(WellKnownAttributes.Name.Prefix); - profile.Avatar = fetchprofile.GetAttributeValue(WellKnownAttributes.Media.Images.Default); - profile.EMail = fetchprofile.GetAttributeValue(WellKnownAttributes.Contact.Email); - profile.Gender = fetchprofile.GetAttributeValue(WellKnownAttributes.Person.Gender); - profile.BirthDay = fetchprofile.GetAttributeValue(WellKnownAttributes.BirthDate.WholeBirthDate); - } - profile.RealmUrl = realmUrlString; - return profile; - } - } -} \ No newline at end of file diff --git a/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/ProviderManager.cs b/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/ProviderManager.cs index 43eda1af7..65477a333 100644 --- a/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/ProviderManager.cs +++ b/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/ProviderManager.cs @@ -28,9 +28,7 @@ namespace ASC.FederatedLogin.LoginProviders { public static ILoginProvider GetLoginProvider(string providerType) { - return providerType == ProviderConstants.OpenId - ? new OpenIdLoginProvider() - : ConsumerFactory.GetByName(providerType) as ILoginProvider; + return ConsumerFactory.GetByName(providerType) as ILoginProvider; } public static LoginProfile Process(string providerType, HttpContext context, IDictionary @params) @@ -38,13 +36,18 @@ namespace ASC.FederatedLogin.LoginProviders return GetLoginProvider(providerType).ProcessAuthoriztion(context, @params); } - public static LoginProfile GetLoginProfile(string providerType, string accessToken) + public static LoginProfile GetLoginProfile(string providerType, string accessToken = null, string codeOAuth = null) { var consumer = GetLoginProvider(providerType); if (consumer == null) throw new ArgumentException("Unknown provider type", "providerType"); try { + if(accessToken == null && codeOAuth != null) + { + return consumer.GetLoginProfile(consumer.GetToken(codeOAuth)); + } + return consumer.GetLoginProfile(accessToken); } catch (Exception ex) diff --git a/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/TwitterLoginProvider.cs b/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/TwitterLoginProvider.cs index 326467464..f490424dc 100644 --- a/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/TwitterLoginProvider.cs +++ b/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/TwitterLoginProvider.cs @@ -22,7 +22,11 @@ using System.Web; using ASC.FederatedLogin.Profile; -using Twitterizer; +using Newtonsoft.Json.Linq; + +using Tweetinvi; +using Tweetinvi.Auth; +using Tweetinvi.Parameters; namespace ASC.FederatedLogin.LoginProviders { @@ -39,6 +43,8 @@ namespace ASC.FederatedLogin.LoginProviders public override string ClientSecret { get { return this["twitterSecret"]; } } public override string CodeUrl { get { return "https://api.twitter.com/oauth/request_token"; } } + private static readonly IAuthenticationRequestStore _myAuthRequestStore = new LocalAuthenticationRequestStore(); + public override bool IsEnabled { get @@ -58,6 +64,8 @@ namespace ASC.FederatedLogin.LoginProviders return LoginProfile.FromError(new Exception("Canceled at provider")); } + var appClient = new TwitterClient(TwitterKey, TwitterSecret); + if (string.IsNullOrEmpty(context.Request["oauth_token"])) { var callbackAddress = new UriBuilder(RedirectUri) @@ -65,27 +73,61 @@ namespace ASC.FederatedLogin.LoginProviders Query = "state=" + HttpUtility.UrlEncode(context.Request.GetUrlRewriter().AbsoluteUri) }; - var reqToken = OAuthUtility.GetRequestToken(TwitterKey, TwitterSecret, callbackAddress.ToString()); - var url = OAuthUtility.BuildAuthorizationUri(reqToken.Token).ToString(); - context.Response.Redirect(url, true); + var authenticationRequestId = Guid.NewGuid().ToString(); + + // Add the user identifier as a query parameters that will be received by `ValidateTwitterAuth` + var redirectURL = _myAuthRequestStore.AppendAuthenticationRequestIdToCallbackUrl(callbackAddress.ToString(), authenticationRequestId); + + // Initialize the authentication process + var authenticationRequestToken = appClient.Auth.RequestAuthenticationUrlAsync(redirectURL) + .ConfigureAwait(false) + .GetAwaiter() + .GetResult(); + + // Store the token information in the store + _myAuthRequestStore.AddAuthenticationTokenAsync(authenticationRequestId, authenticationRequestToken) + .ConfigureAwait(false) + .GetAwaiter() + .GetResult(); + + context.Response.Redirect(authenticationRequestToken.AuthorizationURL, true); + return null; } - var requestToken = context.Request["oauth_token"]; - var pin = context.Request["oauth_verifier"]; + // Extract the information from the redirection url + var requestParameters = RequestCredentialsParameters.FromCallbackUrlAsync(context.Request.RawUrl, _myAuthRequestStore).GetAwaiter().GetResult(); + // Request Twitter to generate the credentials. + var userCreds = appClient.Auth.RequestCredentialsAsync(requestParameters) + .ConfigureAwait(false) + .GetAwaiter() + .GetResult(); - var tokens = OAuthUtility.GetAccessToken(TwitterKey, TwitterSecret, requestToken, pin); + // Congratulations the user is now authenticated! + var userClient = new TwitterClient(userCreds); - var accesstoken = new OAuthTokens - { - AccessToken = tokens.Token, - AccessTokenSecret = tokens.TokenSecret, - ConsumerKey = TwitterKey, - ConsumerSecret = TwitterSecret - }; + var user = userClient.Users.GetAuthenticatedUserAsync() + .ConfigureAwait(false) + .GetAwaiter() + .GetResult(); + + var userSettings = userClient.AccountSettings.GetAccountSettingsAsync() + .ConfigureAwait(false) + .GetAwaiter() + .GetResult(); + + return user == null + ? null + : new LoginProfile + { + Name = user.Name, + DisplayName = user.ScreenName, + Avatar = user.ProfileImageUrl, + Locale = userSettings.Language.ToString(), + Id = user.Id.ToString(CultureInfo.InvariantCulture), + Provider = ProviderConstants.Twitter + }; - var account = TwitterAccount.VerifyCredentials(accesstoken).ResponseObject; - return ProfileFromTwitter(account); } protected override OAuth20Token Auth(HttpContext context, string scopes, Dictionary additional = null) @@ -98,20 +140,18 @@ namespace ASC.FederatedLogin.LoginProviders throw new NotImplementedException(); } - internal static LoginProfile ProfileFromTwitter(TwitterUser twitterUser) + internal static LoginProfile ProfileFromTwitter(string twitterProfile) { - return twitterUser == null - ? null - : new LoginProfile - { - Name = twitterUser.Name, - DisplayName = twitterUser.ScreenName, - Avatar = twitterUser.ProfileImageSecureLocation, - TimeZone = twitterUser.TimeZone, - Locale = twitterUser.Language, - Id = twitterUser.Id.ToString(CultureInfo.InvariantCulture), - Provider = ProviderConstants.Twitter - }; + var jProfile = JObject.Parse(twitterProfile); + if (jProfile == null) throw new Exception("Failed to correctly process the response"); + + return new LoginProfile + { + DisplayName = jProfile.Value("name"), + Locale = jProfile.Value("lang"), + Id = jProfile.Value("id"), + Provider = ProviderConstants.Twitter + }; } } } \ No newline at end of file diff --git a/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/YahooLoginProvider.cs b/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/YahooLoginProvider.cs deleted file mode 100644 index b1df9ef38..000000000 --- a/module/ASC.Thrdparty/ASC.FederatedLogin/LoginProviders/YahooLoginProvider.cs +++ /dev/null @@ -1,51 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.Web; - -using ASC.FederatedLogin.Profile; - -namespace ASC.FederatedLogin.LoginProviders -{ - public class YahooLoginProvider : BaseLoginProvider - { - public const string YahooUrlUserGuid = "https://social.yahooapis.com/v1/me/guid"; - public const string YahooUrlContactsFormat = "https://social.yahooapis.com/v1/user/{0}/contacts"; - - public override string CodeUrl { get { return "https://api.login.yahoo.com/oauth2/request_auth"; } } - public override string AccessTokenUrl { get { return "https://api.login.yahoo.com/oauth2/get_token"; } } - public override string RedirectUri { get { return this["yahooRedirectUrl"]; } } - public override string ClientID { get { return this["yahooClientId"]; } } - public override string ClientSecret { get { return this["yahooClientSecret"]; } } - public override string Scopes { get { return "sdct-r"; } } - - public YahooLoginProvider() { } - public YahooLoginProvider(string name, int order, Dictionary props, Dictionary additional = null) : base(name, order, props, additional) { } - - public OAuth20Token Auth(HttpContext context) - { - return Auth(context, Scopes); - } - - public override LoginProfile GetLoginProfile(string accessToken) - { - throw new NotImplementedException(); - } - } -} diff --git a/module/ASC.Thrdparty/ASC.FederatedLogin/Profile/LoginProfile.cs b/module/ASC.Thrdparty/ASC.FederatedLogin/Profile/LoginProfile.cs index a30796f4a..e92bb0ac4 100644 --- a/module/ASC.Thrdparty/ASC.FederatedLogin/Profile/LoginProfile.cs +++ b/module/ASC.Thrdparty/ASC.FederatedLogin/Profile/LoginProfile.cs @@ -313,18 +313,9 @@ namespace ASC.FederatedLogin.Profile var key = HashHelper.MD5(Transport()); HttpRuntime.Cache.Add(key, this, null, Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(15), CacheItemPriority.High, null); - return AppendQueryParam(uri, QuerySessionParamName, key); + return AppendQueryParam(uri, QueryCacheParamName, key); } - internal Uri AppendSessionProfile(Uri uri, HttpContext context) - { - //gen key - var key = HashHelper.MD5(Transport()); - context.Session[key] = this; - return AppendQueryParam(uri, QuerySessionParamName, key); - } - - internal void ParseFromUrl(Uri uri) { var queryString = HttpUtility.ParseQueryString(uri.Query); @@ -332,10 +323,6 @@ namespace ASC.FederatedLogin.Profile { FromTransport(queryString[QueryParamName]); } - else if (!string.IsNullOrEmpty(queryString[QuerySessionParamName])) - { - FromTransport((string)HttpContext.Current.Session[queryString[QuerySessionParamName]]); - } else if (!string.IsNullOrEmpty(queryString[QueryCacheParamName])) { FromTransport((string)HttpRuntime.Cache[queryString[QueryCacheParamName]]); diff --git a/module/ASC.Thrdparty/ASC.FederatedLogin/Profile/LoginProfileExtensions.cs b/module/ASC.Thrdparty/ASC.FederatedLogin/Profile/LoginProfileExtensions.cs index 51663fae8..93783f13e 100644 --- a/module/ASC.Thrdparty/ASC.FederatedLogin/Profile/LoginProfileExtensions.cs +++ b/module/ASC.Thrdparty/ASC.FederatedLogin/Profile/LoginProfileExtensions.cs @@ -26,10 +26,6 @@ namespace ASC.FederatedLogin.Profile { return profile.AppendProfile(uri); } - public static Uri AddProfileSession(this Uri uri, LoginProfile profile, HttpContext context) - { - return profile.AppendSessionProfile(uri, context); - } public static Uri AddProfileCache(this Uri uri, LoginProfile profile) { @@ -40,10 +36,6 @@ namespace ASC.FederatedLogin.Profile { var profile = new LoginProfile(); var queryString = HttpUtility.ParseQueryString(uri.Query); - if (!string.IsNullOrEmpty(queryString[LoginProfile.QuerySessionParamName]) && HttpContext.Current != null && HttpContext.Current.Session != null) - { - return (LoginProfile)HttpContext.Current.Session[queryString[LoginProfile.QuerySessionParamName]]; - } if (!string.IsNullOrEmpty(queryString[LoginProfile.QueryParamName])) { profile.ParseFromUrl(uri); @@ -51,7 +43,7 @@ namespace ASC.FederatedLogin.Profile } if (!string.IsNullOrEmpty(queryString[LoginProfile.QueryCacheParamName])) { - return (LoginProfile)HttpRuntime.Cache.Get(queryString[LoginProfile.QuerySessionParamName]); + return (LoginProfile)HttpRuntime.Cache.Get(queryString[LoginProfile.QueryCacheParamName]); } return null; } diff --git a/module/ASC.Thrdparty/ASC.FederatedLogin/ProviderConstants.cs b/module/ASC.Thrdparty/ASC.FederatedLogin/ProviderConstants.cs index ce2b9c827..18287f1bd 100644 --- a/module/ASC.Thrdparty/ASC.FederatedLogin/ProviderConstants.cs +++ b/module/ASC.Thrdparty/ASC.FederatedLogin/ProviderConstants.cs @@ -22,7 +22,6 @@ namespace ASC.FederatedLogin public const string Twitter = "twitter"; public const string Facebook = "facebook"; public const string LinkedIn = "linkedin"; - public const string OpenId = "openid"; public const string Box = "box"; public const string Google = "google"; public const string Yandex = "yandex"; @@ -30,5 +29,7 @@ namespace ASC.FederatedLogin public const string VK = "vk"; public const string GosUslugi = "gosuslugi"; public const string Encryption = "e2e"; + public const string AppleId = "appleid"; + public const string Microsoft = "microsoft"; } } diff --git a/module/ASC.Thrdparty/ASC.Thrdparty/ASC.Thrdparty.csproj b/module/ASC.Thrdparty/ASC.Thrdparty/ASC.Thrdparty.csproj index 87233d0ca..762ede1bc 100644 --- a/module/ASC.Thrdparty/ASC.Thrdparty/ASC.Thrdparty.csproj +++ b/module/ASC.Thrdparty/ASC.Thrdparty/ASC.Thrdparty.csproj @@ -57,8 +57,6 @@ - - @@ -69,11 +67,11 @@ - - 4.1.0.12182 - - 12.0.3 + 13.0.1 + + + 5.0.4 @@ -81,6 +79,13 @@ {76de7717-3d4b-4a5b-b740-15b8913df0cb} ASC.Common + + {a51d0454-4afa-46de-89d4-b03d37e1816c} + ASC.Core.Common + + + + \ No newline at end of file diff --git a/module/ASC.Thrdparty/ASC.Thrdparty/InMemoryTokenManager.cs b/module/ASC.Thrdparty/ASC.Thrdparty/InMemoryTokenManager.cs deleted file mode 100644 index 9120bdccc..000000000 --- a/module/ASC.Thrdparty/ASC.Thrdparty/InMemoryTokenManager.cs +++ /dev/null @@ -1,192 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.Linq; - -using DotNetOpenAuth.OAuth; -using DotNetOpenAuth.OAuth.ChannelElements; -using DotNetOpenAuth.OAuth.Messages; -using DotNetOpenAuth.OpenId.Extensions.OAuth; - -namespace ASC.Thrdparty -{ - public class InMemoryTokenManager : IConsumerTokenManager, IOpenIdOAuthTokenManager - { - private readonly Dictionary _tokensAndSecrets = new Dictionary(); - private readonly Dictionary _tokensAndAssociations = new Dictionary(); - - /// - /// Initializes a new instance of the class. - /// - /// The consumer key. - /// The consumer secret. - public InMemoryTokenManager(string consumerKey, string consumerSecret) - { - if (String.IsNullOrEmpty(consumerKey)) - { - throw new ArgumentNullException("consumerKey"); - } - - ConsumerKey = consumerKey; - ConsumerSecret = consumerSecret; - } - - /// - /// Gets the consumer key. - /// - /// The consumer key. - public string ConsumerKey { get; private set; } - - /// - /// Gets the consumer secret. - /// - /// The consumer secret. - public string ConsumerSecret { get; private set; } - - #region ITokenManager Members - - /// - /// Gets the Token Secret given a request or access token. - /// - /// The request or access token. - /// - /// The secret associated with the given token. - /// - /// Thrown if the secret cannot be found for the given token. - public string GetTokenSecret(string token) - { - return _tokensAndSecrets[token]; - } - - /// - /// Stores a newly generated unauthorized request token, secret, and optional - /// application-specific parameters for later recall. - /// - /// The request message that resulted in the generation of a new unauthorized request token. - /// The response message that includes the unauthorized request token. - /// Thrown if the consumer key is not registered, or a required parameter was not found in the parameters collection. - /// - /// Request tokens stored by this method SHOULD NOT associate any user account with this token. - /// It usually opens up security holes in your application to do so. Instead, you associate a user - /// account with access tokens (not request tokens) in the - /// method. - /// - public void StoreNewRequestToken(UnauthorizedTokenRequest request, ITokenSecretContainingMessage response) - { - _tokensAndSecrets[response.Token] = response.TokenSecret; - } - - /// - /// Deletes a request token and its associated secret and stores a new access token and secret. - /// - /// The Consumer that is exchanging its request token for an access token. - /// The Consumer's request token that should be deleted/expired. - /// The new access token that is being issued to the Consumer. - /// The secret associated with the newly issued access token. - /// - /// - /// Any scope of granted privileges associated with the request token from the - /// original call to should be carried over - /// to the new Access Token. - /// - /// - /// To associate a user account with the new access token, - /// HttpContext.Current.User may be - /// useful in an ASP.NET web application within the implementation of this method. - /// Alternatively you may store the access token here without associating with a user account, - /// and wait until or - /// return the access - /// token to associate the access token with a user account at that point. - /// - /// - public void ExpireRequestTokenAndStoreNewAccessToken(string consumerKey, string requestToken, string accessToken, string accessTokenSecret) - { - _tokensAndSecrets.Remove(requestToken); - _tokensAndSecrets[accessToken] = accessTokenSecret; - } - - /// - /// Classifies a token as a request token or an access token. - /// - /// The token to classify. - /// Request or Access token, or invalid if the token is not recognized. - public TokenType GetTokenType(string token) - { - throw new NotImplementedException(); - } - - #endregion - - #region IOpenIdOAuthTokenManager Members - - /// - /// Stores a new request token obtained over an OpenID request. - /// - /// The consumer key. - /// The authorization message carrying the request token and authorized access scope. - /// - /// The token secret is the empty string. - /// Tokens stored by this method should be short-lived to mitigate - /// possible security threats. Their lifetime should be sufficient for the - /// relying party to receive the positive authentication assertion and immediately - /// send a follow-up request for the access token. - /// - public void StoreOpenIdAuthorizedRequestToken(string consumerKey, AuthorizationApprovedResponse authorization) - { - _tokensAndSecrets[authorization.RequestToken] = String.Empty; - } - - #endregion - - public IEnumerable GetAccessTokens() - { - return _tokensAndSecrets.Keys; - } - - public IEnumerable GetAssociations() - { - return _tokensAndAssociations.Values; - } - - public string GetAssociation(string token) - { - return _tokensAndAssociations[token]; - } - - public IEnumerable GetTokensByAssociation(string associateData) - { - return _tokensAndAssociations.Where(x => x.Value == associateData).Select(x => x.Key); - } - - public void AssociateToken(string token, string associateData) - { - _tokensAndAssociations.Add(token, associateData); - } - - public void RemoveAssociationFromToken(string token) - { - _tokensAndAssociations.Remove(token); - } - - public void ExpireToken(string token) - { - _tokensAndSecrets.Remove(token); - } - } -} \ No newline at end of file diff --git a/module/ASC.Thrdparty/ASC.Thrdparty/Twitter/TwitterConsumer.cs b/module/ASC.Thrdparty/ASC.Thrdparty/Twitter/TwitterConsumer.cs deleted file mode 100644 index e51f2d912..000000000 --- a/module/ASC.Thrdparty/ASC.Thrdparty/Twitter/TwitterConsumer.cs +++ /dev/null @@ -1,149 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -//----------------------------------------------------------------------- -// -// Copyright (c) Outercurve Foundation. All rights reserved. -// -//----------------------------------------------------------------------- - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Net; - -using DotNetOpenAuth.Messaging; -using DotNetOpenAuth.OAuth; -using DotNetOpenAuth.OAuth.ChannelElements; - -using Newtonsoft.Json.Linq; - -namespace ASC.Thrdparty.Twitter -{ - /// - /// A consumer capable of communicating with Twitter. - /// - public static class TwitterConsumer - { - /// - /// The description of Twitter's OAuth protocol URIs for use with their "Sign in with Twitter" feature. - /// - public static readonly ServiceProviderDescription SignInWithTwitterServiceDescription = new ServiceProviderDescription - { - RequestTokenEndpoint = new MessageReceivingEndpoint("https://twitter.com/oauth/request_token", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest), - UserAuthorizationEndpoint = new MessageReceivingEndpoint("https://twitter.com/oauth/authenticate", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest), - AccessTokenEndpoint = new MessageReceivingEndpoint("https://twitter.com/oauth/access_token", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest), - TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() }, - }; - - /// - /// The URI to get a user's favorites. - /// - private static readonly MessageReceivingEndpoint GetFavoritesEndpoint = new MessageReceivingEndpoint("https://twitter.com/favorites.json", HttpDeliveryMethods.GetRequest); - private static readonly MessageReceivingEndpoint GetHomeTimeLineEndpoint = new MessageReceivingEndpoint("https://api.twitter.com/1.1/statuses/home_timeline.json", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest); - private static readonly MessageReceivingEndpoint GetUserTimeLineEndPoint = new MessageReceivingEndpoint("https://api.twitter.com/1.1/statuses/user_timeline.json", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest); - - public static JArray GetUserTimeLine(WebConsumer consumer, String accessToken, int userId, String screenName, bool includeRetweets, int count) - { - var parameters = new Dictionary - { - { "count", count.ToString(CultureInfo.InvariantCulture) }, - { "include_rts", includeRetweets.ToString() } - }; - - if (!String.IsNullOrEmpty(screenName)) - parameters.Add("screen_name", screenName); - - if (userId > 0) - parameters.Add("user_id", userId.ToString(CultureInfo.InvariantCulture)); - - var request = consumer.PrepareAuthorizedRequest(GetUserTimeLineEndPoint, accessToken, parameters); - var response = consumer.Channel.WebRequestHandler.GetResponse(request); - - using (var responseReader = response.GetResponseReader()) - { - var result = responseReader.ReadToEnd(); - - return JArray.Parse(result); - } - } - - public static JArray GetHomeTimeLine(WebConsumer consumer, String accessToken, bool includeRetweets, int count) - { - var parameters = new Dictionary - { - { "count", count.ToString(CultureInfo.InvariantCulture) }, - { "include_rts", includeRetweets.ToString() } - }; - - var request = consumer.PrepareAuthorizedRequest(GetHomeTimeLineEndpoint, accessToken, parameters); - var response = consumer.Channel.WebRequestHandler.GetResponse(request); - - using (var responseReader = response.GetResponseReader()) - { - var result = responseReader.ReadToEnd(); - - return JArray.Parse(result); - } - } - - public static JObject GetUserInfo(WebConsumer consumer, int userid, String screenName, string accessToken) - { - const string baseUri = "https://api.twitter.com/1.1/users/show.json"; - var uri = String.Empty; - - if (userid > 0 && String.IsNullOrEmpty(screenName)) - uri = String.Concat(baseUri, "?user_id=", userid); - - if (userid == 0 && !String.IsNullOrEmpty(screenName)) - uri = String.Concat(baseUri, "?screen_name=", screenName); - - var endpoint = new MessageReceivingEndpoint(uri, HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest); - var response = consumer.PrepareAuthorizedRequestAndSend(endpoint, accessToken); - - using (var responseReader = response.GetResponseReader()) - { - var result = responseReader.ReadToEnd(); - - return JObject.Parse(result); - } - } - - public static JArray SearchUsers(WebConsumer consumer, String search, string accessToken) - { - var endpoint = new MessageReceivingEndpoint("https://api.twitter.com/1.1/users/search.json?q=" + Uri.EscapeDataString(search), HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest); - - var response = consumer.PrepareAuthorizedRequestAndSend(endpoint, accessToken); - - using (var responseReader = response.GetResponseReader()) - { - var result = responseReader.ReadToEnd(); - - return JArray.Parse(result); - } - } - - /// - /// Initializes static members of the class. - /// - static TwitterConsumer() - { - // Twitter can't handle the Expect 100 Continue HTTP header. - ServicePointManager.FindServicePoint(GetFavoritesEndpoint.Location).Expect100Continue = false; - } - } -} \ No newline at end of file diff --git a/module/ASC.Thrdparty/ASC.Thrdparty/Twitter/TwitterDataProvider.cs b/module/ASC.Thrdparty/ASC.Thrdparty/Twitter/TwitterDataProvider.cs index a3a6afc4e..e3394e8ed 100644 --- a/module/ASC.Thrdparty/ASC.Thrdparty/Twitter/TwitterDataProvider.cs +++ b/module/ASC.Thrdparty/ASC.Thrdparty/Twitter/TwitterDataProvider.cs @@ -21,9 +21,9 @@ using System.Globalization; using System.Linq; using System.Text.RegularExpressions; -using ASC.Common.Logging; - -using DotNetOpenAuth.OAuth; +using Tweetinvi; +using Tweetinvi.Models; +using Tweetinvi.Parameters; namespace ASC.Thrdparty.Twitter { @@ -32,37 +32,15 @@ namespace ASC.Thrdparty.Twitter /// public class TwitterDataProvider { - private static WebConsumer _signInConsumer; - private static readonly object SignInConsumerInitLock = new object(); - - private WebConsumer TwitterSignIn - { - get - { - if (_signInConsumer != null) return _signInConsumer; - - lock (SignInConsumerInitLock) - { - if (_signInConsumer == null) - { - var tokenManagerHolder = new InMemoryTokenManager(_apiInfo.ConsumerKey, _apiInfo.ConsumerSecret); - - return _signInConsumer = new WebConsumer(TwitterConsumer.SignInWithTwitterServiceDescription, tokenManagerHolder); - } - } - - return _signInConsumer; - } - } - - private readonly TwitterApiInfo _apiInfo; - public enum ImageSize { Small, Original } + private TwitterClient _twitterClient; + + /// /// Costructor /// @@ -72,20 +50,7 @@ namespace ASC.Thrdparty.Twitter if (apiInfo == null) throw new ArgumentNullException("apiInfo"); - _apiInfo = apiInfo; - - try - { - TwitterSignIn.TokenManager.ExpireRequestTokenAndStoreNewAccessToken( - apiInfo.ConsumerKey, - String.Empty, - apiInfo.AccessToken, - apiInfo.AccessTokenSecret); - } - catch (Exception ex) - { - LogManager.GetLogger(typeof(TwitterDataProvider).ToString()).Error("TwitterSignIn in TwitterDataProvider", ex); - } + _twitterClient = new TwitterClient(apiInfo.ConsumerKey, apiInfo.ConsumerSecret, apiInfo.AccessToken, apiInfo.AccessTokenSecret); } private static String ParseTweetTextIntoHtml(String text) @@ -97,42 +62,27 @@ namespace ASC.Thrdparty.Twitter return text; } - private static DateTime ParseTweetDateTime(String dateTimeAsText) - { - var month = dateTimeAsText.Substring(4, 3).Trim(); - var dayInMonth = dateTimeAsText.Substring(8, 2).Trim(); - var time = dateTimeAsText.Substring(11, 9).Trim(); - var year = dateTimeAsText.Substring(25, 5).Trim(); - - var dateTime = string.Format("{0}-{1}-{2} {3}", dayInMonth, month, year, time); - return DateTime.Parse(dateTime, CultureInfo.InvariantCulture); - } - - /// /// Gets tweets posted by specified user /// /// Message list - public List GetUserTweets(decimal? userID, string screenName, int messageCount) - { - var localUserId = 0; + public List GetUserTweets(string screenName, int messageCount) + { - if (userID.HasValue) - localUserId = (int)userID.Value; - - var userTimeLine = TwitterConsumer.GetUserTimeLine(TwitterSignIn, _apiInfo.AccessToken, localUserId, screenName, true, messageCount); - - if (userTimeLine == null) return new List(); - - return userTimeLine.Select(x => (Message)(new TwitterMessage + var tweets = _twitterClient.Timelines.GetUserTimelineAsync(new GetUserTimelineParameters(screenName) { - UserName = x["user"].Value("name"), - PostedOn = ParseTweetDateTime(x.Value("created_at")), + PageSize = messageCount + }).Result; + + return tweets.Select(x => (Message)new TwitterMessage + { + UserName = x.CreatedBy.Name, + PostedOn = x.CreatedAt.DateTime, Source = SocialNetworks.Twitter, - Text = ParseTweetTextIntoHtml(x.Value("text")), - UserImageUrl = x["user"].Value("profile_image_url"), - UserId = screenName - })).Take(20).ToList(); + Text = ParseTweetTextIntoHtml(x.Text), + UserImageUrl = x.CreatedBy.ProfileImageUrl, + UserId = x.Id.ToString() + }).Take(20).ToList(); } /// @@ -142,20 +92,16 @@ namespace ASC.Thrdparty.Twitter /// TwitterUserInfo list public List FindUsers(string search) { - var findedUsers = TwitterConsumer.SearchUsers(TwitterSignIn, search, _apiInfo.AccessToken); - - if (findedUsers == null) - return new List(); + var findedUsers = _twitterClient.Search.SearchUsersAsync(search).Result; return findedUsers.Select(x => new TwitterUserInfo { - UserID = x.Value("id"), - Description = x.Value("description"), - ScreenName = x.Value("screen_name"), - SmallImageUrl = x.Value("profile_image_url"), - UserName = x.Value("name") + UserID = x.Id, + Description = x.Description, + ScreenName = x.ScreenName, + SmallImageUrl = x.ProfileImageUrl, + UserName = x.Name }).Take(20).ToList(); - } /// @@ -164,11 +110,11 @@ namespace ASC.Thrdparty.Twitter /// Url of image or null if resource does not exist public string GetUrlOfUserImage(string userScreenName, ImageSize imageSize) { - var userInfo = TwitterConsumer.GetUserInfo(TwitterSignIn, 0, userScreenName, _apiInfo.AccessToken); + var userInfo = _twitterClient.Users.GetUserAsync(userScreenName).Result; if (userInfo == null) return null; - var profileImageUrl = userInfo.Value("profile_image_url"); + var profileImageUrl = userInfo.ProfileImageUrl; var size = GetTwitterImageSizeText(imageSize); @@ -176,14 +122,17 @@ namespace ASC.Thrdparty.Twitter profileImageUrl = profileImageUrl.Replace("_normal", String.Empty); return profileImageUrl; - } private static string GetTwitterImageSizeText(ImageSize imageSize) { var result = "original"; + if (imageSize == ImageSize.Small) + { result = "normal"; + } + return result; } } diff --git a/module/ASC.Thumbnails.Svc/ASC.Thumbnails.Svc.csproj b/module/ASC.Thumbnails.Svc/ASC.Thumbnails.Svc.csproj index 96573ea74..fc3dad1d0 100644 --- a/module/ASC.Thumbnails.Svc/ASC.Thumbnails.Svc.csproj +++ b/module/ASC.Thumbnails.Svc/ASC.Thumbnails.Svc.csproj @@ -39,6 +39,10 @@ {76de7717-3d4b-4a5b-b740-15b8913df0cb} ASC.Common + + {A51D0454-4AFA-46DE-89D4-B03D37E1816C} + ASC.Core.Common + @@ -49,7 +53,7 @@ - 4.7.0 + 4.7.11 diff --git a/module/ASC.Thumbnails.Svc/Launcher.cs b/module/ASC.Thumbnails.Svc/Launcher.cs index 531bebbbe..f12e286f0 100644 --- a/module/ASC.Thumbnails.Svc/Launcher.cs +++ b/module/ASC.Thumbnails.Svc/Launcher.cs @@ -23,6 +23,9 @@ using System.Reflection; using ASC.Common.Logging; using ASC.Common.Module; +using ASC.Core.Common.Contracts; + +using LogManager = ASC.Common.Logging.BaseLogManager; namespace ASC.Thumbnails.Svc { @@ -30,7 +33,10 @@ namespace ASC.Thumbnails.Svc { private ProcessStartInfo startInfo; private Process proc; + private HealthCheckSvc HealthCheckSvc; private static readonly ILog Logger = LogManager.GetLogger("ASC"); + private const string ResultOfPing = "OK"; + private const string PathToPing = "/isLife"; public void Start() { @@ -58,6 +64,8 @@ namespace ASC.Thumbnails.Svc startInfo.EnvironmentVariables.Add("savePath", Path.GetFullPath(savePath)); StartNode(); + HealthCheckSvc = new HealthCheckSvc(cfg.Port, ResultOfPing, Logger, PathToPing); + HealthCheckSvc.StartPing(); } catch (Exception e) { @@ -71,6 +79,8 @@ namespace ASC.Thumbnails.Svc { if (proc != null && !proc.HasExited) { + HealthCheckSvc.StopPing(); + proc.Kill(); proc.WaitForExit(10000); diff --git a/module/ASC.Thumbnails/app/isLife.js b/module/ASC.Thumbnails/app/isLife.js new file mode 100644 index 000000000..66fdbef30 --- /dev/null +++ b/module/ASC.Thumbnails/app/isLife.js @@ -0,0 +1,20 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +module.exports = function (req, res) { + res.sendStatus(200); + return; +} \ No newline at end of file diff --git a/module/ASC.Thumbnails/app/thumb.js b/module/ASC.Thumbnails/app/thumb.js index 446cc1cc1..c1b7951c6 100644 --- a/module/ASC.Thumbnails/app/thumb.js +++ b/module/ASC.Thumbnails/app/thumb.js @@ -23,6 +23,9 @@ const filenamify = require('filenamify-url'); const webshot = require('./webshot/webshot'); const config = require('../config'); const log = require('./log.js'); +const fetch = require("node-fetch"); +const dns = require("dns"); +const Address = require("ipaddr.js"); const linkReg = /http(s)?:\/\/.*/; let urls = []; @@ -37,6 +40,29 @@ const cache = new nodeCache({ useClones: false }); +function checkValidUrl(url) { + return new Promise((resolve, reject) => { + fetch(url) + .then(res => { + var host = new URL(res.url).host; + dns.lookup(host, (err, ip, family) => { + if (err) { + log.error(error); + resolve(false); + return; + } + const address = Address.parse(ip); + const range = address.range(); + resolve(range === 'unicast'); + }); + }) + .catch(error => { + log.error(error); + resolve(false); + }); + }); +} + function checkFileExist(pathToFile) { return new Promise((resolve, reject) => { fs.stat(pathToFile, @@ -101,6 +127,12 @@ module.exports = function (req, res) { } co(function* () { + const isValidUrl = yield checkValidUrl(url); + if (!isValidUrl) { + res.sendFile(noThumb); + return; + } + const exists = yield checkFileExist(pathToFile); if (exists) { success(); diff --git a/module/ASC.Thumbnails/index.js b/module/ASC.Thumbnails/index.js index d8b7eb084..5e3e68ae3 100644 --- a/module/ASC.Thumbnails/index.js +++ b/module/ASC.Thumbnails/index.js @@ -22,6 +22,7 @@ const config = require('./config'); const log = require('./app/log.js'); const thumb = require('./app/thumb.js'); +const isLife = require('./app/isLife.js'); log.stream = { write: (message) => log.info(message) @@ -32,5 +33,6 @@ var app = express(); app.use(cookieParser()); app.get('/', thumb); +app.get('/isLife', isLife); app.listen(config.get("port")); \ No newline at end of file diff --git a/module/ASC.Thumbnails/package.json b/module/ASC.Thumbnails/package.json index 8fd81d4ea..612aa7163 100644 --- a/module/ASC.Thumbnails/package.json +++ b/module/ASC.Thumbnails/package.json @@ -14,9 +14,11 @@ "express": "^4.17.1", "filenamify-url": "^2.1.2", "graceful-fs": "~4.2.4", + "ipaddr.js": "^2.0.1", "moment": "^2.26.0", "nconf": "^0.11.0", "node-cache": "^5.1.0", + "node-fetch": "2.6.1", "phantomjs-prebuilt": "^2.1.16", "request": "^2.88.2", "tmp": "~0.2.1", diff --git a/module/ASC.Thumbnails/yarn.lock b/module/ASC.Thumbnails/yarn.lock index c4c7e5d79..5c95aa899 100644 --- a/module/ASC.Thumbnails/yarn.lock +++ b/module/ASC.Thumbnails/yarn.lock @@ -655,6 +655,11 @@ ipaddr.js@1.9.1: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== +ipaddr.js@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.0.1.tgz#eca256a7a877e917aeb368b0a7497ddf42ef81c0" + integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng== + is-arrayish@^0.3.1: version "0.3.2" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" @@ -853,6 +858,11 @@ node-cache@^5.1.0: dependencies: clone "2.x" +node-fetch@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + normalize-url@^4.5.1: version "4.5.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" diff --git a/module/ASC.UrlShortener.Svc/ASC.UrlShortener.Svc.csproj b/module/ASC.UrlShortener.Svc/ASC.UrlShortener.Svc.csproj index de8848a8c..3e7defa18 100644 --- a/module/ASC.UrlShortener.Svc/ASC.UrlShortener.Svc.csproj +++ b/module/ASC.UrlShortener.Svc/ASC.UrlShortener.Svc.csproj @@ -39,6 +39,10 @@ {76de7717-3d4b-4a5b-b740-15b8913df0cb} ASC.Common + + {A51D0454-4AFA-46DE-89D4-B03D37E1816C} + ASC.Core.Common + @@ -47,9 +51,9 @@ - + - 4.7.0 + 4.7.11 diff --git a/module/ASC.UrlShortener.Svc/Launcher.cs b/module/ASC.UrlShortener.Svc/Launcher.cs index 08f74728d..756f18378 100644 --- a/module/ASC.UrlShortener.Svc/Launcher.cs +++ b/module/ASC.UrlShortener.Svc/Launcher.cs @@ -24,6 +24,9 @@ using System.Reflection; using ASC.Common.Logging; using ASC.Common.Module; +using ASC.Core.Common.Contracts; + +using LogManager = ASC.Common.Logging.BaseLogManager; namespace ASC.UrlShortener.Svc { @@ -31,7 +34,10 @@ namespace ASC.UrlShortener.Svc { private ProcessStartInfo startInfo; private Process proc; + private HealthCheckSvc HealthCheckSvc; private static readonly ILog Logger = LogManager.GetLogger("ASC"); + private const string ResultOfPing = "OK"; + private const string PathToPing = "/isLife"; public void Start() { @@ -78,6 +84,8 @@ namespace ASC.UrlShortener.Svc startInfo.EnvironmentVariables.Add("logPath", Path.Combine(Logger.LogDirectory, "web.urlshortener.log")); StartNode(); + HealthCheckSvc = new HealthCheckSvc(cfg.Port, ResultOfPing, Logger, PathToPing); + HealthCheckSvc.StartPing(); } catch (Exception e) { @@ -91,6 +99,8 @@ namespace ASC.UrlShortener.Svc { if (proc != null && !proc.HasExited) { + HealthCheckSvc.StopPing(); + proc.Kill(); proc.WaitForExit(10000); diff --git a/module/ASC.UrlShortener/app/app.js b/module/ASC.UrlShortener/app/app.js index 11a140c16..71dcf46f2 100644 --- a/module/ASC.UrlShortener/app/app.js +++ b/module/ASC.UrlShortener/app/app.js @@ -87,7 +87,12 @@ function make(req, res) { }).catch((err) => processError(err, res)); } +function isLife(req, res) { + res.sendStatus(200); +} + module.exports = { redirect: redirect, - make: make + make: make, + isLife: isLife }; \ No newline at end of file diff --git a/module/ASC.UrlShortener/index.js b/module/ASC.UrlShortener/index.js index 9bb441d8e..cc5386775 100644 --- a/module/ASC.UrlShortener/index.js +++ b/module/ASC.UrlShortener/index.js @@ -35,6 +35,7 @@ var app = express(); app.use(cookieParser()); +app.get('/isLife', short.isLife); app.get('/', short.make); app.get('/*', short.redirect); diff --git a/module/ASC.VoipService/ASC.VoipService.csproj b/module/ASC.VoipService/ASC.VoipService.csproj index 670d17af0..ae11641ae 100644 --- a/module/ASC.VoipService/ASC.VoipService.csproj +++ b/module/ASC.VoipService/ASC.VoipService.csproj @@ -61,10 +61,10 @@ - 12.0.3 + 13.0.1 - 5.6.0 + 5.69.0 diff --git a/module/ASC.VoipService/Twilio/TwilioPhone.cs b/module/ASC.VoipService/Twilio/TwilioPhone.cs index f2ae3b440..e6fdbaed4 100644 --- a/module/ASC.VoipService/Twilio/TwilioPhone.cs +++ b/module/ASC.VoipService/Twilio/TwilioPhone.cs @@ -100,7 +100,7 @@ namespace ASC.VoipService.Twilio var calls = QueueCalls(queueId); if (calls.Contains(callId)) { - MemberResource.Update(queueId, callId, new System.Uri(Settings.Dequeue(reject)), HttpMethod.Post, + MemberResource.Update(queueId, callId, new System.Uri(Settings.Dequeue(reject)), method: HttpMethod.Post, client: twilio); } } diff --git a/module/ASC.VoipService/Twilio/TwilioProvider.cs b/module/ASC.VoipService/Twilio/TwilioProvider.cs index 58ca693fa..3dbf0c1ce 100644 --- a/module/ASC.VoipService/Twilio/TwilioProvider.cs +++ b/module/ASC.VoipService/Twilio/TwilioProvider.cs @@ -177,14 +177,14 @@ namespace ASC.VoipService.Twilio try { var call = CallResource.Fetch(result.Id, client: client); - if (!call.Price.HasValue || string.IsNullOrEmpty(call.Duration)) + if (String.IsNullOrEmpty(call.Price) || string.IsNullOrEmpty(call.Duration)) { count--; Thread.Sleep(10000); continue; } - result.Price = (-1) * call.Price.Value; + result.Price = (-1) * Convert.ToDecimal(call.Price); result.DialDuration = Convert.ToInt32(call.Duration); break; } diff --git a/module/ASC.VoipService/Twilio/TwilioResponseHelper.cs b/module/ASC.VoipService/Twilio/TwilioResponseHelper.cs index 717e1759e..0c8034b09 100644 --- a/module/ASC.VoipService/Twilio/TwilioResponseHelper.cs +++ b/module/ASC.VoipService/Twilio/TwilioResponseHelper.cs @@ -22,7 +22,9 @@ using System.Linq; using ASC.Core; using ASC.Core.Tenants; +using Twilio.Http; using Twilio.TwiML; +using Twilio.TwiML.Voice; namespace ASC.VoipService.Twilio { @@ -56,11 +58,11 @@ namespace ASC.VoipService.Twilio { if (!string.IsNullOrEmpty(settings.GreetingAudio)) { - response.Play(Uri.EscapeUriString(settings.GreetingAudio)); + response.Play(new Uri(settings.GreetingAudio)); } - response.Enqueue(settings.Queue.Name, GetEcho("Enqueue", agent != null), "POST", - GetEcho("Wait", agent != null), "POST"); + response.Enqueue(settings.Queue.Name, new Uri(GetEcho("Enqueue", agent != null)), "POST", + new Uri(GetEcho("Wait", agent != null)), "POST"); } return AddVoiceMail(response); @@ -107,9 +109,9 @@ namespace ASC.VoipService.Twilio if (!string.IsNullOrEmpty(queue.WaitUrl)) { - var gather = new Gather(method: "POST", action: GetEcho("gatherQueue")); - gather.Play(Uri.EscapeUriString(queue.WaitUrl)); - response.Gather(gather); + var gather = new Gather(method: "POST", action: new Uri(GetEcho("gatherQueue"))); + gather.Play(new Uri(queue.WaitUrl)); + response.Append(gather); } else { @@ -135,18 +137,18 @@ namespace ASC.VoipService.Twilio { if (to == "hold") { - return new VoiceResponse().Play(Uri.EscapeUriString(settings.HoldAudio), 0); + return new VoiceResponse().Play(new Uri(settings.HoldAudio), 0); } Guid newCallerId; if (Guid.TryParse(to, out newCallerId)) { - SecurityContext.AuthenticateMe(newCallerId); + SecurityContext.CurrentUser = newCallerId; } - return new VoiceResponse().Enqueue(settings.Queue.Name, GetEcho("enqueue"), "POST", - GetEcho("wait") + "&RedirectTo=" + to, "POST"); + return new VoiceResponse().Enqueue(settings.Queue.Name, new Uri(GetEcho("enqueue")), "POST", + new Uri(GetEcho("wait") + "&RedirectTo=" + to), "POST"); } public VoiceResponse VoiceMail() @@ -156,18 +158,18 @@ namespace ASC.VoipService.Twilio private VoiceResponse AddToResponse(VoiceResponse response, Agent agent) { - var dial = new Dial(method: "POST", action: GetEcho("dial"), timeout: agent.TimeOut, record: agent.Record ? "record-from-answer" : "do-not-record"); + var dial = new Dial(method: "POST", action: new Uri(GetEcho("dial")), timeout: agent.TimeOut, record: agent.Record ? "record-from-answer" : "do-not-record"); switch (agent.Answer) { case AnswerType.Number: - response.Dial(dial.Number(agent.PhoneNumber, method: "POST", url: GetEcho("client"))); + response.Append(dial.Number(agent.PhoneNumber, method: "POST", url: new Uri(GetEcho("client")))); break; case AnswerType.Client: - response.Dial(dial.Client(agent.ClientID, "POST", GetEcho("client"))); + response.Append(dial.Client(agent.ClientID, method: HttpMethod.Post, url: new Uri(GetEcho("client")))); break; case AnswerType.Sip: - response.Dial(dial.Sip(agent.ClientID, method: "POST", url: GetEcho("client"))); + response.Append(dial.Sip(new Uri(agent.ClientID), method: "POST", url: new Uri(GetEcho("client")))); break; } @@ -179,7 +181,7 @@ namespace ASC.VoipService.Twilio { return string.IsNullOrEmpty(settings.VoiceMail) ? response.Say("") - : response.Play(Uri.EscapeUriString(settings.VoiceMail)).Record(method: "POST", action: GetEcho("voiceMail"), maxLength: 30); + : response.Play(new Uri(settings.VoiceMail)).Record(method: "POST", action: new Uri(GetEcho("voiceMail")), maxLength: 30); } public string GetEcho(string action, bool user = true) diff --git a/module/ASC.WebDav.Svc/ASC.WebDav.Svc.csproj b/module/ASC.WebDav.Svc/ASC.WebDav.Svc.csproj new file mode 100644 index 000000000..3d0a99508 --- /dev/null +++ b/module/ASC.WebDav.Svc/ASC.WebDav.Svc.csproj @@ -0,0 +1,56 @@ + + + + + Debug + AnyCPU + {B84EC090-06B6-4960-A847-51017D3B0743} + Library + Properties + ASC.WebDav.Svc + ASC.WebDav.Svc + v4.8 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + {76de7717-3d4b-4a5b-b740-15b8913df0cb} + ASC.Common + + + {a51d0454-4afa-46de-89d4-b03d37e1816c} + ASC.Core.Common + + + + \ No newline at end of file diff --git a/module/ASC.WebDav.Svc/ConfigHandler.cs b/module/ASC.WebDav.Svc/ConfigHandler.cs new file mode 100644 index 000000000..9cbe3dd67 --- /dev/null +++ b/module/ASC.WebDav.Svc/ConfigHandler.cs @@ -0,0 +1,36 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System.Configuration; + +namespace ASC.WebDav.Svc +{ + class ConfigHandler : ConfigurationSection + { + [ConfigurationProperty("path", DefaultValue = "../ASC.WebDav")] + public string Path + { + get { return (string)base["path"]; } + } + + [ConfigurationProperty("port", DefaultValue = "9889")] + public string Port + { + get { return (string)base["port"]; } + } + } +} diff --git a/module/ASC.WebDav.Svc/Launcher.cs b/module/ASC.WebDav.Svc/Launcher.cs new file mode 100644 index 000000000..26f27b340 --- /dev/null +++ b/module/ASC.WebDav.Svc/Launcher.cs @@ -0,0 +1,99 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Diagnostics; +using System.IO; +using System.Reflection; + +using ASC.Common.Logging; +using ASC.Common.Module; +using ASC.Core.Common.Contracts; + +using LogManager = ASC.Common.Logging.BaseLogManager; + +namespace ASC.WebDav.Svc +{ + public class Launcher : IServiceController + { + private ProcessStartInfo startInfo; + private Process proc; + private HealthCheckSvc HealthCheckSvc; + private static readonly ILog Logger = LogManager.GetLogger("ASC"); + private const string ResultOfPing = "OK"; + private const string PathToPing = "/isLife"; + + public void Start() + { + try + { + var cfg = (ConfigHandler)ConfigurationManagerExtension.GetSection("webdav"); + + startInfo = new ProcessStartInfo + { + CreateNoWindow = false, + UseShellExecute = false, + FileName = "node", + WindowStyle = ProcessWindowStyle.Hidden, + Arguments = string.Format("\"{0}\"", Path.GetFullPath(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), cfg.Path, "server", "webDavServer.js"))), + WorkingDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + }; + + startInfo.EnvironmentVariables.Add("port", cfg.Port); + startInfo.EnvironmentVariables.Add("logPath", Path.Combine(Logger.LogDirectory, "web.webdav.log")); + + StartNode(); + HealthCheckSvc = new HealthCheckSvc(cfg.Port, ResultOfPing, Logger, PathToPing); + HealthCheckSvc.StartPing(); + } + catch (Exception e) + { + Logger.Error("Start", e); + } + } + + public void Stop() + { + try + { + if (proc != null && !proc.HasExited) + { + HealthCheckSvc.StopPing(); + + proc.Kill(); + proc.WaitForExit(10000); + + proc.Close(); + proc.Dispose(); + proc = null; + } + } + catch (Exception e) + { + Logger.Error("stop", e); + } + } + + private void StartNode() + { + Stop(); + proc = Process.Start(startInfo); + } + } +} diff --git a/module/ASC.WebDav.Svc/Properties/AssemblyInfo.cs b/module/ASC.WebDav.Svc/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..6f103f249 --- /dev/null +++ b/module/ASC.WebDav.Svc/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ASC.WebDav.Svc")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Ascensio System SIA")] +[assembly: AssemblyProduct("ASC.WebDav.Svc")] +[assembly: AssemblyCopyright("(c) Ascensio System SIA. All rights reserved")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("b84ec090-06b6-4960-a847-51017d3b0743")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/module/ASC.WebDav.Svc/app.config b/module/ASC.WebDav.Svc/app.config new file mode 100644 index 000000000..f3ed3d893 --- /dev/null +++ b/module/ASC.WebDav.Svc/app.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/module/ASC.WebDav/helper/helper.js b/module/ASC.WebDav/helper/helper.js new file mode 100644 index 000000000..b5e3e167a --- /dev/null +++ b/module/ASC.WebDav/helper/helper.js @@ -0,0 +1,138 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +const logger = require('./logger.js'); + +const logSplitter = ' | '; + +var getHeader = function (contentType, token) { + const headers = { + ContentType: contentType, + Accept: 'application/json' + }; + if (token) { + headers.Authorization = token; + } + return headers; +}; + +var getHeaderPeople = function (token) { + const headers = { + Accept: 'text/html,application/xhtml+xml,application/xml' + }; + if (token) { + headers.Authorization = token; + } + return headers; +}; + +var getErrorLogMsg = function (error) { + let message = ''; + if (error) { + if (error.message) { + message = `${logSplitter}${error.message}`; + } + if (error.request) { + message += `${logSplitter}${error.request.method + ' ' + error.request.path}` + } + if (error.response) { + if (error.response.config) { + message += `${logSplitter}${error.response.config.data}` + } + if (error.response.data && error.response.data.error) { + message += `${logSplitter}${error.response.data.error.message + '\n' + error.response.data.error.stack}` + } + } + } + return message; +}; + +var getContextLogMsg = function (ctx) { + let message = ''; + if (ctx) { + if (ctx.user) { + message += `${logSplitter}${ctx.user.username}` + } + if (ctx.headers) { + message += `${logSplitter}${ctx.headers.headers["user-agent"]}` + } + if (ctx.request) { + message += `${logSplitter}${ctx.request.method} ${ctx.requested.uri}` + } + if (ctx.response) { + message += `${logSplitter}${ctx.response.statusCode} ${ctx.response.statusMessage || ''}` + } + } + return message; +} + +var getResponseLogMsg = function (response) { + let message = ''; + if (response) { + if (response.config) { + let url = response.config.baseURL + ? response.config.baseURL + "/" + response.config.url.replace(/^\//, '') + : response.config.url; + + message += `${logSplitter}${response.config.method} ${url}` + if (response.config.data) { + message += `${logSplitter}${response.config.data}` + } + } + if (response.status) { + message += `${logSplitter}${response.status} ${response.statusText}` + } + if (response.data) { + //message += `${logSplitter}${JSON.stringify(response.data.response)}` + } + } + return message; +} + +var logError = function (error, method) { + let message = method || ''; + message += getErrorLogMsg(error); + logger.error(message); +}; + +var logResponse = function (ctx, response, method) { + let message = method || ''; + message += getContextLogMsg(ctx); + message += getResponseLogMsg(response); + logMessage(message); +}; + +var logContext = function (ctx, method) { + let message = method || ''; + message += getContextLogMsg(ctx); + logMessage(message); +} + +var logMessage = function () { + if (arguments.length > 0) { + logger.debug(Array.prototype.join.call(arguments, logSplitter)); + } +} + +module.exports = { + getHeader, + getHeaderPeople, + logError, + logResponse, + logContext, + logMessage +}; \ No newline at end of file diff --git a/module/ASC.WebDav/helper/localLockManager.js b/module/ASC.WebDav/helper/localLockManager.js new file mode 100644 index 000000000..d99302271 --- /dev/null +++ b/module/ASC.WebDav/helper/localLockManager.js @@ -0,0 +1,26 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +const webdav = require('webdav-server').v2; +class CLocalLockManager extends webdav.LocalLockManager { + getLocks(callback) { + this.locks = this.locks.filter(function (lock) { return !lock.expired(); }); + callback(null, []); // bug 55572 + }; +} + +module.exports = CLocalLockManager; \ No newline at end of file diff --git a/module/ASC.WebDav/helper/logger.js b/module/ASC.WebDav/helper/logger.js new file mode 100644 index 000000000..fa97e1ba2 --- /dev/null +++ b/module/ASC.WebDav/helper/logger.js @@ -0,0 +1,59 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +const winston = require("winston"); +const { format } = require("winston"); +require("winston-daily-rotate-file"); + +const path = require("path"); +const config = require("../server/config.js"); +const fs = require("fs"); +const logLevel = process.env.logLevel || config.logLevel || "info"; +const fileName = process.env.logPath || path.join(__dirname, "..", "logs", "web.webdav.%DATE%.log"); +const dirName = path.dirname(fileName); + +if (!fs.existsSync(dirName)) { + fs.mkdirSync(dirName); +} + +const fileTransport = new (winston.transports.DailyRotateFile)({ + filename: fileName, + datePattern: "MM-DD", + handleExceptions: true, + humanReadableUnhandledException: true, + zippedArchive: true, + maxSize: "50m", + maxFiles: "30d" +}); + +const transports = [ + new (winston.transports.Console)(), + fileTransport +]; + +winston.exceptions.handle(fileTransport); + +module.exports = winston.createLogger({ + level: logLevel, + transports: transports, + exitOnError: false, + format: format.combine( + format.timestamp({ + format: "YYYY-MM-DD HH:mm:ss" + }), + format.printf(info => `${info.timestamp} - ${info.level}: ${info.message}`) + )}); \ No newline at end of file diff --git a/module/ASC.WebDav/helper/propertyParser.js b/module/ASC.WebDav/helper/propertyParser.js new file mode 100644 index 000000000..f94b3195d --- /dev/null +++ b/module/ASC.WebDav/helper/propertyParser.js @@ -0,0 +1,63 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +class PropertyParser { + static parsePath(path) { + let pathArray = path.split('/'); + let targetElement = pathArray.pop(); + let targetElementArray = targetElement.split('.'); + if (targetElementArray.length > 1) { + let ext = targetElementArray.pop(); + targetElementArray.push(ext.toLowerCase()); + targetElement = targetElementArray.join('.'); + } + if (pathArray.length <= 1) { + pathArray[0] = '/'; + } + let parentPath = pathArray.join('/'); + return { + element: targetElement, + parentFolder: parentPath + }; + } + + static parsePathTo(pathTo) { + let pathArray = pathTo.split('/'); + if (pathArray[pathArray.length - 1] == '' && pathTo !== '/') { + pathArray.pop(); + var newPath = pathArray.join('/'); + } else { + var newPath = pathTo; + } + return newPath; + } + + static parseDate(dateString) { + let dateArray = dateString.split('.'); + dateArray = dateArray[0].split('T'); + let date = dateArray[0].split('-'); + let time = dateArray[1].split(':'); + return new Date(date[0], date[1] - 1, date[2], time[0], time[1], time[2]); + } + + static parseFileExt(fileName) { + let fileNameArray = (fileName || '').split('.'); + return fileNameArray.length > 1 ? fileNameArray.pop() : null; + } +} + +module.exports = PropertyParser; \ No newline at end of file diff --git a/module/ASC.WebDav/helper/renamingDuplicateElements.js b/module/ASC.WebDav/helper/renamingDuplicateElements.js new file mode 100644 index 000000000..c7371567f --- /dev/null +++ b/module/ASC.WebDav/helper/renamingDuplicateElements.js @@ -0,0 +1,109 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +var addRealTitle = function (response, folderId) { + if (folderId != '@root') { + let structFile = response.data.response.files; + for (let i = 0; i < structFile.length; i++) { + response.data.response.files[i]['realTitle'] = structFile[i].title; + } + let structFolder = response.data.response.folders; + for (let i = 0; i < structFolder.length; i++) { + response.data.response.folders[i]['realTitle'] = structFolder[i].title; + } + return response; + } else { + return response; + } +}; + +var checkDuplicateNames = function (response) { + let structFile = response.data.response.files; + for (let i = 0; i < structFile.length; i++) { + for (let j = i; j < structFile.length; j++) { + if ((i != j) && (structFile[i].title == structFile[j].title)) { + return false; + } + } + } + let structFolder = response.data.response.folders; + for (let i = 0; i < structFolder.length; i++) { + for (let j = i; j < structFolder.length; j++) { + if ((i != j) && (structFolder[i].title == structFolder[j].title)) { + return false; + } + } + } + return true; +}; + + + +var localRename = function (response, folderId) { + if (folderId != '@root') { + if (!checkDuplicateNames(response)) { + let structFile = response.data.response.files; + for (let i = 0; i < structFile.length; i++) { + let c = 1; + for (let j = i; j < structFile.length; j++) { + if ((i != j) && (structFile[i].title == structFile[j].title)) { + const title = structFile[j].title; + const splitedTitle = title.split("."); + const realTitle = structFile[j].realTitle; + if (realTitle == title) { + response.data.response.files[j].title = splitedTitle[0] + `(${c}).` + splitedTitle[1]; + c++; + } else { + let reversTitle = title.split("").reverse().join(""); + let num = reversTitle.split(")", 2)[1].split("(")[0].split("").reverse().join(""); + response.data.response.files[j].title = realTitle.split(".")[0] + `(${Number(num)+1}).` + splitedTitle[1]; + } + } + } + } + let structFolders = response.data.response.folders; + for (let i = 0; i < structFolders.length; i++) { + let c = 1; + for (let j = i; j < structFolders.length; j++) { + if ((i != j) && (structFolders[i].title == structFolders[j].title)) { + const title = structFolders[j].title; + const realTitle = structFolders[j].realTitle; + if (realTitle == title) { + response.data.response.folders[j].title = title + `(${c})`; + c++; + } else { + let reversTitle = title.split("").reverse().join(""); + let num = reversTitle.split(")", 2)[1].split("(")[0].split("").reverse().join(""); + response.data.response.folders[j].title = realTitle.split(".")[0] + `(${Number(num)+1})`; + } + } + } + } + return localRename(response, folderId); + } else { + return response; + } + } else { + return response; + } +}; + +module.exports = { + addRealTitle, + checkDuplicateNames, + localRename +}; \ No newline at end of file diff --git a/module/ASC.WebDav/helper/writable.js b/module/ASC.WebDav/helper/writable.js new file mode 100644 index 000000000..ca29b2370 --- /dev/null +++ b/module/ASC.WebDav/helper/writable.js @@ -0,0 +1,108 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +const { + Duplex +} = require('stream'); +const { + maxChunkSize +} = require('../server/config.js'); +const FormData = require("form-data"); +const { + chunkedUploader, + rewritingFile +} = require('../server/requestAPI.js'); + +class streamWrite extends Duplex { + constructor(contents, arrayBufLength, ctx, location, user, file, createdNow) { + super(null); + this.contents = contents; + this.contentsLength = 0; + this.location = location; + this.arrayBuf = arrayBufLength; + this.firstPosition = 0; + this.lastChunk = 0; + this.count = -1; + this.totalCount = 0; + this.ctx = ctx; + this.user = user; + this.file = file; + this.createdNow = createdNow; + } + + _read() { + for (let i = 0; i < this.contents.length; i++) { + this.push(this.contents[i]); + } + this.push(null); + } + + async _write(chunk, encoding, callback) { + if (this.file) { + if (!this.createdNow) { + this.contents.push(chunk); + this.contentsLength += chunk.length; + if (this.contentsLength == this.ctx.estimatedSize) { + const form_data = new FormData(); + form_data.append("FileExtension", this.file.fileExst); + form_data.append("DownloadUri", ""); + form_data.append("Stream", this, { + filename: this.file.realTitle, + contentType: "text/plain" + }); + form_data.append("Doc", ""); + form_data.append("Forcesave", 'false'); + await rewritingFile(this.ctx, this.file.id, form_data, this.user.token); + } + + } else { + for (let i = 0; chunk.length > i; i++) { + this.count++; + this.totalCount++; + this.arrayBuf[this.count] = chunk[i]; + + let fullChunk = this.count == (maxChunkSize - 1); + //let lastByte = chunk[i + 1] == undefined && (this.lastChunk > chunk.length || this.count == this.ctx.estimatedSize - 1); + let lastByte = this.totalCount == this.ctx.estimatedSize; + + if (fullChunk || lastByte) { + this.arrayBuf.length = this.count + 1; + const form_data = new FormData(); + form_data.append("files[]", Buffer.from(this.arrayBuf), "chunk" + i); + await chunkedUploader(this.ctx, this.location, form_data, this.user.token, this.ctx.estimatedSize, this.firstPosition, this.firstPosition + this.arrayBuf.length - 1); + this.firstPosition += this.arrayBuf.length; + if (this.ctx.estimatedSize < maxChunkSize) { + this.arrayBuf = []; + } + this.count = -1; + } + if (lastByte) { + if (global.gc) { + global.gc(); + } + this.arrayBuf = null; + this.ctx = null; + } + } + this.lastChunk = chunk.length; + } + } + callback(null); + } +} + +module.exports = streamWrite; \ No newline at end of file diff --git a/module/ASC.WebDav/manager/customFileSystem.js b/module/ASC.WebDav/manager/customFileSystem.js new file mode 100644 index 000000000..e076c3741 --- /dev/null +++ b/module/ASC.WebDav/manager/customFileSystem.js @@ -0,0 +1,296 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +const webdav = require('webdav-server').v2; +const VirtualResources = require('../resource/customVirtualResource'); +const promise_1 = require('webdav-server/lib/helper/v2/promise'); +const StandardMethods_1 = require('webdav-server/lib/manager/v2/fileSystem/StandardMethods'); +const Errors_1 = require("webdav-server/lib/Errors"); +const Path_1 = require("webdav-server/lib/manager/v2/Path"); +const LocalLockManager = require('../helper/localLockManager'); +const parse = require('../helper/propertyParser.js'); +const { virtualPath, fileHandlerPath } = require('../server/config.js'); + +const fixPath = function(path) { + if (virtualPath && path.paths[0] != virtualPath) { + path.paths.unshift(virtualPath); + } +} + +class customFileSystem extends webdav.FileSystem { + constructor() { + super(); + this.props = new webdav.LocalPropertyManager(); + this.locks = new LocalLockManager(); + this.manageResource = new VirtualResources(); + } + + _lockManager(path, ctx, callback) { + callback(null, this.locks); + } + + _propertyManager(path, ctx, callback) { + callback(null, this.props); + } + + _fastExistCheck(ctx, path, callback) { + fixPath(path); + const sPath = path.toString(); + const exist = this.manageResource.fastExistCheck(sPath, ctx); + if (exist) { + callback(exist); + return; + } + (async () => { + try { + const { + element, + parentFolder + } = parse.parsePath(sPath); + + var struct = await this.manageResource.readDir({ + context: ctx + }, parentFolder); + + if (!struct || struct instanceof Error) { + callback(false); + return; + } + + if (this.manageResource.findFile(struct, element) || this.manageResource.findFolder(struct, element)) { + callback(true); + } else { + callback(false); + } + } catch (error) { + callback(false); + } + })(); + } + + _create(path, ctx, callback) { + (async () => { + const sPath = path.toString(); + try { + await this.manageResource.create(ctx, sPath); + callback(); + } catch (error) { + callback(error); + } + })(); + } + + _delete(path, ctx, callback) { + (async () => { + const sPath = path.toString(); + try { + await this.manageResource.delete(ctx, sPath); + callback(); + } catch (error) { + callback(error); + } + })(); + } + + _move(pathFrom, pathTo, ctx, callback) { + (async () => { + const sPathFrom = pathFrom.toString(); + const sPathTo = pathTo.toString(); + let isMove = false; + try { + isMove = await this.manageResource.move(ctx, sPathFrom, sPathTo); + callback(null, isMove); + } catch (error) { + callback(error, isMove); + } + })(); + } + + issuePrivilegeCheck(fs, ctx, path, privilege, badCallback, goodCallback) { + fs.checkPrivilege(ctx, path, privilege, function (e, can) { + if (e) + badCallback(e); + else if (!can) + badCallback(Errors_1.Errors.NotEnoughPrivilege); + else + goodCallback(); + }); + } + + move(ctx, _pathFrom, _pathTo, _overwrite, _callback) { + var _this = this; + var callbackFinal = _callback ? _callback : _overwrite; + var overwrite = promise_1.ensureValue(_callback ? _overwrite : undefined, false); + var pathFrom = new Path_1.Path(_pathFrom); + var pathTo = new Path_1.Path(_pathTo); + var path = pathTo; + if (pathFrom.paths.length == pathTo.paths.length) { + let counter = 0; + for (let i = 0; i < pathFrom.paths.length; i++) { + if (pathFrom.paths[i] == pathTo.paths[i]) { + counter++; + } + } + if (counter == pathFrom.paths.length - 1) { + path = pathFrom; + } + } + var callback = function (e, overrided) { + if (!e) + _this.emit('move', ctx, pathFrom, { + pathFrom: pathFrom, + pathTo: pathTo, + overwrite: overwrite, + overrided: overrided + }); + callbackFinal(e, overrided); + }; + this.emit('before-move', ctx, pathFrom, { + pathFrom: pathFrom, + pathTo: pathTo, + overwrite: overwrite + }); + this.issuePrivilegeCheck(this, ctx, pathFrom, 'canRead', callback, function () { + _this.issuePrivilegeCheck(_this, ctx, path, 'canWrite', callback, function () { + _this.isLocked(ctx, pathFrom, function (e, isLocked) { + if (e || isLocked) + return callback(e ? e : Errors_1.Errors.Locked); + _this.isLocked(ctx, pathTo, function (e, isLocked) { + if (e || isLocked) + return callback(e ? e : Errors_1.Errors.Locked); + var go = function () { + if (_this._move) { + _this._move(pathFrom, pathTo, { + context: ctx, + overwrite: overwrite + }, callback); + return; + } + StandardMethods_1.StandardMethods.standardMove(ctx, pathFrom, _this, pathTo, _this, overwrite, callback); + }; + _this.fastExistCheckEx(ctx, pathFrom, callback, function () { + if (!overwrite) + _this.fastExistCheckExReverse(ctx, pathTo, callback, go); + else + go(); + }); + }); + }); + }); + }); + } + + _copy(pathFrom, pathTo, ctx, callback) { + (async () => { + if (pathFrom.paths.length == 1) { + callback(Errors_1.Errors.NotEnoughPrivilege, null); + return; + } + if (pathFrom.paths[pathFrom.paths.length - 1] == pathTo.paths[pathTo.paths.length - 1]) { + delete pathTo.paths[pathTo.paths.length - 1]; + } + const sPathFrom = pathFrom.toString(); + const sPathTo = pathTo.toString(); + try { + const isCopy = await this.manageResource.copy(ctx, sPathFrom, sPathTo); + callback(null, isCopy); + } catch (error) { + callback(error, null); + } + })(); + } + + _size(path, ctx, callback) { + fixPath(path); + const sPath = path.toString(); + const size = this.manageResource.getSize(sPath, ctx); + callback(null, size); + } + + _openWriteStream(path, ctx, callback) { + const sPath = path.toString(); + (async () => { + try { + const streamWrite = await this.manageResource.writeFile(sPath, ctx); + callback(null, streamWrite); + } catch (error) { + callback(error, null); + } + })(); + } + + _openReadStream(path, ctx, callback) { + const sPath = path.toString(); + + if (fileHandlerPath) { + this.manageResource.readFile(ctx, sPath, callback); + return; + } + + (async () => { + try { + const readStream = await this.manageResource.getReadStream(ctx, sPath); + callback(null, readStream); + } catch (error) { + callback(error, null); + } + })(); + } + + _type(path, ctx, callback) { + fixPath(path); + const sPath = path.toString(); + const type = this.manageResource.getType(sPath, ctx); + callback(null, type); + } + + _lastModifiedDate(path, ctx, callback) { + fixPath(path); + const sPath = path.toString(); + const date = this.manageResource.getlastModifiedDate(sPath, ctx); + callback(null, date); + } + + _readDir(path, ctx, callback) { + (async () => { + const sPath = path.toString(); + const elements = []; + try { + var struct = await this.manageResource.readDir(ctx, sPath); + + if (struct instanceof Error) { + callback(struct); + return; + } + + struct.folders.forEach(el => { + elements.push(el.title); + }); + + struct.files.forEach(el => { + elements.push(el.title); + }); + + callback(null, elements); + } catch (error) { + callback(error); + } + })(); + } +} + +module.exports = customFileSystem; \ No newline at end of file diff --git a/module/ASC.WebDav/package-lock.json b/module/ASC.WebDav/package-lock.json new file mode 100644 index 000000000..f91bfae3f --- /dev/null +++ b/module/ASC.WebDav/package-lock.json @@ -0,0 +1,1116 @@ +{ + "name": "webdav_server", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "ajv": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", + "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "requires": { + "lodash": "^4.17.14" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", + "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==" + }, + "axios": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.0.tgz", + "integrity": "sha512-fmkJBknJKoZwem3/IKSSLpkdNXZeBu5Q7GA/aRsr2btgrptmSCxi2oFjZHqGdK9DoTil9PIHlPIZw2EcRJXRvw==", + "requires": { + "follow-redirects": "^1.10.0" + } + }, + "base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + } + } + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "color": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", + "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.2" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "color-string": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", + "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colornames": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz", + "integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y=" + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" + }, + "colorspace": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", + "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", + "requires": { + "color": "3.0.x", + "text-hex": "1.0.x" + } + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "diagnostics": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz", + "integrity": "sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ==", + "requires": { + "colorspace": "1.1.x", + "enabled": "1.0.x", + "kuler": "1.0.x" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "enabled": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", + "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", + "requires": { + "env-variable": "0.0.x" + } + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "env-variable": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.6.tgz", + "integrity": "sha512-bHz59NlBbtS0NhftmR8+ExBEekE7br0e01jw+kk0NDro7TtZzBYZ5ScGPs3OmwnpyfHTHOtr1Y6uedCdrIldtg==" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "fast-safe-stringify": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" + }, + "fecha": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz", + "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==" + }, + "file-stream-rotator": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.5.7.tgz", + "integrity": "sha512-VYb3HZ/GiAGUCrfeakO8Mp54YGswNUHvL7P09WQcXAJNSj3iQ5QraYSp3cIn1MUyw6uzfgN/EFOarCNa4JvUHQ==", + "requires": { + "moment": "^2.11.2" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "follow-redirects": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", + "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", + "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "get-byte": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/get-byte/-/get-byte-0.0.0.tgz", + "integrity": "sha512-dFdDPbQR4zCgSxsmMEjVrLHtvSdTVqb2JUfcZ8KYhXUQD8eiE7uTgeHzse5K479Kd8IIRt6503+2gmQhahvJyg==" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "help": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/help/-/help-3.0.2.tgz", + "integrity": "sha1-luGQ1KCkU7icLLSwWrOOOo+f2t0=" + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "kuler": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz", + "integrity": "sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==", + "requires": { + "colornames": "^1.1.1" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, + "logform": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.1.2.tgz", + "integrity": "sha512-+lZh4OpERDBLqjiwDLpAWNQu6KMjnlXH2ByZwCuSqVPJletw0kTWJf5CgSNAUKn1KUkv3m2cUz/LK8zyEy7wzQ==", + "requires": { + "colors": "^1.2.1", + "fast-safe-stringify": "^2.0.4", + "fecha": "^2.3.3", + "ms": "^2.1.1", + "triple-beam": "^1.3.0" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", + "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==" + }, + "mime-types": { + "version": "2.1.26", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", + "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", + "requires": { + "mime-db": "1.43.0" + } + }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "one-time": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz", + "integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=" + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "psl": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz", + "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==" + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + } + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "requires": { + "is-arrayish": "^0.3.1" + } + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, + "to-byte-array": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/to-byte-array/-/to-byte-array-1.0.2.tgz", + "integrity": "sha512-3Owdexa2azV7gAW5ZlSqp5646oqOOf3EZ3JsbX6AtIdpA0h3y8YVMS70NwMYzMJdZdgMnXW6UIetkeyIWSycrw==", + "requires": { + "base64url": "^3.0.1", + "is-buffer": "^2.0.3" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + } + } + }, + "triple-beam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "webdav-server": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/webdav-server/-/webdav-server-2.6.2.tgz", + "integrity": "sha512-0iHdrOzlKGFD96bTvPF8IIEfxw9Q7jB5LqWqhjyBYsofD6T6mOYqWtAvR88VY9Mq0xeg8bCRHC2Vifc9iuTYuw==", + "requires": { + "mime-types": "^2.1.18", + "xml-js-builder": "^1.0.3" + } + }, + "winston": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.2.1.tgz", + "integrity": "sha512-zU6vgnS9dAWCEKg/QYigd6cgMVVNwyTzKs81XZtTFuRwJOcDdBg7AU0mXVyNbs7O5RH2zdv+BdNZUlx7mXPuOw==", + "requires": { + "async": "^2.6.1", + "diagnostics": "^1.1.1", + "is-stream": "^1.1.0", + "logform": "^2.1.1", + "one-time": "0.0.4", + "readable-stream": "^3.1.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.3.0" + } + }, + "winston-daily-rotate-file": { + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/winston-daily-rotate-file/-/winston-daily-rotate-file-4.5.5.tgz", + "integrity": "sha512-ds0WahIjiDhKCiMXmY799pDBW+58ByqIBtUcsqr4oDoXrAI3Zn+hbgFdUxzMfqA93OG0mPLYVMiotqTgE/WeWQ==", + "requires": { + "file-stream-rotator": "^0.5.7", + "object-hash": "^2.0.1", + "triple-beam": "^1.3.0", + "winston-transport": "^4.4.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "winston-transport": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", + "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", + "requires": { + "readable-stream": "^2.3.7", + "triple-beam": "^1.2.0" + } + } + } + }, + "winston-transport": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.3.0.tgz", + "integrity": "sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A==", + "requires": { + "readable-stream": "^2.3.6", + "triple-beam": "^1.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "xml-js": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", + "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", + "requires": { + "sax": "^1.2.4" + } + }, + "xml-js-builder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/xml-js-builder/-/xml-js-builder-1.0.3.tgz", + "integrity": "sha512-BoLgG/glT45M0jK5PGh9h+iGrQxa8jJk9ofR63GroRifl2tbGB3/yYiVY3wQWHrZgWWfl9+7fhEB/VoD9mWnSg==", + "requires": { + "xml-js": "^1.6.2" + } + } + } +} diff --git a/module/ASC.WebDav/package.json b/module/ASC.WebDav/package.json new file mode 100644 index 000000000..b6bad2a7d --- /dev/null +++ b/module/ASC.WebDav/package.json @@ -0,0 +1,27 @@ +{ + "name": "webdav_server", + "version": "1.0.0", + "description": "WebDAV server", + "private": true, + "author": { + "name": "Ascensio System SIA" + }, + "scripts": { + "start": "node server/webDavServer.js", + "postinstall": "patch-package" + }, + "dependencies": { + "axios": "^0.21.0", + "express": "^4.17.1", + "form-data": "^3.0.0", + "get-byte": "0.0.0", + "help": "^3.0.2", + "patch-package": "^6.4.7", + "postinstall-postinstall": "^2.1.0", + "request": "^2.88.0", + "to-byte-array": "^1.0.2", + "webdav-server": "2.6.2", + "winston": "^3.2.1", + "winston-daily-rotate-file": "^4.5.5" + } +} diff --git a/module/ASC.WebDav/patches/webdav-server+2.6.2.patch b/module/ASC.WebDav/patches/webdav-server+2.6.2.patch new file mode 100644 index 000000000..7541547ec --- /dev/null +++ b/module/ASC.WebDav/patches/webdav-server+2.6.2.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/webdav-server/lib/server/v2/commands/Get.js b/node_modules/webdav-server/lib/server/v2/commands/Get.js +index 646c6e8..a3ba2d3 100644 +--- a/node_modules/webdav-server/lib/server/v2/commands/Get.js ++++ b/node_modules/webdav-server/lib/server/v2/commands/Get.js +@@ -178,7 +178,7 @@ var default_1 = /** @class */ (function () { + ctx.response.setHeader('Content-Length', len.toString()); + if (ranges_1.length <= 1) { + ctx.response.setHeader('Content-Type', mimeType); +- ctx.response.setHeader('Content-Range', "bytes " + ranges_1[0].min + "-" + ranges_1[0].max + "/*"); ++ ctx.response.setHeader('Content-Range', "bytes " + ranges_1[0].min + "-" + ranges_1[0].max + "/" + size.toString()); + rstream.on('end', callback); + return rstream.pipe(new RangedStream(ranges_1[0].min, ranges_1[0].max)).pipe(ctx.response); + } diff --git a/module/ASC.WebDav/resource/customVirtualResource.js b/module/ASC.WebDav/resource/customVirtualResource.js new file mode 100644 index 000000000..93eb3cae9 --- /dev/null +++ b/module/ASC.WebDav/resource/customVirtualResource.js @@ -0,0 +1,582 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +const webdav = require('webdav-server').v2; +const { + getStructDirectory, + statusOperation, + getPresignedUri, + getReadStream, + readFile, + createFile, + createFolder, + deleteFile, + deleteFolder, + copyFile, + copyFolder, + moveFile, + moveFolder, + renameFile, + renameFolder, + createFileTxt, + createFileHtml, + createEditSession +} = require('../server/requestAPI.js'); +const { + method, + maxChunkSize, + virtualPath +} = require('../server/config.js'); +const streamWrite = require('../helper/writable.js'); +const SimpleStruct = require('./simpleStruct.js'); +const parse = require('../helper/propertyParser.js'); +const { + maxExecutionTime +} = require('../server/config'); +const { logMessage } = require('../helper/helper.js'); + +class CustomVirtualResources { + constructor() { + this.structСache = new SimpleStruct(); + this.createdNow = []; + } + + async getRootFolder(ctx, user) { + const structRoot = { + files: [], + folders: [], + current: {} + }; + try { + logMessage('CustomVirtualResources.getRootFolder', user.username); + const structDir = await getStructDirectory(ctx, method.pathRootDirectory, user.token); + for (let i = 0; i < structDir.length; i++) { + structRoot.folders.push(structDir[i].current); + } + return structRoot; + } catch (error) { + return webdav.Errors.ResourceNotFound; + } + } + + findFolder(struct, element) { + try { + return struct.folders.find(folder => folder.title == element); + } catch (error) { + return false; + } + } + + findFile(struct, element) { + try { + return struct.files.find(file => file.title == element); + } catch (error) { + return false; + } + } + + async isOperationOver(ctx, token, response) { + let isFinished = false; + let status = {}; + const startTime = new Date(); + while (!isFinished) { + status = await statusOperation(ctx, response.id, token); + if (!status) { + isFinished = true; + status = response; + } else { + isFinished = status.finished; + } + if ((new Date() - startTime) >= maxExecutionTime) { + throw new Error('The maximum waiting time for the operation has been exceeded'); + } + if (status.error != null) { + throw new Error(`Operation failed. Error: ${status.error}`); + } + if (!isFinished) { + await new Promise(resolve => setTimeout(resolve, 200)); + } + } + return status; + } + + fastExistCheck(path, ctx) { + //logMessage('CustomVirtualResources.fastExistCheck', ctx.user.username, path); + const substituteVP = virtualPath ? virtualPath : ''; + if (path == '/' || path == '/' + substituteVP) { + return true; + } + const user = ctx.user; + const { + element, + parentFolder + } = parse.parsePath(path); + let struct = this.structСache.getStruct(parentFolder, user.uid); + if (struct) { + if (this.findFile(struct, element) || this.findFolder(struct, element)) { + return true; + } + } + return false; + } + + async create(ctx, path) { + logMessage('CustomVirtualResources.create', ctx.context.user.username, path); + const user = ctx.context.user; + let { + element, + parentFolder + } = parse.parsePath(path); + let parentId = this.structСache.getStruct(parentFolder, user.uid).current.id; + + + if (ctx.type.isDirectory) { + let createdObj = await createFolder(ctx, parentId, element, user.token); + this.structСache.setFolderObject(parentFolder, user.uid, createdObj); + await this.readDir(ctx, path); + } + if (ctx.type.isFile) { + let createdObj = null; + switch (parse.parseFileExt(element)) { + case 'docx': + case 'pptx': + case 'xlsx': + createdObj = await createFile(ctx, parentId, element, user.token, false); + this.createdNow.push(createdObj.id); + this.structСache.setFileObject(parentFolder, user.uid, createdObj); + break; + case 'txt': + createdObj = await createFileTxt(ctx, parentId, element, user.token); + this.createdNow.push(createdObj.id); + this.structСache.setFileObject(parentFolder, user.uid, createdObj); + break; + case 'html': + createdObj = await createFileHtml(ctx, parentId, element, user.token); + this.createdNow.push(createdObj.id); + this.structСache.setFileObject(parentFolder, user.uid, createdObj); + break; + default: + //TODO: think about .tmp when create .xlsx in Windows Exploder + createdObj = await createFile(ctx, parentId, element, user.token, true); + this.createdNow.push(createdObj.id); + this.structСache.setFileObject(parentFolder, user.uid, createdObj); + break; + } + } + } + + async delete(ctx, path) { + logMessage('CustomVirtualResources.delete', ctx.context.user.username, path); + const user = ctx.context.user; + const { + element, + parentFolder + } = parse.parsePath(path); + const struct = this.structСache.getStruct(parentFolder, user.uid); + try { + const folder = this.findFolder(struct, element); + if (folder) { + const deleteResponse = await deleteFolder(ctx, folder.id, user.token); + const status = await this.isOperationOver(ctx, user.token, deleteResponse); + this.structСache.dropFolderObject(parentFolder, user.uid, folder); + this.structСache.dropPath(path, user.uid); + } + const file = this.findFile(struct, element); + if (file) { + const deleteResponse = await deleteFile(ctx, file.id, user.token); + const status = await this.isOperationOver(ctx, user.token, deleteResponse); + this.structСache.dropFileObject(parentFolder, user.uid, file); + } + } catch (error) { + return new Error(error); + } + } + + + addPrivilege(ctx, sPath, title, parentId, access, rootFolderType) { + let path = ""; + let privilageList = ""; + + if (parentId == -1) { + path = sPath + title; + privilageList = ['all']; + } else if (parentId == 0) { + if (rootFolderType == 1 || rootFolderType == 5) { + path = sPath + '/' + title; + privilageList = ['all']; + } else { + path = sPath + '/' + title; + privilageList = ['canRead']; + } + } else switch (access) { + case 0: + path = sPath + '/' + title; + privilageList = ['all']; + break; + case 1: + path = sPath + '/' + title; + privilageList = ['canRead', 'canWriteContent', 'canWriteProperties', 'canWriteLocks']; + break; + default: + path = sPath + '/' + title; + privilageList = ['canRead']; + break; + } + + ctx.context.server.privilegeManager.setRights(ctx.context.user, path, privilageList); + } + + addStructPrivilege(ctx, path, struct) { + struct.folders.forEach(el => { + this.addPrivilege(ctx, path, el.title, el.parentId, el.access, el.rootFolderType); + }); + + struct.files.forEach(el => { + this.addPrivilege(ctx, path, el.title, el.parentId, el.access); + }); + } + + async readDir(ctx, path) { + logMessage('CustomVirtualResources.readDir', ctx.context.user.username, path); + const user = ctx.context.user; + const substituteVP = virtualPath ? virtualPath : ''; + + if (virtualPath && path == '/') { + let virtualRootFolder = this.structСache.getStruct(path, user.uid); + if (virtualRootFolder) { + return virtualRootFolder; + } + virtualRootFolder = { + files: [], + current: { + id: -1 + }, + folders: [{ + id: 0, + title: virtualPath, + parentId: -1 + }] + }; + this.structСache.setStruct(path, user.uid, virtualRootFolder); + this.addStructPrivilege(ctx, path, virtualRootFolder); + return this.structСache.getStruct(path, user.uid); + } + + if (path == '/' + substituteVP) { + try { + let rootFolder = this.structСache.getStruct(path, user.uid); + if (rootFolder) { + return rootFolder; + } + rootFolder = await this.getRootFolder(ctx, user); + this.structСache.setStruct(path, user.uid, rootFolder); + this.addStructPrivilege(ctx, path, rootFolder); + return this.structСache.getStruct(path, user.uid); + } catch (error) { + return new Error(webdav.Errors.ResourceNotFound); + } + } + + const { + element, + parentFolder + } = parse.parsePath(path); + + let struct = this.structСache.getStruct(parentFolder, user.uid); + if (!struct) { + struct = await this.readDir(ctx, parentFolder); + if (!struct || struct instanceof Error) { + return new Error(webdav.Errors.ResourceNotFound); + } else { + const folder = this.findFolder(struct, element); + if (folder) { + try { + const structDirectory = await getStructDirectory(ctx, folder.id, user.token); + this.structСache.setStruct(path, user.uid, structDirectory); + this.addStructPrivilege(ctx, path, structDirectory); + return this.structСache.getStruct(path, user.uid); + } catch (error) { + return new Error(webdav.Errors.ResourceNotFound); + } + } + } + } + + const folder = this.findFolder(struct, element); + if (folder) { + if (this.structСache.structIsNotExpire(path, user.uid)) { + return this.structСache.getStruct(path, user.uid); + } + try { + const structDirectory = await getStructDirectory(ctx, folder.id, user.token); + this.structСache.setStruct(path, user.uid, structDirectory); + this.addStructPrivilege(ctx, path, structDirectory); + return this.structСache.getStruct(path, user.uid); + } catch (error) { + return new Error(webdav.Errors.ResourceNotFound); + } + } + + return new Error(webdav.Errors.ResourceNotFound); + } + + async getReadStream(ctx, path) { + logMessage('CustomVirtualResources.getReadStream', ctx.context.user.username, path); + const user = ctx.context.user; + const { element, parentFolder } = parse.parsePath(path); + const file = this.findFile(this.structСache.getStruct(parentFolder, user.uid), element); + if (file) { + const uri = await getPresignedUri(ctx, file.id, user.token); + const readStream = await getReadStream(ctx, uri, user.token); + return readStream; + } else { + throw new Error(webdav.Errors.ResourceNotFound); + } + } + + readFile(ctx, path, callback) { + logMessage('CustomVirtualResources.readFile', ctx.context.user.username, path); + const user = ctx.context.user; + const { element, parentFolder } = parse.parsePath(path); + const file = this.findFile(this.structСache.getStruct(parentFolder, user.uid), element); + if (file) { + readFile(ctx, file.id, user.token, callback); + } else { + callback(new Error(webdav.Errors.ResourceNotFound)); + } + } + + async writeFile(path, ctx) { + logMessage('CustomVirtualResources.writeFile', ctx.context.user.username, path); + const user = ctx.context.user; + const { + element, + parentFolder + } = parse.parsePath(path); + + if (ctx.estimatedSize > 0) { + + const struct = this.structСache.getStruct(parentFolder, user.uid); + const file = this.findFile(struct, element); + const content = []; + const positionId = this.createdNow.indexOf(file.id); + if (positionId != -1) { + var createdNow = true; + this.createdNow = this.createdNow.splice(positionId + 1, 1); + + var location = await createEditSession(ctx, file.id, ctx.estimatedSize, user.token); + } + let arrayBufLength = []; + if (ctx.estimatedSize > maxChunkSize) { + arrayBufLength = new Array(maxChunkSize); + } + const stream = new streamWrite(content, arrayBufLength, ctx, location, user, file, createdNow); + return stream; + } else { + const content = []; + const stream = new streamWrite(content, undefined, undefined, undefined, undefined); + return stream; + } + } + + async copy(ctx, pathFrom, pathTo) { + logMessage('CustomVirtualResources.copy', ctx.context.user.username, pathFrom, pathTo); + const user = ctx.context.user; + const { + element, + parentFolder + } = parse.parsePath(pathFrom); + pathTo = parse.parsePathTo(pathTo); + const structTo = this.structСache.getStruct(pathTo, user.uid); + if (!structTo) { + return new Error(webdav.Errors.ResourceNotFound); + } + const folderId = structTo.current.id; + const structFrom = this.structСache.getStruct(parentFolder, user.uid); + const folder = this.findFolder(structFrom, element); + if (folder) { + try { + const copyResponse = await copyFolder(ctx, folderId, folder.id, user.token); + const status = await this.isOperationOver(ctx, user.token, copyResponse); + return true; + } catch (error) { + return new Error(error); + } + } + const file = this.findFile(structFrom, element); + if (file) { + try { + const copyResponse = await copyFile(ctx, folderId, file.id, user.token); + const status = await this.isOperationOver(ctx, user.token, copyResponse); + return true; + } catch (error) { + return new Error(error); + } + } + } + + async rename(ctx, path, newName) { + logMessage('CustomVirtualResources.rename', ctx.context.user.username, path, newName); + const user = ctx.context.user; + const { + element, + parentFolder + } = parse.parsePath(path); + const struct = this.structСache.getStruct(parentFolder, user.uid); + const folder = this.findFolder(struct, element); + if (folder) { + try { + await renameFolder(ctx, folder.id, newName, user.token); + this.structСache.renameFolderObject(element, newName, parentFolder, user.uid); + folder.realTitle = newName; + return true; + } catch (error) { + return new Error(error); + } + } + const file = this.findFile(struct, element); + if (file) { + try { + await renameFile(ctx, file.id, newName, user.token); + this.structСache.renameFileObject(element, newName, parentFolder, user.uid); + file.realTitle = newName; + return true; + } catch (error) { + return new Error(error); + } + } + } + + async move(ctx, pathFrom, pathTo) { + logMessage('CustomVirtualResources.move', ctx.context.user.username, pathFrom, pathTo); + pathTo = parse.parsePathTo(pathTo); + const { + element: elementFrom, + parentFolder: parentFolderFrom + } = parse.parsePath(pathFrom); + const { + element: elementTo, + parentFolder: parentFolderTo + } = parse.parsePath(pathTo); + const user = ctx.context.user; + let isRename = false; + if (parentFolderFrom == parentFolderTo) { + isRename = this.structСache.checkRename(elementFrom, elementTo, parentFolderFrom, parentFolderTo, user); + } + if (isRename) { + try { + const rename = this.rename(ctx, pathFrom, elementTo); + return rename; + } catch (error) { + return new Error(error); + } + } + if (!this.structСache.getStruct(parentFolderTo, user.uid)) { + return new Error(webdav.Errors.ResourceNotFound); + } + const folderId = this.structСache.getStruct(parentFolderTo, user.uid).current.id; + const structFrom = this.structСache.getStruct(parentFolderFrom, user.uid); + const folder = this.findFolder(structFrom, elementFrom); + if (folder) { + try { + const moveResponse = await moveFolder(ctx, folderId, folder.id, user.token); + const status = await this.isOperationOver(ctx, user.token, moveResponse); + this.structСache.dropFolderObject(parentFolderFrom, user.uid, folder); + this.structСache.dropPath(pathFrom, user.uid); + const folderWithNewLocation = status.folders.find(newLocation => newLocation.id == folder.id); + this.structСache.setFolderObject(parentFolderTo, user.uid, folderWithNewLocation); + return true; + } catch (error) { + return new Error(error); + } + } + const file = this.findFile(structFrom, elementFrom); + if (file) { + try { + const moveResponse = await moveFile(ctx, folderId, file.id, user.token); + const status = await this.isOperationOver(ctx, user.token, moveResponse); + this.structСache.dropFileObject(parentFolderFrom, user.uid, file); + const fileWithNewLocation = status.files.find(newLocation => newLocation.id == file.id); + this.structСache.setFileObject(parentFolderTo, user.uid, fileWithNewLocation); + return true; + } catch (error) { + return new Error(error); + } + } + } + + getType(path, ctx) { + const user = ctx.context.user; + const { + element, + parentFolder + } = parse.parsePath(path); + const substituteVP = virtualPath ? virtualPath : ''; + if (parentFolder == '/' || parentFolder == '/' + substituteVP) { + return webdav.ResourceType.Directory; + } + const struct = this.structСache.getStruct(parentFolder, user.uid); + const folder = this.findFolder(struct, element); + if (folder) { + return webdav.ResourceType.Directory; + } + const file = this.findFile(struct, element); + if (file) { + return webdav.ResourceType.File; + } + } + + getSize(path, ctx) { + const { + element, + parentFolder + } = parse.parsePath(path); + const user = ctx.context.user; + const struct = this.structСache.getStruct(parentFolder, user.uid); + const folder = this.findFolder(struct, element); + if (folder) { + return null; + } + const file = this.findFile(struct, element); + if (file) { + return file.pureContentLength; + } + } + + getlastModifiedDate(path, ctx) { + const substituteVP = virtualPath ? virtualPath : ''; + if (path == '/' || path == '/' + substituteVP) { + return new Date(0, 0, 0, 0, 0, 0); + } + const { + element, + parentFolder + } = parse.parsePath(path); + const user = ctx.context.user; + const struct = this.structСache.getStruct(parentFolder, user.uid); + const folder = this.findFolder(struct, element); + if (folder) { + return parse.parseDate(folder.updated); + } + const file = this.findFile(struct, element); + if (file) { + return parse.parseDate(file.updated); + } + } +} + +module.exports = CustomVirtualResources; \ No newline at end of file diff --git a/module/ASC.WebDav/resource/simpleStruct.js b/module/ASC.WebDav/resource/simpleStruct.js new file mode 100644 index 000000000..fd674c260 --- /dev/null +++ b/module/ASC.WebDav/resource/simpleStruct.js @@ -0,0 +1,153 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +class SimpleStruct { + constructor() { + this.struct = {}; + } + + setStruct(path, uid, structDir) { + if (!this.struct) { + this.struct = {}; + } + if (!this.struct[uid]) { + this.struct[uid] = {}; + } + this.struct[uid][path] = structDir; + this.struct[uid].lastUpdate = new Date(); + } + + getStruct(path, uid) { + return this.struct[uid] && this.struct[uid][path]; + } + + deleteStruct(uid) { + delete this.struct[uid]; + } + + deleteStructs(uids) { + uids.forEach(uid => { + this.deleteStruct(uid); + }); + } + + setFileObject(path, uid, newFile) { + this.struct[uid][path].files.push(newFile); + this.struct[uid].lastUpdate = new Date(); + } + + setFolderObject(path, uid, newFile) { + this.struct[uid][path].folders.push(newFile); + this.struct[uid].lastUpdate = new Date(); + } + + dropFileObject(Folder, uid, file) { + this.struct[uid][Folder].files.forEach(el => { + if (el.id == file.id) { + const id = this.struct[uid][Folder].files.indexOf(el); + this.struct[uid][Folder].files.splice(id, 1); + this.struct[uid].lastUpdate = new Date(); + } + }); + } + + dropFolderObject(Folder, uid, folder) { + this.struct[uid][Folder].folders.forEach(el => { + if (el.id == folder.id) { + const id = this.struct[uid][Folder].folders.indexOf(el); + this.struct[uid][Folder].folders.splice(id, 1); + this.struct[uid].lastUpdate = new Date(); + } + }); + } + + dropPath(path, uid) { + if (this.struct[uid][path]) { + delete this.struct[uid][path]; + this.struct[uid].lastUpdate = new Date(); + } + } + + checkRename(elementFrom, elementTo, parentFolderFrom, parentFolderTo, user) { + + if (parentFolderFrom != parentFolderTo) return false; + let elementFromIsExist = false; + let elementToIsExist = false; + let structFrom = this.struct[user.uid][parentFolderFrom]; + let structTo = this.struct[user.uid][parentFolderTo]; + structFrom.files.forEach((el) => { + if (elementFrom == el.title) { + elementFromIsExist = true; + } + }); + if (!elementFromIsExist) { + structFrom.folders.forEach((el) => { + if (elementFrom == el.title) { + elementFromIsExist = true; + } + }); + } + if (!elementFromIsExist) return false; + + structTo.files.forEach((el) => { + if (elementTo == el.title) { + elementToIsExist = true; + } + }); + if (!elementToIsExist) { + structTo.folders.forEach((el) => { + if (elementTo == el.title) { + elementToIsExist = true; + } + }); + } + if (!elementToIsExist) return true; + return true; + } + + renameFolderObject(element, newName, parentFolder, uid) { + this.struct[uid][parentFolder].folders.forEach(el => { + if (el.title == element) { + const id = this.struct[uid][parentFolder].folders.indexOf(el); + this.struct[uid][parentFolder].folders[id].title = newName; + this.struct[uid].lastUpdate = new Date(); + } + }); + } + + renameFileObject(element, newName, parentFolder, uid) { + this.struct[uid][parentFolder].files.forEach(el => { + if (el.title == element) { + const id = this.struct[uid][parentFolder].files.indexOf(el); + this.struct[uid][parentFolder].files[id].title = newName; + this.struct[uid].lastUpdate = new Date(); + } + }); + } + + structIsNotExpire(path, uid) { + if (!this.struct[uid][path]) { + return false; + } else { + const difference = 1000; + const notExpire = (new Date() - this.struct[uid].lastUpdate) < difference; + return notExpire; + } + } +} + +module.exports = SimpleStruct; \ No newline at end of file diff --git a/module/ASC.WebDav/server/config.js b/module/ASC.WebDav/server/config.js new file mode 100644 index 000000000..a0b84f720 --- /dev/null +++ b/module/ASC.WebDav/server/config.js @@ -0,0 +1,84 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +/* Config */ + +module.exports = { + // Port listener WebDav Server + port: 1900, + // Path to pfx key + pfxKeyPath: null, + // Pass phrase for pfx key + pfxPassPhrase: null, + // Path to .crt + certPath: null, + // Path to .key + keyPath: null, + // Enable secure connection + isHttps: false, + // root virtual directory + virtualPath: "webdav", + // Logging level + logLevel: "debug", + // Maximum execution time of long-running operations + maxExecutionTime: 600000, + // User cache storage time (msec) + userLifeTime : 3600000, + // Cleanup interval of expired users (msec) + usersCleanupInterval: 600000, + // Port of community server OnlyOffice */ + onlyOfficePort: ":80", + // Maximum chunk size + maxChunkSize: 10485760, + // Api constant + api: "/api/2.0", + // Api authentication method + apiAuth: "authentication.json", + // Sub-method for files/folders operations + apiFiles: "/files", + // Path to read the file + fileHandlerPath: "/Products/Files/HttpHandlers/filehandler.ashx?action=stream&fileid={0}", + + method: { + // Get root directory in "Root" + pathRootDirectory: "@root", + // Operations with folders + folder: "/folder", + // Operations with files + file: "/file", + // Create new file "*.txt" + text: "/text", + // Create new file "*.html" + html: "/html", + // Get all active operations + fileops: "/fileops", + // File saving method + saveediting: "/saveediting", + // Method copy for files or folders + copy: "/copy", + // Method move for files or folders + move: "/move", + // Method for getting a link to download a file + presigneduri: "/presigneduri", + // Submethod to create a session + upload: "/upload", + // Method for creating a session + createSession: "/create_session", + // Method to create session to edit existing file with multiple chunks + editSession: "/edit_session" + } +}; \ No newline at end of file diff --git a/module/ASC.WebDav/server/requestAPI.js b/module/ASC.WebDav/server/requestAPI.js new file mode 100644 index 000000000..ef430513d --- /dev/null +++ b/module/ASC.WebDav/server/requestAPI.js @@ -0,0 +1,464 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +const request = require('request'); +const axios = require('axios'); +const { + getHeader, + getHeaderPeople, + logError, + logResponse, + logMessage +} = require('../helper/helper.js'); +const { + onlyOfficePort, + api, + apiFiles, + apiAuth, + fileHandlerPath, + method, + isHttps +} = require('./config.js'); +const renamingDuplicateElements = require('../helper/renamingDuplicateElements.js'); + + +function getDomain(ctx) { + const rewriterUrl = ctx.headers.headers["x-rewriter-url"]; + + if (rewriterUrl) { + return rewriterUrl; + } + + const protocol = isHttps ? "https://" : "http://"; + const hostStr = ctx.headers.host; + return protocol + hostStr.split(":")[0] + onlyOfficePort; +} + +function instanceFunc(ctx, token = null, header = 'application/json', service = 'asc.files') { + const domain = getDomain(ctx); + + switch (service) { + case 'asc.files': + return axios.create({ + baseURL: `${domain}${api}`, + timeout: 30000, + headers: getHeader(header, token) + }); + case 'asc.people': + return axios.create({ + baseURL: `${domain}${api}`, + timeout: 30000, + headers: getHeaderPeople(token) + }); + } + +} + +function logErrorAndCheckStatus(ctx, error, method) { + logError(error, method); + try { + if (error.response && error.response.status == 401) { + ctx.server.httpAuthentication.userManager.storeUser.deleteUser(ctx.user.username); + ctx.server.fileSystems['/'].manageResource.structСache.deleteStruct(ctx.user.uid); + delete ctx.server.httpAuthentication.userManager.users[ctx.user.username]; + delete ctx.server.privilegeManager.rights[ctx.user.uid]; + } + } catch (ex) { + logError(ex, "requestAPI.logErrorAndCheckStatus"); + } +} + +var requestAuth = async function (ctx, username, password) { + try { + const instance = instanceFunc(ctx); + const response = await instance.post(`${apiAuth}`, { + "userName": username, + "password": password + }); + logResponse(ctx, response, "requestAPI.requestAuth"); + return response.data.response.token; + } catch (error) { + logError(error, "requestAPI.requestAuth"); + throw error; + } +}; + +var requestUser = async function (ctx, token) { + try { + const instance = instanceFunc(ctx, token, undefined, 'asc.people'); + const response = await instance.get("people/@self.json"); + logResponse(ctx, response, "requestAPI.requestUser"); + return response.data.response.id; + } catch (error) { + logError(error, "requestAPI.requestUser"); + throw error; + } +}; + +var getStructDirectory = async function (ctx, folderId, token) { + try { + const instance = instanceFunc(ctx.context, token); + let response = await instance.get(`${apiFiles}/${folderId}`); + response = renamingDuplicateElements.addRealTitle(response, folderId); + response = renamingDuplicateElements.localRename(response, folderId); + logResponse(ctx.context, response, "requestAPI.getStructDirectory"); + return response.data.response; + } catch (error) { + logErrorAndCheckStatus(ctx.context, error, "requestAPI.getStructDirectory"); + throw error; + } +}; + +var createFile = async function (ctx, folderId, title, token, enableExternalExt) { + try { + const instance = instanceFunc(ctx.context, token); + let response = await instance.post(`${apiFiles}/${folderId}${method.file}`, { + "title": title, + "EnableExternalExt": enableExternalExt + }); + response.data.response['realTitle'] = response.data.response.title; + logResponse(ctx.context, response, "requestAPI.createFile id=" + response.data.response.id); + return response.data.response; + } catch (error) { + logErrorAndCheckStatus(ctx.context, error, "requestAPI.createFile"); + throw error; + } +}; + +var createFolder = async function (ctx, parentId, title, token) { + try { + const instance = instanceFunc(ctx.context, token); + const response = await instance.post(`${apiFiles}${method.folder}/${parentId}`, { + "title": title + }); + logResponse(ctx.context, response, "requestAPI.createFolder"); + return response.data.response; + } catch (error) { + logErrorAndCheckStatus(ctx.context, error, "requestAPI.createFolder"); + throw error; + } +}; + +var deleteFile = async function (ctx, fileId, token) { + try { + const instance = instanceFunc(ctx.context, token); + const response = await instance.delete(`${apiFiles}${method.file}/${fileId}`, { + data: { + "DeleteAfter": true, + "Immediately": false + } + }); + logResponse(ctx.context, response, "requestAPI.deleteFile id=" + fileId); + return response.data.response[0]; + } catch (error) { + logErrorAndCheckStatus(ctx.context, error, "requestAPI.deleteFile"); + throw error; + } +}; + +var deleteFolder = async function (ctx, folderId, token) { + try { + const instance = instanceFunc(ctx.context, token); + const response = await instance.delete(`${apiFiles}${method.folder}/${folderId}`, { + data: { + "DeleteAfter": true, + "Immediately": false + } + }); + logResponse(ctx.context, response, "requestAPI.deleteFolder"); + return response.data.response[0]; + } catch (error) { + logErrorAndCheckStatus(ctx.context, error, "requestAPI.deleteFolder"); + throw error; + } +}; + +var copyFile = async function (ctx, folderId, files, token) { + try { + const instance = instanceFunc(ctx.context, token); + const response = await instance.put(`${apiFiles}${method.fileops}${method.copy}`, { + "destFolderId": folderId, + "folderIds": [], + "fileIds": [files], + "conflictResolveType": "Skip", + "deleteAfter": true + }); + logResponse(ctx.context, response, "requestAPI.copyFile"); + return response.data.response[0]; + } catch (error) { + logErrorAndCheckStatus(ctx.context, error, "requestAPI.copyFile"); + throw error; + } +}; + +var copyFolder = async function (ctx, folderId, folders, token) { + try { + const instance = instanceFunc(ctx.context, token); + const response = await instance.put(`${apiFiles}${method.fileops}${method.copy}`, { + "destFolderId": folderId, + "folderIds": [folders], + "fileIds": [], + "conflictResolveType": "Skip", + "deleteAfter": true + }); + logResponse(ctx.context, response, "requestAPI.copyFolder"); + return response.data.response[0]; + } catch (error) { + logErrorAndCheckStatus(ctx.context, error, "requestAPI.copyFolder"); + throw error; + } +}; + +var moveFile = async function (ctx, folderId, files, token) { + try { + const instance = instanceFunc(ctx.context, token); + const response = await instance.put(`${apiFiles}${method.fileops}${method.move}`, { + "destFolderId": folderId, + "folderIds": [], + "fileIds": [files], + "resolveType": "Skip", + "holdResult": true + }); + logResponse(ctx.context, response, "requestAPI.moveFile"); + return response.data.response[0]; + } catch (error) { + logErrorAndCheckStatus(ctx.context, error, "requestAPI.moveFile"); + throw error; + } +}; + +var moveFolder = async function (ctx, folderId, folders, token) { + try { + const instance = instanceFunc(ctx.context, token); + const response = await instance.put(`${apiFiles}${method.fileops}${method.move}`, { + "destFolderId": folderId, + "folderIds": [folders], + "fileIds": [], + "resolveType": "Skip", + "holdResult": true + }); + logResponse(ctx.context, response, "requestAPI.moveFolder"); + return response.data.response[0]; + } catch (error) { + logErrorAndCheckStatus(ctx.context, error, "requestAPI.moveFolder"); + throw error; + } +}; + +var statusOperation = async function (ctx, operationId, token) { + try { + const instance = instanceFunc(ctx.context, token); + const response = await instance.get(`${apiFiles}${method.fileops}`); + const operation = response.data.response.find(operation => operation.id == operationId); + logResponse(ctx.context, response, "requestAPI.statusOperation"); + return operation; + } catch (error) { + logErrorAndCheckStatus(ctx.context, error, "requestAPI.statusOperation"); + throw error; + } +} + +var renameFile = async function (ctx, fileId, newName, token) { + try { + const instance = instanceFunc(ctx.context, token); + const response = await instance.put(`${apiFiles}${method.file}/${fileId}`, { + "title": newName + }); + logResponse(ctx.context, response, "requestAPI.renameFile"); + } catch (error) { + logErrorAndCheckStatus(ctx.context, error, "requestAPI.renameFile"); + throw error; + } +}; + +var renameFolder = async function (ctx, folderId, newName, token) { + try { + const instance = instanceFunc(ctx.context, token); + const response = await instance.put(`${apiFiles}${method.folder}/${folderId}`, { + "title": newName + }); + logResponse(ctx.context, response, "requestAPI.renameFolder"); + } catch (error) { + logErrorAndCheckStatus(ctx.context, error, "requestAPI.renameFolder"); + throw error; + } +}; + +var rewritingFile = async function (ctx, fileId, data, token) { + try { + const Authorization = token ? token : null; + const instance = instanceFunc(ctx.context, token); + const response = await instance.put(`${apiFiles}${method.file}/${fileId}${method.saveediting}`, data, { + maxBodyLength: Infinity, + headers: { + Authorization, + "Content-Type": `multipart/form-data; boundary=${data._boundary}` + } + }); + logResponse(ctx.context, response, "requestAPI.rewritingFile"); + } catch (error) { + logErrorAndCheckStatus(ctx.context, error, "requestAPI.rewritingFile"); + throw error; + } +}; + +var createSession = async function (ctx, folderId, formData, token) { + try { + const instance = instanceFunc(ctx.context, token) + const response = await instance.post(`${apiFiles}/${folderId}${method.upload}${method.createSession}`, formData, { + headers: { + Connection: "keep-alive" + } + }); + logResponse(ctx.context, response, "requestAPI.createSession"); + return response.data.response.data.location; + } catch (error) { + logErrorAndCheckStatus(ctx.context, error, "requestAPI.createSession"); + throw error; + } +} + +var createEditSession = async function (ctx, fileId, fileSize, token) { + try { + const instance = instanceFunc(ctx.context, token) + const response = await instance.post(`${apiFiles}${method.file}/${fileId}${method.editSession}`, { + fileSize: fileSize + }); + logResponse(ctx.context, response, "requestAPI.createEditSession"); + return response.data.response.data.location; + } catch (error) { + logErrorAndCheckStatus(ctx.context, error, "requestAPI.createEditSession"); + throw error; + } +} + +var chunkedUploader = async function (ctx, url, data, token, bytesAmount, firstPosition, secondPosition) { + try { + const Authorization = token ? token : null; + const response = await axios.post(url, data, { + maxBodyLength: Infinity, + headers: { + Authorization, + "Content-Type": `multipart/form-data; boundary=${data._boundary}`, + "Content-Range": `bytes ${firstPosition}-${secondPosition}/${bytesAmount}` + } + }); + logResponse(null, response, "requestAPI.chunkedUploader"); + } catch (error) { + logErrorAndCheckStatus(ctx.context, error, "requestAPI.chunkedUploader"); + throw error; + } +} + +var getPresignedUri = async function (ctx, fileId, token) { + try { + const instance = instanceFunc(ctx.context, token); + const response = await instance.get(`${apiFiles}${method.file}/${fileId}${method.presigneduri}`); + logResponse(ctx.context, response, "requestAPI.getPresignedUri"); + return response.data.response; + } catch (error) { + logErrorAndCheckStatus(ctx.context, error, "requestAPI.getPresignedUri"); + throw error; + } +}; + +var getReadStream = async function (ctx, uri, token) { + try { + const streamRequest = await request.get({ + url: uri, + headers: getHeader("application/octet-stream", token) + }); + logMessage("requestAPI.getReadStream", uri, "OK"); + return streamRequest; + } catch (error) { + logErrorAndCheckStatus(ctx.context, error, "requestAPI.getReadStream"); + throw error; + } +}; + +var readFile = function (ctx, fileId, token, callback) { + try { + const url = getDomain(ctx.context) + fileHandlerPath.replace("{0}", fileId); + const headers = getHeader("application/octet-stream", token); + const range = ctx.context.headers.find("Range"); + const streamRequest = request.get({ url, headers }); + callback(null, streamRequest); + streamRequest.end(); + logMessage("requestAPI.readFile", "fileId: " + fileId + (range ? " range: " + range : ""), "OK"); + } catch (error) { + logErrorAndCheckStatus(ctx.context, error, "requestAPI.readFile"); + callback(error); + } +}; + +var createFileTxt = async function (ctx, folderId, title, token) { + try { + const instance = await instanceFunc(ctx.context, token); + const response = await instance.post(`${apiFiles}/${folderId}${method.text}`, { + "title": title, + "content": ' ' + }); + response.data.response['realTitle'] = response.data.response.title; + logResponse(ctx.context, response, "requestAPI.createFileTxt"); + return response.data.response; + } catch (error) { + logErrorAndCheckStatus(ctx.context, error, "requestAPI.createFileTxt"); + throw error; + } +}; + +var createFileHtml = async function (ctx, folderId, title, token) { + try { + const instance = await instanceFunc(ctx.context, token); + const response = await instance.post(`${apiFiles}/${folderId}${method.html}`, { + "title": title, + "content": ' ' + }); + response.data.response['realTitle'] = response.data.response.title; + logResponse(ctx.context, response, "requestAPI.createFileHtml"); + return response.data.response; + } catch (error) { + logErrorAndCheckStatus(ctx.context, error, "requestAPI.createFileHtml"); + throw error; + } +}; + +module.exports = { + getStructDirectory, + getPresignedUri, + getReadStream, + readFile, + createSession, + rewritingFile, + createFileTxt, + createFileHtml, + chunkedUploader, + requestAuth, + requestUser, + createFile, + createFolder, + deleteFile, + deleteFolder, + copyFile, + copyFolder, + moveFile, + moveFolder, + renameFile, + renameFolder, + createEditSession, + statusOperation +}; \ No newline at end of file diff --git a/module/ASC.WebDav/server/webDavServer.js b/module/ASC.WebDav/server/webDavServer.js new file mode 100644 index 000000000..3f776db4a --- /dev/null +++ b/module/ASC.WebDav/server/webDavServer.js @@ -0,0 +1,99 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + +const webdav = require('webdav-server').v2; +const express = require('express'); + +const FileSystem = require('../manager/customFileSystem'); +const customUserManager = require('../user/customUserManager'); +const customHTTPBasicAuthentication = require('../user/authentication/customHTTPBasicAuthentication'); +const fs = require('fs'); +const { + port, + usersCleanupInterval, + pfxKeyPath, + pfxPassPhrase, + certPath, + keyPath, + isHttps, + virtualPath +} = require('./config.js'); +const { logContext, logMessage } = require('../helper/helper.js'); + +const userManager = new customUserManager(); +const privilegeManager = new webdav.SimplePathPrivilegeManager(); + +const options = { + port: process.env.port || port, + requireAuthentification: true, + httpAuthentication: new customHTTPBasicAuthentication(userManager), + rootFileSystem: new FileSystem(), + privilegeManager: privilegeManager +}; +if (isHttps) { + if (!(pfxKeyPath && pfxPassPhrase) && !(certPath && keyPath)) { + throw new Error("A secure connection is activated, but there are no keys"); + } + if (pfxKeyPath && pfxPassPhrase) { + options.https = { + pfx: fs.readFileSync(pfxKeyPath), + passphrase: pfxPassPhrase + }; + } else { + options.https = { + cert: fs.readFileSync(certPath), + key: fs.readFileSync(keyPath) + }; + } +} + +const server = new webdav.WebDAVServer( + options +); + +setInterval(function () { + userManager.storeUser.deleteExpiredUsers((expiredUserIds) => { + logMessage("server.deleteExpiredUsers", expiredUserIds); + server.fileSystems['/'].manageResource.structСache.deleteStructs(expiredUserIds); + }) +}, usersCleanupInterval); + +server.afterRequest((ctx, next) => { + //logContext(ctx, "afterRequest"); + next(); +}); +server.beforeRequest((ctx, next) => { + if (virtualPath) { + if (ctx.requested.path.paths[0] != virtualPath) { + ctx.requested.path.paths.unshift(virtualPath); + ctx.requested.uri = ctx.requested.path.toString(); + } + } + //logContext(ctx, "beforeRequest"); + next(); +}); +//server.start((s) => console.log('Ready on port', s.address().port)); + +const app = express(); + +app.use("/isLife", (req, res) => { + res.sendStatus(200); +}); + +app.use(webdav.extensions.express('/', server)); + +app.listen(options.port); \ No newline at end of file diff --git a/module/ASC.WebDav/user/authentication/customHTTPBasicAuthentication.js b/module/ASC.WebDav/user/authentication/customHTTPBasicAuthentication.js new file mode 100644 index 000000000..9ee5fac08 --- /dev/null +++ b/module/ASC.WebDav/user/authentication/customHTTPBasicAuthentication.js @@ -0,0 +1,51 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +const webdav = require('webdav-server').v2; + +class customHTTPBasicAuthentication extends webdav.HTTPBasicAuthentication +{ + + getUser (ctx, callback) { + var _this = this; + var onError = function (error) { + _this.userManager.getDefaultUser(function (defaultUser) { + callback(error, defaultUser); + }); + }; + var authHeader = ctx.headers.find('Authorization'); + if (!authHeader) { + onError(webdav.Errors.MissingAuthorisationHeader); + return; + } + if (!/^Basic \s*[a-zA-Z0-9]+=*\s*$/.test(authHeader)) { + onError(webdav.Errors.WrongHeaderFormat); + return; + } + var value = Buffer.from(/^Basic \s*([a-zA-Z0-9]+=*)\s*$/.exec(authHeader)[1], 'base64').toString().split(':', 2); + var username = value[0]; + var password = value[1]; + this.userManager.getUserByNamePassword(ctx, username, password, function (e, user) { + if (e) + onError(webdav.Errors.BadAuthentication); + else + callback(null, user); + }); + } +} + +module.exports = customHTTPBasicAuthentication; \ No newline at end of file diff --git a/module/ASC.WebDav/user/customSimpleUser.js b/module/ASC.WebDav/user/customSimpleUser.js new file mode 100644 index 000000000..8bbd22d36 --- /dev/null +++ b/module/ASC.WebDav/user/customSimpleUser.js @@ -0,0 +1,29 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +class customSimpleUser{ + constructor(uid, isAdministrator, isDefaultUser, username, token){ + this.uid = uid; + this.isAdministrator = isAdministrator; + this.isDefaultUser = isDefaultUser; + this.username = username; + this.token = token; + this.lastVisit = new Date(); + } +} + +module.exports = customSimpleUser; \ No newline at end of file diff --git a/module/ASC.WebDav/user/customUserLayout.js b/module/ASC.WebDav/user/customUserLayout.js new file mode 100644 index 000000000..82013576f --- /dev/null +++ b/module/ASC.WebDav/user/customUserLayout.js @@ -0,0 +1,53 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +const customSimpleUser = require('./customSimpleUser.js'); +const { userLifeTime } = require('../server/config.js'); + +class customUserLayout{ + constructor(){ + this.storage = new Map(); + } + + setUser(username, token, uid){ + const user = new customSimpleUser(uid, false, false, username, token); + this.storage.set(username, user); + return user; + } + + getUser(username){ + return this.storage.get(username); + } + + deleteUser(username){ + this.storage.delete(username); + } + + deleteExpiredUsers(callback){ + const expiredUserIds = []; + const now = new Date(); + this.storage.forEach(user => { + if(now - user.lastVisit > userLifeTime){ + this.deleteUser(user.username); + expiredUserIds.push(user.uid); + } + }); + callback(expiredUserIds); + } +} + +module.exports = customUserLayout; \ No newline at end of file diff --git a/module/ASC.WebDav/user/customUserManager.js b/module/ASC.WebDav/user/customUserManager.js new file mode 100644 index 000000000..f7683a614 --- /dev/null +++ b/module/ASC.WebDav/user/customUserManager.js @@ -0,0 +1,55 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +const webdav = require('webdav-server').v2; +const { + requestAuth, + requestUser +} = require('../server/requestAPI.js'); +const customUserLayout = require('./customUserLayout.js'); + +class customUserManager extends webdav.SimpleUserManager { + constructor() { + super(); + this.storeUser = new customUserLayout(); + } + + addUser(name, token, uid) { + const user = this.storeUser.setUser(name, token, uid); + this.users[name] = user; + return user; + } + + async getUserByNamePassword(ctx, username, password, callback) { + try { + let user = this.storeUser.getUser(username); + if (user) { + user = this.addUser(username, user.token, user.uid); // update last visit + } else { + const token = await requestAuth(ctx, username, password); + const uid = await requestUser(ctx, token); + user = this.addUser(username, token, uid); + ctx.server.privilegeManager.setRights(user, '/', ['canRead']); + } + callback(null, user); + } catch (error) { + callback(webdav.Errors.UserNotFound); + } + } +} + +module.exports = customUserManager; \ No newline at end of file diff --git a/module/ASC.WebDav/yarn.lock b/module/ASC.WebDav/yarn.lock new file mode 100644 index 000000000..7a3227b8b --- /dev/null +++ b/module/ASC.WebDav/yarn.lock @@ -0,0 +1,1317 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@dabh/diagnostics@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.2.tgz#290d08f7b381b8f94607dc8f471a12c675f9db31" + integrity sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q== + dependencies: + colorspace "1.1.x" + enabled "2.0.x" + kuler "^2.0.0" + +"@yarnpkg/lockfile@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" + integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== + +accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + +ajv@^6.12.3: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +async@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720" + integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== + +axios@^0.21.0: + version "0.21.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8" + integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA== + dependencies: + follow-redirects "^1.10.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base64url@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" + integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +body-parser@1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== + dependencies: + bytes "3.1.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.7.2" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +color-convert@^1.9.0, color-convert@^1.9.1: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-string@^1.5.2: + version "1.5.4" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.4.tgz#dd51cd25cfee953d138fe4002372cc3d0e504cb6" + integrity sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@3.0.x: + version "3.0.0" + resolved "https://registry.yarnpkg.com/color/-/color-3.0.0.tgz#d920b4328d534a3ac8295d68f7bd4ba6c427be9a" + integrity sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w== + dependencies: + color-convert "^1.9.1" + color-string "^1.5.2" + +colors@^1.2.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== + +colorspace@1.1.x: + version "1.1.2" + resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.2.tgz#e0128950d082b86a2168580796a0aa5d6c68d8c5" + integrity sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ== + dependencies: + color "3.0.x" + text-hex "1.0.x" + +combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +content-disposition@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + dependencies: + safe-buffer "5.1.2" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + +cookie@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +enabled@2.0.x: + version "2.0.0" + resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" + integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +express@^4.17.1: + version "4.17.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== + dependencies: + accepts "~1.3.7" + array-flatten "1.1.1" + body-parser "1.19.0" + content-disposition "0.5.3" + content-type "~1.0.4" + cookie "0.4.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "~1.1.2" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.5" + qs "6.7.0" + range-parser "~1.2.1" + safe-buffer "5.1.2" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-safe-stringify@^2.0.4: + version "2.0.7" + resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" + integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== + +fecha@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.0.tgz#3ffb6395453e3f3efff850404f0a59b6747f5f41" + integrity sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg== + +file-stream-rotator@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/file-stream-rotator/-/file-stream-rotator-0.5.7.tgz#868a2e5966f7640a17dd86eda0e4467c089f6286" + integrity sha512-VYb3HZ/GiAGUCrfeakO8Mp54YGswNUHvL7P09WQcXAJNSj3iQ5QraYSp3cIn1MUyw6uzfgN/EFOarCNa4JvUHQ== + dependencies: + moment "^2.11.2" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +find-yarn-workspace-root@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz#f47fb8d239c900eb78179aa81b66673eac88f7bd" + integrity sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ== + dependencies: + micromatch "^4.0.2" + +fn.name@1.x.x: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" + integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== + +follow-redirects@^1.10.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7" + integrity sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg== + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +form-data@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + +fs-extra@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +get-byte@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/get-byte/-/get-byte-0.0.0.tgz#e8013923a21f8724dd77f06f916a07ae93f325ed" + integrity sha512-dFdDPbQR4zCgSxsmMEjVrLHtvSdTVqb2JUfcZ8KYhXUQD8eiE7uTgeHzse5K479Kd8IIRt6503+2gmQhahvJyg== + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +glob@^7.1.3: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6: + version "4.2.10" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +help@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/help/-/help-3.0.2.tgz#96e190d4a0a453b89c2cb4b05ab38e3a8f9fdadd" + integrity sha1-luGQ1KCkU7icLLSwWrOOOo+f2t0= + +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + +is-buffer@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" + integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== + +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + +is-docker@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" + integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-wsl@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +klaw-sync@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/klaw-sync/-/klaw-sync-6.0.0.tgz#1fd2cfd56ebb6250181114f0a581167099c2b28c" + integrity sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ== + dependencies: + graceful-fs "^4.1.11" + +kuler@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" + integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== + +logform@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/logform/-/logform-2.2.0.tgz#40f036d19161fc76b68ab50fdc7fe495544492f2" + integrity sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg== + dependencies: + colors "^1.2.1" + fast-safe-stringify "^2.0.4" + fecha "^4.2.0" + ms "^2.1.1" + triple-beam "^1.3.0" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + +micromatch@^4.0.2: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +mime-db@1.45.0: + version "1.45.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.45.0.tgz#cceeda21ccd7c3a745eba2decd55d4b73e7879ea" + integrity sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w== + +mime-db@1.49.0: + version "1.49.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.49.0.tgz#f3dfde60c99e9cf3bc9701d687778f537001cbed" + integrity sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA== + +mime-types@^2.1.12, mime-types@^2.1.18, mime-types@~2.1.19: + version "2.1.28" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.28.tgz#1160c4757eab2c5363888e005273ecf79d2a0ecd" + integrity sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ== + dependencies: + mime-db "1.45.0" + +mime-types@~2.1.24: + version "2.1.32" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.32.tgz#1d00e89e7de7fe02008db61001d9e02852670fd5" + integrity sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A== + dependencies: + mime-db "1.49.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +minimatch@^3.0.4: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.0: + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== + +moment@^2.11.2: + version "2.29.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" + integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-hash@^2.0.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" + integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +one-time@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45" + integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g== + dependencies: + fn.name "1.x.x" + +open@^7.4.2: + version "7.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" + integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== + dependencies: + is-docker "^2.0.0" + is-wsl "^2.1.1" + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +patch-package@^6.4.7: + version "6.4.7" + resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-6.4.7.tgz#2282d53c397909a0d9ef92dae3fdeb558382b148" + integrity sha512-S0vh/ZEafZ17hbhgqdnpunKDfzHQibQizx9g8yEf5dcVk3KOflOfdufRXQX8CSEkyOQwuM/bNz1GwKvFj54kaQ== + dependencies: + "@yarnpkg/lockfile" "^1.1.0" + chalk "^2.4.2" + cross-spawn "^6.0.5" + find-yarn-workspace-root "^2.0.0" + fs-extra "^7.0.1" + is-ci "^2.0.0" + klaw-sync "^6.0.0" + minimist "^1.2.0" + open "^7.4.2" + rimraf "^2.6.3" + semver "^5.6.0" + slash "^2.0.0" + tmp "^0.0.33" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +postinstall-postinstall@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postinstall-postinstall/-/postinstall-postinstall-2.1.0.tgz#4f7f77441ef539d1512c40bd04c71b06a4704ca3" + integrity sha512-7hQX6ZlZXIoRiWNrbMQaLzUUfH+sSx39u8EJ9HYuDc1kLo9IXKWjM5RSquZN1ad5GnH8CGFM78fsAAQi3OKEEQ== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +proxy-addr@~2.0.5: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +qs@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + dependencies: + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" + unpipe "1.0.0" + +readable-stream@^2.3.7: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.4.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +request@^2.88.0: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sax@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + +semver@^5.5.0, semver@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= + dependencies: + is-arrayish "^0.3.1" + +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +stack-trace@0.0.x: + version "0.0.10" + resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" + integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= + +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +text-hex@1.0.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" + integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +to-byte-array@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/to-byte-array/-/to-byte-array-1.0.2.tgz#da3c121ff6c7eb56e19567f8568b1f351ac842d8" + integrity sha512-3Owdexa2azV7gAW5ZlSqp5646oqOOf3EZ3JsbX6AtIdpA0h3y8YVMS70NwMYzMJdZdgMnXW6UIetkeyIWSycrw== + dependencies: + base64url "^3.0.1" + is-buffer "^2.0.3" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +triple-beam@^1.2.0, triple-beam@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" + integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +type-is@~1.6.17, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +webdav-server@2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/webdav-server/-/webdav-server-2.6.2.tgz#5ea39b269aa34a1512c150e29c1b7e27d3a68908" + integrity sha512-0iHdrOzlKGFD96bTvPF8IIEfxw9Q7jB5LqWqhjyBYsofD6T6mOYqWtAvR88VY9Mq0xeg8bCRHC2Vifc9iuTYuw== + dependencies: + mime-types "^2.1.18" + xml-js-builder "^1.0.3" + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +winston-daily-rotate-file@^4.5.5: + version "4.5.5" + resolved "https://registry.yarnpkg.com/winston-daily-rotate-file/-/winston-daily-rotate-file-4.5.5.tgz#cfa3a89f4eb0e4126917592b375759b772bcd972" + integrity sha512-ds0WahIjiDhKCiMXmY799pDBW+58ByqIBtUcsqr4oDoXrAI3Zn+hbgFdUxzMfqA93OG0mPLYVMiotqTgE/WeWQ== + dependencies: + file-stream-rotator "^0.5.7" + object-hash "^2.0.1" + triple-beam "^1.3.0" + winston-transport "^4.4.0" + +winston-transport@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.4.0.tgz#17af518daa690d5b2ecccaa7acf7b20ca7925e59" + integrity sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw== + dependencies: + readable-stream "^2.3.7" + triple-beam "^1.2.0" + +winston@^3.2.1: + version "3.3.3" + resolved "https://registry.yarnpkg.com/winston/-/winston-3.3.3.tgz#ae6172042cafb29786afa3d09c8ff833ab7c9170" + integrity sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw== + dependencies: + "@dabh/diagnostics" "^2.0.2" + async "^3.1.0" + is-stream "^2.0.0" + logform "^2.2.0" + one-time "^1.0.0" + readable-stream "^3.4.0" + stack-trace "0.0.x" + triple-beam "^1.3.0" + winston-transport "^4.4.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +xml-js-builder@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/xml-js-builder/-/xml-js-builder-1.0.3.tgz#91d275cb926f9dc4167f029357a5b2875b5d3894" + integrity sha512-BoLgG/glT45M0jK5PGh9h+iGrQxa8jJk9ofR63GroRifl2tbGB3/yYiVY3wQWHrZgWWfl9+7fhEB/VoD9mWnSg== + dependencies: + xml-js "^1.6.2" + +xml-js@^1.6.2: + version "1.6.11" + resolved "https://registry.yarnpkg.com/xml-js/-/xml-js-1.6.11.tgz#927d2f6947f7f1c19a316dd8eea3614e8b18f8e9" + integrity sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g== + dependencies: + sax "^1.2.4" diff --git a/redistributable/AjaxPro/AjaxPro.2.license b/redistributable/AjaxPro/AjaxPro.2.license index d4c2bf446..fd71fe580 100644 --- a/redistributable/AjaxPro/AjaxPro.2.license +++ b/redistributable/AjaxPro/AjaxPro.2.license @@ -1,20 +1,21 @@ -Copyright (c) 2006, Michael Schwarz +MIT License -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or -sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: +Copyright (c) 2019 Michael Schwarz -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR -ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/redistributable/AjaxPro/AjaxPro.csproj b/redistributable/AjaxPro/AjaxPro.csproj index fe626c775..27e492094 100644 --- a/redistributable/AjaxPro/AjaxPro.csproj +++ b/redistributable/AjaxPro/AjaxPro.csproj @@ -253,10 +253,6 @@ - - - - Code @@ -267,7 +263,7 @@ Code - + Code @@ -287,10 +283,6 @@ - - - - \ No newline at end of file diff --git a/redistributable/AjaxPro/AssemblyInfo.cs b/redistributable/AjaxPro/AssemblyInfo.cs index 79ecc1c27..0361423fc 100644 --- a/redistributable/AjaxPro/AssemblyInfo.cs +++ b/redistributable/AjaxPro/AssemblyInfo.cs @@ -1,7 +1,7 @@ /* * AssemblyInfo.cs * - * Copyright 2009 Michael Schwarz (http://www.ajaxpro.info). + * Copyright © 2009 Michael Schwarz (http://www.ajaxpro.info). * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person @@ -24,13 +24,11 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* Developers of Ajax.NET Professional (AjaxPro) - * MS Michael Schwarz info@schwarz-interactive.de + * MS Michael Schwarz * TB Tim Byng * MR Matthew Raymer * * - * - * * MS 06-04-03 fixed missing http error status code in core.js * MS 06-04-04 added AjaxPro.onError, onTimeout, onStateChanged, onLoading to core.js * MS 06-04-05 removed Object.prototype.extend from prototype.js and all othere files using this @@ -39,7 +37,10 @@ * MS 06-04-25 added ComVisible and CLSCompliant attribute * MS 06-06-11 added ReflectionPermission attribute * MS 06-07-19 removed ReflectionPermission attribute (why did we add it?) - * + * MS 21-10-30 added contentSecurityPolicy to specify a nonce for all scripts + * MS 21-11-22 changed default behavior of passing types during deserialization to deny + * MS 21-11-29 added check for custom type deserialization + * MS 21-11-30 changed error message for unsupported deserialization types in debug mode * */ using System; @@ -70,7 +71,7 @@ using System.Runtime.CompilerServices; [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Michael Schwarz")] [assembly: AssemblyProduct("Ajax.NET Professional")] -[assembly: AssemblyCopyright("2009, Michael Schwarz")] +[assembly: AssemblyCopyright("2021, Michael Schwarz")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: System.Security.AllowPartiallyTrustedCallersAttribute()] @@ -91,7 +92,7 @@ using System.Runtime.CompilerServices; // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: -[assembly: AssemblyVersion("2.9.17.2")] // do not remove the blanks!!!! +[assembly: AssemblyVersion(AjaxPro.Constant.AssemblyVersion)] // // In order to sign your assembly you must specify a key to use. Refer to the @@ -127,4 +128,4 @@ using System.Runtime.CompilerServices; #else [assembly: AssemblyKeyFile("michael.schwarz.snk")] #endif -#endif \ No newline at end of file +#endif diff --git a/redistributable/AjaxPro/Configuration/AjaxSettingsSectionHandler.cs b/redistributable/AjaxPro/Configuration/AjaxSettingsSectionHandler.cs index 7e30f686a..7f4324828 100644 --- a/redistributable/AjaxPro/Configuration/AjaxSettingsSectionHandler.cs +++ b/redistributable/AjaxPro/Configuration/AjaxSettingsSectionHandler.cs @@ -34,6 +34,8 @@ * MS 07-04-24 added new settings (oldStyle == configuration) * added provider settings * added includeTypeProperty + * MS 21-10-27 added allowed customized types for JSON deserialization + * MS 21-10-30 added contentSecurityPolicy to specify a nonce for all scripts * * */ @@ -154,6 +156,33 @@ namespace AjaxPro if (n.SelectSingleNode("@enabled") != null && n.SelectSingleNode("@enabled").InnerText == "true") settings.DebugEnabled = true; } + else if(n.Name == "contentSecurityPolicy") + { + var a = n.SelectSingleNode("@nonce"); + if (a != null && !string.IsNullOrEmpty(a.InnerText)) + { + // TODO: check if that's a valid nonce + settings.ContentSecurityPolicyNonce = a.InnerText; + } + } + else if (n.Name == "jsonDeserializationCustomTypes") + { + settings.IsCustomTypesDeserializationDisabled = n.Attributes["default"] == null || n.Attributes["default"].InnerText.ToLower() != "allow"; + + foreach (XmlNode sn in n.ChildNodes) + { + switch (sn.Name) + { + case "allow": + settings.JsonDeserializationCustomTypesAllowed.Add(sn.InnerText); + break; + + case "deny": + settings.JsonDeserializationCustomTypesDenied.Add(sn.InnerText); + break; + } + } + } else if (n.Name == "oldStyle" || n.Name == "configuration") { foreach (XmlNode sn in n.ChildNodes) diff --git a/redistributable/AjaxPro/Handler/AjaxProcHelper.cs b/redistributable/AjaxPro/Handler/AjaxProcHelper.cs index 7437d70a9..b2ade167a 100644 --- a/redistributable/AjaxPro/Handler/AjaxProcHelper.cs +++ b/redistributable/AjaxPro/Handler/AjaxProcHelper.cs @@ -36,6 +36,8 @@ * MS 06-06-11 removed WebEvent because of SecurityPermissions not available in medium trust environments * MS 06-10-04 set UTF-8 encoding for XML documents * MS 07-04-24 fixed Ajax token + * MS 21-10-27 added allowed customized types for JSON deserialization + * * */ using System; @@ -43,7 +45,8 @@ using System.Reflection; using System.Web; using System.Web.Caching; using System.IO; -#if(NET20) +using System.Collections.Generic; +#if (NET20) using System.Web.Management; using System.Diagnostics; #endif @@ -115,25 +118,6 @@ namespace AjaxPro object[] po = null; object res = null; - #region Retreive Parameters from the HTTP Request - - try - { - // The IAjaxProcessor will read the values either form the - // request URL or the request input stream. - - po = p.RetreiveParameters(); - } - catch(Exception ex) - { - p.SerializeObject(ex); - - if(p.Context.Trace.IsEnabled) p.Context.Trace.Write(Constant.AjaxID, "End ProcessRequest"); - return; - } - - #endregion - // Check if we have the same request already in our cache. The // cacheKey will be the type and a hashcode from the parameter values. @@ -151,6 +135,23 @@ namespace AjaxPro return; } + #region Retreive Parameters from the HTTP Request + + try + { + // The IAjaxProcessor will read the values either form the + // request URL or the request input stream. + + po = p.RetreiveParameters(); + } + catch (Exception ex) + { + ReturnException(ex); + return; + } + + #endregion + #region Reflection part of Ajax.NET try @@ -331,5 +332,13 @@ namespace AjaxPro winctx.Undo(); } } + + private void ReturnException(Exception ex) + { + p.SerializeObject(ex); + + if (p.Context.Trace.IsEnabled) p.Context.Trace.Write(Constant.AjaxID, "End ProcessRequest"); + return; + } } } diff --git a/redistributable/AjaxPro/Handler/AjaxProcessors/IFrameProcessor.cs b/redistributable/AjaxPro/Handler/AjaxProcessors/IFrameProcessor.cs index fe85a2c80..c9ac889ae 100644 --- a/redistributable/AjaxPro/Handler/AjaxProcessors/IFrameProcessor.cs +++ b/redistributable/AjaxPro/Handler/AjaxProcessors/IFrameProcessor.cs @@ -42,6 +42,8 @@ * MS 06-07-20 removed the fix above and put it to JavaScriptConverter * MS 06-10-03 fixed bug with CryptProvider * MS 07-03-24 fixed Ajax token bug + * MS 21-10-30 added contentSecurityPolicy to specify a nonce for all scripts + * * */ using System; @@ -144,7 +146,7 @@ namespace AjaxPro System.Text.StringBuilder sb = new System.Text.StringBuilder(); - sb.Append("\r\n\r\n"); diff --git a/redistributable/AjaxPro/Handler/AjaxProcessors/XmlHttpRequestProcessor.cs b/redistributable/AjaxPro/Handler/AjaxProcessors/XmlHttpRequestProcessor.cs index e50f8ea81..8bafc93da 100644 --- a/redistributable/AjaxPro/Handler/AjaxProcessors/XmlHttpRequestProcessor.cs +++ b/redistributable/AjaxPro/Handler/AjaxProcessors/XmlHttpRequestProcessor.cs @@ -188,8 +188,6 @@ namespace AjaxPro } } - private MethodInfo m_MethodInfo = null; - /// /// Gets the ajax method. /// @@ -198,23 +196,15 @@ namespace AjaxPro { get { - if (m_MethodInfo!=null) - { - return m_MethodInfo; - } - string m = context.Request.Headers["X-" + Constant.AjaxID + "-Method"]; - if (string.IsNullOrEmpty(m)) + if (m == null || m.Length == 0) m = context.Request["X-" + Constant.AjaxID + "-Method"]; - if (!string.IsNullOrEmpty(m)) - { - m_MethodInfo = GetMethodInfo(m); - return m_MethodInfo; - } + if(m != null && m.Length > 0) + return this.GetMethodInfo(m); - return null; + return null; } } diff --git a/redistributable/AjaxPro/Handler/ConverterJavaScriptHandler.cs b/redistributable/AjaxPro/Handler/ConverterJavaScriptHandler.cs index d882da9ef..e3e69c781 100644 --- a/redistributable/AjaxPro/Handler/ConverterJavaScriptHandler.cs +++ b/redistributable/AjaxPro/Handler/ConverterJavaScriptHandler.cs @@ -93,7 +93,7 @@ namespace AjaxPro } } - etag = MD5Helper.GetHash(System.Text.Encoding.Default.GetBytes("converter")); + etag = Hash5Helper.GetHash(System.Text.Encoding.Default.GetBytes("converter")); DateTime now = DateTime.Now; DateTime lastMod = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second); //.ToUniversalTime(); @@ -105,7 +105,7 @@ namespace AjaxPro context.Response.Cache.SetLastModified(lastMod); context.Response.Write(@"//-------------------------------------------------------------- -// Copyright (C) 2006 Michael Schwarz (http://www.ajaxpro.info). +// Copyright (C) 2021 Michael Schwarz (http://www.ajaxpro.info). // All rights reserved. //-------------------------------------------------------------- // Converter.js diff --git a/redistributable/AjaxPro/Handler/EmbeddedJavaScriptHandler.cs b/redistributable/AjaxPro/Handler/EmbeddedJavaScriptHandler.cs index 908af1140..1126b76be 100644 --- a/redistributable/AjaxPro/Handler/EmbeddedJavaScriptHandler.cs +++ b/redistributable/AjaxPro/Handler/EmbeddedJavaScriptHandler.cs @@ -104,7 +104,7 @@ namespace AjaxPro } } - etag = MD5Helper.GetHash(System.Text.Encoding.Default.GetBytes(fileName)); + etag = Hash5Helper.GetHash(System.Text.Encoding.Default.GetBytes(fileName)); DateTime now = DateTime.Now; DateTime lastMod = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second); //.ToUniversalTime(); @@ -116,7 +116,7 @@ namespace AjaxPro context.Response.Cache.SetLastModified(lastMod); context.Response.Write(@"//-------------------------------------------------------------- -// Copyright (C) 2006 Michael Schwarz (http://www.ajaxpro.info). +// Copyright (C) 2021 Michael Schwarz (http://www.ajaxpro.info). // All rights reserved. //-------------------------------------------------------------- @@ -138,15 +138,17 @@ namespace AjaxPro if (s != null) { - System.IO.StreamReader sr = new System.IO.StreamReader(s); + string content = ""; + + using (System.IO.StreamReader sr = new System.IO.StreamReader(s)) + { + content = sr.ReadToEnd(); + } context.Response.Write("// " + files[i] + ".js\r\n"); - - context.Response.Write(sr.ReadToEnd()); + context.Response.Write(content.Replace("{AssemblyVersion}", Constant.AssemblyVersion)); context.Response.Write("\r\n"); - sr.Close(); - if (files[i] == "prototype" && AjaxPro.Utility.Settings.OldStyle.Contains("objectExtendPrototype")) { context.Response.Write(@" diff --git a/redistributable/AjaxPro/Handler/TypeJavaScriptHandler.cs b/redistributable/AjaxPro/Handler/TypeJavaScriptHandler.cs index 438cf2a3e..b60789bbf 100644 --- a/redistributable/AjaxPro/Handler/TypeJavaScriptHandler.cs +++ b/redistributable/AjaxPro/Handler/TypeJavaScriptHandler.cs @@ -151,7 +151,7 @@ namespace AjaxPro } etag = type.AssemblyQualifiedName; - etag = MD5Helper.GetHash(System.Text.Encoding.Default.GetBytes(etag)); + etag = Hash5Helper.GetHash(System.Text.Encoding.Default.GetBytes(etag)); DateTime now = DateTime.Now; DateTime lastMod = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second); // .ToUniversalTime(); diff --git a/redistributable/AjaxPro/JSON/Converters/DataTableConverter.cs b/redistributable/AjaxPro/JSON/Converters/DataTableConverter.cs index eb2e8e962..301b00c59 100644 --- a/redistributable/AjaxPro/JSON/Converters/DataTableConverter.cs +++ b/redistributable/AjaxPro/JSON/Converters/DataTableConverter.cs @@ -33,7 +33,7 @@ * MS 06-09-26 improved performance using StringBuilder * MS 07-04-24 added renderJsonCompliant serialization * MS 08-03-21 fixed DataTable client-side script - * + * MS 21-11-29 added check for custom type deserialization * * */ @@ -147,7 +147,10 @@ namespace AjaxPro for (int i = 0; i < columns.Count; i++) { column = (JavaScriptArray)columns[i]; + colType = Type.GetType(column[1].ToString(), true); + JavaScriptDeserializer.ThrowExceptionIfNotCustomTypeDeserializationAllowed(colType); + dt.Columns.Add(column[0].ToString(), colType); } diff --git a/redistributable/AjaxPro/JSON/Converters/HashtableConverter.cs b/redistributable/AjaxPro/JSON/Converters/HashtableConverter.cs index 2dc731f86..7cd5bc1dd 100644 --- a/redistributable/AjaxPro/JSON/Converters/HashtableConverter.cs +++ b/redistributable/AjaxPro/JSON/Converters/HashtableConverter.cs @@ -32,7 +32,7 @@ * MS 06-09-24 use QuoteString instead of Serialize * MS 06-09-26 improved performance using StringBuilder * MS 07-04-24 added renderJsonCompliant serialization - * + * MS 21-11-29 added check for custom type deserialization * */ using System; @@ -86,8 +86,15 @@ namespace AjaxPro for (int i = 0; i < a.Count; i++) { aa = (JavaScriptArray)a[i]; - key = JavaScriptDeserializer.Deserialize((IJavaScriptObject)aa[0], Type.GetType(((JavaScriptString)aa[2]).ToString())); - value = JavaScriptDeserializer.Deserialize((IJavaScriptObject)aa[1], Type.GetType(((JavaScriptString)aa[3]).ToString())); + + Type keyType = Type.GetType(((JavaScriptString)aa[2]).ToString()); + Type valueType = Type.GetType(((JavaScriptString)aa[3]).ToString()); + + JavaScriptDeserializer.ThrowExceptionIfNotCustomTypeDeserializationAllowed(keyType); + JavaScriptDeserializer.ThrowExceptionIfNotCustomTypeDeserializationAllowed(valueType); + + key = JavaScriptDeserializer.Deserialize((IJavaScriptObject)aa[0], keyType); + value = JavaScriptDeserializer.Deserialize((IJavaScriptObject)aa[1], valueType); d.Add(key, value); } diff --git a/redistributable/AjaxPro/JSON/Converters/HtmlControlConverter.cs b/redistributable/AjaxPro/JSON/Converters/HtmlControlConverter.cs index 1a7595ef5..780f25bd3 100644 --- a/redistributable/AjaxPro/JSON/Converters/HtmlControlConverter.cs +++ b/redistributable/AjaxPro/JSON/Converters/HtmlControlConverter.cs @@ -26,6 +26,8 @@ /* * MS 06-05-23 using local variables instead of "new Type()" for get De-/SerializableTypes * MS 06-09-26 improved performance using StringBuilder + * MS 21-11-29 added check for custom type deserialization + * * * */ @@ -171,12 +173,14 @@ namespace AjaxPro if(!typeof(HtmlControl).IsAssignableFrom(type)) throw new InvalidCastException("The target type is not a HtmlControlType"); + JavaScriptDeserializer.ThrowExceptionIfNotCustomTypeDeserializationAllowed(type); + html = AddRunAtServer(html, (Activator.CreateInstance(type) as HtmlControl).TagName); if(type.IsAssignableFrom(typeof(HtmlSelect))) html = CorrectAttributes(html); - Control o = HtmlControlConverterHelper.Parse(html);; + Control o = HtmlControlConverterHelper.Parse(html); if(o.GetType() == type) return (o as HtmlControl); diff --git a/redistributable/AjaxPro/JSON/JavaScriptDeserializer.cs b/redistributable/AjaxPro/JSON/JavaScriptDeserializer.cs index 6933bf90a..7f7421d86 100644 --- a/redistributable/AjaxPro/JSON/JavaScriptDeserializer.cs +++ b/redistributable/AjaxPro/JSON/JavaScriptDeserializer.cs @@ -36,13 +36,16 @@ * MS 06-05-30 changed to new converter usage * MS 06-07-11 added generic method for DeserializeFromJson * MS 06-09-26 improved performance removing three-times cast - * + * MS 21-10-27 added allowed customized types for JSON deserialization + * MS 21-11-22 changed error message when type is not allowed + * MS 21-11-29 added check for custom type deserialization * */ using System; using System.Text; using System.Reflection; using System.Collections; +using System.Security; namespace AjaxPro { @@ -142,10 +145,12 @@ namespace AjaxPro { Type t = Type.GetType(jso["__type"].ToString()); if (type == null || type.IsAssignableFrom(t)) + { type = t; + ThrowExceptionIfNotCustomTypeDeserializationAllowed(type); + } } - IJavaScriptConverter c = null; #if(NET20) if (Utility.Settings.DeserializableConverters.TryGetValue(type, out c)) @@ -204,6 +209,51 @@ namespace AjaxPro #region Internal Methods + internal static void ThrowExceptionIfNotCustomTypeDeserializationAllowed(Type type) + { + SecurityException ex = null; + if (!IsCustomTypeDeserializationAllowed(type, out ex) && ex != null) + throw ex; + } + + internal static bool IsCustomTypeDeserializationAllowed(Type type, out SecurityException ex) + { + ex = null; + + // allow all primitive and basic types + if (type.IsPrimitive || type == typeof(string) || type == typeof(DateTime) || type == typeof(TimeSpan) || type == typeof(decimal)) + return true; + + if (AjaxPro.Utility.Settings.IsCustomTypesDeserializationDisabled) + { + bool isCustomTypeAllowed = false; + + foreach (var s in AjaxPro.Utility.Settings.JsonDeserializationCustomTypesAllowed) + if ((s.EndsWith("*") && type.FullName.StartsWith(s.Substring(0, s.Length - 1), StringComparison.InvariantCultureIgnoreCase)) || s == type.FullName) + { + isCustomTypeAllowed = true; + break; + } + + if (!isCustomTypeAllowed) + { + ex = new SecurityException(AjaxPro.Utility.Settings.DebugEnabled ? "The type '" + type.Name + "' is not allowed to be deserialized." : "At least one type passed is not allowed to be deserialized."); + return false; + } + } + else + { + foreach (var s in AjaxPro.Utility.Settings.JsonDeserializationCustomTypesDenied) + if ((s.EndsWith("*") && type.FullName.StartsWith(s.Substring(0, s.Length - 1), StringComparison.InvariantCultureIgnoreCase)) || s == type.FullName) + { + ex = new SecurityException(AjaxPro.Utility.Settings.DebugEnabled ? "The type '" + type.Name + "' is not allowed to be deserialized." : "At least one type passed is not allowed to be deserialized."); + return false; + } + } + + return true; + } + /// /// Deserializes the custom object. /// @@ -214,10 +264,6 @@ namespace AjaxPro { object c = Activator.CreateInstance(type); - // TODO: is this a security problem? - // if(o.GetType().GetCustomAttributes(typeof(AjaxClassAttribute), true).Length == 0) - // throw new System.Security.SecurityException("Could not create class '" + type.FullName + "' because of missing AjaxClass attribute."); - MemberInfo[] members = type.GetMembers(BindingFlags.GetField | BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance); foreach (MemberInfo memberInfo in members) { diff --git a/redistributable/AjaxPro/Services/AuthenticationService.cs b/redistributable/AjaxPro/Services/AuthenticationService.cs deleted file mode 100644 index edfa98ea8..000000000 --- a/redistributable/AjaxPro/Services/AuthenticationService.cs +++ /dev/null @@ -1,89 +0,0 @@ -/* - * AuthenticationService.cs - * - * Copyright 2007 Michael Schwarz (http://www.ajaxpro.info). - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -/* - * MS 05-12-20 initial version - * MS 06-04-16 changed methods to static - * - * - * - * - */ -using System; -using System.Web.Security; - -namespace AjaxPro.Services -{ - [AjaxNamespace("AjaxPro.Services.Authentication")] - public class AuthenticationService - { - /// - /// Logins the specified username. - /// - /// The username. - /// The password. - /// - [AjaxMethod] - public static bool Login(string username, string password) - { -#if(NET20) - if(Membership.Provider.ValidateUser(username, password)) -#else - if(FormsAuthentication.Authenticate(username, password)) -#endif - { - FormsAuthentication.SetAuthCookie(username, false); - return true; - } - - return false; - } - - /// - /// Logouts this instance. - /// - [AjaxMethod] - public static void Logout() - { - FormsAuthentication.SignOut(); - } - - /// - /// Validates the user. - /// - /// The username. - /// The password. - /// - [AjaxMethod] - public static bool ValidateUser(string username, string password) - { -#if(NET20) - return Membership.Provider.ValidateUser(username, password); -#else - throw new NotImplementedException("ValidateUser is not yet implemented."); -#endif - } - } -} diff --git a/redistributable/AjaxPro/Services/CartService.cs b/redistributable/AjaxPro/Services/CartService.cs deleted file mode 100644 index f51e7d950..000000000 --- a/redistributable/AjaxPro/Services/CartService.cs +++ /dev/null @@ -1,61 +0,0 @@ -/* - * CartService.cs - * - * Copyright 2007 Michael Schwarz (http://www.ajaxpro.info). - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#if(NET20) -/* - * MS 06-04-16 initial version - * - * - * - * - * - */ -using System; -using System.Text; - -namespace AjaxPro.Services -{ - [AjaxNamespace("AjaxPro.Services.Cart")] - public abstract class ICartService - { - /// - /// Adds the item. - /// - /// Name of the cart. - /// The item. - /// - [AjaxMethod] - public abstract bool AddItem(string cartName, object item); - - /// - /// Gets the items. - /// - /// Name of the cart. - /// - [AjaxMethod] - public abstract object[] GetItems(string cartName); - } -} -#endif \ No newline at end of file diff --git a/redistributable/AjaxPro/Services/ChatService.cs b/redistributable/AjaxPro/Services/ChatService.cs deleted file mode 100644 index ebe668ca6..000000000 --- a/redistributable/AjaxPro/Services/ChatService.cs +++ /dev/null @@ -1,71 +0,0 @@ -/* - * ChatService.cs - * - * Copyright 2007 Michael Schwarz (http://www.ajaxpro.info). - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#if(NET20) -/* - * MS 06-04-16 initial version - * - * - * - * - * - */ -using System; -using System.Text; - -namespace AjaxPro.Services -{ - [AjaxNamespace("AjaxPro.Services.Chat")] - public abstract class IChatService - { - /// - /// Sends the message. - /// - /// The room. - /// The message. - /// - [AjaxMethod] - public abstract bool SendMessage(string room, string message); - - /// - /// Retrieves the new. - /// - /// The room. - /// The last retreived. - /// - [AjaxMethod] - public abstract object[] RetrieveNew(string room, DateTime lastRetreived); - - /// - /// Retrieves the last. - /// - /// The room. - /// The count. - /// - [AjaxMethod] - public abstract object[] RetrieveLast(string room, int count); - } -} -#endif \ No newline at end of file diff --git a/redistributable/AjaxPro/Services/ProfileService.cs b/redistributable/AjaxPro/Services/ProfileService.cs deleted file mode 100644 index 47fac2b35..000000000 --- a/redistributable/AjaxPro/Services/ProfileService.cs +++ /dev/null @@ -1,91 +0,0 @@ -/* - * ProfileService.cs - * - * Copyright 2007 Michael Schwarz (http://www.ajaxpro.info). - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#if(NET20) -/* - * MS 05-12-20 initial version - * MS 06-04-16 changed methods to static - * MS 06-04-25 changed GetProfile, added ProfileBaseConverter - * - * - * - */ -using System; -using System.Data; -using System.Collections; -using System.Configuration; -using System.Web; -using System.Web.Profile; - -namespace AjaxPro.Services -{ - [AjaxNamespace("AjaxPro.Services.Profile")] - public class ProfileService - { - /// - /// Gets the profile. - /// - /// - [AjaxMethod] - public static ProfileBase GetProfile() - { - return HttpContext.Current.Profile; - } - - /// - /// Gets the profile property. - /// - /// The property. - /// - [AjaxMethod] - public static object GetProfileProperty(string property) - { - ProfileBase profile = HttpContext.Current.Profile; - if (profile == null) - { - return null; - } - return profile[property]; - } - - /// - /// Sets the profile. - /// - /// The o. - /// - [AjaxMethod] - public static bool SetProfile(JavaScriptObject o) - { - ProfileBase profile = HttpContext.Current.Profile; - foreach (string key in o.Keys) - { - profile[key] = JavaScriptDeserializer.Deserialize((IJavaScriptObject)o[key], profile[key].GetType()); - } - - return true; - } - } -} -#endif \ No newline at end of file diff --git a/redistributable/AjaxPro/Utilities/AjaxSettings.cs b/redistributable/AjaxPro/Utilities/AjaxSettings.cs index 209fc1fbb..c8b43d5e0 100644 --- a/redistributable/AjaxPro/Utilities/AjaxSettings.cs +++ b/redistributable/AjaxPro/Utilities/AjaxSettings.cs @@ -34,6 +34,10 @@ * added UseSimpleObjectNaming * using new AjaxSecurityProvider * fixed Ajax token + * MS 21-10-27 added allowed customized types for JSON deserialization + * MS 21-10-30 added contentSecurityPolicy to specify a nonce for all scripts + * MS 21-11-22 changed to set the default behavior to not allow custom types + * * */ using System; @@ -127,6 +131,12 @@ namespace AjaxPro SerializableConverters = new JavaScriptConverterList(); DeserializableConverters = new JavaScriptConverterList(); #endif + + JsonDeserializationCustomTypesAllowed = new List(); + JsonDeserializationCustomTypesDenied = new List(); + + // disable all custom types by default, either add allow list (or not recommended change default to 'allow') + IsCustomTypesDeserializationDisabled = true; } #region Public Properties @@ -244,6 +254,21 @@ namespace AjaxPro set{ m_ScriptReplacements = value; } } + public bool IsCustomTypesDeserializationDisabled { get; set; } + + public List JsonDeserializationCustomTypesAllowed { get; set; } + public List JsonDeserializationCustomTypesDenied { get; set; } + + public string ContentSecurityPolicyNonce { get; set; } + + public string AppendContentSecurityPolicyNonce() + { + if (!string.IsNullOrEmpty(ContentSecurityPolicyNonce)) + return " nounce=\"" + ContentSecurityPolicyNonce + "\""; + + return ""; + } + #endregion } #endif diff --git a/redistributable/AjaxPro/Utilities/Constant.cs b/redistributable/AjaxPro/Utilities/Constant.cs index 0212b2b2b..13589c2d6 100644 --- a/redistributable/AjaxPro/Utilities/Constant.cs +++ b/redistributable/AjaxPro/Utilities/Constant.cs @@ -52,6 +52,6 @@ namespace AjaxPro /// /// The assembly version. /// - public const string AssemblyVersion = "9.2.17.1"; + public const string AssemblyVersion = "21.12.22.2"; } } diff --git a/redistributable/AjaxPro/Utilities/HashHelper.cs b/redistributable/AjaxPro/Utilities/HashHelper.cs new file mode 100644 index 000000000..8e12dea0f --- /dev/null +++ b/redistributable/AjaxPro/Utilities/HashHelper.cs @@ -0,0 +1,67 @@ +/* + * MD5Helper.cs + * + * Copyright 2007 Michael Schwarz (http://www.ajaxpro.info). + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +/* + * MS 07-04-12 changed MD5 compute hash (using BitConverter, now) + * MS 21-12-22 changed hash algorithm from MD5 to SHA256 + * + */ +using System; +using System.Text; +using System.Security.Cryptography; + +namespace AjaxPro +{ + /// + /// Provides methods to get a MD5 hash from a string or byte array. + /// + public class Hash5Helper + { + /// + /// Gets the hash. + /// + /// The data. + /// + public static string GetHash(string data) + { + byte[] b = System.Text.Encoding.Default.GetBytes(data); + + return GetHash(b); + } + + /// + /// Gets the hash. + /// + /// The data. + /// + public static string GetHash(byte[] data) + { + // This is one implementation of the abstract class MD5. + MD5 md5 = new MD5CryptoServiceProvider(); + + return BitConverter.ToString(new SHA256Managed().ComputeHash(data)).Replace("-", String.Empty); + } + } +} diff --git a/redistributable/AjaxPro/Utilities/MD5Helper.cs b/redistributable/AjaxPro/Utilities/MD5Helper.cs deleted file mode 100644 index 8be811aa2..000000000 --- a/redistributable/AjaxPro/Utilities/MD5Helper.cs +++ /dev/null @@ -1,67 +0,0 @@ -/* - * MD5Helper.cs - * - * Copyright 2007 Michael Schwarz (http://www.ajaxpro.info). - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -/* - * MS 07-04-12 changed MD5 compute hash (using BitConverter, now) - * - * - */ -using System; -using System.Text; -using System.Security.Cryptography; - -namespace AjaxPro -{ - /// - /// Provides methods to get a MD5 hash from a string or byte array. - /// - public class MD5Helper - { - /// - /// Gets the hash. - /// - /// The data. - /// - public static string GetHash(string data) - { - byte[] b = System.Text.Encoding.Default.GetBytes(data); - - return GetHash(b); - } - - /// - /// Gets the hash. - /// - /// The data. - /// - public static string GetHash(byte[] data) - { - // This is one implementation of the abstract class MD5. - MD5 md5 = new MD5CryptoServiceProvider(); - - return BitConverter.ToString(md5.ComputeHash(data)).Replace("-", String.Empty); - } - } -} diff --git a/redistributable/AjaxPro/Utilities/Utility.cs b/redistributable/AjaxPro/Utilities/Utility.cs index 94b0a4739..b7dec440e 100644 --- a/redistributable/AjaxPro/Utilities/Utility.cs +++ b/redistributable/AjaxPro/Utilities/Utility.cs @@ -47,6 +47,8 @@ * MS 07-04-24 fixed Ajax token * using new AjaxSecurityProvider * MS 09-02-17 fixed memory problem + * MS 21-10-30 added contentSecurityPolicy to specify a nonce for all scripts + * MS 21-11-29 removed HtmlControlConverter from default converters * * * @@ -163,7 +165,7 @@ namespace AjaxPro RegisterCommonAjax(page); RegisterClientScriptBlock(page, Constant.AjaxID + ".AjaxEnum." + type.FullName, - ""); + "\r\n" + JavaScriptUtil.GetEnumRepresentation(type) + "\r\n"); } @@ -237,7 +239,7 @@ namespace AjaxPro AddConverter(settings, new IEnumerableConverter()); AddConverter(settings, new DataRowConverter()); - AddConverter(settings, new HtmlControlConverter()); + //AddConverter(settings, new HtmlControlConverter()); #endregion } @@ -453,21 +455,21 @@ namespace AjaxPro if (prototypeJs.Length > 0 && coreJs.Length > 0 && convertersJs.Length > 0) { RegisterClientScriptBlock(page, Constant.AjaxID + ".core-prototype-converter", - ""); + ""); } else { if (prototypeJs.Length > 0) RegisterClientScriptBlock(page, Constant.AjaxID + ".prototype", - ""); + ""); if (coreJs.Length > 0) RegisterClientScriptBlock(page, Constant.AjaxID + ".core", - ""); + ""); if (convertersJs.Length > 0) RegisterClientScriptBlock(page, Constant.AjaxID + ".converters", - ""); + ""); } @@ -477,7 +479,7 @@ namespace AjaxPro string msJs = rootFolder + Utility.HandlerPath + "/" + Utility.GetSessionUri() + "ms" + Utility.HandlerExtension; RegisterClientScriptBlock(page, Constant.AjaxID + ".ms", - ""); + ""); } StringBuilder sb = new StringBuilder(); @@ -491,7 +493,7 @@ namespace AjaxPro if (sb.Length > 0) { RegisterClientScriptBlock(page, Constant.AjaxID + ".ajaxproinit", - "\r\n"); + "\r\n" + sb.ToString() + "\r\n"); } } diff --git a/redistributable/AjaxPro/converter.js b/redistributable/AjaxPro/converter.js deleted file mode 100644 index a9a20a280..000000000 --- a/redistributable/AjaxPro/converter.js +++ /dev/null @@ -1,178 +0,0 @@ -if (typeof Ajax == "undefined") Ajax = {}; -if (typeof Ajax.Web == "undefined") Ajax.Web = {}; -if (typeof Ajax.Web.NameValueCollection == "undefined") Ajax.Web.NameValueCollection = {}; - -Ajax.Web.NameValueCollection = function (items) { - this.__type = "System.Collections.Specialized.NameValueCollection"; - this.keys = []; - this.values = []; - - if (items != null && !isNaN(items.length)) { - for (var i = 0; i < items.length; i++) - this.add(items[i][0], items[i][1]); - } -}; -Object.extend(Ajax.Web.NameValueCollection.prototype, { - add: function (k, v) { - if (k == null || k.constructor != String || v == null || v.constructor != String) - return -1; - this.keys.push(k); - this.values.push(v); - return this.values.length - 1; - }, - containsKey: function (key) { - for (var i = 0; i < this.keys.length; i++) { - if (this.keys[i] == key) return true; - } - return false; - }, - getKeys: function () { - return this.keys; - }, - getValue: function (k) { - for (var i = 0; i < this.keys.length && i < this.values.length; i++) { - if (this.keys[i] == k) return this.values[i]; - } - return null; - }, - setValue: function (k, v) { - if (k == null || k.constructor != String || v == null || v.constructor != String) - return -1; - for (var i = 0; i < this.keys.length && i < this.values.length; i++) { - if (this.keys[i] == k) this.values[i] = v; - return i; - } - return this.add(k, v); - }, - toJSON: function () { - return AjaxPro.toJSON({ __type: this.__type, keys: this.keys, values: this.values }); - } -}, true); - -// DataSetConverter -if (typeof Ajax == "undefined") Ajax = {}; -if (typeof Ajax.Web == "undefined") Ajax.Web = {}; -if (typeof Ajax.Web.DataSet == "undefined") Ajax.Web.DataSet = {}; - -Ajax.Web.DataSet = function (t) { - this.__type = "System.Data.DataSet,System.Data"; - this.Tables = []; - this.addTable = function (t) { - this.Tables.push(t); - }; - if (t != null) { - for (var i = 0; i < t.length; i++) { - this.addTable(t[i]); - } - } -}; - -// DataTableConverter -if (typeof Ajax == "undefined") Ajax = {}; -if (typeof Ajax.Web == "undefined") Ajax.Web = {}; -if (typeof Ajax.Web.DataTable == "undefined") Ajax.Web.DataTable = {}; - -Ajax.Web.DataTable = function (c, r) { - this.__type = "System.Data.DataTable,System.Data"; - this.Columns = []; - this.Rows = []; - this.addColumn = function (name, type) { - this.Columns.push({ Name: name, __type: type }); - }; - this.toJSON = function () { - var dt = {}; - var i; - dt.Columns = []; - for (i = 0; i < this.Columns.length; i++) - dt.Columns.push([this.Columns[i].Name, this.Columns[i].__type]); - dt.Rows = []; - for (i = 0; i < this.Rows.length; i++) { - var row = []; - for (var j = 0; j < this.Columns.length; j++) - row.push(this.Rows[i][this.Columns[j].Name]); - dt.Rows.push(row); - } - return AjaxPro.toJSON(dt); - }; - this.addRow = function (row) { - this.Rows.push(row); - }; - if (c != null) { - for (var i = 0; i < c.length; i++) - this.addColumn(c[i][0], c[i][1]); - } - if (r != null) { - for (var y = 0; y < r.length; y++) { - var row = {}; - for (var z = 0; z < this.Columns.length && z < r[y].length; z++) - row[this.Columns[z].Name] = r[y][z]; - this.addRow(row); - } - } -}; - -// ProfileBaseConverter -if (typeof Ajax == "undefined") Ajax = {}; -if (typeof Ajax.Web == "undefined") Ajax.Web = {}; -if (typeof Ajax.Web.Profile == "undefined") Ajax.Web.Profile = {}; - -Ajax.Web.Profile = function () { - this.toJSON = function () { - throw "Ajax.Web.Profile cannot be converted to JSON format."; - }; - this.setProperty_callback = function (res) { - }; - this.setProperty = function (name, object) { - this[name] = object; - AjaxPro.Services.Profile.SetProfile({ name: o }, this.setProperty_callback.bind(this)); - }; -}; - -// IDictionaryConverter -if (typeof Ajax == "undefined") Ajax = {}; -if (typeof Ajax.Web == "undefined") Ajax.Web = {}; -if (typeof Ajax.Web.Dictionary == "undefined") Ajax.Web.Dictionary = {}; - -Ajax.Web.Dictionary = function (type, items) { - this.__type = type; - this.keys = []; - this.values = []; - - if (items != null && !isNaN(items.length)) { - for (var i = 0; i < items.length; i++) - this.add(items[i][0], items[i][1]); - } -}; -Object.extend(Ajax.Web.Dictionary.prototype, { - add: function (k, v) { - this.keys.push(k); - this.values.push(v); - return this.values.length - 1; - }, - containsKey: function (key) { - for (var i = 0; i < this.keys.length; i++) { - if (this.keys[i] == key) return true; - } - return false; - }, - getKeys: function () { - return this.keys; - }, - getValue: function (key) { - for (var i = 0; i < this.keys.length && i < this.values.length; i++) { - if (this.keys[i] == key) { return this.values[i]; } - } - return null; - }, - setValue: function (k, v) { - for (var i = 0; i < this.keys.length && i < this.values.length; i++) { - if (this.keys[i] == k) { this.values[i] = v; } - return i; - } - return this.add(k, v); - }, - toJSON: function () { - return AjaxPro.toJSON({ __type: this.__type, keys: this.keys, values: this.values }); - } -}, true); - diff --git a/redistributable/AjaxPro/core.js b/redistributable/AjaxPro/core.js index 3b394d647..aeeade2b2 100644 --- a/redistributable/AjaxPro/core.js +++ b/redistributable/AjaxPro/core.js @@ -1,14 +1,14 @@ Object.extend(Function.prototype, { - getArguments: function() { + getArguments: function () { var args = []; - for(var i=0; i - - BirthdayPatternResource.resx - - - BirthdayPatternResource.resx - - - BirthdayPatternResource.resx - - - BirthdayPatternResource.resx - - - BirthdayPatternResource.resx - - - BirthdayPatternResource.resx - - - BirthdayPatternResource.resx - - - BirthdayPatternResource.resx - - - BirthdayPatternResource.resx - - - BirthdayPatternResource.resx - - - BirthdayPatternResource.resx - - - BirthdayPatternResource.resx - - - BirthdayPatternResource.resx - - - Designer - PublicResXFileCodeGenerator - BirthdayPatternResource.Designer.cs - - - BirthdayPatternResource.resx - - - BirthdayPatternResource.resx - - - BirthdayPatternResource.resx - - - BirthdayPatternResource.resx - - - BirthdayPatternResource.resx - - - BirthdayPatternResource.resx - - - BirthdayPatternResource.resx - - - True - True - BirthdayPatternResource.resx - - - BirthdaysResource.resx - - - BirthdaysResource.resx - - - BirthdaysResource.resx - - - BirthdaysResource.resx - - - BirthdaysResource.resx - - - BirthdaysResource.resx - - - BirthdaysResource.resx - - - BirthdaysResource.resx - - - BirthdaysResource.resx - - - BirthdaysResource.resx - - - BirthdaysResource.resx - - - BirthdaysResource.resx - - - BirthdaysResource.resx - - - Designer - PublicResXFileCodeGenerator - BirthdaysResource.Designer.cs - - - BirthdaysResource.resx - - - BirthdaysResource.resx - - - BirthdaysResource.resx - - - BirthdaysResource.resx - - - BirthdaysResource.resx - - - BirthdaysResource.resx - - - BirthdaysResource.resx - - - True - True - BirthdaysResource.resx - PublicResXFileCodeGenerator BlogPatternsResource.Designer.cs @@ -2026,6 +1879,7 @@ Designer PublicResXFileCodeGenerator + WikiPatternResource.Designer.cs WikiPatternResource.resx @@ -2055,48 +1909,63 @@ CommunityResource.resx + Designer CommunityResource.resx + Designer CommunityResource.resx + Designer CommunityResource.resx + Designer CommunityResource.resx + Designer CommunityResource.resx + Designer CommunityResource.resx + Designer CommunityResource.resx + Designer CommunityResource.resx + Designer CommunityResource.resx + Designer CommunityResource.resx + Designer CommunityResource.resx + Designer CommunityResource.resx + Designer CommunityResource.resx + Designer CommunityResource.resx + Designer CommunityResource.resx @@ -2108,24 +1977,31 @@ CommunityResource.resx + Designer CommunityResource.resx + Designer CommunityResource.resx + Designer CommunityResource.resx + Designer CommunityResource.resx + Designer CommunityResource.resx + Designer CommunityResource.resx + Designer True @@ -2134,6 +2010,7 @@ CommunityResource.resx + Designer CommunityResource.resx @@ -2149,33 +2026,40 @@ CommunityResource.resx + Designer CommunityResource.resx CommunityResource.resx + Designer CommunityResource.resx + Designer CommunityResource.resx CommunityResource.resx + Designer CommunityResource.resx CommunityResource.resx + Designer CommunityResource.resx + Designer CommunityResource.resx + Designer CommunityResource.resx @@ -2185,12 +2069,14 @@ CommunityResource.resx + Designer CommunityResource.resx CommunityResource.resx + Designer BookmarkingBusinessResources.resx @@ -2483,51 +2369,6 @@ WikiResource.resx - - BirthdayPatternResource.resx - - - BirthdayPatternResource.resx - - - BirthdayPatternResource.resx - - - BirthdayPatternResource.resx - - - BirthdayPatternResource.resx - - - BirthdayPatternResource.resx - - - BirthdayPatternResource.resx - - - BirthdaysResource.resx - - - BirthdaysResource.resx - - - BirthdaysResource.resx - - - BirthdaysResource.resx - - - BirthdaysResource.resx - - - BirthdaysResource.resx - - - BirthdaysResource.resx - - - BirthdaysResource.resx - @@ -2548,14 +2389,6 @@ - - - - - ASPXCodeBehind - - - ASPXCodeBehind @@ -3280,7 +3113,7 @@ - 2.9.17.2 + 21.12.22.2 2.6.3.19702 @@ -3289,7 +3122,7 @@ 1.6.0.1 - 12.0.3 + 13.0.1 diff --git a/web/studio/ASC.Web.Studio/Products/Community/Controls/ButtonSidePanel.ascx.cs b/web/studio/ASC.Web.Studio/Products/Community/Controls/ButtonSidePanel.ascx.cs index f780be07b..914d9b079 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Controls/ButtonSidePanel.ascx.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Controls/ButtonSidePanel.ascx.cs @@ -43,7 +43,6 @@ namespace ASC.Web.Community.Controls protected bool IsForumsAvailable { get; set; } protected bool IsBookmarksAvailable { get; set; } protected bool IsWikiAvailable { get; set; } - protected bool IsBirthdaysAvailable { get; set; } protected bool IsAdmin { get; set; } protected bool IsVisitor { get; set; } @@ -103,9 +102,6 @@ namespace ASC.Web.Community.Controls case "community-wiki": IsWikiAvailable = true; break; - case "community-birthdays": - IsBirthdaysAvailable = true; - break; } } } diff --git a/web/studio/ASC.Web.Studio/Products/Community/Controls/NavigationSidePanel.ascx b/web/studio/ASC.Web.Studio/Products/Community/Controls/NavigationSidePanel.ascx index 12b775a85..9c881bee8 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Controls/NavigationSidePanel.ascx +++ b/web/studio/ASC.Web.Studio/Products/Community/Controls/NavigationSidePanel.ascx @@ -132,16 +132,6 @@ <% } %> - <%if (IsBirthdaysAvailable) - { %> - - <%} %> - <% if (IsAdmin && (IsForumsAvailable || IsFullAdministrator)) %> <% { %> diff --git a/web/studio/ASC.Web.Studio/Products/Community/Controls/NavigationSidePanel.ascx.cs b/web/studio/ASC.Web.Studio/Products/Community/Controls/NavigationSidePanel.ascx.cs index c988650af..402470c5a 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Controls/NavigationSidePanel.ascx.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Controls/NavigationSidePanel.ascx.cs @@ -51,7 +51,6 @@ namespace ASC.Web.Community.Controls protected bool IsForumsAvailable { get; set; } protected bool IsBookmarksAvailable { get; set; } protected bool IsWikiAvailable { get; set; } - protected bool IsBirthdaysAvailable { get; set; } protected bool IsAdmin { get; set; } protected bool IsVisitor { get; set; } @@ -120,9 +119,6 @@ namespace ASC.Web.Community.Controls case "community-wiki": IsWikiAvailable = true; break; - case "community-birthdays": - IsBirthdaysAvailable = true; - break; } } } @@ -226,10 +222,6 @@ namespace ASC.Web.Community.Controls CurrentPage = "wikihelp"; } } - else if (currentPath.IndexOf("Modules/Birthdays", StringComparison.OrdinalIgnoreCase) > 0) - { - CurrentPage = "birthdays"; - } else if (currentPath.IndexOf("Help.aspx", StringComparison.OrdinalIgnoreCase) > 0) { CurrentPage = "help"; diff --git a/web/studio/ASC.Web.Studio/Products/Community/Default.aspx.cs b/web/studio/ASC.Web.Studio/Products/Community/Default.aspx.cs index ad11f816f..5b8c8d3ef 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Default.aspx.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Default.aspx.cs @@ -111,7 +111,7 @@ namespace ASC.Web.Community keys.Sort(); return enabledList[keys[0]]; } - return "Modules/Birthdays/"; + return "Modules/Blogs/"; } } } \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Classes/BirthdaysModule.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Classes/BirthdaysModule.cs deleted file mode 100644 index a8afdd8a9..000000000 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Classes/BirthdaysModule.cs +++ /dev/null @@ -1,83 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; - -using ASC.Web.Community.Modules.Birthdays.Resources; -using ASC.Web.Community.Product; -using ASC.Web.Core; -using ASC.Web.Core.ModuleManagement; - - -namespace ASC.Web.Community.Birthdays -{ - public class BirthdaysModule : Module - { - public static readonly Guid ModuleId = WebItemManager.BirthdaysProductID; - - private static readonly object locker = new object(); - private static bool registered; - - public override Guid ID - { - get { return ModuleId; } - } - - public override Guid ProjectId - { - get { return CommunityProduct.ID; } - } - - public override string Name - { - get { return BirthdaysResource.BirthdaysModuleTitle; } - } - - public override string Description - { - get { return BirthdaysResource.BirthdaysModuleDescription; } - } - - public override string StartURL - { - get { return "~/Products/Community/Modules/Birthdays/"; } - } - - public BirthdaysModule() - { - Context = new ModuleContext - { - DefaultSortOrder = 6, - SmallIconFileName = string.Empty, - IconFileName = string.Empty, - SubscriptionManager = new BirthdaysSubscriptionManager() - }; - } - - public static void RegisterSendMethod() - { - lock (locker) - { - if (!registered) - { - registered = true; - BirthdaysNotifyClient.Instance.RegisterSendMethod(); - } - } - } - } -} diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Classes/BirthdaysSubscriptionManager.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Classes/BirthdaysSubscriptionManager.cs deleted file mode 100644 index 20a627eb6..000000000 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Classes/BirthdaysSubscriptionManager.cs +++ /dev/null @@ -1,60 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; - -using ASC.Notify.Model; -using ASC.Web.Community.Modules.Birthdays.Resources; -using ASC.Web.Core.Subscriptions; - -namespace ASC.Web.Community.Birthdays -{ - public class BirthdaysSubscriptionManager : ISubscriptionManager - { - public List GetSubscriptionObjects(Guid subItem) - { - return new List(); - } - - public List GetSubscriptionTypes() - { - var subscriptionTypes = new List(); - - subscriptionTypes.Add(new SubscriptionType() - { - ID = new Guid("{3177E937-9189-45db-BA7C-916C69C4A574}"), - Name = BirthdaysResource.BirthdaysSubscribeAll, - NotifyAction = BirthdaysNotifyClient.Event_BirthdayReminder, - Single = true, - IsEmptySubscriptionType = new IsEmptySubscriptionTypeDelegate(IsEmptySubscriptionType), - CanSubscribe = true - }); - return subscriptionTypes; - } - - private bool IsEmptySubscriptionType(Guid productID, Guid moduleID, Guid typeID) - { - return false; - } - - public ISubscriptionProvider SubscriptionProvider - { - get { return BirthdaysNotifyClient.NotifySource.GetSubscriptionProvider(); } - } - } -} diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Default.aspx b/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Default.aspx deleted file mode 100644 index 5c86a4fa8..000000000 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Default.aspx +++ /dev/null @@ -1,118 +0,0 @@ -<%@ Assembly Name="ASC.Web.Community" %> -<%@ Assembly Name="ASC.Web.Core" %> - -<%@ Page Language="C#" MasterPageFile="~/Products/Community/Master/Community.Master" AutoEventWireup="true" - CodeBehind="Default.aspx.cs" Inherits="ASC.Web.Community.Birthdays.Default" %> - -<%@ Import Namespace="ASC.Core.Users" %> -<%@ Import Namespace="ASC.Core" %> -<%@ Import Namespace="ASC.Web.Community.Modules.Birthdays.Resources" %> -<%@ Import Namespace="ASC.Web.Core.Users" %> -<%@ Import Namespace="ASC.Web.Studio.Utility" %> - - - - - <% if (todayBirthdays != null && todayBirthdays.Count > 0) %> - <% { %> -
    - <%= BirthdaysResource.BirthdaysTodayTitle%> -
    - - - - - - - - - - - -
    - <%= DateTime.UtcNow.ToString("dd MMMM").ToLower()%> - - -
    - <% } %> - - - - - - - <% if (upcomingBirthdays != null && upcomingBirthdays.Count > 0) %> - <% { %> -
    - <%= BirthdaysResource.BirthdaysUpcomingTitle%> -
    - - - - - - - <% foreach (var bday in upcomingBirthdays) %> - <% { %> - - - - - <% } %> - -
    -
    - <%= bday.Date.ToShortDayMonth()%> -
    -
    -
    - <% foreach (var usr in bday.Users) %> - <% { %> -
    "> -
    - -
    - -
    - <% } %> -
    -
    - <% } %> - -
    \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Default.aspx.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Default.aspx.cs deleted file mode 100644 index df5ce693e..000000000 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Default.aspx.cs +++ /dev/null @@ -1,117 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; - -using ASC.Core; -using ASC.Core.Tenants; -using ASC.Core.Users; -using ASC.Web.Community.Modules.Birthdays.Resources; -using ASC.Web.Studio; -using ASC.Web.Studio.Controls.Common; -using ASC.Web.Studio.Utility; - -namespace ASC.Web.Community.Birthdays -{ - public partial class Default : MainPage - { - - protected List todayBirthdays; - protected List upcomingBirthdays; - - protected void Page_Load(object sender, EventArgs e) - { - RenderScripts(); - - todayBirthdays = GetTodayBirthdays(); - upcomingBirthdays = GetUpcomingBirthdays().ToList(); - - if (upcomingBirthdays == null || !upcomingBirthdays.Any()) - { - upcomingEmptyContent.Controls.Add(new EmptyScreenControl - { - ImgSrc = VirtualPathUtility.ToAbsolute("~/Products/Community/Modules/Birthdays/App_Themes/default/images/birthday.png"), - Header = BirthdaysResource.BirthdayEmptyScreenCaption, - Describe = BirthdaysResource.BirthdaysEmptyModuleDescription - }); - } - - Title = HeaderStringHelper.GetPageTitle(BirthdaysResource.BirthdaysModuleTitle); - } - - protected void RenderScripts() - { - Page - .RegisterStyle("~/Products/Community/Modules/Birthdays/App_Themes/default/birthdays.css") - .RegisterBodyScripts("~/Products/Community/Modules/Birthdays/js/birthdays.js"); - } - - private static List GetTodayBirthdays() - { - var today = TenantUtil.DateTimeNow(); - return (from u in CoreContext.UserManager.GetUsers(EmployeeStatus.Active, EmployeeType.User) - where u.BirthDate.HasValue && u.BirthDate.Value.Month.Equals(today.Month) && u.BirthDate.Value.Day.Equals(today.Day) - orderby u.DisplayUserName() - select u) - .ToList(); - } - - private class BirthDateComparer : IComparer - { - public int Compare(DateTime x, DateTime y) - { - var today = TenantUtil.DateTimeNow(); - var leap = new DateTime(2000, today.Month, today.Day); - - var dx = new DateTime(2000, x.Month, x.Day).DayOfYear - leap.DayOfYear; - var dy = new DateTime(2000, y.Month, y.Day).DayOfYear - leap.DayOfYear; - - if (dx < 0 && dy >= 0) return 1; - if (dx >= 0 && dy < 0) return -1; - - return dx.CompareTo(dy); - } - } - - private static IEnumerable GetUpcomingBirthdays() - { - var today = TenantUtil.DateTimeNow(); - - return CoreContext.UserManager.GetUsers(EmployeeStatus.Active, EmployeeType.User) - .Where(x => x.BirthDate.HasValue) - .OrderBy(x => x.BirthDate.Value, new BirthDateComparer()) - .GroupBy(x => new DateTime(2000, x.BirthDate.Value.Month, x.BirthDate.Value.Day)) // 29 february - .Select(x => new BirthdayWrapper { Date = x.Key, Users = x.ToList() }) - .SkipWhile(x => x.Date.Month.Equals(today.Month) && x.Date.Day.Equals(today.Day)) - .Take(10); - } - - protected bool IsInRemindList(Guid userID) - { - return BirthdaysNotifyClient.Instance.IsSubscribe(SecurityContext.CurrentAccount.ID, userID); - } - - protected class BirthdayWrapper - { - public DateTime Date; - public List Users; - } - } -} \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Default.aspx.designer.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Default.aspx.designer.cs deleted file mode 100644 index 7b20e2aa7..000000000 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Default.aspx.designer.cs +++ /dev/null @@ -1,24 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace ASC.Web.Community.Birthdays { - - - public partial class Default { - - /// - /// upcomingEmptyContent control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder upcomingEmptyContent; - } -} diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/patterns.xml b/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/patterns.xml deleted file mode 100644 index fa660c6a0..000000000 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/patterns.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - -${__VirtualRootPath}/Products/Community/Modules/Birthdays/ - - - - - - - [${__VirtualRootPath}/Products/Community/Modules/Birthdays/](${__VirtualRootPath}/Products/Community/Modules/Birthdays/) - - - - \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/js/birthdays.js b/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/js/birthdays.js deleted file mode 100644 index 63254ad32..000000000 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/js/birthdays.js +++ /dev/null @@ -1,78 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -if (typeof ASC === "undefined") - ASC = {}; - -if (typeof ASC.Community === "undefined") - ASC.Community = (function () { return {} })(); - -ASC.Community.Birthdays = (function() { - return { - openContact: function(obj) { - var name = jq(obj).attr("username"), - tcExist = false; - try { - tcExist = !!ASC.Controls.JabberClient; - } catch (err) { - tcExist = false; - } - if (tcExist === true) { - try { - ASC.Controls.JabberClient.open(name); - } catch (err) { - } - } - }, - - remind: function(obj) { - var userCard = jq(obj).parents(".small-user-card"), - userId = jq(userCard).find("input[type=hidden]").val(); - - Teamlab.subscribeCmtBirthday( - { userCard: userCard }, - { userid: userId, onRemind: true }, - { - error: function(params, errors) { - toastr.error(errors[0]); - return false; - }, - success: function(params, response){ - jq(params.userCard).addClass("active"); - } - }); - }, - - clearRemind: function(obj) { - var userCard = jq(obj).parents(".small-user-card"), - userId = jq(userCard).find("input[type=hidden]").val(); - - Teamlab.subscribeCmtBirthday( - { userCard: userCard }, - { userid: userId, onRemind: false }, - { - error: function (params, errors) { - toastr.error(errors[0]); - return false; - }, - success: function (params, response) { - jq(params.userCard).removeClass("active"); - } - }); - } - }; -})(jQuery); \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Core/BlogsEngine.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Core/BlogsEngine.cs index ce2a212f2..bd640e2ff 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Core/BlogsEngine.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Core/BlogsEngine.cs @@ -35,6 +35,7 @@ using ASC.Notify.Recipients; using ASC.Web.Community.Product; using ASC.Web.Community.Search; using ASC.Web.Core.Users; +using ASC.Web.Studio.Core.Notify; using ASC.Web.Studio.Utility; namespace ASC.Blogs.Core @@ -240,8 +241,7 @@ namespace ASC.Blogs.Core new TagValue(Constants.TagURL, CommonLinkUtility.GetFullAbsolutePath( Constants.ViewBlogPageUrl + - "?blogid=" + post.ID.ToString())), - GetReplyToTag(Guid.Empty, post) + "?blogid=" + post.ID.ToString())) }; NotifyClient.SendNoticeAsync( @@ -327,7 +327,7 @@ namespace ASC.Blogs.Core NotifyClient.BeginSingleRecipientEvent("asc_blog_c"); NotifyClient.AddInterceptor(initiatorInterceptor); - var tags = new List + var tags = new ITagValue[] { new TagValue(Constants.TagPostSubject, post.Title), new TagValue(Constants.TagPostPreview, post.GetPreviewText(500)), @@ -335,17 +335,28 @@ namespace ASC.Blogs.Core new TagValue(Constants.TagUserURL, CommonLinkUtility.GetFullAbsolutePath(CommonLinkUtility.GetUserProfile(comment.UserID))), new TagValue(Constants.TagDate, string.Format("{0:d} {0:t}", comment.Datetime)), new TagValue(Constants.TagCommentBody, comment.Content), - new TagValue(Constants.TagURL, CommonLinkUtility.GetFullAbsolutePath(Constants.ViewBlogPageUrl + "?blogid=" + post.ID.ToString())), - new TagValue(Constants.TagCommentURL, CommonLinkUtility.GetFullAbsolutePath(Constants.ViewBlogPageUrl + "?blogid=" + post.ID.ToString() + "#container_" + comment.ID.ToString())), - GetReplyToTag(comment.ID, post) + new TagValue(Constants.TagCommentURL, CommonLinkUtility.GetFullAbsolutePath(Constants.ViewBlogPageUrl + "?blogid=" + post.ID.ToString() + "#container_" + comment.ID.ToString())) }; - NotifyClient.SendNoticeAsync( - Constants.NewComment, - post.ID.ToString(), - null, - tags.ToArray()); + var mentionedUsers = MentionProvider.GetMentionedUsers(comment.Content); + var mentionedUserIds = mentionedUsers.Select(u => u.ID.ToString()); + + var provider = _notifySource.GetSubscriptionProvider(); + + var objectID = post.ID.ToString(); + + var recipients = provider + .GetRecipients(Constants.NewComment, objectID) + .Where(r => !mentionedUserIds.Contains(r.ID)) + .ToArray(); + + NotifyClient.SendNoticeToAsync(Constants.NewComment, objectID, recipients, false, tags); + + if (mentionedUsers.Length > 0) + { + NotifyClient.SendNoticeToAsync(Constants.MentionForPostComment, objectID, mentionedUsers, false, tags); + } NotifyClient.EndSingleRecipientEvent("asc_blog_c"); } @@ -368,11 +379,6 @@ namespace ASC.Blogs.Core } } - private static TagValue GetReplyToTag(Guid commentId, Post post) - { - return ReplyToTagProvider.Comment("blog", post.ID.ToString(), commentId.ToString()); - } - private void SaveComment(Comment comment) { CommunitySecurity.DemandPermissions(comment, Constants.Action_EditRemoveComment); diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Core/Constants.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Core/Constants.cs index 05483ad35..663b453be 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Core/Constants.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Core/Constants.cs @@ -36,6 +36,7 @@ namespace ASC.Blogs.Core public static INotifyAction NewPost = new NotifyAction("new post"); public static INotifyAction NewPostByAuthor = new NotifyAction("new personal post"); public static INotifyAction NewComment = new NotifyAction("new comment"); + public static INotifyAction MentionForPostComment = new NotifyAction("mention for post comment"); public const string _NewBlogSubscribeCategory = "{9C8ED95F-07D2-42d0-B241-C0A51F7D26D5}"; diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Core/Resources/BlogsResource.az-Latn-AZ.resx b/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Core/Resources/BlogsResource.az-Latn-AZ.resx index 7d859a997..4af239489 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Core/Resources/BlogsResource.az-Latn-AZ.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Core/Resources/BlogsResource.az-Latn-AZ.resx @@ -109,6 +109,9 @@ Öz səhifənizi yaradın ki, maraqlı məlumat, fikir və təəssürat paylaşasınız, fikrinizi ifadə edəsiniz və digər istifadəçilərdən cavab şərhləri alasınız. + + Maraqlı məlumatlar, rəylər və digər istifadəçilərin rəyləri olan səhifələrə baxın, fikirlərinizi bölüşün və şərhlərdə müzakirə edin. + Gərəkli etiketləri vergül ilə ayıraraq daxil edin @@ -184,4 +187,4 @@ Bütün yazılara olan abunəliyi ləğv et - \ No newline at end of file + diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Core/Service/BlogPatternResource.Designer.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Core/Service/BlogPatternResource.Designer.cs index b579baa3b..ac4ee816b 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Core/Service/BlogPatternResource.Designer.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Core/Service/BlogPatternResource.Designer.cs @@ -60,13 +60,30 @@ namespace ASC.Web.Community.Modules.Blogs.Core.Service { } } + /// + /// Looks up a localized string similar to h1.You were mentioned in comment to Blog Post: "$PostSubject":"$URL" + /// + /// $Date "$UserName":"$UserURL" mentioned you in the comment to the "$PostSubject":"$URL" blog post: + /// + ///$CommentBody + /// + ///"Read More":"$CommentURL" + /// + ///^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. If you do not want to receive the notifications about new comments added to this blog post, please manage your "subscription settings":"$RecipientSubscriptionConfigURL".^. + /// + public static string pattern_mention_for_blog_comment { + get { + return ResourceManager.GetString("pattern_mention_for_blog_comment", resourceCulture); + } + } + /// /// Looks up a localized string similar to h1.New Comment to Blog Post: "$PostSubject":"$URL" /// /// $Date "$UserName":"$UserURL" has added a new comment to the "$PostSubject":"$URL" blog post: /// - ///$CommentBody - /// + ///$CommentBody + /// ///"Read More":"$CommentURL" /// ///^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. If you do not want to receive the notifications about new comments added to this blog post, please manage your "subscription settings":"$RecipientSubscriptionConfigURL".^. @@ -94,6 +111,24 @@ namespace ASC.Web.Community.Modules.Blogs.Core.Service { } } + /// + /// Looks up a localized string similar to Community.Mention in comment to blog post: $PostSubject. + /// + public static string subject_mention_for_blog_comment { + get { + return ResourceManager.GetString("subject_mention_for_blog_comment", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Community. Mention in comment to blog post: [$PostSubject]($CommentURL). + /// + public static string subject_mention_for_blog_comment_tg { + get { + return ResourceManager.GetString("subject_mention_for_blog_comment_tg", resourceCulture); + } + } + /// /// Looks up a localized string similar to Community. New comment to blog post: $PostSubject. /// diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Core/Service/BlogPatternResource.az-Latn-AZ.resx b/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Core/Service/BlogPatternResource.az-Latn-AZ.resx index 48fa771fc..cf0416a14 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Core/Service/BlogPatternResource.az-Latn-AZ.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Core/Service/BlogPatternResource.az-Latn-AZ.resx @@ -67,7 +67,7 @@ $CommentBody "Ardını oxu":"$CommentURL" -^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bloq yazılarına yeni şərhlər haqqında bildirişlər almaq istəmirsinizsə, lütfən,"abunəlik ayarlarını" dəyişin:"$RecipientSubscriptionConfigURL".^ +^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bloq yazılarına yeni şərhlər haqqında bildirişlər almaq istəmirsinizsə, lütfən,"abunəlik ayarlarını":"$RecipientSubscriptionConfigURL" dəyişin.^ h1.Bloqlarda yeni yazı: "$PostSubject":"$URL" @@ -78,12 +78,18 @@ $PostPreview "Ardını oxuyun":"$URL" -^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Yeni bloq yazıları haqqında bildirişlər almaq istəmirsinizsə, lütfən,"abunəlik ayarlarını" dəyişin:"$RecipientSubscriptionConfigURL".^ +^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Yeni bloq yazıları haqqında bildirişlər almaq istəmirsinizsə, lütfən,"abunəlik ayarlarını":"$RecipientSubscriptionConfigURL" dəyişin.^ İcma. Bloq yazısına yeni şərh: $PostSubject + + İcma. Bloq paylaşımına yeni şərh əlavə edildi:[$PostSubject]($CommentURL) + İcma. Bloqlarda yeni yazı: $PostSubject - \ No newline at end of file + + İcma. Bloqlarda yeni paylaşım:[$PostSubject]($URL) + + diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Core/Service/BlogPatternResource.resx b/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Core/Service/BlogPatternResource.resx index bc59b137b..1ed04eaa6 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Core/Service/BlogPatternResource.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Core/Service/BlogPatternResource.resx @@ -58,13 +58,24 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + h1.You were mentioned in comment to Blog Post: "$PostSubject":"$URL" + + $Date "$UserName":"$UserURL" mentioned you in the comment to the "$PostSubject":"$URL" blog post: + +$CommentBody + +"Read More":"$CommentURL" + +^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. If you do not want to receive the notifications about new comments added to this blog post, please manage your "subscription settings":"$RecipientSubscriptionConfigURL".^ + h1.New Comment to Blog Post: "$PostSubject":"$URL" $Date "$UserName":"$UserURL" has added a new comment to the "$PostSubject":"$URL" blog post: -$CommentBody - +$CommentBody + "Read More":"$CommentURL" ^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. If you do not want to receive the notifications about new comments added to this blog post, please manage your "subscription settings":"$RecipientSubscriptionConfigURL".^ @@ -80,6 +91,12 @@ $PostPreview ^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. If you do not want to receive the notifications about new posts in blogs, please manage your "subscription settings":"$RecipientSubscriptionConfigURL".^ + + Community.Mention in comment to blog post: $PostSubject + + + Community. Mention in comment to blog post: [$PostSubject]($CommentURL) + Community. New comment to blog post: $PostSubject diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Core/Service/blog_patterns.xml b/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Core/Service/blog_patterns.xml index 4faaa3746..fc4e68ad8 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Core/Service/blog_patterns.xml +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Core/Service/blog_patterns.xml @@ -16,7 +16,7 @@ $URL - $UserName + $UserName $PostPreview @@ -41,9 +41,32 @@ $CommentURL - $UserName + $UserName $CommentBody + + + + + + + + + $UserName + + $PostPreview + + $URL + + + + + + $UserName + + $PostPreview + + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Default.aspx.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Default.aspx.cs index 3e37bebac..f56ded75b 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Default.aspx.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/Default.aspx.cs @@ -128,7 +128,7 @@ namespace ASC.Web.Community.Blogs var jsResource = new StringBuilder(); jsResource.Append(String.Format("ASC.Community.BlogsJSResource = {{}};ASC.Community.BlogsJSResource.ReadMoreLink = \"{0}\";", BlogsResource.ReadMoreLink)); - jsResource.Append("jq('#tableForNavigation select').val(" + BlogsPageSize + ").change(function(evt) {changeBlogsCountOfRows(this.value);}).tlCombobox();"); + jsResource.Append("jq('#tableForNavigation select').val(" + BlogsPageSize + ").on('change', function(evt) {changeBlogsCountOfRows(this.value);}).tlCombobox();"); Page.RegisterInlineScript(jsResource.ToString(), true); } diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/EditBlog.aspx b/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/EditBlog.aspx index 24a6515fc..3b79c770e 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/EditBlog.aspx +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/EditBlog.aspx @@ -54,7 +54,7 @@
    <%=BlogsResource.ContentTitle %>:
    - +
    diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/EditBlog.aspx.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/EditBlog.aspx.cs index 9ca25c198..4946c67b1 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/EditBlog.aspx.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/EditBlog.aspx.cs @@ -178,7 +178,7 @@ namespace ASC.Web.Community.Blogs protected override string RenderRedirectUpload() { - return string.Format("{0}://{1}:{2}{3}", Request.GetUrlRewriter().Scheme, Request.GetUrlRewriter().Host, Request.GetUrlRewriter().Port, VirtualPathUtility.ToAbsolute("~/") + "fckuploader.ashx?newEditor=true&esid=blogs&iid=" + BlogId); + return string.Format("{0}://{1}:{2}{3}", Request.GetUrlRewriter().Scheme, Request.GetUrlRewriter().Host, Request.GetUrlRewriter().Port, VirtualPathUtility.ToAbsolute("~/") + "fckuploader.ashx?esid=blogs&iid=" + BlogId); } public void UpdatePost(Post post, BlogsEngine engine) diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/PageControllers/BasePage.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/PageControllers/BasePage.cs index d354bf889..1225caff7 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/PageControllers/BasePage.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/PageControllers/BasePage.cs @@ -61,7 +61,7 @@ namespace ASC.Web.Community.Blogs protected virtual string RenderRedirectUpload() { - return string.Format("{0}://{1}:{2}{3}", Request.GetUrlRewriter().Scheme, Request.GetUrlRewriter().Host, Request.GetUrlRewriter().Port, VirtualPathUtility.ToAbsolute("~/") + "fckuploader.ashx?newEditor=true&esid=blogs"); + return string.Format("{0}://{1}:{2}{3}", Request.GetUrlRewriter().Scheme, Request.GetUrlRewriter().Host, Request.GetUrlRewriter().Port, VirtualPathUtility.ToAbsolute("~/") + "fckuploader.ashx?esid=blogs"); } protected void RenderScripts() diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/js/blogs.js b/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/js/blogs.js index 6714e9d5b..00e58153b 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/js/blogs.js +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Blogs/js/blogs.js @@ -33,7 +33,7 @@ var BlogsManager = new function() { }; this.CheckData = function() { var titleText = jq("input[id$='PageContent_CommunityPageContent_txtTitle']").val(); - if (jq.trim(titleText) == "") { + if (titleText.trim() == "") { ShowRequiredError(jq("input[id$='PageContent_CommunityPageContent_txtTitle']")); BlogsManager.UnBlockButtons(); } @@ -312,7 +312,7 @@ jq(document).ready(function() { }); resizeContent(); - jq(window).resize(function() { + jq(window).on("resize", function() { resizeContent(); }); @@ -346,5 +346,5 @@ jq(document).ready(function() { } var textInput = jq("input[id$='CommunityPageContent_txtTitle']"); if (textInput.length) - textInput.focus(); + textInput.trigger("focus"); }); diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Business/BookmarkingService.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Business/BookmarkingService.cs index 1737f632f..fcacae4ef 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Business/BookmarkingService.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Business/BookmarkingService.cs @@ -354,14 +354,27 @@ namespace ASC.Bookmarking.Business new TagValue(BookmarkSubscriptionConstants.UserURL, BookmarkingBusinessUtil.RenderProfileLink(c.UserID)), new TagValue(BookmarkSubscriptionConstants.Date, c.Datetime.ToShortString()), - new TagValue(BookmarkSubscriptionConstants.CommentBody, c.Content),GetReplyToTag(b,c) + new TagValue(BookmarkSubscriptionConstants.CommentBody, c.Content) }; + var mentionedUsers = MentionProvider.GetMentionedUsers(c.Content); + var mentionedUserIds = mentionedUsers.Select(u => u.ID.ToString()); + + var provider = BookmarkingNotifySource.Instance.GetSubscriptionProvider(); + var objectID = b.ID.ToString(CultureInfo.InvariantCulture); - var action = BookmarkingBusinessConstants.NotifyActionNewComment; + var recipients = provider + .GetRecipients(BookmarkingBusinessConstants.NotifyActionNewComment, objectID) + .Where(r => !mentionedUserIds.Contains(r.ID)) + .ToArray(); - SendBookmarkNoticeAsync(action, objectID, tags); + SendBookmarkNoticeToAsync(BookmarkingBusinessConstants.NotifyActionNewComment, objectID, recipients, tags); + + if (mentionedUsers.Length > 0) + { + SendBookmarkNoticeToAsync(BookmarkingBusinessConstants.NotifyActionMentionForBookmarkComment, objectID, mentionedUsers, tags); + } } private void SendRecentBookmarkUpdates(Bookmark b) @@ -378,8 +391,7 @@ namespace ASC.Bookmarking.Business DisplayUserSettings.GetFullUserName(b.UserCreatorID))), //TODO: Rewrite patterns new TagValue(BookmarkSubscriptionConstants.Date, b.Date.ToShortString()), - new TagValue(BookmarkSubscriptionConstants.BookmarkDescription, HttpUtility.HtmlDecode(b.Description)), - GetReplyToTag(b, null) + new TagValue(BookmarkSubscriptionConstants.BookmarkDescription, HttpUtility.HtmlDecode(b.Description)) }; const string objectID = BookmarkingBusinessConstants.SubscriptionRecentBookmarkID; @@ -389,11 +401,6 @@ namespace ASC.Bookmarking.Business SendBookmarkNoticeAsync(action, objectID, tags); } - private static TagValue GetReplyToTag(Bookmark bookmark, Comment comment) - { - return ReplyToTagProvider.Comment("bookmark", bookmark.ID.ToString(CultureInfo.InvariantCulture), comment != null ? comment.ID.ToString() : null); - } - internal static string ModifyBookmarkUrl(Bookmark b) { var bookmarkUrl = HttpUtility.UrlEncode(HttpUtility.HtmlDecode(b.URL)); @@ -418,6 +425,21 @@ namespace ASC.Bookmarking.Business } } + private static void SendBookmarkNoticeToAsync(INotifyAction action, string objectID, IRecipient[] recipients, TagValue[] tags) + { + var initatorInterceptor = new InitiatorInterceptor(GetCurrentRecipient()); + var notifyClient = BookmarkingNotifyClient.NotifyClient; + try + { + notifyClient.AddInterceptor(initatorInterceptor); + notifyClient.SendNoticeToAsync(action, objectID, recipients, null, tags); + } + finally + { + notifyClient.RemoveInterceptor(initatorInterceptor.Name); + } + } + private static IRecipient GetCurrentRecipient() { return GetRecipient(SecurityContext.CurrentAccount.ID); diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Common/BookmarkingBusinessConstants.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Common/BookmarkingBusinessConstants.cs index f9b5564c8..08ac3e12f 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Common/BookmarkingBusinessConstants.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Common/BookmarkingBusinessConstants.cs @@ -34,6 +34,7 @@ namespace ASC.Bookmarking.Common public const string BookmarkCreatedID = "new bookmark created"; public const string BookmarkCommentCreatedID = "new bookmark comment created"; + public const string MentionForBookmarkComment = "mention for bookmark comment"; #endregion @@ -45,6 +46,7 @@ namespace ASC.Bookmarking.Common public static INotifyAction NotifyActionNewComment = new NotifyAction(BookmarkCommentCreatedID, "new-bookmark-comment"); internal static Guid NotifyActionNewCommentID = Guid.NewGuid(); + public static INotifyAction NotifyActionMentionForBookmarkComment = new NotifyAction(MentionForBookmarkComment, "mention-for-bookmark-comment"); public const string SubscriptionRecentBookmarkID = null; diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Common/BookmarkingBusinessFactory.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Common/BookmarkingBusinessFactory.cs index 934df72b6..2cad49299 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Common/BookmarkingBusinessFactory.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Common/BookmarkingBusinessFactory.cs @@ -15,73 +15,52 @@ */ -using System.Web; +using System; + +using ASC.Common.Caching; +using ASC.Core; namespace ASC.Bookmarking.Common { public static class BookmarkingBusinessFactory { + private static readonly ICache CacheAsc = AscCache.Memory; public static T GetObjectFromSession() where T : class, new() { T obj; - var key = typeof(T).ToString(); - if (HttpContext.Current.Session != null) + var key = typeof(T).ToString() + SecurityContext.CurrentAccount.ID.ToString(); + obj = CacheAsc.Get(key); + if (obj == null) { - obj = (T)HttpContext.Current.Session[key]; - if (obj == null) - { - obj = new T(); - HttpContext.Current.Session[key] = obj; - } - } - else - { - obj = (T)HttpContext.Current.Items[key]; - if (obj == null) - { - obj = new T(); - HttpContext.Current.Items[key] = obj; - } + obj = new T(); + CacheAsc.Insert(key, obj, TimeSpan.FromMinutes(15)); } return obj; + } public static void UpdateObjectInSession(T obj) where T : class, new() { - var key = typeof(T).ToString(); - if (HttpContext.Current.Session != null) - { - HttpContext.Current.Session[key] = obj; - } - else - { - HttpContext.Current.Items[key] = obj; - } + var key = typeof(T).ToString() + SecurityContext.CurrentAccount.ID.ToString(); + CacheAsc.Insert(key, obj, TimeSpan.FromMinutes(15)); } public static void UpdateDisplayMode(BookmarkDisplayMode mode) { - var key = typeof(BookmarkDisplayMode).Name; + var key = typeof(BookmarkDisplayMode).Name + SecurityContext.CurrentAccount.ID.ToString(); - if (HttpContext.Current != null && HttpContext.Current.Session != null) - { - HttpContext.Current.Session.Add(key, mode); - } + CacheAsc.Insert(key, mode, TimeSpan.FromMinutes(15)); } public static BookmarkDisplayMode GetDisplayMode() { - var key = typeof(BookmarkDisplayMode).Name; + var key = typeof(BookmarkDisplayMode).Name + SecurityContext.CurrentAccount.ID.ToString(); - if (HttpContext.Current != null && HttpContext.Current.Session != null) + var value = CacheAsc.Get(key); + if (value != null) { - var value = HttpContext.Current.Session[key]; - - if (value != null) - { - return (BookmarkDisplayMode)value; - } + return (BookmarkDisplayMode)value; } return BookmarkDisplayMode.AllBookmarks; diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Patterns/BookmarkingPatternResource.Designer.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Patterns/BookmarkingPatternResource.Designer.cs index c80510ce4..4905fac2c 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Patterns/BookmarkingPatternResource.Designer.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Patterns/BookmarkingPatternResource.Designer.cs @@ -92,6 +92,23 @@ namespace ASC.Web.Community.Modules.Bookmarking.Core.Patterns { } } + /// + /// Looks up a localized string similar to h1.You were mentioned in comment to Bookmark: "$BookmarkTitle":"$BookmarkUrl" + /// + ///$Date "$__AuthorName":"$__AuthorUrl" mentioned you in the comment to the bookmark:"$BookmarkTitle":"$BookmarkUrl" + /// + ///$CommentBody + /// + ///"Read More":"$BookmarkUrl" + /// + ///^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. If you do not want to receive the notifications about new comments added to this bookmark, please manage your "subscription settings":"$RecipientSubscription [rest of string was truncated]";. + /// + public static string pattern_MentionForBookmarkComment { + get { + return ResourceManager.GetString("pattern_MentionForBookmarkComment", resourceCulture); + } + } + /// /// Looks up a localized string similar to Community. New comment to bookmark: $BookmarkTitle. /// @@ -127,5 +144,23 @@ namespace ASC.Web.Community.Modules.Bookmarking.Core.Patterns { return ResourceManager.GetString("subject_BookmarkCreatedID_tg", resourceCulture); } } + + /// + /// Looks up a localized string similar to Community. Mention in comment to bookmark: $BookmarkTitle. + /// + public static string subject_MentionForBookmarkComment { + get { + return ResourceManager.GetString("subject_MentionForBookmarkComment", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Community. Mention in comment to bookmark: [$BookmarkTitle]($BookmarkUrl). + /// + public static string subject_MentionForBookmarkComment_tg { + get { + return ResourceManager.GetString("subject_MentionForBookmarkComment_tg", resourceCulture); + } + } } } diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Patterns/BookmarkingPatternResource.az-Latn-AZ.resx b/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Patterns/BookmarkingPatternResource.az-Latn-AZ.resx index 93d6d8503..f2a5c135c 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Patterns/BookmarkingPatternResource.az-Latn-AZ.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Patterns/BookmarkingPatternResource.az-Latn-AZ.resx @@ -67,7 +67,7 @@ $CommentBody "Ardını oxu":"$BookmarkUrl" -^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Əlfəcinlərə yeni şərhlər haqqında bildirişlər almaq istəmirsinizsə, lütfən,"abunəlik ayarlarını" dəyişin:"$RecipientSubscriptionConfigURL".^ +^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Əlfəcinlərə yeni şərhlər haqqında bildirişlər almaq istəmirsinizsə, lütfən,"abunəlik ayarlarını":"$RecipientSubscriptionConfigURL" dəyişin.^ h1.Yeni əlfəcin əlavə olundu: "$BookmarkTitle":"$BookmarkUrl" @@ -76,12 +76,18 @@ $Date istifadəçi "$__AuthorName":"$__AuthorUrl" yeni əlfəcini əlavə etdi:" $BookmarkDescription -^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Yeni əlavə olunmuş əlfəcinlər haqqında bildirişlər almaq istəmirsinizsə, lütfən,"abunəlik ayarlarını" dəyişin:"$RecipientSubscriptionConfigURL".^ +^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Yeni əlavə olunmuş əlfəcinlər haqqında bildirişlər almaq istəmirsinizsə, lütfən,"abunəlik ayarlarını":"$RecipientSubscriptionConfigURL" dəyişin.^ İcma. Əlfəcinə yeni şərh var: $BookmarkTitle + + İcma. Yeni şərh əlavə edildi: [$BookmarkTitle]($BookmarkUrl) + İcma. Yeni əlfəcin əlavə olundu: $BookmarkTitle - \ No newline at end of file + + İcma. Yeni əlfəcin əlavə edildi: [$BookmarkTitle]($BookmarkUrl) + + diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Patterns/BookmarkingPatternResource.resx b/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Patterns/BookmarkingPatternResource.resx index 00fac1ba6..2a9ee26c0 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Patterns/BookmarkingPatternResource.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Patterns/BookmarkingPatternResource.resx @@ -77,6 +77,17 @@ $Date "$__AuthorName":"$__AuthorUrl" has added a new bookmark:"$BookmarkTitle":" $BookmarkDescription ^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. If you do not want to receive the notifications about new bookmarks added, please manage your "subscription settings":"$RecipientSubscriptionConfigURL".^ + + + h1.You were mentioned in comment to Bookmark: "$BookmarkTitle":"$BookmarkUrl" + +$Date "$__AuthorName":"$__AuthorUrl" mentioned you in the comment to the bookmark:"$BookmarkTitle":"$BookmarkUrl" + +$CommentBody + +"Read More":"$BookmarkUrl" + +^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. If you do not want to receive the notifications about new comments added to this bookmark, please manage your "subscription settings":"$RecipientSubscriptionConfigURL".^ Community. New comment to bookmark: $BookmarkTitle @@ -90,4 +101,10 @@ $BookmarkDescription Community. New bookmark added: [$BookmarkTitle]($BookmarkUrl) + + Community. Mention in comment to bookmark: $BookmarkTitle + + + Community. Mention in comment to bookmark: [$BookmarkTitle]($BookmarkUrl) + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Patterns/bookmarking_patterns.xml b/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Patterns/bookmarking_patterns.xml index 1df127590..adbf5f796 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Patterns/bookmarking_patterns.xml +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Patterns/bookmarking_patterns.xml @@ -19,12 +19,34 @@ $BookmarkUrl - $__AuthorName + $__AuthorName $CommentBody + + + + + + + + + $__AuthorName +$CommentBody + +$BookmarkUrl + + + + + + $__AuthorName + +$CommentBody + + @@ -43,7 +65,7 @@ $BookmarkUrl - $__AuthorName + $__AuthorName $BookmarkDescription diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Resources/BookmarkingBusinessResources.az-Latn-AZ.resx b/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Resources/BookmarkingBusinessResources.az-Latn-AZ.resx index 4a4a65d0e..e15628823 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Resources/BookmarkingBusinessResources.az-Latn-AZ.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Core/Resources/BookmarkingBusinessResources.az-Latn-AZ.resx @@ -64,4 +64,4 @@ Əlfəcinə şərhlər - \ No newline at end of file + diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Resources/BookmarkingResource.az-Latn-AZ.resx b/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Resources/BookmarkingResource.az-Latn-AZ.resx index 1041fb342..1f14065bd 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Resources/BookmarkingResource.az-Latn-AZ.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/Resources/BookmarkingResource.az-Latn-AZ.resx @@ -67,4 +67,4 @@ Əlfəcinlər - \ No newline at end of file + diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/UserControls/BookmarkingUserControl.ascx.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/UserControls/BookmarkingUserControl.ascx.cs index a278d2b04..062e6735c 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/UserControls/BookmarkingUserControl.ascx.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/UserControls/BookmarkingUserControl.ascx.cs @@ -650,7 +650,7 @@ namespace ASC.Web.UserControls.Bookmarking private void InitScripts() { var jsResource = new StringBuilder(); - jsResource.Append("jq('#tableForNavigation select').val(" + BookmarkPageCounter + ").change(function(evt) {changeBookmarksCountOfRows(this.value);}).tlCombobox();"); + jsResource.Append("jq('#tableForNavigation select').val(" + BookmarkPageCounter + ").on('change', function(evt) {changeBookmarksCountOfRows(this.value);}).tlCombobox();"); Page.RegisterInlineScript(jsResource.ToString(), true); } } diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/UserControls/Resources/BookmarkingUCResource.az-Latn-AZ.resx b/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/UserControls/Resources/BookmarkingUCResource.az-Latn-AZ.resx index fdf876e52..938a911c4 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/UserControls/Resources/BookmarkingUCResource.az-Latn-AZ.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/UserControls/Resources/BookmarkingUCResource.az-Latn-AZ.resx @@ -151,6 +151,9 @@ Əlfəcinlər yaradın, web resurslara keçidləri qeyd edərək faydalı informasiyanı saxlayıb onu həmkarlarınızla elə portalınızda paylaşın, bu, sizə sevimli səhifələrinizə asanlıqla qayıtmağınıza imkan verəcək. + + Faydalı məlumatları birbaşa portalda saxlamağa imkan verən əlfəcinlərə baxın. Veb resurslarına keçidləri yadda saxlayın və istədiyiniz zaman sevimli yerlərə daxil olun. + Seçilmişlər @@ -169,6 +172,9 @@ Yığışdır + + Əlfəcini silmək istədiyinizə əminsiniz? + Siz {0} əlfəcini seçilmişlər siyahısından çıxamraq istədiyinizə əminsiniz mi? @@ -211,4 +217,4 @@ İl - \ No newline at end of file + diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/js/bookmarking.js b/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/js/bookmarking.js index 10fb04153..17b62c310 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/js/bookmarking.js +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Bookmarking/js/bookmarking.js @@ -262,7 +262,7 @@ function disableFieldsForSaveBookmarkAjaxRequest(flag, singleBookmarkDivID) { function disableBookmarkingField(elementID, flag) { var el = jq('#' + elementID); if (el != null) { - el.attr('disabled', flag); + el.prop('disabled', flag); } } @@ -420,8 +420,8 @@ function cancelButtonClick() { } function disableSaveButton(flag) { - jq('#SaveBookmarkButton').attr('disabled', flag); - jq('#SaveBookmarkButtonCopy').attr('disabled', flag); + jq('#SaveBookmarkButton').prop('disabled', flag); + jq('#SaveBookmarkButtonCopy').prop('disabled', flag); } function copyValue(sourceElementID, destElementID) { @@ -460,10 +460,10 @@ function getBookmarkByUrl() { } function disableAddBookmarkElements(disableFlag) { - getBookmarkUrlInput().attr('disabled', !disableFlag); - getBookmarkNameInput().attr('disabled', disableFlag); - getBookmarkDescriptionInput().attr('disabled', disableFlag); - getBookmarkTagsInput().attr('disabled', disableFlag); + getBookmarkUrlInput().prop('disabled', !disableFlag); + getBookmarkNameInput().prop('disabled', disableFlag); + getBookmarkDescriptionInput().prop('disabled', disableFlag); + getBookmarkTagsInput().prop('disabled', disableFlag); } function showHideCheckBookmarkUrlButtons(showHideFlag) { @@ -517,7 +517,7 @@ function getBookmarkByUrlButtonClick() { emptyUrlAlert(true); return; } - getBookmarkUrlInput().attr('disabled', true); + getBookmarkUrlInput().prop('disabled', true); moveAddBookmarkToFavouritePanel(); getBookmarkByUrl(); } @@ -643,7 +643,7 @@ function bookmarkInputUrlOnKeyDown(event) { //Enter key was pressed if (event.keyCode == 13) { //getBookmarkByUrlButtonClick(); - jq('#CheckBookmarkUrlLinkButton').click(); + jq('#CheckBookmarkUrlLinkButton').trigger("click"); return false; } return true; @@ -670,7 +670,7 @@ function createBookmarkOnCtrlEnterKeyDown(event, textArea) { function setFocusOnFeild(fieldID) { - setTimeout('jq("#' + fieldID + '").focus();', 100); + setTimeout('jq("#' + fieldID + '").trigger("focus");', 100); } function isCtrlEnterKeyPressed(event) { @@ -690,9 +690,13 @@ function isEscapeKeyPressed(event) { } function createBookmarkActionButtonClick() { - jq('#SaveBookmarkButton').click(); - jq('#SaveBookmarkButtonCopy').click(); - eval(jq('#SaveBookmarkButtonCopy').attr('href')); + if (jq('#SaveBookmarkButton').is(":visible")) { + jq('#SaveBookmarkButton').trigger("click"); + } + if (jq('#SaveBookmarkButtonCopy').is(":visible")) { + jq('#SaveBookmarkButtonCopy').trigger("click"); + eval(jq('#SaveBookmarkButtonCopy').attr('href')); + } } function getURLParam(strParamName) { @@ -760,7 +764,7 @@ jq(document).ready(function() { } var textInput = jq("#BookmarkUrl"); if (textInput.length) - textInput.focus(); + textInput.trigger("focus"); var $actionButton = jq('.bookmarksHeaderBlock .menu-small'); jq.dropdownToggle({ diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/Core/Dao/ForumDataProvider.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/Core/Dao/ForumDataProvider.cs index e1a38ee5f..41c8eb71b 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/Core/Dao/ForumDataProvider.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/Core/Dao/ForumDataProvider.cs @@ -22,7 +22,6 @@ using System.Data; using System.Globalization; using System.Linq; using System.Text; -using System.Web; using ASC.Common.Caching; using ASC.Common.Data; @@ -65,6 +64,8 @@ namespace ASC.Forum public int ThreadID { get; set; } public Dictionary TopicViewRecentPostIDs { get; set; } + private static readonly ICache CacheAsc = AscCache.Memory; + public ThreadVisitInfo() { TopicViewRecentPostIDs = new Dictionary(); @@ -73,50 +74,40 @@ namespace ASC.Forum public static ThreadVisitInfo GetThreadVisitInfo(int threadID) { - if (HttpContext.Current != null) + var key = UserKeys.StringSessionKey + SecurityContext.CurrentAccount.ID.ToString(); + var hash = CacheAsc.Get(key); + if (hash == null) { - var key = UserKeys.StringSessionKey + SecurityContext.CurrentAccount.ID.ToString(); - var hash = HttpContext.Current.Session[key] as Hashtable; - if (hash == null) + hash = Hashtable.Synchronized(new Hashtable()); + foreach (var f in ForumDataProvider.InitFirstVisit()) { - hash = Hashtable.Synchronized(new Hashtable()); - foreach (var f in ForumDataProvider.InitFirstVisit()) - { - hash[UserKeys.StringThreadKey + f.ThreadID.ToString(CultureInfo.InvariantCulture)] = f; - } - HttpContext.Current.Session.Add(key, hash); + hash[UserKeys.StringThreadKey + f.ThreadID.ToString(CultureInfo.InvariantCulture)] = f; } - - var threadKey = UserKeys.StringThreadKey + threadID.ToString(CultureInfo.InvariantCulture); - var tvi = new ThreadVisitInfo { ThreadID = threadID }; - if (hash.Contains(threadKey)) - { - tvi = (ThreadVisitInfo)hash[threadKey]; - } - else - { - hash[threadKey] = tvi; - HttpContext.Current.Session[key] = hash; - } - - return tvi; + CacheAsc.Insert(key, hash, TimeSpan.FromMinutes(15)); } - return null; + var threadKey = UserKeys.StringThreadKey + threadID.ToString(CultureInfo.InvariantCulture); + var tvi = new ThreadVisitInfo { ThreadID = threadID }; + if (hash.Contains(threadKey)) + { + tvi = (ThreadVisitInfo)hash[threadKey]; + } + else + { + hash[threadKey] = tvi; + CacheAsc.Insert(key, hash, TimeSpan.FromMinutes(15)); + } + return tvi; } public static DateTime GetMinVisitThreadDate() { - HttpContext context = HttpContext.Current; - if (context == null) + var key = UserKeys.StringSessionKey + SecurityContext.CurrentAccount.ID.ToString(); + if (CacheAsc.Get(key) == null) return DateTime.MinValue; - Guid userID = SecurityContext.CurrentAccount.ID; - if (context.Session[UserKeys.StringSessionKey + userID.ToString()] == null) - return DateTime.MinValue; - - DateTime result = DateTime.MinValue; - var hash = (Hashtable)context.Session[UserKeys.StringSessionKey + userID.ToString()]; + var result = DateTime.MinValue; + var hash = CacheAsc.Get(key); foreach (var tvi in hash.Values) { diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/Core/Module/ForumPatternResource.az-Latn-AZ.resx b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/Core/Module/ForumPatternResource.az-Latn-AZ.resx index 18c14fd48..fea945087 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/Core/Module/ForumPatternResource.az-Latn-AZ.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/Core/Module/ForumPatternResource.az-Latn-AZ.resx @@ -67,7 +67,7 @@ $PostText "Ardını oxu":"$PostURL" -^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Əgər siz bu mövzulara dair yeni yazılar haqqında bildirişlər almaq istəmirsinizsə, lütfən, "abunəlik parametrlərini" dəyişdirin:"$RecipientSubscriptionConfigURL".^ +^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Əgər siz bu mövzulara dair yeni yazılar haqqında bildirişlər almaq istəmirsinizsə, lütfən, "abunəlik parametrlərini":"$RecipientSubscriptionConfigURL" dəyişdirin.^ h1.Forumlarda yeni mövzu: "$TopicTitle":"$TopicURL" @@ -78,12 +78,18 @@ $PostText "Ardını oxu":"$PostURL" -^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Əgər siz forumlarda yaradılmış yeni mövzular haqqında bildirişlər almaq istəmirsinizsə, lütfən, "abunəlik parametrlərini" dəyişdirin:"$RecipientSubscriptionConfigURL".^ +^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Əgər siz forumlarda yaradılmış yeni mövzular haqqında bildirişlər almaq istəmirsinizsə, lütfən, "abunəlik parametrlərini":"$RecipientSubscriptionConfigURL" dəyişdirin.^ İcma. Forum mövzusunda yeni yazı: $TopicTitle + + İcma. Forum mövzularında yeni paylaşım: [$TopicTitle]($PostURL) + İcma. Forumda yeni mövzu: $TopicTitle - \ No newline at end of file + + İcma. Forumlarda yeni mövzu: [$TopicTitle]($PostURL) + + diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/Core/Module/forum_patterns.xml b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/Core/Module/forum_patterns.xml index fb9019d90..90c02053c 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/Core/Module/forum_patterns.xml +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/Core/Module/forum_patterns.xml @@ -17,7 +17,7 @@ $PostURL - $UserName + $UserName $PostText @@ -47,7 +47,7 @@ $PostURL - $UserName + $UserName $PostText diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/Core/Presenters/NotifierPresenter.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/Core/Presenters/NotifierPresenter.cs index 723eb7b69..e879d915e 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/Core/Presenters/NotifierPresenter.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/Core/Presenters/NotifierPresenter.cs @@ -45,8 +45,7 @@ namespace ASC.Forum new TagValue(Constants.TagThreadURL, e.ThreadURL), new TagValue(Constants.TagPostText, e.PostText), new TagValue(Constants.TagUserURL, e.UserURL), - new TagValue(Constants.TagUserName, e.Poster.ToString()), - ReplyToTagProvider.Comment("forum.topic", e.TopicId.ToString(), e.PostId.ToString())); + new TagValue(Constants.TagUserName, e.Poster.ToString())); } @@ -63,8 +62,7 @@ namespace ASC.Forum new TagValue(Constants.TagThreadURL, e.ThreadURL), new TagValue(Constants.TagPostText, e.PostText), new TagValue(Constants.TagUserURL, e.UserURL), - new TagValue(Constants.TagUserName, e.Poster.ToString()), - ReplyToTagProvider.Comment("forum.topic", e.TopicId.ToString(), e.PostId.ToString())); + new TagValue(Constants.TagUserName, e.Poster.ToString())); } @@ -97,8 +95,7 @@ namespace ASC.Forum new TagValue(Constants.TagTagName, e.TagName), new TagValue(Constants.TagTagURL, e.TagURL), new TagValue(Constants.TagUserURL, e.UserURL), - new TagValue(Constants.TagUserName, e.Poster.ToString()), - ReplyToTagProvider.Comment("forum.topic", e.TopicId.ToString())); + new TagValue(Constants.TagUserName, e.Poster.ToString())); } diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/NewForum.aspx.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/NewForum.aspx.cs index d98314324..5240eeba3 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/NewForum.aspx.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/NewForum.aspx.cs @@ -34,7 +34,7 @@ namespace ASC.Web.Community.Forum { if (!ForumManager.Instance.ValidateAccessSecurityAction(ForumAction.GetAccessForumEditor, null)) { - Response.Redirect(ForumManager.Instance.PreviousPage.Url); + Response.Redirect(ForumManager.Instance.PreviousPage); return; } diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/Resources/ForumResource.az-Latn-AZ.resx b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/Resources/ForumResource.az-Latn-AZ.resx index 54067743e..92539aa5e 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/Resources/ForumResource.az-Latn-AZ.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/Resources/ForumResource.az-Latn-AZ.resx @@ -100,6 +100,9 @@ Öz forumlarınızı yaradın, mövzularda iştirak edin, bəzi maraqlı məlumatları paylaşın, forumu daha interaktiv edən sorğular təşkil edin. + + Forumları ziyarət edin, müzakirələrdə iştirak edin və sizi maraqlandıran mövzulara şərhlər bildirin. + Mövzular tapılmadı @@ -244,4 +247,4 @@ Görənlərin siyahısı - \ No newline at end of file + diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/Common/ForumManager.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/Common/ForumManager.cs index f6ad95946..2103ad662 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/Common/ForumManager.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/Common/ForumManager.cs @@ -19,6 +19,8 @@ using System; using System.Collections; using System.Web; +using ASC.Common.Caching; +using ASC.Core; using ASC.Data.Storage; using ASC.Forum; using ASC.Web.Core.Users; @@ -59,6 +61,8 @@ namespace ASC.Web.UserControls.Forum.Common public Settings Settings { get; private set; } + private static readonly ICache CacheAsc = AscCache.Memory; + internal ForumManager(Settings settings) { SessionKeys = new ForumSessionKeys(settings.ID); @@ -230,47 +234,24 @@ namespace ASC.Web.UserControls.Forum.Common return "\"\""; } - - - public PageLocation CurrentPage - { - get - { - if (HttpContext.Current != null && HttpContext.Current.Session[this.SessionKeys.CurrentPageLocation] != null) - return (PageLocation)HttpContext.Current.Session[this.SessionKeys.CurrentPageLocation]; - - return new PageLocation(ForumPage.Default, Settings.StartPageAbsolutePath); - - } - } - public PageLocation PreviousPage - { - get - { - if (HttpContext.Current != null && HttpContext.Current.Session[this.SessionKeys.CurrentPageLocation] != null) - return (PageLocation)HttpContext.Current.Session[this.SessionKeys.PreviousPageLocation]; - - return new PageLocation(ForumPage.Default, Settings.StartPageAbsolutePath); - - } - } + public string PreviousPage => + CacheAsc.Get(this.SessionKeys.PreviousPageLocation) != null + ? CacheAsc.Get(this.SessionKeys.PreviousPageLocation).Url + : Settings.StartPageAbsolutePath; public void SetCurrentPage(ForumPage page) { - if (HttpContext.Current != null) - { - PageLocation current = new PageLocation(ForumPage.Default, Settings.StartPageAbsolutePath); - if (HttpContext.Current.Session[this.SessionKeys.CurrentPageLocation] != null) - current = (PageLocation)HttpContext.Current.Session[this.SessionKeys.CurrentPageLocation]; + var current = new PageLocation(ForumPage.Default, Settings.StartPageAbsolutePath); + if (CacheAsc.Get(this.SessionKeys.CurrentPageLocation) != null) + current = CacheAsc.Get(this.SessionKeys.CurrentPageLocation); - PageLocation previous = (PageLocation)current.Clone(); + var previous = (PageLocation)current.Clone(); - if (previous.Page != page) - HttpContext.Current.Session[this.SessionKeys.PreviousPageLocation] = previous; + if (previous.Page != page) + CacheAsc.Insert(this.SessionKeys.PreviousPageLocation, previous, TimeSpan.FromMinutes(15)); - current = new PageLocation(page, HttpContext.Current.Request.GetUrlRewriter().AbsoluteUri); - HttpContext.Current.Session[this.SessionKeys.CurrentPageLocation] = current; - } + current = new PageLocation(page, HttpContext.Current.Request.GetUrlRewriter().AbsoluteUri); + CacheAsc.Insert(this.SessionKeys.CurrentPageLocation, current, TimeSpan.FromMinutes(15)); } public ForumSessionKeys SessionKeys { get; private set; } @@ -283,17 +264,8 @@ namespace ASC.Web.UserControls.Forum.Common { _settingsID = settingsID; } - - public string CurrentPageLocation - { - get { return "forum_current_page_location" + _settingsID.ToString(); } - } - - public string PreviousPageLocation - { - get { return "forum_previous_page_location" + _settingsID.ToString(); } - } - + public string CurrentPageLocation => "forum_current_page_location" + _settingsID.ToString() + SecurityContext.CurrentAccount.ID.ToString(); + public string PreviousPageLocation => "forum_previous_page_location" + _settingsID.ToString() + SecurityContext.CurrentAccount.ID.ToString(); } } diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/ForumEditor.ascx.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/ForumEditor.ascx.cs index 8451ad572..30c82d1d6 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/ForumEditor.ascx.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/ForumEditor.ascx.cs @@ -45,7 +45,7 @@ namespace ASC.Web.Community.Forum if (!ForumManager.Instance.ValidateAccessSecurityAction(ForumAction.GetAccessForumEditor, null)) { - Response.Redirect(ForumManager.Instance.PreviousPage.Url); + Response.Redirect(ForumManager.Instance.PreviousPage); return; } diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/NewPostControl.ascx b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/NewPostControl.ascx index 0f0334f37..4f05b275e 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/NewPostControl.ascx +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/NewPostControl.ascx @@ -22,7 +22,7 @@ <%=ForumUCResource.Message%>:
    - +
    <%if (PostAction == PostAction.Edit)%> diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/NewPostControl.ascx.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/NewPostControl.ascx.cs index 4375a0806..034ce9aaf 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/NewPostControl.ascx.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/NewPostControl.ascx.cs @@ -1079,7 +1079,7 @@ namespace ASC.Web.UserControls.Forum protected string RenderRedirectUpload() { return string.Format("{0}://{1}:{2}{3}", Request.GetUrlRewriter().Scheme, Request.GetUrlRewriter().Host, Request.GetUrlRewriter().Port, - VirtualPathUtility.ToAbsolute("~/") + "fckuploader.ashx?newEditor=true&esid=" + _settings.FileStoreModuleID + (PostAction == PostAction.Edit ? "&iid=" + EditedPost.ID.ToString() : "")); + VirtualPathUtility.ToAbsolute("~/") + "fckuploader.ashx?esid=" + _settings.FileStoreModuleID + (PostAction == PostAction.Edit ? "&iid=" + EditedPost.ID.ToString() : "")); } protected void OnUnSubscribeForSubscriptionType(INotifyAction action, string objId, Guid userId) diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/PostListControl.ascx.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/PostListControl.ascx.cs index c6c2aab02..07e44ba11 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/PostListControl.ascx.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/PostListControl.ascx.cs @@ -225,7 +225,7 @@ namespace ASC.Web.UserControls.Forum private void InitScripts() { var jsResource = new StringBuilder(); - jsResource.Append("jq('#tableForNavigation select').val(" + PostPageSize + ").change(function(evt) {changePostCountOfRows(this.value);}).tlCombobox();"); + jsResource.Append("jq('#tableForNavigation select').val(" + PostPageSize + ").on('change', function(evt) {changePostCountOfRows(this.value);}).tlCombobox();"); Page.RegisterInlineScript(jsResource.ToString(), true); } } diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/Resources/ForumUCResource.az-Latn-AZ.resx b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/Resources/ForumUCResource.az-Latn-AZ.resx index 9415d4458..7a4a1aa43 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/Resources/ForumUCResource.az-Latn-AZ.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/Resources/ForumUCResource.az-Latn-AZ.resx @@ -85,6 +85,9 @@ Redaktə olundu + + Boş fayl + Bu əyləmi gerçəkləşdirməyə icazə yoxdur @@ -103,6 +106,9 @@ Yazı silinə bilməz, çünki başqa yazılar da bu yazı ilə əlaqəlidir + + Maksimum fayl ölçüsü keçildi + Fayl yapışdır @@ -226,4 +232,4 @@ Dünən - \ No newline at end of file + diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/TopicEditorControl.ascx.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/TopicEditorControl.ascx.cs index b5ec89c82..873ac5ff3 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/TopicEditorControl.ascx.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/TopicEditorControl.ascx.cs @@ -47,10 +47,7 @@ namespace ASC.Web.UserControls.Forum protected string _errorMessage = ""; protected Settings _settings; - protected string PreviousPageUrl - { - get { return _forumManager.PreviousPage.Url; } - } + protected string PreviousPageUrl => _forumManager.PreviousPage; private string _tagString = ""; private string _tagValues = ""; @@ -77,7 +74,7 @@ namespace ASC.Web.UserControls.Forum if (idTopic == 0) { - Response.Redirect(_forumManager.PreviousPage.Url); + Response.Redirect(_forumManager.PreviousPage); return; } topicId = idTopic; @@ -85,13 +82,13 @@ namespace ASC.Web.UserControls.Forum EditableTopic = ForumDataProvider.GetTopicByID(TenantProvider.CurrentTenantID, idTopic); if (EditableTopic == null) { - Response.Redirect(_forumManager.PreviousPage.Url); + Response.Redirect(_forumManager.PreviousPage); return; } if (!_forumManager.ValidateAccessSecurityAction(ForumAction.TopicEdit, EditableTopic)) { - Response.Redirect(_forumManager.PreviousPage.Url); + Response.Redirect(_forumManager.PreviousPage); return; } @@ -202,7 +199,7 @@ namespace ASC.Web.UserControls.Forum EditableTopic.Sticky, EditableTopic.Closed); FactoryIndexer.UpdateAsync(EditableTopic); _errorMessage = "
    " + ForumUCResource.SuccessfullyEditTopicMessage + "
    "; - Response.Redirect(_forumManager.PreviousPage.Url); + Response.Redirect(_forumManager.PreviousPage); } diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/TopicListControl.ascx.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/TopicListControl.ascx.cs index f628783e3..40d7621d0 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/TopicListControl.ascx.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/UserControls/TopicListControl.ascx.cs @@ -123,7 +123,7 @@ namespace ASC.Web.UserControls.Forum private void InitScripts() { var jsResource = new StringBuilder(); - jsResource.Append("jq('#tableForNavigation select').val(" + PageSize + ").change(function(evt) {changeCountOfRows(this.value);}).tlCombobox();"); + jsResource.Append("jq('#tableForNavigation select').val(" + PageSize + ").on('change', function(evt) {changeCountOfRows(this.value);}).tlCombobox();"); Page.RegisterInlineScript(jsResource.ToString(), true); } } diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/js/forum.js b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/js/forum.js index e9efdfcca..da85694c3 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/js/forum.js +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/js/forum.js @@ -42,7 +42,7 @@ var ForumManager = new function() { var subject = ''; if (jq('#forum_postType').val() != 0) - subject = jq.trim(jq('#forum_subject').val()); + subject = jq('#forum_subject').val().trim(); if (subject == '' && jq('#forum_postType').val() != 0) { if (jq('#forum_postType').val() == 2) { @@ -54,7 +54,7 @@ var ForumManager = new function() { ShowRequiredError(jq("input[name$='forum_subject']")); } - jq('#forum_subject').focus(); + jq('#forum_subject').trigger("focus"); return; } @@ -62,7 +62,7 @@ var ForumManager = new function() { jq('#forum_subject').val(subject); var html = ForumManager.forumEditor.getData(); - if (jq.trim(html) == "") { + if (html.trim() == "") { toastr.error(this.TextEmptyMessage); return; } @@ -117,14 +117,14 @@ var ForumManager = new function() { } this.SaveEditTopic = function() { - var subject = jq.trim(jq('#forum_subject').val()); + var subject = jq('#forum_subject').val().trim(); if (subject == '') { if (jq('#forum_topicType').val() == 1) jq('#forum_errorMessage').html('
    ' + this.QuestionEmptyMessage + '
    '); else jq('#forum_errorMessage').html('
    ' + this.SubjectEmptyMessage + '
    '); - jq('#forum_subject').focus(); + jq('#forum_subject').trigger("focus"); jq('#forum_errorMessage').scrollTo(); return; } @@ -358,7 +358,7 @@ var ForumManager = new function() { jq('#forum_mf').attr('class', 'tintLight cornerAll borderBase'); - jq('body').click(function(event) { + jq('body').on("click", function(event) { var elt = (event.target) ? event.target : event.srcElement; var isHide = true; @@ -616,11 +616,11 @@ jq(document).ready(function() { } }); resizeContent(); - jq(window).resize(function () { + jq(window).on("resize", function () { resizeContent(); }); var textInput = jq("#forum_subject"); if (textInput.length) - textInput.focus(); + textInput.trigger("focus"); }); diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/js/forummaker.js b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/js/forummaker.js index f4f9ce395..349f617e9 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/js/forummaker.js +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/js/forummaker.js @@ -591,7 +591,7 @@ var ForumMakerProvider = new function() { ForumMakerProvider.InitSortCategory(); var textInput = jq("#forum_fmCategoryName"); if (textInput.length) - textInput.focus(); + textInput.trigger("focus"); }); }; diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/js/searchhelper.js b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/js/searchhelper.js index 2f6fbe3e8..d6ae75246 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/js/searchhelper.js +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Forum/js/searchhelper.js @@ -98,7 +98,7 @@ var SearchHelper = function(inputID,hsItemClass,hsItemSelectClass,code,selectCod } jq('#'+ContainerElementID).append(bodySH+bodySHLoader); - eval('jq("#'+inputID+'").keyup(function(event){'+varName+'.Handler(event);})'); + eval('jq("#'+inputID+'").on("keyup", function(event){'+varName+'.Handler(event);})'); } catch(e){}; } @@ -112,7 +112,7 @@ var SearchHelper = function(inputID,hsItemClass,hsItemSelectClass,code,selectCod var bodySHLoader = ''; jq('#'+ContainerElementID).append(bodySH+bodySHLoader); - eval('jq("#'+inputID+'").keyup(function(event){'+varName+'.Handler(event);})'); + eval('jq("#'+inputID+'").on("keyup", function(event){'+varName+'.Handler(event);})'); } catch(e){}; }); @@ -126,7 +126,7 @@ var SearchHelper = function(inputID,hsItemClass,hsItemSelectClass,code,selectCod else if (e.which) code = e.which; var isVisible = jq('#'+this.ID).is(':visible'); - var text = jq.trim(jq('#'+this.InputID).val()); + var text = jq('#'+this.InputID).val().trim(); if (code == 38 && isVisible)//up { @@ -298,7 +298,7 @@ var SearchHelper = function(inputID,hsItemClass,hsItemSelectClass,code,selectCod { jq('#'+this.ID).hide(); this.DeleteItems(); - jq("body").unbind("click"); + jq("body").off("click"); }; this.GetItemById = function(idItem) @@ -384,16 +384,16 @@ var SearchHelper = function(inputID,hsItemClass,hsItemSelectClass,code,selectCod var isFirst = 1; for(var i=0;i(v => v.Name)), + new TagValue(NewsConst.TagCaption, feed.Caption), + new TagValue("CommentBody", HtmlUtility.GetFull(comment.Comment)), + new TagValue(NewsConst.TagDate, comment.Date.ToShortString()), + new TagValue(NewsConst.TagURL, CommonLinkUtility.GetFullAbsolutePath("~/Products/Community/Modules/News/Default.aspx?docid=" + feed.Id)), + new TagValue("CommentURL", CommonLinkUtility.GetFullAbsolutePath("~/Products/Community/Modules/News/Default.aspx?docid=" + feed.Id + "#container_" + comment.Id.ToString(CultureInfo.InvariantCulture))), + new TagValue(NewsConst.TagUserName, DisplayUserSettings.GetFullUserName(SecurityContext.CurrentAccount.ID)), + new TagValue(NewsConst.TagUserUrl, CommonLinkUtility.GetFullAbsolutePath(CommonLinkUtility.GetUserProfile(SecurityContext.CurrentAccount.ID))) + }; + var initatorInterceptor = new InitiatorInterceptor(new DirectRecipient(comment.Creator, "")); try { NewsNotifyClient.NotifyClient.AddInterceptor(initatorInterceptor); - NewsNotifyClient.NotifyClient.SendNoticeAsync( - NewsConst.NewComment, feed.Id.ToString(CultureInfo.InvariantCulture), - null, - new TagValue(NewsConst.TagFEED_TYPE, feedType), - //new TagValue(NewsConst.TagAnswers, feed.Variants.ConvertAll(v => v.Name)), - new TagValue(NewsConst.TagCaption, feed.Caption), - new TagValue("CommentBody", HtmlUtility.GetFull(comment.Comment)), - new TagValue(NewsConst.TagDate, comment.Date.ToShortString()), - new TagValue(NewsConst.TagURL, CommonLinkUtility.GetFullAbsolutePath("~/Products/Community/Modules/News/Default.aspx?docid=" + feed.Id)), - new TagValue("CommentURL", CommonLinkUtility.GetFullAbsolutePath("~/Products/Community/Modules/News/Default.aspx?docid=" + feed.Id + "#container_" + comment.Id.ToString(CultureInfo.InvariantCulture))), - new TagValue(NewsConst.TagUserName, DisplayUserSettings.GetFullUserName(SecurityContext.CurrentAccount.ID)), - new TagValue(NewsConst.TagUserUrl, CommonLinkUtility.GetFullAbsolutePath(CommonLinkUtility.GetUserProfile(SecurityContext.CurrentAccount.ID))), - GetReplyToTag(feed, comment) - ); + + var mentionedUsers = MentionProvider.GetMentionedUsers(comment.Comment); + var mentionedUserIds = mentionedUsers.Select(u => u.ID.ToString()); + + var provider = NewsNotifySource.Instance.GetSubscriptionProvider(); + + var objectID = feed.Id.ToString(CultureInfo.InvariantCulture); + + var recipients = provider + .GetRecipients(NewsConst.NewComment, objectID) + .Where(r => !mentionedUserIds.Contains(r.ID)) + .ToArray(); + + NewsNotifyClient.NotifyClient.SendNoticeToAsync(NewsConst.NewComment, objectID, recipients, false, tags); + + if (mentionedUsers.Length > 0) + { + NewsNotifyClient.NotifyClient.SendNoticeToAsync(NewsConst.MentionForFeedComment, objectID, mentionedUsers, false, tags); + } } finally { @@ -442,11 +458,6 @@ namespace ASC.Web.Community.News.Code.DAO } } - private static TagValue GetReplyToTag(Feed feed, FeedComment feedComment) - { - return ReplyToTagProvider.Comment("event", feed.Id.ToString(CultureInfo.InvariantCulture), feedComment != null ? feedComment.Id.ToString(CultureInfo.InvariantCulture) : null); - } - public void RemoveFeedComment(long commentId) { dbManager.ExecuteNonQuery(Delete("events_comment").Where("Id", commentId)); diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/News/Default.aspx.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/News/Default.aspx.cs index 9a50bde8e..2d216d509 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/News/Default.aspx.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/News/Default.aspx.cs @@ -147,7 +147,7 @@ namespace ASC.Web.Community.News private void InitScripts() { var jsResource = new StringBuilder(); - jsResource.Append("jq('#tableForNavigation select').val(" + PageSize + ").change(function(evt) {changeCountOfRows(this.value);}).tlCombobox();"); + jsResource.Append("jq('#tableForNavigation select').val(" + PageSize + ").on('change', function(evt) {changeCountOfRows(this.value);}).tlCombobox();"); Page.RegisterInlineScript(jsResource.ToString(), true); } diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/News/EditNews.aspx b/web/studio/ASC.Web.Studio/Products/Community/Modules/News/EditNews.aspx index 113e22936..1c830a419 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/News/EditNews.aspx +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/News/EditNews.aspx @@ -29,7 +29,7 @@
    <%=NewsResource.NewsBody%>:
    - +
    diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/News/EditNews.aspx.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/News/EditNews.aspx.cs index 9e7057bc6..d58fe368b 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/News/EditNews.aspx.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/News/EditNews.aspx.cs @@ -187,7 +187,7 @@ namespace ASC.Web.Community.News [AjaxMethod(HttpSessionStateRequirement.ReadWrite)] public string GetPreviewFull(string html) { - return HtmlUtility.GetFull(html); + return HtmlUtility.GetFull(html, false); } [AjaxMethod(HttpSessionStateRequirement.ReadWrite)] @@ -243,7 +243,7 @@ namespace ASC.Web.Community.News protected string RedirectUpload() { return string.Format("{0}://{1}:{2}{3}", Request.GetUrlRewriter().Scheme, Request.GetUrlRewriter().Host, Request.GetUrlRewriter().Port, - VirtualPathUtility.ToAbsolute("~/") + "fckuploader.ashx?esid=news&newEditor=true" + (FeedId != 0 ? "&iid=" + FeedId.ToString() : "")); + VirtualPathUtility.ToAbsolute("~/") + "fckuploader.ashx?esid=news" + (FeedId != 0 ? "&iid=" + FeedId.ToString() : "")); } } diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/News/NewsConst.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/News/NewsConst.cs index 199d9fe4d..4eab7ceee 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/News/NewsConst.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/News/NewsConst.cs @@ -35,6 +35,7 @@ namespace ASC.Web.Community.News public static INotifyAction NewFeed = new NotifyAction("new feed", "news added"); public static INotifyAction NewComment = new NotifyAction("new feed comment", "new feed comment"); + public static INotifyAction MentionForFeedComment = new NotifyAction("mention for feed comment", "mention for feed comment"); public static string TagCaption = "Caption"; public static string TagText = "Text"; diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/News/Resources/NewsPatternResource.Designer.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/News/Resources/NewsPatternResource.Designer.cs index 04564a860..ee43e1e61 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/News/Resources/NewsPatternResource.Designer.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/News/Resources/NewsPatternResource.Designer.cs @@ -60,6 +60,23 @@ namespace ASC.Web.Community.Modules.News.Resources { } } + /// + /// Looks up a localized string similar to h1.#if($FEED_TYPE == "poll")You were mentioned in comment to Poll#end#if($FEED_TYPE == "feed")You were mentioned in comment to Event#end: "$Caption":"$URL" + /// + ///$Date "$UserName":"$UserURL" mentioned you in the comment to the "$Caption":"$URL" #if($FEED_TYPE == "poll")poll#end#if($FEED_TYPE == "feed")event#end: + /// + ///$CommentBody + /// + ///"Read More":"$CommentURL" + /// + ///^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. If you do not want to receive the notif [rest of string was truncated]";. + /// + public static string pattern_mention_for_feed_comment { + get { + return ResourceManager.GetString("pattern_mention_for_feed_comment", resourceCulture); + } + } + /// /// Looks up a localized string similar to h1.#if($FEED_TYPE == "feed")New Event Added: "$Caption":"$URL" /// @@ -110,6 +127,24 @@ namespace ASC.Web.Community.Modules.News.Resources { } } + /// + /// Looks up a localized string similar to Community. #if($FEED_TYPE == "poll")Mention in comment to poll#end#if($FEED_TYPE == "feed")Mention in comment to event#end: $Caption. + /// + public static string subject_mention_for_feed_comment { + get { + return ResourceManager.GetString("subject_mention_for_feed_comment", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Community. #if($FEED_TYPE == "poll")Mention in comment to poll#end#if($FEED_TYPE == "feed")Mention in comment to event#end: [$Caption]($URL). + /// + public static string subject_mention_for_feed_comment_tg { + get { + return ResourceManager.GetString("subject_mention_for_feed_comment_tg", resourceCulture); + } + } + /// /// Looks up a localized string similar to Community. #if($FEED_TYPE == "poll")New poll added#end#if($FEED_TYPE == "feed")New event added#end: $Caption. /// diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/News/Resources/NewsPatternResource.az-Latn-AZ.resx b/web/studio/ASC.Web.Studio/Products/Community/Modules/News/Resources/NewsPatternResource.az-Latn-AZ.resx index 395df02e1..1e9bf7d03 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/News/Resources/NewsPatternResource.az-Latn-AZ.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/News/Resources/NewsPatternResource.az-Latn-AZ.resx @@ -83,7 +83,7 @@ $Date пользователем "$UserName":"$UserURL" добавлен нов #end -^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Əgər siz xəbərlərin əlavə olunmasına dair bildirişlər almaq istəmirsinizdə, "abunəlik ayarlarını" dəyişdirə bilərsiniz:"$RecipientSubscriptionConfigURL".^ +^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Əgər siz xəbərlərin əlavə olunmasına dair bildirişlər almaq istəmirsinizdə, "abunəlik ayarlarını":"$RecipientSubscriptionConfigURL" dəyişdirə bilərsiniz.^ h1.#if($FEED_TYPE == "poll")Sorğuya yeni şərh#end#if($FEED_TYPE == "feed")Xəbərlərə yeni şərh#end: "$Caption":"$URL" @@ -94,7 +94,7 @@ $CommentBody "Ardını oxu":"$CommentURL" -^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu xəbərə dair yeni şərhlər almaq istəmirsinizsə, "abunəlik ayarlarını" dəyişdirin:"$RecipientSubscriptionConfigURL".^ +^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu xəbərə dair yeni şərhlər almaq istəmirsinizsə, "abunəlik ayarlarını":"$RecipientSubscriptionConfigURL" dəyişdirin.^ İcma. #if($FEED_TYPE == "poll")Yeni sorğu əlavə olundu#end#if($FEED_TYPE == "feed")Yeni olay əlavə olundu#end: $Caption @@ -102,4 +102,10 @@ $CommentBody İcma. #if($FEED_TYPE == "poll")Sorğuya yeni şərh#end#if($FEED_TYPE == "feed")Xəbərlərə yeni şərh#end: $Caption - \ No newline at end of file + + İcma. #if($FEED_TYPE == "poll") Sorğuya yeni şərh#end#if($FEED_TYPE == "feed") Tədbirə yeni şərh:[$Caption]($CommentURL) + + + İcma. #if($FEED_TYPE == "poll")Yeni sorğu əlavə edildi#end#if($FEED_TYPE == "feed")Yeni tədbir əlavəedildi#end: [$Caption]($URL) + + diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/News/Resources/NewsPatternResource.resx b/web/studio/ASC.Web.Studio/Products/Community/Modules/News/Resources/NewsPatternResource.resx index 4997b5c5e..2d9749eb5 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/News/Resources/NewsPatternResource.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/News/Resources/NewsPatternResource.resx @@ -58,6 +58,17 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + h1.#if($FEED_TYPE == "poll")You were mentioned in comment to Poll#end#if($FEED_TYPE == "feed")You were mentioned in comment to Event#end: "$Caption":"$URL" + +$Date "$UserName":"$UserURL" mentioned you in the comment to the "$Caption":"$URL" #if($FEED_TYPE == "poll")poll#end#if($FEED_TYPE == "feed")event#end: + +$CommentBody + +"Read More":"$CommentURL" + +^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. If you do not want to receive the notifications about new comments added to this event, please manage your "subscription settings":"$RecipientSubscriptionConfigURL".^ + h1.#if($FEED_TYPE == "feed")New Event Added: "$Caption":"$URL" @@ -96,6 +107,12 @@ $CommentBody ^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. If you do not want to receive the notifications about new comments added to this event, please manage your "subscription settings":"$RecipientSubscriptionConfigURL".^ + + Community. #if($FEED_TYPE == "poll")Mention in comment to poll#end#if($FEED_TYPE == "feed")Mention in comment to event#end: $Caption + + + Community. #if($FEED_TYPE == "poll")Mention in comment to poll#end#if($FEED_TYPE == "feed")Mention in comment to event#end: [$Caption]($URL) + Community. #if($FEED_TYPE == "poll")New poll added#end#if($FEED_TYPE == "feed")New event added#end: $Caption diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/News/Resources/NewsResource.az-Latn-AZ.resx b/web/studio/ASC.Web.Studio/Products/Community/Modules/News/Resources/NewsResource.az-Latn-AZ.resx index 71bb63e8b..3e107ab4a 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/News/Resources/NewsResource.az-Latn-AZ.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/News/Resources/NewsResource.az-Latn-AZ.resx @@ -118,6 +118,9 @@ Öz olaylarınızı yaradın, önəmli elanlarınızı dərc edin, həmkarlarınızın fikirlərini öyrənmək üçün son xəbərləri və sorğuları portalınızda paylaşın. + + Tədbirlərə, mühüm elanlara, son xəbərlərə və sorğulara baxın və portalınızda həmkarlarınızın fikirlərini öyrənin. + Giriş rədd edildi @@ -208,4 +211,4 @@ Şərhlərə abunəliyi ləğv et - \ No newline at end of file + diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/News/Resources/news_patterns.xml b/web/studio/ASC.Web.Studio/Products/Community/Modules/News/Resources/news_patterns.xml index 8f2811025..928902604 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/News/Resources/news_patterns.xml +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/News/Resources/news_patterns.xml @@ -25,7 +25,7 @@ $URL#end - #if($FEED_TYPE == "feed")$UserName + #if($FEED_TYPE == "feed")$UserName $Text @@ -54,9 +54,31 @@ $CommentURL - $UserName + $UserName $CommentBody + + + + + + + + + $UserName + +$CommentBody + +$CommentURL + + + + + $UserName + +$CommentBody + + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/News/js/news.js b/web/studio/ASC.Web.Studio/Products/Community/Modules/News/js/news.js index 6a5ac5838..02915e602 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/News/js/news.js +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/News/js/news.js @@ -253,7 +253,7 @@ jq(document).ready(function() { } var $firstInput = jq("input[id$='feedName']"); if ($firstInput.length) { - $firstInput.focus(); + $firstInput.trigger("focus"); } var anchor = ASC.Controls.AnchorController.getAnchor(); if (anchor == "addcomment" && CommentsManagerObj) { @@ -262,7 +262,7 @@ jq(document).ready(function() { resizeContent(); - jq(window).resize(function () { + jq(window).on("resize", function () { resizeContent(); }); diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Code/Constants.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Code/Constants.cs index e5e7205d8..a22888ef5 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Code/Constants.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Code/Constants.cs @@ -35,6 +35,7 @@ namespace ASC.Web.UserControls.Wiki public static INotifyAction NewPage = new NotifyAction("new wiki page", WikiResource.NotifyAction_NewPage); public static INotifyAction EditPage = new NotifyAction("edit wiki page", WikiResource.NotifyAction_ChangePage); public static INotifyAction AddPageToCat = new NotifyAction("add page to cat", WikiResource.NotifyAction_AddPageToCat); + public static INotifyAction MentionForWikiComment = new NotifyAction("mention for wiki comment"); public static string TagPageName = "PageName"; public static string TagURL = "URL"; diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Code/Patterns/WikiPatternResource.Designer.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Code/Patterns/WikiPatternResource.Designer.cs index fe1925fea..f31e10011 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Code/Patterns/WikiPatternResource.Designer.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Code/Patterns/WikiPatternResource.Designer.cs @@ -111,6 +111,23 @@ namespace ASC.Web.Community.Modules.Wiki.Code.Patterns { } } + /// + /// Looks up a localized string similar to h1.You were mentioned in comment to Wiki Page "$PageName":"$URL" + /// + ///$Date "$UserName":"$UserURL" mentioned you in the comment to the "$PageName":"$URL" Wiki page: + /// + ///$CommentBody + /// + ///"Read More":"$URL" + /// + ///^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. If you do not want to receive the notifications about new comments added to this Wiki page, please manage your "subscription settings":"$RecipientSubscriptionConfigURL".^. + /// + public static string pattern_MentionForWikiComment { + get { + return ResourceManager.GetString("pattern_MentionForWikiComment", resourceCulture); + } + } + /// /// Looks up a localized string similar to Community. New Wiki page added: $PageName. /// @@ -164,5 +181,23 @@ namespace ASC.Web.Community.Modules.Wiki.Code.Patterns { return ResourceManager.GetString("subject_3_tg", resourceCulture); } } + + /// + /// Looks up a localized string similar to Community. Mention in comment to Wiki page: $PageName. + /// + public static string subject_MentionForWikiComment { + get { + return ResourceManager.GetString("subject_MentionForWikiComment", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Community. Mention in comment to Wiki page: [$PageName]($URL). + /// + public static string subject_MentionForWikiComment_tg { + get { + return ResourceManager.GetString("subject_MentionForWikiComment_tg", resourceCulture); + } + } } } diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Code/Patterns/WikiPatternResource.az-Latn-AZ.resx b/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Code/Patterns/WikiPatternResource.az-Latn-AZ.resx index abfae5645..71e7d8594 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Code/Patterns/WikiPatternResource.az-Latn-AZ.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Code/Patterns/WikiPatternResource.az-Latn-AZ.resx @@ -67,7 +67,7 @@ $PagePreview "Səhifəyə tam şəkildə bamxaq üçün bu keçiddən istifadə edin":"$URL" -^Portalda qeydiyyatdan keçmək üçün bu mıktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını" dəyişdirin:"$RecipientSubscriptionConfigURL".^ +^Portalda qeydiyyatdan keçmək üçün bu mıktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını":"$RecipientSubscriptionConfigURL" dəyişdirin.^ h1.Wiki'də səhifə redaktə edildi: "$PageName":"$URL" @@ -78,7 +78,7 @@ $PagePreview "Səhifəyə tam şəkildə bamxaq üçün bu keçiddən istifadə edin":"$URL" -^Portalda qeydiyyatdan keçmək üçün bu mıktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını" dəyişdirin:"$RecipientSubscriptionConfigURL".^ +^Portalda qeydiyyatdan keçmək üçün bu mıktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını":"$RecipientSubscriptionConfigURL" dəyişdirin.^ h1. Wiki səhifəsinə yeni şərh "$PageName":"$URL" @@ -89,15 +89,24 @@ $CommentBody "Səhifəyə tam şəkildə bamxaq üçün bu keçiddən istifadə edin":"$URL" -^Portalda qeydiyyatdan keçmək üçün bu mıktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını" dəyişdirin:"$RecipientSubscriptionConfigURL".^ +^Portalda qeydiyyatdan keçmək üçün bu mıktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını":"$RecipientSubscriptionConfigURL" dəyişdirin.^ İcma. Yeni Wiki səhifə əlavə olundu: $PageName + + İcma. Yeni Viki səhifəsi əlavə edildi: [$PageName]($URL) + İcma. Wiki səhifəsi redaktə olundu: $PageName + + İcma. Viki səhifəsi redaktə edildi: [$PageName]($URL) + İcma. Wiki səhifəsinə yeni şərh: $PageName - \ No newline at end of file + + İcma. Viki səhifəsinə yeni şərh: [$PageName]($URL) + + diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Code/Patterns/WikiPatternResource.resx b/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Code/Patterns/WikiPatternResource.resx index 8ed66f14a..57020dfde 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Code/Patterns/WikiPatternResource.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Code/Patterns/WikiPatternResource.resx @@ -90,6 +90,17 @@ $CommentBody "Read More":"$URL" ^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. If you do not want to receive the notifications about new comments added to this Wiki page, please manage your "subscription settings":"$RecipientSubscriptionConfigURL".^ + + + h1.You were mentioned in comment to Wiki Page "$PageName":"$URL" + +$Date "$UserName":"$UserURL" mentioned you in the comment to the "$PageName":"$URL" Wiki page: + +$CommentBody + +"Read More":"$URL" + +^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. If you do not want to receive the notifications about new comments added to this Wiki page, please manage your "subscription settings":"$RecipientSubscriptionConfigURL".^ Community. New Wiki page added: $PageName @@ -109,4 +120,10 @@ $CommentBody Community. New comment to Wiki page: [$PageName]($URL) + + Community. Mention in comment to Wiki page: $PageName + + + Community. Mention in comment to Wiki page: [$PageName]($URL) + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Code/Patterns/wiki_patterns.xml b/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Code/Patterns/wiki_patterns.xml index 8e1b878e8..d20f502be 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Code/Patterns/wiki_patterns.xml +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Code/Patterns/wiki_patterns.xml @@ -18,7 +18,7 @@ $URL - $UserName + $UserName $PagePreview @@ -42,7 +42,7 @@ $URL - $UserName + $UserName $PagePreview @@ -65,8 +65,29 @@ $URL + $UserName + +$CommentBody + + + + + + + + + $UserName +$CommentBody + +$URL + + + + + $UserName + $CommentBody diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Code/WikiEngine.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Code/WikiEngine.cs index 48f34f372..934eda414 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Code/WikiEngine.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Code/WikiEngine.cs @@ -590,12 +590,27 @@ namespace ASC.Web.UserControls.Wiki private void NotifyCommentCreated(Page page, Comment comment) { - WikiNotifyClient.SendNoticeAsync( - SecurityContext.CurrentAccount.ID.ToString(), - Constants.EditPage, - PageNameUtil.ReplaceSpaces(page.PageName), - null, - GetNotifyTags(page.PageName, "new wiki page comment", comment)); + var mentionedUsers = MentionProvider.GetMentionedUsers(comment.Body); + var mentionedUserIds = mentionedUsers.Select(u => u.ID.ToString()); + + var provider = WikiNotifySource.Instance.GetSubscriptionProvider(); + + var authorID = SecurityContext.CurrentAccount.ID.ToString(); + var objectID = PageNameUtil.ReplaceSpaces(page.PageName); + + var recipients = provider + .GetRecipients(Constants.EditPage, objectID) + .Where(r => !mentionedUserIds.Contains(r.ID)) + .ToArray(); + + var tags = GetNotifyTags(page.PageName, "new wiki page comment", comment); + + WikiNotifyClient.SendNoticeToAsync(authorID, Constants.EditPage, objectID, recipients, tags); + + if (mentionedUsers.Length > 0) + { + WikiNotifyClient.SendNoticeToAsync(authorID, Constants.MentionForWikiComment, objectID, mentionedUsers, tags); + } } private ITagValue[] GetNotifyTags(string pageName) @@ -620,8 +635,6 @@ namespace ASC.Web.UserControls.Wiki new TagValue(Constants.TagDate, TenantUtil.DateTimeNow()), new TagValue(Constants.TagPostPreview, HtmlUtil.GetText(EditPage.ConvertWikiToHtml(page.PageName, page.Body, defPage, WikiSection.Section.ImageHangler.UrlFormat, CoreContext.TenantManager.GetCurrentTenant().TenantId), 120)) }; - if (comment != null && !string.IsNullOrEmpty(pageName)) - ReplyToTagProvider.Comment("wiki", pageName, comment.Id.ToString()); if (!string.IsNullOrEmpty(patternType)) { @@ -651,8 +664,7 @@ namespace ASC.Web.UserControls.Wiki new TagValue(Constants.TagUserURL, CommonLinkUtility.GetFullAbsolutePath(CommonLinkUtility.GetUserProfile(user.ID))), new TagValue(Constants.TagDate, TenantUtil.DateTimeNow()), new TagValue(Constants.TagPostPreview, HtmlUtil.GetText(EditPage.ConvertWikiToHtml(page.PageName, page.Body, defPageHref, WikiSection.Section.ImageHangler.UrlFormat, CoreContext.TenantManager.GetCurrentTenant().TenantId), 120)), - new TagValue(Constants.TagCatName, objectId), - ReplyToTagProvider.Comment("wiki", pageName) + new TagValue(Constants.TagCatName, objectId) }; return tags.ToArray(); diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Code/WikiSubscriptionManager.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Code/WikiSubscriptionManager.cs index 6d73ee01e..87e6906e8 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Code/WikiSubscriptionManager.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Code/WikiSubscriptionManager.cs @@ -50,6 +50,20 @@ namespace ASC.Web.UserControls.Wiki NotifyClient.RemoveInterceptor(initatorInterceptor.Name); } } + + public static void SendNoticeToAsync(string AuthorID, INotifyAction action, string objectID, IRecipient[] recipients, params ITagValue[] args) + { + InitiatorInterceptor initatorInterceptor = new InitiatorInterceptor(new DirectRecipient(AuthorID, "")); + try + { + NotifyClient.AddInterceptor(initatorInterceptor); + NotifyClient.SendNoticeToAsync(action, objectID, recipients, false, args); + } + finally + { + NotifyClient.RemoveInterceptor(initatorInterceptor.Name); + } + } } diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Default.aspx.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Default.aspx.cs index a57c4f84b..4c9d56369 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Default.aspx.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Default.aspx.cs @@ -301,7 +301,7 @@ namespace ASC.Web.Community.Wiki if (jq('#WikiActionsMenuPanel .dropdown-content a').length == 0) { jq('span.menu-small').hide(); } - jq('input[id$=txtPageName]').focus();"; + jq('input[id$=txtPageName]').trigger('focus');"; if (Action == ActionOnPage.AddNew || Action == ActionOnPage.Edit) script += "jq.confirmBeforeUnload(window.checkUnloadFunc);"; diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Handlers/WikiFileHandler.cs b/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Handlers/WikiFileHandler.cs index 1b0b17e2e..0e01b1e39 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Handlers/WikiFileHandler.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Handlers/WikiFileHandler.cs @@ -69,8 +69,25 @@ namespace ASC.Web.UserControls.Wiki.Handlers return; } + var inline = context.Request["inline"] ?? string.Empty; + var storage = StorageFactory.GetStorage(CoreContext.TenantManager.GetCurrentTenant().TenantId.ToString(), WikiSection.Section.DataStorage.ModuleName); - context.Response.Redirect(storage.GetUri(WikiSection.Section.DataStorage.DefaultDomain, file.FileLocation).ToString()); + + if (inline.ToLowerInvariant() == "true") + { + context.Response.Redirect(storage.GetUri(WikiSection.Section.DataStorage.DefaultDomain, file.FileLocation).ToString()); + } + else + { + context.Response.ContentType = MimeMapping.GetMimeMapping(file.FileName); + context.Response.AddHeader("Content-Disposition", string.Format("attachment; filename=\"{0}\"", file.FileName)); + + using (var stream = storage.GetReadStream(WikiSection.Section.DataStorage.DefaultDomain, file.FileLocation)) + { + context.Response.AddHeader("Content-Length", stream.Length.ToString()); + stream.CopyTo(context.Response.OutputStream); + } + } } private Data.File GetFile(string fileName) diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Resources/WikiResource.az-Latn-AZ.resx b/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Resources/WikiResource.az-Latn-AZ.resx index 9b89c5732..39ff46027 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Resources/WikiResource.az-Latn-AZ.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Resources/WikiResource.az-Latn-AZ.resx @@ -193,6 +193,9 @@ Səhifənin adı düzgün deyil. Lütfən, alternativ ad daxil etməyə çalışın. + + Mətn sahəsi boşdur. + Bu başlıqlı səhifə artıq mövcuddur. @@ -265,4 +268,4 @@ Versiya - \ No newline at end of file + diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Resources/WikiUCResource.az-Latn-AZ.resx b/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Resources/WikiUCResource.az-Latn-AZ.resx index c13fe6285..bb6a4ed4b 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Resources/WikiUCResource.az-Latn-AZ.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/Resources/WikiUCResource.az-Latn-AZ.resx @@ -402,4 +402,4 @@ Son versiyanı tapa bilərsiniz [[{0}|burada]] Mündəricat - \ No newline at end of file + diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/WikiUC/fckeditor/editor/fckdialog.html b/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/WikiUC/fckeditor/editor/fckdialog.html index 5568e9d0a..686b38540 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/WikiUC/fckeditor/editor/fckdialog.html +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/WikiUC/fckeditor/editor/fckdialog.html @@ -412,7 +412,7 @@ var Selection = // Move the focus to the Cancel button so even if the dialog contains a // contentEditable element the selection is properly restored in the editor #2496 window.focus() ; - $( 'btnCancel' ).focus() ; + $( 'btnCancel' ).focus(); FCK.Selection.Restore() ; }, diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/WikiUC/fckeditor/editor/js/fckeditorcode_gecko.js b/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/WikiUC/fckeditor/editor/js/fckeditorcode_gecko.js index 3c72bd474..4d5ed0e6f 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/WikiUC/fckeditor/editor/js/fckeditorcode_gecko.js +++ b/web/studio/ASC.Web.Studio/Products/Community/Modules/Wiki/WikiUC/fckeditor/editor/js/fckeditorcode_gecko.js @@ -62,7 +62,7 @@ var FCKW3CRange=function(A){this._Document=A;this.startContainer=null;this.start var FCKEnterKey=function(A,B,C,D){this.Window=A;this.EnterMode=B||'p';this.ShiftEnterMode=C||'br';var E=new FCKKeystrokeHandler(false);E._EnterKey=this;E.OnKeystroke=FCKEnterKey_OnKeystroke;E.SetKeystrokes([[13,'Enter'],[SHIFT+13,'ShiftEnter'],[8,'Backspace'],[CTRL+8,'CtrlBackspace'],[46,'Delete']]);this.TabText='';if (D>0||FCKBrowserInfo.IsSafari){while (D--) this.TabText+='\xa0';E.SetKeystrokes([9,'Tab']);};E.AttachToElement(A.document);};function FCKEnterKey_OnKeystroke(A,B){var C=this._EnterKey;try{switch (B){case 'Enter':return C.DoEnter();break;case 'ShiftEnter':return C.DoShiftEnter();break;case 'Backspace':return C.DoBackspace();break;case 'Delete':return C.DoDelete();break;case 'Tab':return C.DoTab();break;case 'CtrlBackspace':return C.DoCtrlBackspace();break;}}catch (e){};return false;};FCKEnterKey.prototype.DoEnter=function(A,B){FCKUndo.SaveUndoStep();this._HasShift=(B===true);var C=FCKSelection.GetParentElement();var D=new FCKElementPath(C);var E=A||this.EnterMode;if (E=='br'||D.Block&&D.Block.tagName.toLowerCase()=='pre') return this._ExecuteEnterBr();else return this._ExecuteEnterBlock(E);};FCKEnterKey.prototype.DoShiftEnter=function(){return this.DoEnter(this.ShiftEnterMode,true);};FCKEnterKey.prototype.DoBackspace=function(){var A=false;var B=new FCKDomRange(this.Window);B.MoveToSelection();if (FCKBrowserInfo.IsIE&&this._CheckIsAllContentsIncluded(B,this.Window.document.body)){this._FixIESelectAllBug(B);return true;};var C=B.CheckIsCollapsed();if (!C){if (FCKBrowserInfo.IsIE&&this.Window.document.selection.type.toLowerCase()=="control"){var D=this.Window.document.selection.createRange();for (var i=D.length-1;i>=0;i--){var E=D.item(i);E.parentNode.removeChild(E);};return true;};return false;};if (FCKBrowserInfo.IsIE){var F=FCKDomTools.GetPreviousSourceElement(B.StartNode,true);if (F&&F.nodeName.toLowerCase()=='br'){var G=B.Clone();G.SetStart(F,4);if (G.CheckIsEmpty()){F.parentNode.removeChild(F);return true;}}};var H=B.StartBlock;var I=B.EndBlock;if (B.StartBlockLimit==B.EndBlockLimit&&H&&I){if (!C){var J=B.CheckEndOfBlock();B.DeleteContents();if (H!=I){B.SetStart(I,1);B.SetEnd(I,1);};B.Select();A=(H==I);};if (B.CheckStartOfBlock()){var K=B.StartBlock;var L=FCKDomTools.GetPreviousSourceElement(K,true,['BODY',B.StartBlockLimit.nodeName],['UL','OL']);A=this._ExecuteBackspace(B,L,K);}else if (FCKBrowserInfo.IsGeckoLike){B.Select();}};B.Release();return A;};FCKEnterKey.prototype.DoCtrlBackspace=function(){FCKUndo.SaveUndoStep();var A=new FCKDomRange(this.Window);A.MoveToSelection();if (FCKBrowserInfo.IsIE&&this._CheckIsAllContentsIncluded(A,this.Window.document.body)){this._FixIESelectAllBug(A);return true;};return false;};FCKEnterKey.prototype._ExecuteBackspace=function(A,B,C){var D=false;if (!B&&C&&C.nodeName.IEquals('LI')&&C.parentNode.parentNode.nodeName.IEquals('LI')){this._OutdentWithSelection(C,A);return true;};if (B&&B.nodeName.IEquals('LI')){var E=FCKDomTools.GetLastChild(B,['UL','OL']);while (E){B=FCKDomTools.GetLastChild(E,'LI');E=FCKDomTools.GetLastChild(B,['UL','OL']);}};if (B&&C){if (C.nodeName.IEquals('LI')&&!B.nodeName.IEquals('LI')){this._OutdentWithSelection(C,A);return true;};var F=C.parentNode;var G=B.nodeName.toLowerCase();if (FCKListsLib.EmptyElements[G]!=null||G=='table'){FCKDomTools.RemoveNode(B);D=true;}else{FCKDomTools.RemoveNode(C);while (F.innerHTML.Trim().length==0){var H=F.parentNode;H.removeChild(F);F=H;};FCKDomTools.LTrimNode(C);FCKDomTools.RTrimNode(B);A.SetStart(B,2,true);A.Collapse(true);var I=A.CreateBookmark(true);if (!C.tagName.IEquals(['TABLE'])) FCKDomTools.MoveChildren(C,B);A.SelectBookmark(I);D=true;}};return D;};FCKEnterKey.prototype.DoDelete=function(){FCKUndo.SaveUndoStep();var A=false;var B=new FCKDomRange(this.Window);B.MoveToSelection();if (FCKBrowserInfo.IsIE&&this._CheckIsAllContentsIncluded(B,this.Window.document.body)){this._FixIESelectAllBug(B);return true;};if (B.CheckIsCollapsed()&&B.CheckEndOfBlock(FCKBrowserInfo.IsGeckoLike)){var C=B.StartBlock;var D=FCKTools.GetElementAscensor(C,'td');var E=FCKDomTools.GetNextSourceElement(C,true,[B.StartBlockLimit.nodeName],['UL','OL','TR'],true);if (D){var F=FCKTools.GetElementAscensor(E,'td');if (F!=D) return true;};A=this._ExecuteBackspace(B,C,E);};B.Release();return A;};FCKEnterKey.prototype.DoTab=function(){var A=new FCKDomRange(this.Window);A.MoveToSelection();var B=A._Range.startContainer;while (B){if (B.nodeType==1){var C=B.tagName.toLowerCase();if (C=="tr"||C=="td"||C=="th"||C=="tbody"||C=="table") return false;else break;};B=B.parentNode;};if (this.TabText){A.DeleteContents();A.InsertNode(this.Window.document.createTextNode(this.TabText));A.Collapse(false);A.Select();};return true;};FCKEnterKey.prototype._ExecuteEnterBlock=function(A,B){var C=B||new FCKDomRange(this.Window);var D=C.SplitBlock(A);if (D){var E=D.PreviousBlock;var F=D.NextBlock;var G=D.WasStartOfBlock;var H=D.WasEndOfBlock;if (F){if (F.parentNode.nodeName.IEquals('li')){FCKDomTools.BreakParent(F,F.parentNode);FCKDomTools.MoveNode(F,F.nextSibling,true);}}else if (E&&E.parentNode.nodeName.IEquals('li')){FCKDomTools.BreakParent(E,E.parentNode);C.MoveToElementEditStart(E.nextSibling);FCKDomTools.MoveNode(E,E.previousSibling);};if (!G&&!H){if (F.nodeName.IEquals('li')&&F.firstChild&&F.firstChild.nodeName.IEquals(['ul','ol'])) F.insertBefore(FCKTools.GetElementDocument(F).createTextNode('\xa0'),F.firstChild);if (F) C.MoveToElementEditStart(F);}else{if (G&&H&&E.tagName.toUpperCase()=='LI'){C.MoveToElementStart(E);this._OutdentWithSelection(E,C);C.Release();return true;};var I;if (E){var J=E.tagName.toUpperCase();if (!this._HasShift&&!(/^H[1-6]$/).test(J)){I=FCKDomTools.CloneElement(E);}}else if (F) I=FCKDomTools.CloneElement(F);if (!I) I=this.Window.document.createElement(A);var K=D.ElementPath;if (K){for (var i=0,len=K.Elements.length;i=0&&(C=B[i--])){if (C.name.length>0){if (C.innerHTML!==''){if (FCKBrowserInfo.IsIE) C.className+=' FCK__AnchorC';}else{var D=FCKDocumentProcessor_CreateFakeImage('FCK__Anchor',C.cloneNode(true));D.setAttribute('_fckanchor','true',0);C.parentNode.insertBefore(D,C);C.parentNode.removeChild(C);}}}}};var FCKPageBreaksProcessor=FCKDocumentProcessor.AppendNew();FCKPageBreaksProcessor.ProcessDocument=function(A){var B=A.getElementsByTagName('DIV');var C;var i=B.length-1;while (i>=0&&(C=B[i--])){if (C.style.pageBreakAfter=='always'&&C.childNodes.length==1&&C.childNodes[0].style&&C.childNodes[0].style.display=='none'){var D=FCKDocumentProcessor_CreateFakeImage('FCK__PageBreak',C.cloneNode(true));C.parentNode.insertBefore(D,C);C.parentNode.removeChild(C);}}};var FCKEmbedAndObjectProcessor=(function(){var A=[];var B=function(el){var C=el.cloneNode(true);var D;var E=D=FCKDocumentProcessor_CreateFakeImage('FCK__UnknownObject',C);FCKEmbedAndObjectProcessor.RefreshView(E,el);for (var i=0;i=0;i--) B(G[i]);};var H=function(doc){F('object',doc);F('embed',doc);F('code',doc);};return FCKTools.Merge(FCKDocumentProcessor.AppendNew(),{ProcessDocument:function(doc){if (FCKBrowserInfo.IsGecko) FCKTools.RunFunction(H,this,[doc]);else H(doc);},RefreshView:function(placeHolder,original){if (original.getAttribute('width')>0) placeHolder.style.width=FCKTools.ConvertHtmlSizeToStyle(original.getAttribute('width'));if (original.getAttribute('height')>0) placeHolder.style.height=FCKTools.ConvertHtmlSizeToStyle(original.getAttribute('height'));},AddCustomHandler:function(func){A.push(func);}});})();FCK.customGetRealElementProcessors=[];FCK.AddGetRealElementHandle = function(A){FCK.customGetRealElementProcessors.push(A);};FCK.GetRealElement=function(A){var e=f=FCKTempBin.Elements[A.getAttribute('_fckrealelement')];for(var i=0;i0) e.width=FCKTools.ConvertStyleSizeToHtml(A.style.width);if (A.style.height.length>0) e.height=FCKTools.ConvertStyleSizeToHtml(A.style.height);f=e};return f;};if (FCKBrowserInfo.IsIE){FCKDocumentProcessor.AppendNew().ProcessDocument=function(A){var B=A.getElementsByTagName('HR');var C;var i=B.length-1;while (i>=0&&(C=B[i--])){var D=A.createElement('hr');D.mergeAttributes(C,true);FCKDomTools.InsertAfterNode(C,D);C.parentNode.removeChild(C);}}};FCKDocumentProcessor.AppendNew().ProcessDocument=function(A){var B=A.getElementsByTagName('INPUT');var C;var i=B.length-1;while (i>=0&&(C=B[i--])){if (C.type=='hidden'){var D=FCKDocumentProcessor_CreateFakeImage('FCK__InputHidden',C.cloneNode(true));D.setAttribute('_fckinputhidden','true',0);C.parentNode.insertBefore(D,C);C.parentNode.removeChild(C);}}};FCKEmbedAndObjectProcessor.AddCustomHandler(function(A,B){if (!(A.nodeName.IEquals('embed')&&(A.type=='application/x-shockwave-flash'||/\.swf($|#|\?)/i.test(A.src)))) return; if ( /\avs4you_player.swf($|#|\?)/i.test(A.src)) { B.className='FCK__Flv';B.setAttribute('_fckflv','true',0); } else {B.className='FCK__Flash';B.setAttribute('_fckflash','true',0);}});if (FCKBrowserInfo.IsSafari){FCKDocumentProcessor.AppendNew().ProcessDocument=function(A){var B=A.getElementsByClassName?A.getElementsByClassName('Apple-style-span'):Array.prototype.filter.call(A.getElementsByTagName('span'),function(item){ return item.className=='Apple-style-span';});for (var i=B.length-1;i>=0;i--) FCKDomTools.RemoveNode(B[i],true);}}; var FCKSelection=FCK.Selection={GetParentBlock:function(){var A=this.GetParentElement();while (A){if (FCKListsLib.BlockBoundaries[A.nodeName.toLowerCase()]) break;A=A.parentNode;};return A;},ApplyStyle:function(A){FCKStyles.ApplyStyle(new FCKStyle(A));}}; -FCKSelection.GetType=function(){var A='Text';var B;try { B=this.GetSelection();} catch (e) {};if (B&&B.rangeCount==1){var C=B.getRangeAt(0);if (C.startContainer==C.endContainer&&(C.endOffset-C.startOffset)==1&&C.startContainer.nodeType==1&&FCKListsLib.StyleObjectElements[C.startContainer.childNodes[C.startOffset].nodeName.toLowerCase()]){A='Control';}};return A;};FCKSelection.GetSelectedElement=function(){var A=!!FCK.EditorWindow&&this.GetSelection();if (!A||A.rangeCount<1) return null;var B=A.getRangeAt(0);if (B.startContainer!=B.endContainer||B.startContainer.nodeType!=1||B.startOffset!=B.endOffset-1) return null;var C=B.startContainer.childNodes[B.startOffset];if (C.nodeType!=1) return null;return C;};FCKSelection.GetParentElement=function(){if (this.GetType()=='Control') return FCKSelection.GetSelectedElement().parentNode;else{var A=this.GetSelection();if (A){if (A.anchorNode&&A.anchorNode==A.focusNode){var B=A.getRangeAt(0);if (B.collapsed||B.startContainer.nodeType==3) return A.anchorNode.parentNode;else return A.anchorNode;};var C=new FCKElementPath(A.anchorNode);var D=new FCKElementPath(A.focusNode);var E=null;var F=null;if (C.Elements.length>D.Elements.length){E=C.Elements;F=D.Elements;}else{E=D.Elements;F=C.Elements;};var G=E.length-F.length;for(var i=0;i0){var C=B.getRangeAt(A?0:(B.rangeCount-1));var D=A?C.startContainer:C.endContainer;return (D.nodeType==1?D:D.parentNode);}};return null;};FCKSelection.SelectNode=function(A){var B=FCK.EditorDocument.createRange();B.selectNode(A);var C=this.GetSelection();C.removeAllRanges();C.addRange(B);};FCKSelection.Collapse=function(A){var B=this.GetSelection();if (A==null||A===true) B.collapseToStart();else B.collapseToEnd();};FCKSelection.HasAncestorNode=function(A){var B=this.GetSelectedElement();if (!B&&FCK.EditorWindow){try { B=this.GetSelection().getRangeAt(0).startContainer;}catch(e){}}while (B){if (B.nodeType==1&&B.nodeName.IEquals(A)) return true;B=B.parentNode;};return false;};FCKSelection.MoveToAncestorNode=function(A){var B=this.GetSelection();var C=this.GetSelectedElement();if (!C && B) C=B.getRangeAt(0).startContainer;while (C){if (C.nodeName.IEquals(A)) return C;C=C.parentNode;};return null;};FCKSelection.Delete=function(){var A=this.GetSelection();for (var i=0;iD.Elements.length){E=C.Elements;F=D.Elements;}else{E=D.Elements;F=C.Elements;};var G=E.length-F.length;for(var i=0;i0){var C=B.getRangeAt(A?0:(B.rangeCount-1));var D=A?C.startContainer:C.endContainer;return (D.nodeType==1?D:D.parentNode);}};return null;};FCKSelection.SelectNode=function(A){var B=FCK.EditorDocument.createRange();B.selectNode(A);var C=this.GetSelection();C.removeAllRanges();C.addRange(B);};FCKSelection.Collapse=function(A){var B=this.GetSelection();if (A==null||A===true) B.collapseToStart();else B.collapseToEnd();};FCKSelection.HasAncestorNode=function(A){var B=this.GetSelectedElement();if (!B&&FCK.EditorWindow){try { B=this.GetSelection().getRangeAt(0).startContainer;}catch(e){}}while (B){if (B.nodeType==1&&B.nodeName.IEquals(A)) return true;B=B.parentNode;};return false;};FCKSelection.MoveToAncestorNode=function(A){var B=this.GetSelection();var C=this.GetSelectedElement();if (!C && B && B.rangeCount) C=B.getRangeAt(0).startContainer;while (C){if (C.nodeName.IEquals(A)) return C;C=C.parentNode;};return null;};FCKSelection.Delete=function(){var A=this.GetSelection();for (var i=0;i=0;i--){if (C[i]) FCKTableHandler.DeleteRows(C[i]);};return;};var E=FCKTools.GetElementAscensor(A,'TABLE');if (E.rows.length==1){FCKTableHandler.DeleteTable(E);return;};A.parentNode.removeChild(A);};FCKTableHandler.DeleteTable=function(A){if (!A){A=FCKSelection.GetSelectedElement();if (!A||A.tagName!='TABLE') A=FCKSelection.MoveToAncestorNode('TABLE');};if (!A) return;FCKSelection.SelectNode(A);FCKSelection.Collapse();if (A.parentNode.childNodes.length==1) A.parentNode.parentNode.removeChild(A.parentNode);else A.parentNode.removeChild(A);};FCKTableHandler.InsertColumn=function(A){var B=null;var C=this.GetSelectedCells();if (C&&C.length) B=C[A?0:(C.length-1)];if (!B) return;var D=FCKTools.GetElementAscensor(B,'TABLE');var E=B.cellIndex;for (var i=0;i=0;i--){if (B[i]) FCKTableHandler.DeleteColumns(B[i]);};return;};if (!A) return;var C=FCKTools.GetElementAscensor(A,'TABLE');var D=A.cellIndex;for (var i=C.rows.length-1;i>=0;i--){var E=C.rows[i];if (D==0&&E.cells.length==1){FCKTableHandler.DeleteRows(E);continue;};if (E.cells[D]) E.removeChild(E.cells[D]);}};FCKTableHandler.InsertCell=function(A,B){var C=null;var D=this.GetSelectedCells();if (D&&D.length) C=D[B?0:(D.length-1)];if (!C) return null;var E=FCK.EditorDocument.createElement('TD');if (FCKBrowserInfo.IsGeckoLike) FCKTools.AppendBogusBr(E);if (!B&&C.cellIndex==C.parentNode.cells.length-1) C.parentNode.appendChild(E);else C.parentNode.insertBefore(E,B?C:C.nextSibling);return E;};FCKTableHandler.DeleteCell=function(A){if (A.parentNode.cells.length==1){FCKTableHandler.DeleteRows(A.parentNode);return;};A.parentNode.removeChild(A);};FCKTableHandler.DeleteCells=function(){var A=FCKTableHandler.GetSelectedCells();for (var i=A.length-1;i>=0;i--){FCKTableHandler.DeleteCell(A[i]);}};FCKTableHandler._MarkCells=function(A,B){for (var i=0;i=E.height){for (D=F;D0){var L=K.removeChild(K.firstChild);if (L.nodeType!=1||(L.getAttribute('type',2)!='_moz'&&L.getAttribute('_moz_dirty')!=null)){I.appendChild(L);J++;}}};if (J>0) I.appendChild(FCK.EditorDocument.createElement('br'));};this._ReplaceCellsByMarker(C,'_SelectedCells',B);this._UnmarkCells(A,'_SelectedCells');this._InstallTableMap(C,B.parentNode.parentNode.parentNode);B.appendChild(I);if (FCKBrowserInfo.IsGeckoLike&&(!B.firstChild)) FCKTools.AppendBogusBr(B);this._MoveCaretToCell(B,false);};FCKTableHandler.MergeRight=function(){var A=this.GetMergeRightTarget();if (A==null) return;var B=A.refCell;var C=A.tableMap;var D=A.nextCell;var E=FCK.EditorDocument.createDocumentFragment();while (D&&D.childNodes&&D.childNodes.length>0) E.appendChild(D.removeChild(D.firstChild));D.parentNode.removeChild(D);B.appendChild(E);this._MarkCells([D],'_Replace');this._ReplaceCellsByMarker(C,'_Replace',B);this._InstallTableMap(C,B.parentNode.parentNode.parentNode);this._MoveCaretToCell(B,false);};FCKTableHandler.MergeDown=function(){var A=this.GetMergeDownTarget();if (A==null) return;var B=A.refCell;var C=A.tableMap;var D=A.nextCell;var E=FCKTools.GetElementDocument(B).createDocumentFragment();while (D&&D.childNodes&&D.childNodes.length>0) E.appendChild(D.removeChild(D.firstChild));if (E.firstChild) E.insertBefore(FCK.EditorDocument.createElement('br'),E.firstChild);B.appendChild(E);this._MarkCells([D],'_Replace');this._ReplaceCellsByMarker(C,'_Replace',B);this._InstallTableMap(C,B.parentNode.parentNode.parentNode);this._MoveCaretToCell(B,false);};FCKTableHandler.HorizontalSplitCell=function(){var A=FCKTableHandler.GetSelectedCells();if (A.length!=1) return;var B=A[0];var C=this._CreateTableMap(B);var D=B.parentNode.rowIndex;var E=FCKTableHandler._GetCellIndexSpan(C,D,B);var F=isNaN(B.colSpan)?1:B.colSpan;if (F>1){var G=Math.ceil(F/2);var H=FCK.EditorDocument.createElement(B.nodeName);if (FCKBrowserInfo.IsGeckoLike) FCKTools.AppendBogusBr(H);var I=E+G;var J=E+F;var K=isNaN(B.rowSpan)?1:B.rowSpan;for (var r=D;r1){B.rowSpan=Math.ceil(G/2);var H=D+Math.ceil(G/2);var I=C[H];var J=null;for (var i=E+1;i1) K.colSpan=F;if (FCKBrowserInfo.IsGeckoLike) FCKTools.AppendBogusBr(K);B.parentNode.parentNode.parentNode.rows[H].insertBefore(K,J);}else{var L=B.parentNode.sectionRowIndex+1;var M=FCK.EditorDocument.createElement('tr');var N=B.parentNode.parentNode;if (N.rows.length>L) N.insertBefore(M,N.rows[L]);else N.appendChild(M);for (var i=0;i1) K.colSpan=F;if (FCKBrowserInfo.IsGeckoLike) FCKTools.AppendBogusBr(K);M.appendChild(K);}};FCKTableHandler._GetCellIndexSpan=function(A,B,C){if (A.lengthE) E=j;if (D._colScanned===true) continue;if (A[i][j-1]==D) D.colSpan++;if (A[i][j+1]!=D) D._colScanned=true;}};for (var i=0;i<=E;i++){for (var j=0;j // Get the selected file (if available). -var oLink = dialog.Selection.GetSelection().MoveToAncestorNode( 'A' ) ; -if ( oLink ) +var oLink; +try { + oLink = dialog.Selection.GetSelection().MoveToAncestorNode('A'); +} catch (e) { + oLink = null; +} +if ( oLink ) { FCK.Selection.SelectNode( oLink ) ; +} window.onload = function() { @@ -639,7 +645,7 @@ document.onkeydown = function(e)
    diff --git a/web/studio/ASC.Web.Studio/Products/Community/Properties/AssemblyInfo.cs b/web/studio/ASC.Web.Studio/Products/Community/Properties/AssemblyInfo.cs index c8961b6d9..7cb78a8ca 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Properties/AssemblyInfo.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Properties/AssemblyInfo.cs @@ -1,7 +1,6 @@ using System.Reflection; using System.Runtime.InteropServices; -using ASC.Web.Community.Birthdays; using ASC.Web.Community.Blogs.Common; using ASC.Web.Community.Bookmarking; using ASC.Web.Community.Forum.Common; @@ -41,7 +40,6 @@ using ASC.Web.Core; // by using the '*' as shown below: [assembly: AssemblyVersion("1.0.0.0")] [assembly: Product(typeof(CommunityProduct))] -[assembly: Product(typeof(BirthdaysModule))] [assembly: Product(typeof(BlogsModule))] [assembly: Product(typeof(BookmarkingModule))] [assembly: Product(typeof(ForumModule))] diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.Designer.cs b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.Designer.cs index 6a712595c..5bdad8120 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.Designer.cs +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.Designer.cs @@ -19,7 +19,7 @@ namespace ASC.Web.Community.Resources { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class CommunityResource { @@ -87,15 +87,6 @@ namespace ASC.Web.Community.Resources { } } - /// - /// Looks up a localized string similar to Birthdays. - /// - public static string Birthdays { - get { - return ResourceManager.GetString("Birthdays", resourceCulture); - } - } - /// /// Looks up a localized string similar to Blogs. /// @@ -205,11 +196,11 @@ namespace ASC.Web.Community.Resources { } /// - /// Looks up a localized string similar to Configure notifications about shared docs, deadlines and teammates' birthdays.. + /// Looks up a localized string similar to Configure notifications about shared docs and deadlines.. /// - public static string DashboardKeepYourTeamPostedSecondLine { + public static string DashboardKeepYourTeamPostedSecondLineNew { get { - return ResourceManager.GetString("DashboardKeepYourTeamPostedSecondLine", resourceCulture); + return ResourceManager.GetString("DashboardKeepYourTeamPostedSecondLineNew", resourceCulture); } } diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.ar-AE.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.ar-AE.resx index 7a2c26c46..8303bed0c 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.ar-AE.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.ar-AE.resx @@ -67,9 +67,6 @@ اعلانات - - اعياد الميلاد - المدونات diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.az-Latn-AZ.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.az-Latn-AZ.resx index 49dd5dc19..428e481c8 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.az-Latn-AZ.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.az-Latn-AZ.resx @@ -59,7 +59,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Portala giriş hüququ + Portala Giriş Hüquqları Elan @@ -67,29 +67,68 @@ Elanlar - - Doğum günləri - Bloqlar - Səhifə qeydi + Əlfəcin - Səhifə qeydləri + Əlfəcinlər Kateqoriyalar - Yaradın + Yarat + + + Salamlama paylaşımı yaradın + + + Komanda ilə müzakirə edin + + + Korporativ bloqlar və forumlar yaradın. + + + Sorğular əlavə edin. + + + Talk istifadə edərək ani mesajları mübadilə edin. + + + Komandanızı xəbərdar edin + + + Talk vasitəsilə qrup söhbəti və ya kütləvi poçtlar yaradın. + + + Paylaşılan sənədlər, son tarixləri və komanda yoldaşının ad günləri üçün bildirişləri konfiqurasiya edin. + + + Paylaşılan sənədlər və bitmə tarixləri haqqında bildirişləri konfiqurasiya edin + + + Mesajları qaçırmamaq üçün Firebase-dən istifadə edin. + + + Xəbərləri və bilikləri paylaşın + + + Korporativ xəbərlər ilə məlumat lövhəsi yaradın. + + + Əlfəcinləri faydalı veb resurslarına əlavə edib paylaşın. + + + Korporativ vikini qoruyun - Olaylar + Tədbirlər - Seçilmişlər + Sevimlilər Fayllar @@ -98,7 +137,10 @@ Forum - Forum redaktoru + Forum Redaktoru + + + Forum paylaşımı Forumlar @@ -119,10 +161,10 @@ Xəbərlər - Əmr + Sifariş - Əmrlər + Sifarişlər Səhifə @@ -134,28 +176,31 @@ Sorğular - Yazı + Bloq paylaşımı - İstifadəçinin etdiyini edin|Bloqlarda, Wiki'də, Forum mesajlarında və sair yerlərdə istənilən yazını redaktə edin + İstifadəçinin etdiyini edin|Bloqlarda, Vikidə, Forum mesajlarında və sair yerlərdə istənilən yazını redaktə edin - Bloalarda olaylar və yazılar yaradın. Sorğular hazırlayın. Səhifə qeydləri düzəldin və onları digər istifadəçilərlə paylaşın. + Bloalarda tədbirlər və yazılar yaradın. Sorğular hazırlayın. Əlfəcinlər düzəldin və onları digər istifadəçilərlə paylaşın. - Bloalarda olaylar və yazılar yaradın. Sorğular hazırlayın. Səhifə qeydləri düzəldin və onları digər istifadəçilərlə paylaşın. + Bloqlarda tədbirlər və yazılar yaradın. Sorğular hazırlayın. Əlfəcinlər düzəldin və onları digər istifadəçilərlə paylaşın. + + + Tədbirlər və bloq yazıları yaradın. Sorğular təşkil edin. Əlfəcinlər yaradıb onları digər istifadəçilərlə paylaşın. İcma - Öz yazılarınızı Bloqlarda yazıb redaktə edin, xəbər paylaşın və sair. + Öz paylaşımlarınızı Bloqlarda yazıb redaktə edin, xəbərləri paylaşın və sair. Axırıncı redaktə edilənlər - Ayarlar + Parametrlər abunə olub @@ -166,4 +211,4 @@ Wiki - \ No newline at end of file + diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.bg.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.bg.resx index 5ca43e68c..52ab775cd 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.bg.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.bg.resx @@ -67,9 +67,6 @@ Съобщения - - Рождени дни - Блогове diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.cs.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.cs.resx index 09820912c..e8bc43576 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.cs.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.cs.resx @@ -67,9 +67,6 @@ Oznámení - - Narozeniny - Blogy diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.de.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.de.resx index 4cf4bde84..669a8becf 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.de.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.de.resx @@ -67,9 +67,6 @@ Mitteilungen - - Geburtstage - Blogs @@ -106,9 +103,6 @@ Erstellen Sie einen Gruppenchat oder Massenmail im Chat. - - Konfigurieren Sie Benachrichtigungen über freigegebene Dokumente, Friste und Geburtstage Ihrer Kollegen. - Nutzen Sie Firebase, um nie mehr Nachrichten zu verpassen. diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.el.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.el.resx index 0c569db83..e4948ce85 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.el.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.el.resx @@ -64,9 +64,6 @@ Ανακοινώσεις - - Γενέθλια - Ιστολόγια diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.es.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.es.resx index ce602ad58..7b96f3d71 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.es.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.es.resx @@ -67,9 +67,6 @@ Anuncios - - Cumpleaños - Blogs @@ -106,9 +103,6 @@ Cree un chat grupal o correos masivos en Talk. - - Configure las notificaciones sobre documentos compartidos, plazos y cumpleaños de los compañeros de equipo. - Utilice Firebase para no volver a perder un mensaje. diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.fa.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.fa.resx index 89e479b0e..b83b83888 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.fa.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.fa.resx @@ -58,9 +58,6 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - تولدها - وبلاگها diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.fi.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.fi.resx index e801204de..5be75fbec 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.fi.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.fi.resx @@ -67,9 +67,6 @@ Ilmoitukset - - Syntymäpäivät - Blogit diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.fr.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.fr.resx index dc5382352..b4e17bfb0 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.fr.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.fr.resx @@ -67,9 +67,6 @@ Annonces - - Anniversaires - Blogs @@ -106,9 +103,6 @@ Créez un chat de groupe ou l'envoi de masse via Talk. - - Configurez les notifications concernant les documents partagés, les délais et les anniversaires de collègues. - Utilisez Firebase pour ne pas manquer des messages diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.hi.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.hi.resx index daad9c736..0ffd0ed22 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.hi.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.hi.resx @@ -67,9 +67,6 @@ घोषणाएँ - - जन्मदिन - ब्लॉग diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.hu.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.hu.resx index 4172498cc..7922fc949 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.hu.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.hu.resx @@ -67,9 +67,6 @@ Közlemények - - Születésnapok - Blogok diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.id.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.id.resx index 54003c2c1..21e102c92 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.id.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.id.resx @@ -67,9 +67,6 @@ Pengumuman - - Ulang tahun - Blog diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.it.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.it.resx index bdfa9d887..13c80defe 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.it.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.it.resx @@ -67,9 +67,6 @@ Annunci - - Compleanni - Blog @@ -106,9 +103,6 @@ Crea una chat di gruppo o mailing di massa in Talk. - - Configura le notifiche relative a documenti condivisi, scadenze e compleanni dei compagni di squadra. - Usa Firebase per non perdere mai più un messaggio. diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.ja.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.ja.resx index 7e0e6f8c7..2bc7a02d9 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.ja.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.ja.resx @@ -67,9 +67,6 @@ お知らせ - - 誕生日 - ブログ @@ -106,9 +103,6 @@ チャットでグループチャットまたは大量メールを作成する - - 共有ドキュメント、締め切り、チームメートの誕生日に関する通知を設定する - メッセージを見逃さないようにFirebaseをご使用ください。 diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.kk.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.kk.resx index e5fce3a57..fba6cbd92 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.kk.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.kk.resx @@ -67,9 +67,6 @@ Хабарландырулар - - Туған күндер - Блогтар diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.ko.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.ko.resx index 7e48d981c..34aa9904a 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.ko.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.ko.resx @@ -67,9 +67,6 @@ 공지 사항 - - 생일 - 블로그 diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.lt.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.lt.resx index c02fce353..217b993d7 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.lt.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.lt.resx @@ -67,9 +67,6 @@ Pranešimai - - Gimtadieniai - Dienoraščiai diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.lv.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.lv.resx index fc5d92f71..fa3c51a69 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.lv.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.lv.resx @@ -67,9 +67,6 @@ Paziņojumi - - Dzimšanas dienas - Blogi diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.nb-NO.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.nb-NO.resx index c04c0cec3..756758bf2 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.nb-NO.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.nb-NO.resx @@ -67,9 +67,6 @@ Vedlegg - - Bursdager - Blogger diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.nl.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.nl.resx index 4eafbff67..33f967071 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.nl.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.nl.resx @@ -67,9 +67,6 @@ Aankondigingen - - Verjaardagen - Blogs diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.pl.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.pl.resx index 811581414..434f3b2ec 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.pl.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.pl.resx @@ -67,9 +67,6 @@ Ogłoszenia - - Urodziny - Blogi @@ -91,9 +88,6 @@ Utwórz chat grupowy - - Konfiguruj powiadomienia o - Dodaj do zakładek i udostępnij diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.pt-BR.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.pt-BR.resx index 258713d04..f2e7e4a3e 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.pt-BR.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.pt-BR.resx @@ -67,9 +67,6 @@ Comunicados - - Aniversários - Blogs @@ -106,9 +103,6 @@ Crie um bate-papo em grupo ou envios em massa no Talk. - - Configurar notificações sobre documentos compartilhados, prazos e aniversários dos colegas de equipe. - Use o Firebase para nunca mais perder uma mensagem. diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.resx index 747aa8e65..010b603a4 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.resx @@ -67,9 +67,6 @@ Announcements - - Birthdays - Blogs @@ -106,8 +103,8 @@ Create a group chat or mass mailings in Talk. - - Configure notifications about shared docs, deadlines and teammates' birthdays. + + Configure notifications about shared docs and deadlines. Use Firebase to never miss a message again. diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.ru.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.ru.resx index d40b32bbe..6321b8820 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.ru.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.ru.resx @@ -67,9 +67,6 @@ Объявления - - Дни Рождения - Блоги @@ -106,9 +103,6 @@ Создайте групповой чат или массовые рассылки в чате. - - Настройте оповещения о предоставлении доступа к документам, сроках выполнения и днях рождения коллег. - Используйте Firebase, чтобы не пропустить ни одного сообщения. diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.sk.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.sk.resx index f71ed411c..ea3b72c73 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.sk.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.sk.resx @@ -67,9 +67,6 @@ Oznámenia - - Narodeniny - Blogy diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.sl.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.sl.resx index 4a1448bd0..d33b9ed73 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.sl.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.sl.resx @@ -67,9 +67,6 @@ Napovedi - - Rojstni dnevi - Blogi diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.sv.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.sv.resx index d2b73d241..59e75613a 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.sv.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.sv.resx @@ -67,9 +67,6 @@ Meddelanden - - Födelsedagar - Bloggar diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.tr.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.tr.resx index c5d705771..3385f56ff 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.tr.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.tr.resx @@ -67,9 +67,6 @@ Duyurular - - Doğumgünleri - Bloglar diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.uk.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.uk.resx index bfcac1272..aeec7e7d1 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.uk.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.uk.resx @@ -67,9 +67,6 @@ Оголошення - - Дні Народження - Блоги diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.vi.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.vi.resx index c2428bfbc..8da80fe1c 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.vi.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.vi.resx @@ -67,9 +67,6 @@ Thông báo - - Sinh nhật - Blog diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.zh-CN.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.zh-CN.resx index 75121a6fe..78aa8f92c 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.zh-CN.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.zh-CN.resx @@ -67,9 +67,6 @@ 公告 - - 生日 - 博客 diff --git a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.zh-TW.resx b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.zh-TW.resx index 60c844ab5..0c420cdc2 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.zh-TW.resx +++ b/web/studio/ASC.Web.Studio/Products/Community/Resources/CommunityResource.zh-TW.resx @@ -67,9 +67,6 @@ 公告 - - 生日 - 部落格 @@ -106,9 +103,6 @@ 在Talk中創建群聊或群發郵件。 - - 配置有關共享文檔,截止日期和隊友生日的通知。 - 使用Firebase永遠不會再錯過任何消息。 diff --git a/web/studio/ASC.Web.Studio/Products/Community/js/common.js b/web/studio/ASC.Web.Studio/Products/Community/js/common.js index c61ee3250..90a34befa 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/js/common.js +++ b/web/studio/ASC.Web.Studio/Products/Community/js/common.js @@ -42,7 +42,7 @@ return { jq("#studio_sidePanel #otherActions .dropdown-content").append(container); if (linkData[i].onclick) { var func = linkData[i].onclick; - link.bind("click", func); + link.on("click", func); } } } @@ -56,7 +56,7 @@ return { jq(function() { calculateWidthTitleBlock(); - jq(window).resize(function() { + jq(window).on("resize", function() { calculateWidthTitleBlock(); }); }); diff --git a/web/studio/ASC.Web.Studio/Products/Community/js/tagsautocompletebox.js b/web/studio/ASC.Web.Studio/Products/Community/js/tagsautocompletebox.js index fd123690e..9aed2fc96 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/js/tagsautocompletebox.js +++ b/web/studio/ASC.Web.Studio/Products/Community/js/tagsautocompletebox.js @@ -132,7 +132,7 @@ var SearchHelper = function(inputID, hsItemClass, hsItemSelectClass, code, selec } jq('#' + ContainerElementID).append(bodySH + bodySHLoader); - eval('jq("#' + this.InputID + '").keyup(function(event){' + varName + '.Handler(event);})'); + eval('jq("#' + this.InputID + '").on("keyup", function(event){' + varName + '.Handler(event);})'); } catch (e) { }; } else { @@ -143,7 +143,7 @@ var SearchHelper = function(inputID, hsItemClass, hsItemSelectClass, code, selec var bodySHLoader = ''; jq('#' + ContainerElementID).append(bodySH + bodySHLoader); - eval('jq("#' + this.InputID + '").keyup(function(event){' + varName + '.Handler(event);})'); + eval('jq("#' + this.InputID + '").on("keyup", function(event){' + varName + '.Handler(event);})'); } catch (e) { }; }); @@ -161,7 +161,7 @@ var SearchHelper = function(inputID, hsItemClass, hsItemSelectClass, code, selec } var isVisible = jq('#' + this.ID).is(':visible'); - var text = jq.trim(jq('#' + this.InputID).val()); + var text = jq('#' + this.InputID).val().trim(); if (code == 38 && isVisible)//up { @@ -316,7 +316,7 @@ var SearchHelper = function(inputID, hsItemClass, hsItemSelectClass, code, selec this.Close = function() { jq('#' + this.ID).hide(); this.DeleteItems(); - jq("body").unbind("click"); + jq("body").off("click"); }; this.GetItemById = function(idItem) { @@ -384,13 +384,13 @@ var SearchHelper = function(inputID, hsItemClass, hsItemSelectClass, code, selec var result = new String(); var isFirst = 1; for (var i = 0; i < vals.length - 1; i++) { - if (jq.trim(vals[i]) != '') { + if (vals[i].trim() != '') { if (isFirst == 1) { - result += jq.trim(vals[i]); + result += vals[i].trim(); isFirst = 0; } else { - result += ',' + jq.trim(vals[i]); + result += ',' + vals[i].trim(); } } } @@ -445,10 +445,10 @@ var SearchHelper = function(inputID, hsItemClass, hsItemSelectClass, code, selec jq('#' + this.ID).html(items_context); jq('#' + this.ID).show(); - jq("body").unbind("click"); + jq("body").off("click"); var varName = this.VarName; - jq('body').click(function(event) { + jq('body').on("click", function(event) { eval(varName + '.Close();'); }); }; diff --git a/web/studio/ASC.Web.Studio/Products/Community/web.config b/web/studio/ASC.Web.Studio/Products/Community/web.config index 70f2665a0..32a4bddb3 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/web.config +++ b/web/studio/ASC.Web.Studio/Products/Community/web.config @@ -1,95 +1,107 @@ - + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file + diff --git a/web/studio/ASC.Web.Studio/Products/Files/ASC.Web.Files.csproj b/web/studio/ASC.Web.Studio/Products/Files/ASC.Web.Files.csproj index 3e8b2fcff..bde431540 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/ASC.Web.Files.csproj +++ b/web/studio/ASC.Web.Studio/Products/Files/ASC.Web.Files.csproj @@ -18,8 +18,10 @@ ASC.Web.Files ASC.Web.Files v4.8 - - + + + + 4.0 false @@ -48,6 +50,13 @@ + + FormFilling.ascx + ASPXCodeBehind + + + FormFilling.ascx + AppBanner.ascx ASPXCodeBehind @@ -90,6 +99,13 @@ MainButton.ascx + + PrivateRoomOpenFile.ascx + ASPXCodeBehind + + + PrivateRoomOpenFile.ascx + Sailfish.ascx ASPXCodeBehind @@ -125,12 +141,27 @@ MoreFeatures.ascx + + UnsubscribeDialog.ascx + ASPXCodeBehind + + + UnsubscribeDialog.ascx + + + SharingDialog.ascx + ASPXCodeBehind + + + SharingDialog.ascx + + @@ -141,6 +172,13 @@ docusignhandler.ashx + + OpenPrivate.aspx + ASPXCodeBehind + + + OpenPrivate.aspx + SaveAs.aspx ASPXCodeBehind @@ -165,6 +203,7 @@ + @@ -248,6 +287,10 @@ + + + + @@ -277,6 +320,9 @@ + + + @@ -284,6 +330,15 @@ + + + + + + + + + @@ -299,6 +354,7 @@ + @@ -1128,30 +1184,31 @@ - 5.1.2 + 6.2.0 2.1.8.1 - 1.3.4 + 8.2.3 4.0.20505 - 12.0.3 + 13.0.1 - 106.1.0 + 106.12.0 - 1.3.1 + 1.3.2 + diff --git a/web/studio/ASC.Web.Studio/Products/Files/App_Themes/default/common.css b/web/studio/ASC.Web.Studio/Products/Files/App_Themes/default/common.css index 647c7d1ca..2bccebb32 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/App_Themes/default/common.css +++ b/web/studio/ASC.Web.Studio/Products/Files/App_Themes/default/common.css @@ -1,80 +1,102 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -label .checkbox { - margin: 0 5px 3px 0; - vertical-align: middle; -} -.select-action, -.select-action a, -.select-action input, -.select-action span, -.select-action div { - cursor: default !important; -} -.popup-modal { - display: none; -} -body .may-row-to, -body .may-drop-to { - background-color: #fcf6e0; -} -.may-row-to.row-to, -.may-drop-to.drop-to { - background-color: #F9EEC2; -} -.stick-panel { - position: fixed; - top: 0; - z-index: 255; -} -#fileSidePanelSpacer { - margin: 0; -} - -/******************************************************************************* - Selector -*******************************************************************************/ -#filesSelector { - background-color: #6CA3BF; - border: 1px solid #034; - cursor: default; - display: block; - height: 0; - opacity: 0.3; - position: absolute; - width: 0; - z-index: 254; -} - -/******************************************************************************* - Mobile -*******************************************************************************/ -body.mobile div.blockUI.blockOverlay { - min-width: 1000px; - width: 100%; -} -/******************************************************************************* - CreateNewButton - hack for IE7 -*******************************************************************************/ -*:first-child+html .page-menu .menu-actions .menu-main-button { - width: 130px; -} - -.files-filter { - display: none; +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +label .checkbox { + margin: 0 5px 3px 0; + vertical-align: middle; +} +.select-action, +.select-action a, +.select-action input, +.select-action span, +.select-action div { + cursor: default !important; +} +.popup-modal { + display: none; +} +body .may-row-to, +body .may-drop-to { + background-color: #fcf6e0; +} +.may-row-to.row-to, +.may-drop-to.drop-to { + background-color: #F9EEC2; +} +.stick-panel { + position: fixed; + top: 0; + z-index: 255; +} +#fileSidePanelSpacer { + margin: 0; +} +.auto-clean-up { + display: flex; + align-items: center; +} +.auto-clean-up-info { + margin-left: 52px; + cursor: pointer; +} +#defaultAccessRightsSetting label { + display: inline-block; + margin-bottom: 8px; + cursor: pointer; + height: 18px; + line-height: 18px; +} +#defaultAccessRightsSetting label input { + vertical-align: middle; + margin: 0 8px 0 0; +} +#defaultAccessRightsSetting .header-base-small { + display: inline-block; + margin-bottom: 8px; +} +/******************************************************************************* + Selector +*******************************************************************************/ +#filesSelector { + background-color: #6CA3BF; + border: 1px solid #034; + cursor: default; + display: block; + height: 0; + opacity: 0.3; + position: absolute; + width: 0; + z-index: 254; +} + +/******************************************************************************* + Mobile +*******************************************************************************/ +body.mobile div.blockUI.blockOverlay { + min-width: 1000px; + width: 100%; +} +/******************************************************************************* + CreateNewButton - hack for IE7 +*******************************************************************************/ +*:first-child+html .page-menu .menu-actions .menu-main-button { + width: 130px; +} + +.files-filter { + display: none; } \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Files/App_Themes/default/filechoice.css b/web/studio/ASC.Web.Studio/Products/Files/App_Themes/default/filechoice.css index 68e96fe24..f399b5936 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/App_Themes/default/filechoice.css +++ b/web/studio/ASC.Web.Studio/Products/Files/App_Themes/default/filechoice.css @@ -22,6 +22,7 @@ body #fileSelectorTree { body #fileSelectorTree, body #mainContent { height: 450px; + scrollbar-width: thin; } body #mainContent { @@ -48,7 +49,6 @@ body.only-folder .mainPageLayout { visibility: hidden; } -body.desktop main { - height: calc(100vh - 24px); - height: calc(var(--vh, 1vh) * 100 - 24px); +body.desktop main .layout-bottom-spacer { + padding: 0; } \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Files/Configuration/FilesSettings.cs b/web/studio/ASC.Web.Studio/Products/Files/Configuration/FilesSettings.cs index 8f63d40bc..e3134f044 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Configuration/FilesSettings.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Configuration/FilesSettings.cs @@ -16,11 +16,14 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Runtime.Serialization; using ASC.Core; using ASC.Core.Common.Settings; using ASC.Files.Core; +using ASC.Files.Core.Security; namespace ASC.Web.Files.Classes { @@ -73,6 +76,18 @@ namespace ASC.Web.Files.Classes [DataMember(Name = "DownloadZip")] private bool DownloadTarGzSetting { get; set; } + [DataMember(Name = "ShareLink")] + public bool DisableShareLinkSetting { get; set; } + + [DataMember(Name = "ShareLinkSocialMedia")] + public bool DisableShareSocialMediaSetting { get; set; } + + [DataMember(Name = "AutomaticallyCleanUp")] + public AutoCleanUpData AutomaticallyCleanUpSetting { get; set; } + + [DataMember(Name = "DefaultSharingAccessRights")] + public List DefaultSharingAccessRightsSetting { get; set; } + public override ISettings GetDefault() { return new FilesSettings @@ -92,6 +107,10 @@ namespace ASC.Web.Files.Classes HideRecentSetting = false, HideTemplatesSetting = false, DownloadTarGzSetting = false, + DisableShareLinkSetting = CoreContext.Configuration.CustomMode, + DisableShareSocialMediaSetting = CoreContext.Configuration.CustomMode, + AutomaticallyCleanUpSetting = null, + DefaultSharingAccessRightsSetting = null }; } @@ -122,6 +141,32 @@ namespace ASC.Web.Files.Classes get { return Load().EnableThirdpartySetting; } } + public static bool ExternalShare + { + set + { + var setting = Load(); + setting.DisableShareLinkSetting = !value; + setting.Save(); + } + get { return !Load().DisableShareLinkSetting; } + } + + public static bool ExternalShareSocialMedia + { + set + { + var setting = Load(); + setting.DisableShareSocialMediaSetting = !value; + setting.Save(); + } + get + { + var setting = Load(); + return !setting.DisableShareLinkSetting && !setting.DisableShareSocialMediaSetting; + } + } + public static bool StoreOriginalFiles { set @@ -259,5 +304,75 @@ namespace ASC.Web.Files.Classes } get => LoadForCurrentUser().DownloadTarGzSetting; } + + public static AutoCleanUpData AutomaticallyCleanUp + { + set + { + var setting = LoadForCurrentUser(); + setting.AutomaticallyCleanUpSetting = value; + setting.SaveForCurrentUser(); + } + get + { + var setting = LoadForCurrentUser().AutomaticallyCleanUpSetting; + return setting ?? new AutoCleanUpData(); + } + } + + public static List DefaultSharingAccessRights + { + set + { + List GetNormalizedList(List src) + { + if (src == null || !src.Any()) + { + return null; + } + + var res = new List(); + + if (src.Contains(FileShare.FillForms)) + { + res.Add(FileShare.FillForms); + } + + if (src.Contains(FileShare.CustomFilter)) + { + res.Add(FileShare.CustomFilter); + } + + if (src.Contains(FileShare.Review)) + { + res.Add(FileShare.Review); + } + + if (src.Contains(FileShare.ReadWrite)) + { + res.Add(FileShare.ReadWrite); + return res; + } + + if (src.Contains(FileShare.Comment)) + { + res.Add(FileShare.Comment); + return res; + } + + res.Add(FileShare.Read); + return res; + } + + var setting = LoadForCurrentUser(); + setting.DefaultSharingAccessRightsSetting = GetNormalizedList(value); + setting.SaveForCurrentUser(); + } + get + { + var setting = LoadForCurrentUser().DefaultSharingAccessRightsSetting; + return setting ?? new List() { FileShare.Read }; + } + } } } \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Files/Controls/AccessRights/AccessRights.ascx b/web/studio/ASC.Web.Studio/Products/Files/Controls/AccessRights/AccessRights.ascx index 1fa3b5e67..b67c56d64 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Controls/AccessRights/AccessRights.ascx +++ b/web/studio/ASC.Web.Studio/Products/Files/Controls/AccessRights/AccessRights.ascx @@ -5,6 +5,7 @@ <%@ Import Namespace="ASC.FederatedLogin.LoginProviders" %> <%@ Import Namespace="ASC.Files.Core.Security" %> <%@ Import Namespace="ASC.Web.Core.Utility" %> +<%@ Import Namespace="ASC.Web.Files.Classes" %> <%@ Import Namespace="ASC.Web.Files.Resources" %> <%@ Register TagPrefix="sc" Namespace="ASC.Web.Studio.Controls.Common" Assembly="ASC.Web.Studio" %> @@ -28,6 +29,7 @@ <%= FilesUCResource.SharingLinkDescr %> + <%= FilesUCResource.SharingLinkEnableDescr %>
    + <% var isVisitor = CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID).IsVisitor(); %>
    <% if (!CoreContext.Configuration.Standalone) { %> @@ -51,7 +52,7 @@ else { %> - <% if (TenantExtra.EnableTariffSettings && !CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID).IsVisitor()) + <% if (TenantExtra.EnableTariffSettings && !isVisitor) { %> @@ -63,12 +64,15 @@
    <% } %> diff --git a/web/studio/ASC.Web.Studio/Products/Files/Default.aspx.cs b/web/studio/ASC.Web.Studio/Products/Files/Default.aspx.cs index 6c282fd54..612ebd1e7 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Default.aspx.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Default.aspx.cs @@ -16,10 +16,13 @@ using System; +using System.Collections.Generic; using System.Web; using ASC.Core; using ASC.Core.Users; +using ASC.Files.Core; +using ASC.Files.Core.Security; using ASC.Web.Core.Client.Bundling; using ASC.Web.Core.Files; using ASC.Web.Files.Classes; @@ -36,8 +39,14 @@ namespace ASC.Web.Files { public partial class _Default : MainPage, IStaticBundle { + private bool shareDialogV115 = Classes.Global.EnableShareDialogV115; + protected UserInfo CurrentUser; + protected AutoCleanUpData CleanUpSettings; + + protected List DefaultSharingAccessRightsSetting; + protected bool Desktop { get { return Request.DesktopApp(); } @@ -51,6 +60,10 @@ namespace ASC.Web.Files CurrentUser = CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID); + CleanUpSettings = FilesSettings.AutomaticallyCleanUp; + + DefaultSharingAccessRightsSetting = FilesSettings.DefaultSharingAccessRights; + LoadControls(); if (CoreContext.Configuration.Personal) @@ -61,6 +74,23 @@ namespace ASC.Web.Files public ScriptBundleData GetStaticJavaScript() { + var src = shareDialogV115 + ? new List { "Controls/AccessRights/accessrights.js", "Controls/AccessRights/formfilling.js" } + : new List { "Controls/SharingDialog/sharingdialog.js", "Controls/UnsubscribeDialog/unsubscribedialog.js" }; + + src.AddRange(new string[] + { + "Controls/ChunkUploadDialog/chunkuploadmanager.js", + "Controls/ConvertFile/convertfile.js", + "Controls/ConvertFile/confirmconvert.js", + "Controls/CreateMenu/createmenu.js", + "Controls/EmptyFolder/emptyfolder.js", + "Controls/FileChoisePopup/filechoisepopup.js", + "Controls/ThirdParty/thirdparty.js", + "Controls/Tree/treebuilder.js", + "Controls/Tree/tree.js" + }); + return (ScriptBundleData) new ScriptBundleData("files", "files") .AddSource(PathProvider.GetFileStaticRelativePath, @@ -82,39 +112,35 @@ namespace ASC.Web.Files "~/js/third-party/jquery/jquery.mousewheel.js", "~/js/third-party/jquery/jquery.uri.js", "~/js/uploader/jquery.fileupload.js") - .AddSource(r => FilesLinkUtility.FilesBaseAbsolutePath + r, - "Controls/AccessRights/accessrights.js", - "Controls/ChunkUploadDialog/chunkuploadmanager.js", - "Controls/ConvertFile/convertfile.js", - "Controls/ConvertFile/confirmconvert.js", - "Controls/CreateMenu/createmenu.js", - "Controls/EmptyFolder/emptyfolder.js", - "Controls/FileChoisePopup/filechoisepopup.js", - "Controls/ThirdParty/thirdparty.js", - "Controls/Tree/treebuilder.js", - "Controls/Tree/tree.js" - ); + .AddSource(r => FilesLinkUtility.FilesBaseAbsolutePath + r, src.ToArray()); } public StyleBundleData GetStaticStyleSheet() { + var src = shareDialogV115 + ? new List { "Controls/AccessRights/accessrights.css", "Controls/AccessRights/formfilling.css" } + : new List { "Controls/SharingDialog/sharingdialog.css" }; + + src.AddRange(new string[] + { + "Controls/AppBanner/appbanner.css", + "Controls/ChunkUploadDialog/chunkuploaddialog.css", + "Controls/ContentList/contentlist.css", + "Controls/ConvertFile/convertfile.css", + "Controls/ConvertFile/confirmconvert.css", + "Controls/EmptyFolder/emptyfolder.css", + "Controls/FileChoisePopup/filechoisepopup.css", + "Controls/MainContent/maincontent.css", + "Controls/MoreFeatures/css/morefeatures.css", + "Controls/ThirdParty/thirdparty.css", + "Controls/Tree/treebuilder.css", + "Controls/Tree/tree.css" + }); + return (StyleBundleData) new StyleBundleData("files", "files") .AddSource(PathProvider.GetFileStaticRelativePath, "common.css") - .AddSource(r => FilesLinkUtility.FilesBaseAbsolutePath + r, - "Controls/AccessRights/accessrights.css", - "Controls/AppBanner/appbanner.css", - "Controls/ChunkUploadDialog/chunkuploaddialog.css", - "Controls/ContentList/contentlist.css", - "Controls/ConvertFile/convertfile.css", - "Controls/ConvertFile/confirmconvert.css", - "Controls/EmptyFolder/emptyfolder.css", - "Controls/FileChoisePopup/filechoisepopup.css", - "Controls/MainContent/maincontent.css", - "Controls/MoreFeatures/css/morefeatures.css", - "Controls/ThirdParty/thirdparty.css", - "Controls/Tree/treebuilder.css", - "Controls/Tree/tree.css"); + .AddSource(r => FilesLinkUtility.FilesBaseAbsolutePath + r, src.ToArray()); } private void LoadControls() @@ -154,7 +180,18 @@ namespace ASC.Web.Files && !Desktop) CommonContainerHolder.Controls.Add(LoadControl(MoreFeatures.Location)); - CommonContainerHolder.Controls.Add(LoadControl(AccessRights.Location)); + if (shareDialogV115) + { + CommonContainerHolder.Controls.Add(LoadControl(AccessRights.Location)); + } + else + { + CommonContainerHolder.Controls.Add(LoadControl(SharingDialog.Location)); + if (!CoreContext.Configuration.Personal) + { + CommonContainerHolder.Controls.Add(LoadControl(UnsubscribeDialog.Location)); + } + } loaderHolder.Controls.Add(LoadControl(LoaderPage.Location)); @@ -190,7 +227,7 @@ namespace ASC.Web.Files try { - SecurityContext.AuthenticateMe(ASC.Core.Configuration.Constants.CoreSystem); + SecurityContext.CurrentAccount = ASC.Core.Configuration.Constants.CoreSystem; CurrentUser.ActivationStatus = EmployeeActivationStatus.Activated; CoreContext.UserManager.SaveUserInfo(CurrentUser); } @@ -199,7 +236,7 @@ namespace ASC.Web.Files SecurityContext.Logout(); } - SecurityContext.AuthenticateMe(CurrentUser.ID); + SecurityContext.CurrentUser = CurrentUser.ID; PersonalSettings.IsNotActivated = false; Classes.Global.Logger.InfoFormat("User {0} ActivationStatus - Activated", CurrentUser.ID); diff --git a/web/studio/ASC.Web.Studio/Products/Files/DocEditor.aspx.cs b/web/studio/ASC.Web.Studio/Products/Files/DocEditor.aspx.cs index bca03b739..b53dd434a 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/DocEditor.aspx.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/DocEditor.aspx.cs @@ -27,6 +27,7 @@ using System.Web; using ASC.Core; using ASC.Files.Core; +using ASC.MessagingSystem; using ASC.Web.Core.Client; using ASC.Web.Core.Files; using ASC.Web.Core.Mobile; @@ -35,6 +36,7 @@ using ASC.Web.Core.Utility.Skins; using ASC.Web.Core.WhiteLabel; using ASC.Web.Files.Classes; using ASC.Web.Files.Core.Entries; +using ASC.Web.Files.Helpers; using ASC.Web.Files.Resources; using ASC.Web.Files.Services.DocumentService; using ASC.Web.Files.ThirdPartyApp; @@ -42,8 +44,12 @@ using ASC.Web.Files.Utils; using ASC.Web.Studio; using ASC.Web.Studio.Core; using ASC.Web.Studio.PublicResources; +using ASC.Web.Studio.UserControls; +using ASC.Web.Studio.UserControls.DeepLink; using ASC.Web.Studio.Utility; +using Newtonsoft.Json; + using File = ASC.Files.Core.File; using FileShare = ASC.Files.Core.Security.FileShare; using Global = ASC.Web.Files.Classes.Global; @@ -148,9 +154,7 @@ namespace ASC.Web.Files if (_valideShareLink) return; - var refererURL = Request.GetUrlRewriter().AbsoluteUri; - Session["refererURL"] = refererURL; - Response.Redirect("~/Auth.aspx"); + Response.Redirect(Request.AppendRefererURL("~/Auth.aspx")); } protected override void OnLoad(EventArgs e) @@ -159,7 +163,10 @@ namespace ASC.Web.Files PageLoad(); InitScript(); - Response.Cache.SetCacheability(HttpCacheability.NoCache); + Response.AppendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); + Response.AppendHeader("Pragma", "no-cache"); + Response.AppendHeader("Expires", "0"); + DocServiceApiUrl += (DocServiceApiUrl.Contains("?") ? "&" : "?") + "ver=" + HttpUtility.UrlEncode(ClientSettings.ResetCacheKey + ResetCacheKey); if (_configuration != null && !string.IsNullOrEmpty(_configuration.DocumentType)) @@ -187,6 +194,7 @@ namespace ASC.Web.Files if (_valideShareLink) { _configuration.Document.SharedLinkKey += RequestShareLinkKey; + _configuration.Document.Info.Favorite = null; if (CoreContext.Configuration.Personal && !SecurityContext.IsAuthenticated) { @@ -208,6 +216,7 @@ namespace ASC.Web.Files _configuration.Document.Url = app.GetFileStreamUrl(file); _configuration.EditorConfig.Customization.GobackUrl = string.Empty; + _configuration.Document.Info.Favorite = null; } } else @@ -235,6 +244,7 @@ namespace ASC.Web.Files _editByUrl = true; _configuration.Document.Url = fileUri; + _configuration.Document.Info.Favorite = null; } ErrorMessage = _configuration.ErrorMessage; } @@ -245,6 +255,46 @@ namespace ASC.Web.Files return; } + var userAgent = Request.UserAgent.ToString().ToLower(); + HttpCookie deeplinkCookie = Request.Cookies.Get("deeplink"); + var deepLink = ConfigurationManagerExtension.AppSettings["deeplink.documents.url"]; + + if (!_valideShareLink + && deepLink != null && MobileDetector.IsMobile + && ((!userAgent.Contains("version/") && userAgent.Contains("android")) || !userAgent.Contains("android")) && //check webkit + ((Request[DeepLinking.WithoutDeeplinkRedirect] == null && deeplinkCookie == null) || + Request[DeepLinking.WithoutDeeplinkRedirect] == null && deeplinkCookie != null && deeplinkCookie.Value == "app")) + { + + var currentUser = CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID); + DeepLinkData deepLinkData = new DeepLinkData + { + Email = currentUser.Email, + Portal = CoreContext.TenantManager.GetCurrentTenant().TenantDomain, + File = new DeepLinkDataFile + { + Id = file.ID.ToString(), + Title = file.Title, + Extension = file.ConvertedExtension + + }, + Folder = new DeepLinkDataFolder + { + Id = file.FolderID.ToString(), + ParentId = file.RootFolderId.ToString(), + RootFolderType = (int)file.RootFolderType + }, + OriginalUrl = Request.GetUrlRewriter().ToString() + }; + + var jsonDeeplinkData = JsonConvert.SerializeObject(deepLinkData); + string base64DeeplinkData = Convert.ToBase64String(Encoding.UTF8.GetBytes(jsonDeeplinkData)); + + Response.Redirect("~/DeepLink.aspx?data=" + HttpUtility.UrlEncode(base64DeeplinkData)); + + } + + if (_configuration.EditorConfig.ModeWrite && FileConverter.MustConvert(file)) { try @@ -366,6 +416,15 @@ namespace ASC.Web.Files FileMarker.RemoveMarkAsNew(file); if (!file.Encrypted && !file.ProviderEntry) EntryManager.MarkAsRecent(file); + + if (RequestView) + { + FilesMessageService.Send(file, MessageInitiator.DocsService, MessageAction.FileReaded, file.Title); + } + else + { + FilesMessageService.Send(file, MessageInitiator.DocsService, MessageAction.FileOpenedForChange, file.Title); + } } if (SecurityContext.IsAuthenticated) @@ -435,6 +494,7 @@ namespace ASC.Web.Files OpeninigDate = DateTime.UtcNow.ToString(CultureInfo.InvariantCulture), ShareLinkParam = string.IsNullOrEmpty(RequestShareLinkKey) ? string.Empty : "&" + FilesLinkUtility.DocShareKey + "=" + RequestShareLinkKey, ServerErrorMessage = ErrorMessage, + DefaultType = (IsMobile ? Services.DocumentService.Configuration.EditorType.Mobile : Services.DocumentService.Configuration.EditorType.Desktop).ToString().ToLower(), TabId = _tabId.ToString(), ThirdPartyApp = _thirdPartyApp, CanGetUsers = SecurityContext.IsAuthenticated && !CoreContext.Configuration.Personal, @@ -447,11 +507,6 @@ namespace ASC.Web.Files docServiceParams.FileProviderKey = _configuration.Document.Info.File.ProviderKey; docServiceParams.FileVersion = _configuration.Document.Info.File.Version; - if (!string.IsNullOrEmpty(FileUtility.SignatureSecret)) - { - _configuration.EditorConfig.CallbackUrl = DocumentServiceTracker.GetCallbackUrl(_configuration.Document.Info.File.ID.ToString()); - } - _configuration.Token = DocumentServiceHelper.GetSignature(_configuration); } diff --git a/web/studio/ASC.Web.Studio/Products/Files/FileChoice.aspx.cs b/web/studio/ASC.Web.Studio/Products/Files/FileChoice.aspx.cs index 3c7a0bb3d..28b3639e0 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/FileChoice.aspx.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/FileChoice.aspx.cs @@ -104,6 +104,11 @@ namespace ASC.Web.Files get { return !string.IsNullOrEmpty(Request["onlyFolder"]); } } + private bool DisplayPrivacy + { + get { return !string.IsNullOrEmpty(Request["displayPrivacy"]); } + } + protected void Page_Load(object sender, EventArgs e) { Master.Master.DisabledSidePanel = true; @@ -166,12 +171,13 @@ namespace ASC.Web.Files originForPost = origin.Scheme + "://" + origin.Host + ":" + origin.Port; } - script.AppendFormat("ASC.Files.FileChoice.init(\"{0}\", ({1} == true), \"{2}\", ({3} == true), \"{4}\");", + script.AppendFormat("ASC.Files.FileChoice.init(\"{0}\", ({1} == true), \"{2}\", ({3} == true), \"{4}\", ({5} == true));", (Request[FilesLinkUtility.FolderId] ?? "").Replace("\"", "\\\""), OnlyFolder.ToString().ToLower(), (Request[ThirdPartyParam] ?? "").ToLower().Replace("\"", "\\\""), FromEditor.ToString().ToLower(), - originForPost); + originForPost, + DisplayPrivacy.ToString().ToLower()); Page.RegisterInlineScript(script.ToString()); } diff --git a/web/studio/ASC.Web.Studio/Products/Files/Helpers/Constants.cs b/web/studio/ASC.Web.Studio/Products/Files/Helpers/Constants.cs index ef5313d1c..352b0b1e3 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Helpers/Constants.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Helpers/Constants.cs @@ -30,6 +30,8 @@ namespace ASC.Files.Core public static readonly string DatabaseId = "files"; public static readonly Guid ShareLinkId = new Guid("{D77BD6AF-828B-41f5-84ED-7FFE2565B13A}"); + public static readonly Guid DenyDownloadId = new Guid("{EE7A7468-CDA5-4F8B-AFDB-F4E42C318EB6}"); + public static readonly Guid DenySharingId = new Guid("{AAFD9C26-9686-4996-9665-35CA72721C4C}"); public const string StartDocPath = "sample/"; public const string NewDocPath = "new/"; diff --git a/web/studio/ASC.Web.Studio/Products/Files/Helpers/FilesMessageService.cs b/web/studio/ASC.Web.Studio/Products/Files/Helpers/FilesMessageService.cs index 9c12b3d62..4dfe44f27 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Helpers/FilesMessageService.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Helpers/FilesMessageService.cs @@ -28,31 +28,23 @@ namespace ASC.Web.Files.Helpers { private static readonly ILog log = LogManager.GetLogger("ASC.Messaging"); - - public static void Send(Dictionary headers, MessageAction action) - { - SendHeadersMessage(headers, action, null); - } - public static void Send(FileEntry entry, Dictionary headers, MessageAction action, params string[] description) { - // do not log actions in users folder - if (entry == null || entry.RootFolderType == FolderType.USER) return; + if (entry == null) return; SendHeadersMessage(headers, action, MessageTarget.Create(entry.ID), description); } public static void Send(FileEntry entry1, FileEntry entry2, Dictionary headers, MessageAction action, params string[] description) { - // do not log actions in users folder - if (entry1 == null || entry2 == null || entry1.RootFolderType == FolderType.USER || entry2.RootFolderType == FolderType.USER) return; + if (entry1 == null || entry2 == null) return; SendHeadersMessage(headers, action, MessageTarget.Create(new[] { entry1.ID, entry2.ID }), description); } private static void SendHeadersMessage(Dictionary headers, MessageAction action, MessageTarget target, params string[] description) { - if (headers == null) + if (headers == null)//todo check need if { log.Debug(string.Format("Empty Request Headers for \"{0}\" type of event", action)); return; @@ -62,10 +54,9 @@ namespace ASC.Web.Files.Helpers } - public static void Send(FileEntry entry, HttpRequest request, MessageAction action, params string[] description) + public static void Send(FileEntry entry, HttpRequest request, MessageAction action, string description) { - // do not log actions in users folder - if (entry == null || entry.RootFolderType == FolderType.USER) return; + if (entry == null) return; if (request == null) { @@ -76,10 +67,22 @@ namespace ASC.Web.Files.Helpers MessageService.Send(request, action, MessageTarget.Create(entry.ID), description); } + public static void Send(FileEntry entry, HttpRequest request, MessageAction action, string d1, string d2) + { + if (entry == null) return; + + if (request == null) + { + log.Debug(string.Format("Empty Http Request for \"{0}\" type of event", action)); + return; + } + + MessageService.Send(request, action, MessageTarget.Create(entry.ID), d1, d2); + } public static void Send(FileEntry entry, MessageInitiator initiator, MessageAction action, params string[] description) { - if (entry == null || entry.RootFolderType == FolderType.USER) return; + if (entry == null) return; MessageService.Send(initiator, action, MessageTarget.Create(entry.ID), description); } diff --git a/web/studio/ASC.Web.Studio/Products/Files/Helpers/Global.cs b/web/studio/ASC.Web.Studio/Products/Files/Helpers/Global.cs index 2ad6d0349..0dd65a88f 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Helpers/Global.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Helpers/Global.cs @@ -145,6 +145,11 @@ namespace ASC.Web.Files.Classes public static string ThumbnailExtension; + public static bool EnableShareDialogV115 + { + get { return Boolean.TrueString.Equals(ConfigurationManagerExtension.AppSettings["files.sharedialog-v115"] ?? "false", StringComparison.InvariantCultureIgnoreCase); } + } + public static bool EnableUploadFilter { get { return Boolean.TrueString.Equals(ConfigurationManagerExtension.AppSettings["files.upload-filter"] ?? "false", StringComparison.InvariantCultureIgnoreCase); } diff --git a/web/studio/ASC.Web.Studio/Products/Files/HttpHandlers/ChunkedUploaderHandler.cs b/web/studio/ASC.Web.Studio/Products/Files/HttpHandlers/ChunkedUploaderHandler.cs index 3d36b74aa..fa4bffc6a 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/HttpHandlers/ChunkedUploaderHandler.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/HttpHandlers/ChunkedUploaderHandler.cs @@ -119,7 +119,7 @@ namespace ASC.Web.Files.HttpHandlers if (request.Type == ChunkedRequestType.Initiate) { CoreContext.TenantManager.SetCurrentTenant(request.TenantId); - SecurityContext.AuthenticateMe(CoreContext.Authentication.GetAccountByID(request.AuthKey)); + SecurityContext.CurrentUser = request.AuthKey; if (request.CultureInfo != null) Thread.CurrentThread.CurrentUICulture = request.CultureInfo; return true; @@ -131,7 +131,7 @@ namespace ASC.Web.Files.HttpHandlers if (uploadSession != null) { CoreContext.TenantManager.SetCurrentTenant(uploadSession.TenantId); - SecurityContext.AuthenticateMe(CoreContext.Authentication.GetAccountByID(uploadSession.UserId)); + SecurityContext.CurrentUser = uploadSession.UserId; var culture = SetupInfo.GetPersonalCulture(uploadSession.CultureName).Value; if (culture != null) Thread.CurrentThread.CurrentUICulture = culture; @@ -235,7 +235,10 @@ namespace ASC.Web.Files.HttpHandlers public string UploadId { - get { return _request["uid"]; } + get + { + return Path.GetFileName(_request["uid"]); + } } public int TenantId diff --git a/web/studio/ASC.Web.Studio/Products/Files/HttpHandlers/FileHandler.ashx.cs b/web/studio/ASC.Web.Studio/Products/Files/HttpHandlers/FileHandler.ashx.cs index 0ede00a32..89523a9c7 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/HttpHandlers/FileHandler.ashx.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/HttpHandlers/FileHandler.ashx.cs @@ -42,8 +42,11 @@ using ASC.Web.Files.Services.FFmpegService; using ASC.Web.Files.Utils; using ASC.Web.Studio.Core; using ASC.Web.Studio.UserControls.Statistics; +using ASC.Web.Studio.Utility; -using JWT; +using JWT.Algorithms; +using JWT.Builder; +using JWT.Exceptions; using Newtonsoft.Json.Linq; @@ -77,6 +80,8 @@ namespace ASC.Web.Files switch ((context.Request[FilesLinkUtility.Action] ?? "").ToLower()) { case "view": + DownloadFile(context, true); + break; case "download": DownloadFile(context); break; @@ -126,9 +131,24 @@ namespace ASC.Web.Files return; } - var ext = CompressToArchive.GetExt(context.Request.QueryString["ext"]); + var filename = context.Request.QueryString["filename"]; + + if (String.IsNullOrEmpty(filename)) + { + var ext = CompressToArchive.GetExt(context.Request.QueryString["ext"]); + + filename = FileConstant.DownloadTitle + ext; + } + else + { + filename = InstanceCrypto.Decrypt(Uri.UnescapeDataString(filename)); + } + + + var path = string.Format(@"{0}\{1}", SecurityContext.CurrentAccount.ID, filename); + var store = Global.GetStore(); - var path = string.Format(@"{0}\{1}{2}", SecurityContext.CurrentAccount.ID, FileConstant.DownloadTitle, ext); + if (!store.IsFile(FileConstant.StorageDomainTmp, path)) { Global.Logger.ErrorFormat("BulkDownload file error. File is not exist on storage. UserId: {0}.", SecurityContext.CurrentAccount.ID); @@ -158,7 +178,7 @@ namespace ASC.Web.Files readStream.Seek(offset, SeekOrigin.Begin); } - SendStreamByChunks(context, length, FileConstant.DownloadTitle + ext, readStream, ref flushed); + SendStreamByChunks(context, length, filename, readStream, ref flushed); } context.Response.Flush(); @@ -172,7 +192,7 @@ namespace ASC.Web.Files } } - private static void DownloadFile(HttpContext context) + private static void DownloadFile(HttpContext context, bool forView = false) { var flushed = false; try @@ -182,13 +202,12 @@ namespace ASC.Web.Files using (var fileDao = Global.DaoFactory.GetFileDao()) { - File file; - var readLink = FileShareLink.Check(doc, true, fileDao, out file); + int version = 0; + var readLink = FileShareLink.Check(doc, true, fileDao, out File file, out FileShare linkShare); if (!readLink && file == null) { fileDao.InvalidateCache(id); - int version; file = int.TryParse(context.Request[FilesLinkUtility.Version], out version) && version > 0 ? fileDao.GetFile(id, version) : fileDao.GetFile(id); @@ -201,7 +220,13 @@ namespace ASC.Web.Files return; } - if (!readLink && !Global.GetFilesSecurity().CanRead(file)) + if (!readLink && !Global.GetFilesSecurity().CanDownload(file)) + { + context.Response.StatusCode = (int)HttpStatusCode.Forbidden; + return; + } + + if (readLink && (linkShare == FileShare.Comment || linkShare == FileShare.Read) && file.DenyDownload) { context.Response.StatusCode = (int)HttpStatusCode.Forbidden; return; @@ -223,7 +248,27 @@ namespace ASC.Web.Files context.Response.ClearHeaders(); context.Response.Charset = "utf-8"; - FilesMessageService.Send(file, context.Request, MessageAction.FileDownloaded, file.Title); + var range = (context.Request.Headers["Range"] ?? "").Split(new[] { '=', '-' }); + var isNeedSendAction = range.Count() < 2 || Convert.ToInt64(range[1]) == 0; + + if (isNeedSendAction) + { + if (forView) + { + FilesMessageService.Send(file, context.Request, MessageAction.FileReaded, file.Title); + } + else + { + if (version == 0) + { + FilesMessageService.Send(file, context.Request, MessageAction.FileDownloaded, file.Title); + } + else + { + FilesMessageService.Send(file, context.Request, MessageAction.FileRevisionDownloaded, file.Title, file.Version.ToString()); + } + } + } if (string.Equals(context.Request.Headers["If-None-Match"], GetEtag(file))) { @@ -490,15 +535,20 @@ namespace ASC.Web.Files try { var header = context.Request.Headers[FileUtility.SignatureHeader]; + if (string.IsNullOrEmpty(header) || !header.StartsWith("Bearer ")) { throw new Exception("Invalid header " + header); } + header = header.Substring("Bearer ".Length); - JsonWebToken.JsonSerializer = new DocumentService.JwtSerializer(); - - var stringPayload = JsonWebToken.Decode(header, FileUtility.SignatureSecret); + var stringPayload = JwtBuilder.Create() + .WithAlgorithm(new HMACSHA256Algorithm()) + .WithSerializer(new DocumentService.JwtSerializer()) + .WithSecret(FileUtility.SignatureSecret) + .MustVerifySignature() + .Decode(header); Global.Logger.Debug("DocService StreamFile payload: " + stringPayload); //var data = JObject.Parse(stringPayload); @@ -544,7 +594,7 @@ namespace ASC.Web.Files return; } - if (linkRight == FileShare.Restrict && SecurityContext.IsAuthenticated && !Global.GetFilesSecurity().CanRead(file)) + if (linkRight == FileShare.Restrict && SecurityContext.IsAuthenticated && !Global.GetFilesSecurity().CanDownload(file)) { context.Response.StatusCode = (int)HttpStatusCode.Forbidden; return; @@ -586,7 +636,7 @@ namespace ASC.Web.Files } catch (HttpException he) { - Global.Logger.ErrorFormat("StreamFile", he); + Global.Logger.Error("StreamFile", he); } } @@ -606,9 +656,12 @@ namespace ASC.Web.Files } header = header.Substring("Bearer ".Length); - JsonWebToken.JsonSerializer = new DocumentService.JwtSerializer(); - - var stringPayload = JsonWebToken.Decode(header, FileUtility.SignatureSecret); + var stringPayload = JwtBuilder.Create() + .WithAlgorithm(new HMACSHA256Algorithm()) + .WithSerializer(new DocumentService.JwtSerializer()) + .WithSecret(FileUtility.SignatureSecret) + .MustVerifySignature() + .Decode(header); Global.Logger.Debug("DocService EmptyFile payload: " + stringPayload); //var data = JObject.Parse(stringPayload); @@ -682,7 +735,7 @@ namespace ASC.Web.Files } catch (HttpException he) { - Global.Logger.ErrorFormat("EmptyFile", he); + Global.Logger.Error("EmptyFile", he); } } @@ -734,7 +787,7 @@ namespace ASC.Web.Files } catch (HttpException he) { - Global.Logger.ErrorFormat("TempFile", he); + Global.Logger.Error("TempFile", he); } } @@ -822,7 +875,7 @@ namespace ASC.Web.Files } catch (HttpException he) { - Global.Logger.ErrorFormat("DifferenceFile", he); + Global.Logger.Error("DifferenceFile", he); } } @@ -897,7 +950,7 @@ namespace ASC.Web.Files } catch (HttpException he) { - Global.Logger.ErrorFormat("Thumbnail", he); + Global.Logger.Error("Thumbnail", he); } } @@ -910,11 +963,7 @@ namespace ASC.Web.Files { if (!SecurityContext.IsAuthenticated) { - var refererURL = context.Request.GetUrlRewriter().AbsoluteUri; - - context.Session["refererURL"] = refererURL; - var authUrl = "~/Auth.aspx"; - context.Response.Redirect(authUrl, true); + context.Response.Redirect(context.Request.AppendRefererURL("~/Auth.aspx"), true); return; } @@ -1139,13 +1188,19 @@ namespace ASC.Web.Files if (!string.IsNullOrEmpty(FileUtility.SignatureSecret)) { - JsonWebToken.JsonSerializer = new DocumentService.JwtSerializer(); if (!string.IsNullOrEmpty(fileData.Token)) { try { - var dataString = JsonWebToken.Decode(fileData.Token, FileUtility.SignatureSecret); + var dataString = JwtBuilder.Create() + .WithAlgorithm(new HMACSHA256Algorithm()) + .WithSerializer(new DocumentService.JwtSerializer()) + .WithSecret(FileUtility.SignatureSecret) + .MustVerifySignature() + .Decode(fileData.Token); + var data = JObject.Parse(dataString); + if (data == null) { throw new ArgumentException("DocService request token is incorrect"); @@ -1171,7 +1226,12 @@ namespace ASC.Web.Files try { - var stringPayload = JsonWebToken.Decode(header, FileUtility.SignatureSecret); + var stringPayload = JwtBuilder.Create() + .WithAlgorithm(new HMACSHA256Algorithm()) + .WithSerializer(new DocumentService.JwtSerializer()) + .WithSecret(FileUtility.SignatureSecret) + .MustVerifySignature() + .Decode(header); Global.Logger.Debug("DocService track payload: " + stringPayload); var jsonPayload = JObject.Parse(stringPayload); diff --git a/web/studio/ASC.Web.Studio/Products/Files/HttpHandlers/docusignhandler.ashx.cs b/web/studio/ASC.Web.Studio/Products/Files/HttpHandlers/docusignhandler.ashx.cs index 7da6f74b5..68540ea31 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/HttpHandlers/docusignhandler.ashx.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/HttpHandlers/docusignhandler.ashx.cs @@ -190,7 +190,7 @@ namespace ASC.Web.Files.HttpHandlers throw new Exception("DocuSign incorrect User ID: " + userIdString); } - SecurityContext.AuthenticateMe(userId); + SecurityContext.CurrentUser = userId; } private static XmlNode GetSingleNode(XmlNode node, string xpath, XmlNamespaceManager mgr, bool canMiss = false) diff --git a/web/studio/ASC.Web.Studio/Products/Files/OpenPrivate.aspx b/web/studio/ASC.Web.Studio/Products/Files/OpenPrivate.aspx new file mode 100644 index 000000000..5b75ed0dc --- /dev/null +++ b/web/studio/ASC.Web.Studio/Products/Files/OpenPrivate.aspx @@ -0,0 +1,9 @@ +<%@ Assembly Name="ASC.Web.Files" %> + +<%@ Page Language="C#" MasterPageFile="~/Products/Files/Masters/BasicTemplate.Master" EnableViewState="false" EnableViewStateMac="false" AutoEventWireup="true" CodeBehind="OpenPrivate.aspx.cs" Inherits="ASC.Web.Files.OpenPrivate" %> + +<%@ MasterType TypeName="ASC.Web.Files.Masters.BasicTemplate" %> + + + + diff --git a/web/studio/ASC.Web.Studio/Products/Files/OpenPrivate.aspx.cs b/web/studio/ASC.Web.Studio/Products/Files/OpenPrivate.aspx.cs new file mode 100644 index 000000000..01383e42e --- /dev/null +++ b/web/studio/ASC.Web.Studio/Products/Files/OpenPrivate.aspx.cs @@ -0,0 +1,86 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System; +using System.Net; +using System.Text; +using System.Web; + +using ASC.Core; +using ASC.Web.Core; +using ASC.Web.Core.Client.Bundling; +using ASC.Web.Core.Files; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Controls; +using ASC.Web.Files.Resources; +using ASC.Web.Studio.Core; +using ASC.Web.Studio; + +using Global = ASC.Web.Files.Classes.Global; + +namespace ASC.Web.Files +{ + public partial class OpenPrivate : MainPage + { + protected override void OnPreInit(EventArgs e) + { + base.OnPreInit(e); + + if (!PrivacyRoomSettings.Available || !PrivacyRoomSettings.Enabled || CoreContext.Configuration.Personal) + { + Response.Redirect(PathProvider.StartURL, true); + } + + if (string.IsNullOrEmpty(Request[FilesLinkUtility.FileId])) + { + Response.Redirect(PathProvider.StartURL + + "#error/" + + HttpUtility.UrlEncode(FilesCommonResource.ErrorMassage_FileNotFound), true); + } + } + + protected void Page_Load(object sender, EventArgs e) + { + Master.Master.DisabledSidePanel = true; + Master.Master.DisabledTopStudioPanel = true; + Master.Master + .AddStaticStyles(GetStaticStyleSheet()) + .AddStaticBodyScripts(GetStaticJavaScript()); + + OpenPrivateHolder.Controls.Add(LoadControl(PrivateRoomOpenFile.Location)); + } + + public ScriptBundleData GetStaticJavaScript() + { + return (ScriptBundleData) + new ScriptBundleData("filesOpenPrivate", "files") + .AddSource(r => FilesLinkUtility.FilesBaseAbsolutePath + r, + "Controls/PrivateRoomOpenFile/privateroomopenfile.js" + ); + } + + public StyleBundleData GetStaticStyleSheet() + { + return (StyleBundleData) + new StyleBundleData("filesOpenPrivate", "files") + .AddSource(r => FilesLinkUtility.FilesBaseAbsolutePath + r, + "Controls/PrivateRoomOpenFile/privateroomopenfile.css" + ); + } + + } +} diff --git a/web/studio/ASC.Web.Studio/Products/Files/OpenPrivate.aspx.designer.cs b/web/studio/ASC.Web.Studio/Products/Files/OpenPrivate.aspx.designer.cs new file mode 100644 index 000000000..987dc4d9d --- /dev/null +++ b/web/studio/ASC.Web.Studio/Products/Files/OpenPrivate.aspx.designer.cs @@ -0,0 +1,40 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ASC.Web.Files +{ + + + public partial class OpenPrivate + { + + /// + /// OpenPrivateHolder control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.PlaceHolder OpenPrivateHolder; + + /// + /// Master property. + /// + /// + /// Auto-generated property. + /// + public new ASC.Web.Files.Masters.BasicTemplate Master + { + get + { + return ((ASC.Web.Files.Masters.BasicTemplate)(base.Master)); + } + } + } +} diff --git a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesCommonResource.Designer.cs b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesCommonResource.Designer.cs index 701c52d93..9084debe4 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesCommonResource.Designer.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesCommonResource.Designer.cs @@ -19,7 +19,7 @@ namespace ASC.Web.Files.Resources { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class FilesCommonResource { @@ -105,6 +105,15 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to None. + /// + public static string AceStatusEnum_None { + get { + return ResourceManager.GetString("AceStatusEnum_None", resourceCulture); + } + } + /// /// Looks up a localized string similar to Read Only. /// @@ -438,6 +447,15 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to Submitted form. + /// + public static string CommentSubmitFillForm { + get { + return ResourceManager.GetString("CommentSubmitFillForm", resourceCulture); + } + } + /// /// Looks up a localized string similar to Uploaded. /// @@ -1330,6 +1348,69 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to Don’t have ONLYOFFICE Desktop Editors?. + /// + public static string PvDesktopEditorsNotInstalledTextBlock { + get { + return ResourceManager.GetString("PvDesktopEditorsNotInstalledTextBlock", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Install now. + /// + public static string PvDownloadNowButton { + get { + return ResourceManager.GetString("PvDownloadNowButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to If you have ONLYOFFICE Desktop Editors installed but can't open it from this page, your browser might be blocking it.{0}You can open this file from the desktop app's interface once your cloud is connected.. + /// + public static string PvFileBlockingFileOpeningTextBlock { + get { + return ResourceManager.GetString("PvFileBlockingFileOpeningTextBlock", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open ONLYOFFICE Desktop Editors. + /// + public static string PvOpenAppButton { + get { + return ResourceManager.GetString("PvOpenAppButton", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to If you don’t see a dialog, click the button below.. + /// + public static string PvOpenAppTextBlock { + get { + return ResourceManager.GetString("PvOpenAppTextBlock", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Click Open {0}ONLYOFFICE Desktop{1} in the browser dialog to work {2}with the encrypted documents.. + /// + public static string PvOpenDocumentTextBlock { + get { + return ResourceManager.GetString("PvOpenDocumentTextBlock", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This document is encrypted. + /// + public static string PvOpenDocumentTitle { + get { + return ResourceManager.GetString("PvOpenDocumentTitle", resourceCulture); + } + } + /// /// Looks up a localized string similar to Mark as Read. /// @@ -1429,6 +1510,15 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to Can't log in with your current password?. + /// + public static string ThirdPartyFaqLinkText { + get { + return ResourceManager.GetString("ThirdPartyFaqLinkText", resourceCulture); + } + } + /// /// Looks up a localized string similar to Folder title. /// @@ -1519,6 +1609,15 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to Will be permanently deleted on. + /// + public static string TitleRemovePermanently { + get { + return ResourceManager.GetString("TitleRemovePermanently", resourceCulture); + } + } + /// /// Looks up a localized string similar to Select. /// diff --git a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesCommonResource.az-Latn-AZ.resx b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesCommonResource.az-Latn-AZ.resx index bf9c4363e..f9641d570 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesCommonResource.az-Latn-AZ.resx +++ b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesCommonResource.az-Latn-AZ.resx @@ -61,15 +61,51 @@ Giriş + + Xarici Portal + + + Şərh + + + Fərdi Filter + + + Formanın Doldurulması + Yalnız oxumaq üçün Tam giriş + + Girişi Rədd Edin + + + Nəzərdən keçirmə + Admin + + Faylla işləmək üçün ona giriş icazəsi verməlisiniz + + + DAVAM EDİN + + + Bu faylı ONLYOFFICE ™-də açmaqla, faylın yeni nüsxəsi Office Open XML formatında Google Diskinizdə yaradılacaq. Beləliklə, orijinal fayl dəyişdirilməyəcək. Faylı daha sonra ONLYOFFICE ™-də redaktə etmək üçün yeni nüsxəni açmalısınız. + + + Bunu bir daha göstərmə + + + Fayl adını daxil edin + + + Fayl Növünü Seçin + Müəllif @@ -82,6 +118,9 @@ Çevir və aç + + Şablona görə Yarat + Sil @@ -91,42 +130,231 @@ Redaktə et + + Formanı doldurun + + + Sevimlilərdən silin + Qeyd et + + Şərh + + + Avtomatik yadda saxlanıldı + + + Sahib dəyişdi + + + Çevirmə: {0} + + + Kopyalandı + + + Yaradıldı + + + DocuSign tərəfindən yaradıldı + + + Formanı doldurmaq üçün yaradıldı + + + Redaktə edildi + + + Şifrələnmə ilə redaktə edildi + + + Şifrələnib + + + Yadda saxlanıldı + + + Üstünə yazılıb + + + {0} reviziyadan bərpa edildi + + + {0} sənəd dəyişikliyindən bərpa edildi + + + Yükləndi + + + Bağlantı url + + + Sənəd redaktə üçün çevrildi: {0} + Buna çevir + + Sənədlərimə daxil olun + + + Birgə redaktəni asanlaşdırın + + + İki birgə redaktə rejimləri arasında keçid edin. + + + Başqalarının real zamanda nə yazdığını görmək üçün Sürətli rejimi seçin. + + + Paraqrafı kilidləmək və daha məxfi işləmək üçün Sərt rejimi aktiv edin. + + + Faylları paylaşın + + + Faylları komandanızla və ya ictimai olaraq paylaşın. Sənədləri veb saytlara yerləşdirin. + + + Formalara baxmaq, redaktə etmək, nəzərdən keçirmək, şərh yazmaq, doldurmaq üçün giriş icazəsi verin. + + + Elektron imza üçün sənədləri göndərin. + + + Daha çox əməkdaşlıq alətlərindən istifadə edin + + + Sənədləri nəzərdən keçirin və Dəyişikliklərin İzlənməsi ilə düzəlişlər təklif edin. + + + Şərhlər əlavə edib müəyyən şəxsə ünvanlayın. + + + Versiya tarixçəsinə baxın və əvvəlki versiyaları bərpa edin. + + + Ofis sənədləri ilə işləyin + + + Sənədləri, cədvəlləri və təqdimatları yadda saxlayın və redaktə edin. + + + docx, xlsx və pptx ilə maksimum uyğunluqdan zövq alın. + + + Google Drive, Dropbox, Box, OneDrive kimi 3-cü tərəf yaddaşları əlaqələndirin. + + + Sənəd Xidməti əlçatan deyil. Dəstəklə əlaqə saxlayın + Sənəd adı + + Rədd edildi + + + Göndərmə zamanı xəta baş verdi + + + Sənədlərin DocuSign vasitəsilə göndərilməsi ləğv edildi + + + Sənəd DocuSign vasitəsilə imzalanmaq üçün göndərildi. + + + Ləğv edildi + + + Sənəddə qeyd edildikdə bildir + + + Şərh daxil edin + Xəta Pis sorğu. + + Bu istifadəçini seçə bilməzsiniz + + + Çevirmə prosesi çox vaxt ala bilər. + + + Sənədlər Xidmətində xəta baş verdi + + + Fayl {0} istifadəçisi tərəfindən redaktə üçün açıqdır. Birgə redaktə, hazırda bu format üçün dəstəklənmir. + + + Fayl {0} tərəfindən mobil tətbiqdə redaktə etmək üçün açıqdır + Boş qovluq Tələb olunan fayl tapılmadı + + {0} ölçüsündən böyük fayl çevrilməyəcək + + + {0} ölçüsündən böyük fayl kopyalanmayacaq + + + {0} ölçüsündən böyük fayl redaktə edilməyəcək + + + {0} ölçüsündən böyük fayl köçürülməyəcək + Qovluğu öz alt qovluğuna köçürə bilməzsiniz Tələb olunan qovluq tapılmadı + + Yanlış başlıq + + + Hazırda fayl başqa istifadəçi tərəfindən kilidlənib. + + + Mesajı göndərmək üçün heç bir hesab yoxdur. Mail-ə daxil olub poçt qutunuzu əlaqələndirin. + + + Ən azı {0} poçt mesajı göndərmək alınmadı. Bunun səbəbini öyrənmək üçün Poçt moduluna daxil olun. + Həddən çox endirmə var Üzr istəyirik, amma bu format dəstəklənmir + + Əməliyyat ləğv edildi + + + Yadda saxlamanı icra edən şəxs bilinmir + + + Yadda saxlamağa çalışarkən fayl pozuldu + + + Yadda saxlanılacaq faylı əldə etmək alınmadı + Əməliyyatı gerçəkləşdirməyə yetərincə icazəniz yoxdur + + Verilən kredensiallar ilə {0} provayerinə qoşulmaq mümkün deyil. + Yaratmağa yetərincə icazəniz yoxdur @@ -148,6 +376,12 @@ Bu sənəd Sizin tərəfinizdən başqa bir nişanda redaktə olunur + + Bu faylı köçürmək üçün kifayət qədər icazəniz yoxdur. + + + Bu qovluğu köçürmək üçün kifayət qədər icazəniz yoxdur + Fayla baxmağa yetərincə icazəniz yoxdur @@ -166,9 +400,18 @@ Qovluğun içinə baxmağa yetərincə icazəniz yoxdur + + Üçüncü tərəf resurs qovluğunun məzmunu mövcud deyil. Hesabınızı yenidən əlaqələndirməyə çalışın. + + + Faylın redaktəsinə başlarkən xəta baş verdi + Redaktə üçün açıq olan faylı güncəlləyə bilməzsiniz + + İStifadəçi tapılmadı + Səbətdə elementlərə baxmağa icazəniz yoxdur @@ -187,6 +430,9 @@ Qonaq + + Sənədlərin məzmununa görə axtarış + yeni @@ -196,6 +442,18 @@ Bərpa et + + "{0}" qovluğunda fayl yaradıldı + + + Doldurula bilən OFORM sənədi hazırdır. {0} qovluğunda yadda saxlanıldı + + + OFORM-un kopyası {0} qovluğunda yaradıldı. Formanı doldurun və nəticəni PDF və ya DOCX formatında yükləyin. + + + Fayl artıq formanı doldurmaq üçün qaralama deyil. + Öncədən bax @@ -211,6 +469,9 @@ Portaldakı sənədlərinizi yaradın, redaktə edin, yaddaşda saxlayın və paylaşın. Google Drive, Zoho, Box'dən sənədlərinizi import edin. + + Paylaşılan sənədlərə baxıb endirin. Faylları paylaşılan qovluqlara yükləyin. docx, xlsx, pptx formatları ilə 100% uyğunluğa zəmanət verilir. + Sənədlər @@ -220,9 +481,33 @@ {!User} silindi + + ONLYOFFICE Masaüstü Redaktorlarınız yoxdur? + + + İndi quraşdır + + + ONLYOFFICE Desktop Redaktorları quraşdırılıbsa, lakin siz onu bu səhifədən aça bilmirsinizsə, brauzeriniz onu bloklamış ola bilər.{0}Buludunuz qoşulduqdan sonra bu faylı masaüstü proqramının interfeysindən aça bilərsiniz. + + + ONLYOFFICE Masaüstü Redaktorları Açın + + + Dialoqu görməsəniz, aşağıdakı düyməyə klikləyin. + + + Şifrələnmiş sənədlərlə {2} işləmək üçün brauzer dialoqunda {0}ONLYOFFICE Masaüstünü{1} Açın seçiminə klikləyin. + + + Göndərmə zamanı xəta baş verdi + Oxunmuş kimi işarələ + + Axtarış + Paylaş @@ -235,18 +520,42 @@ Ölçü + + Sənəd DocuSign ilə imzalandıqda bildir + + + DocuSign-dakı sənədin statusu dəyişdikdə bildir + Paylaşılan zaman bildir + + Göndərmənin başa çatması barədə məlumat ver + + + Dəyişdirmək üçün doldurun + + + Hazırki şifrə ilə daxil ola bilmirsiniz? + Qovluq başlığı + + Təkrar əlaqələndir + + + Hesab + Açıq etmək və 'Ümumi sənədlər' qovluğuna yerləşdirmək Yaradıldı + + {!user} {0} adlı şəxsin sənədləri + Sənədlər @@ -259,6 +568,9 @@ Silindi + + Müvəqqəti olaraq silinəcək + Seç @@ -277,7 +589,25 @@ Yükləndi + + Versiya kimi qeyd edin + + + Reviziya kimi qeyd edin + + + Versiyalar və reviziyalar + + + Ad + + + Soyad + + + Fayl kiliddən çıxarılıb + versiya - \ No newline at end of file + diff --git a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesCommonResource.resx b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesCommonResource.resx index a8547a748..9a7cdcbb6 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesCommonResource.resx +++ b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesCommonResource.resx @@ -1,5 +1,64 @@  + @@ -53,10 +112,10 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Access @@ -184,6 +243,9 @@ Restored from the document change of {0} + + Submitted form + Uploaded @@ -482,6 +544,27 @@ {!User} removed + + Don’t have ONLYOFFICE Desktop Editors? + + + Install now + + + If you have ONLYOFFICE Desktop Editors installed but can't open it from this page, your browser might be blocking it.{0}You can open this file from the desktop app's interface once your cloud is connected. + + + Open ONLYOFFICE Desktop Editors + + + If you don’t see a dialog, click the button below. + + + Click Open {0}ONLYOFFICE Desktop{1} in the browser dialog to work {2}with the encrypted documents. + + + This document is encrypted + Mark as Read @@ -515,6 +598,9 @@ Fill in to change + + Can't log in with your current password? + Folder title @@ -545,6 +631,9 @@ Deleted + + Will be permanently deleted on + Select @@ -584,4 +673,7 @@ ver. + + None + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesJSResource.Designer.cs b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesJSResource.Designer.cs index b25db402b..80635e083 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesJSResource.Designer.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesJSResource.Designer.cs @@ -19,7 +19,7 @@ namespace ASC.Web.Files.Resources { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class FilesJSResource { @@ -393,6 +393,15 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to You are about to delete this file? Please note, that if you have shared it with someone, it will become unavailable. File will be deleted permanently in {0}.. + /// + public static string ConfirmRemoveFilePermanently { + get { + return ResourceManager.GetString("ConfirmRemoveFilePermanently", resourceCulture); + } + } + /// /// Looks up a localized string similar to You are about to delete this folder? Please note, that if you have shared it with someone, it will become unavailable. Are you sure you want to continue?. /// @@ -402,6 +411,15 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to You are about to delete this folder? Please note, that if you have shared it with someone, it will become unavailable. Folder will be deleted permanently in {0}.. + /// + public static string ConfirmRemoveFolderPermanently { + get { + return ResourceManager.GetString("ConfirmRemoveFolderPermanently", resourceCulture); + } + } + /// /// Looks up a localized string similar to You are about to delete these elements? Please note, that if you have shared these files/folders with someone, they will become unavailable. Are you sure you want to continue?. /// @@ -411,6 +429,15 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to You are about to delete these elements? Please note, that if you have shared these files/folders with someone, they will become unavailable. Files/folders will be deleted permanently in {0}.. + /// + public static string ConfirmRemoveListPermanently { + get { + return ResourceManager.GetString("ConfirmRemoveListPermanently", resourceCulture); + } + } + /// /// Looks up a localized string similar to You are about to move elements from the {1} directory. In such a case, they will be deleted from your {1} account and will no longer be accessible to other users, in case these elements have previously been shared. {0}Are you sure you want to move the elements?. /// @@ -438,6 +465,51 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to 1 month. + /// + public static string DateOneMonth { + get { + return ResourceManager.GetString("DateOneMonth", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 1 week. + /// + public static string DateOneWeek { + get { + return ResourceManager.GetString("DateOneWeek", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 3 months. + /// + public static string DateThreeMonths { + get { + return ResourceManager.GetString("DateThreeMonths", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 2 months. + /// + public static string DateTwoMonths { + get { + return ResourceManager.GetString("DateTwoMonths", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 2 weeks. + /// + public static string DateTwoWeeks { + get { + return ResourceManager.GetString("DateTwoWeeks", resourceCulture); + } + } + /// /// Looks up a localized string similar to Groups. /// @@ -726,6 +798,24 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to External link. + /// + public static string ExternalLink { + get { + return ResourceManager.GetString("ExternalLink", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Disabled. + /// + public static string ExternalLinkDisabled { + get { + return ResourceManager.GetString("ExternalLinkDisabled", resourceCulture); + } + } + /// /// Looks up a localized string similar to The file with the name {0} already exists in the folder {1}.. /// @@ -861,6 +951,15 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to Individual rights. + /// + public static string IndividualRights { + get { + return ResourceManager.GetString("IndividualRights", resourceCulture); + } + } + /// /// Looks up a localized string similar to Now '{0}' is the owner of '{1}'. /// @@ -1006,7 +1105,7 @@ namespace ASC.Web.Files.Resources { } /// - /// Looks up a localized string similar to The document '{0}' is deleted. + /// Looks up a localized string similar to The document '{0}' is deleted.. /// public static string InfoRemoveFile { get { @@ -1015,7 +1114,7 @@ namespace ASC.Web.Files.Resources { } /// - /// Looks up a localized string similar to The folder '{0}' is deleted. + /// Looks up a localized string similar to The folder '{0}' is deleted.. /// public static string InfoRemoveFolder { get { @@ -1024,7 +1123,7 @@ namespace ASC.Web.Files.Resources { } /// - /// Looks up a localized string similar to It was successfully deleted {0} from {1}. + /// Looks up a localized string similar to It was successfully deleted {0} from {1}.. /// public static string InfoRemoveGroup { get { @@ -1212,6 +1311,15 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to Shortened. + /// + public static string Shortened { + get { + return ResourceManager.GetString("Shortened", resourceCulture); + } + } + /// /// Looks up a localized string similar to Spreadsheets. /// diff --git a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesJSResource.az-Latn-AZ.resx b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesJSResource.az-Latn-AZ.resx index 0e076c501..33cfb7f6d 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesJSResource.az-Latn-AZ.resx +++ b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesJSResource.az-Latn-AZ.resx @@ -58,6 +58,18 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + {0} üçün Hesab Parametrləri + + + Şərh + + + Fərdi Filter + + + Formanın doldurulması + Sahib @@ -70,21 +82,51 @@ Girişi yasaqla + + Nəzərdən keçirmə + + + Fərqlənir + + + Arxivlər + + + Müəllif + + + Alt-qovluğu çıxarın + Sənədlər + + Bütün Fayllar + Qovluqlar Şəkillər + + Məzmuna görə + + + Media + + + Axtarış + Təqdimatlar Cədvəllər + + Növ + Daha çox göstər {0} @@ -97,6 +139,12 @@ Müəllif + + Yaradılma tarixi + + + Son dəyişdirildiyi tarix + Başlıq @@ -106,6 +154,9 @@ Tip + + Kod mübadilə buferinə kopyalanıb + Siz gerçəkdən mi {0} 'Sənədlər' modulundan silmək istəyirsiniz? Bunun Sizin hesabınıza heç bir təsiri olmayacaq {1}. @@ -118,18 +169,45 @@ Sənədi silmək istədiyinizə əminsinizmi? + + Bu faylı silmək istəyirsiniz? Nəzərə alın ki, bunu kimləsə paylaşsanız, o, əlçatan olmayacaq. Fayl {0} sonra həmişəlik silinəcək. + Qovluğu silmək istədiyinizə əminsinizmi? + + Bu qovluğu silmək istəyirsiniz? Nəzərə alın ki, onu kimləsə paylaşsanız, o, əlçatan olmayacaq. Qovluq {0} sonra həmişəlik silinəcək. + Bu elementləri silmək istədiyinizə əminsinizmi? + + Bu elementləri silmək istəyirsiniz? Nəzərə alın ki, bu fayl/qovluqları kimsə ilə paylaşsanız, onlar əlçatan olmayacaq. Fayllar/qovluqlar {0} sonra həmişəlik silinəcək. + + + Siz {1} kataloqundan elementləri köçürmək üzrəsiniz. Bu halda, onlar {1} hesabınızdan silinəcək və əvvəllər bu elementlərə giriş icazəsi verdiyiniz digər istifadəçilər üçün əlçatan olmayacaq. {0} Elementləri köçürmək istədiyinizə əminsiniz? + Ortaq sənədlər İstifadəçi formatı + + 1 ay + + + 1 həftə + + + 3 ay + + + 2 ay + + + 2 həftə + Bölmələr @@ -139,12 +217,18 @@ Verilənləri dəyişdir + + Versiya tamamlanır + Köçür Yaradılma + + DocuSign vasitəsilə imzalayın + Versiyalar yüklənir... @@ -163,12 +247,27 @@ Versiya yenilənir + + Fayl, şifrələnmə üçün hazırlanır + + + Faylın şifrələnməsi + + + Şifrələnmiş fayl yadda saxlanılır + Sənədlər + + Sənədi({0}) imzalayın + Endirmək üçün ən azı bir qovluq/sənəd seçin + + Şərh daxil edin + Boş sənədi və ya qovluğu yükləmək olmaz @@ -178,9 +277,18 @@ Bütün sətirlərin doldurulması mütləqdir + + Maksimum fayl ölçüsü keçildi + Əlavə etmək imkanının olması üçün daxil olmalısınız + + Bu fayl formatı dəstəklənmir + + + Yanlış parol daxil etmisiniz. Zəhmət olmasa yenidən daha cəhd edin + Öncəki endirmənin tamamlanmasını gözləyin və ya ləğv edin @@ -193,6 +301,12 @@ Başlıq bu simvollardan ibarət olmamalıdır: {0} + + Portalda kifayət qədər boş yer yoxdur. {0} ölçüsündən böyük fayllar yüklənə bilməz. + + + Xarici bağlantı + {0} adlı fayl {1} qovluğunda artıq var. @@ -214,27 +328,57 @@ Box kataloqu + + DocuSign hesabı + Dropbox kataloqu Google kataloqu + + kDrive kataloqu + + + SharePoint Kataloqu + OneDrive kataloqu + + WebDAV Kataloqu + + + Yandex Kataloqu + + + İndi '{0}', '{1}' sahibidir + + + İndi '{0}', {1} elementin sahibidir + '{0}' dəyişdirildi + + Kopyalayın:{1}{0}{2} + {0} köçürülmüş element '{0}' köçürüldü + + {0} element + Yeni fayl '{0}' yaradıldı + + Yeni fayl '{0}' '{1}' qovluğunda yaradıldı + Yeni qovluq '{0}' yaradıldı @@ -244,6 +388,12 @@ Qovluğu öz alt qovluğuna yerləşdirmək olmaz + + Bəzi faylları köçürmək icazəniz yoxdur + + + Köçürün: {1}{0}{2} + {0} element daşındı @@ -271,6 +421,12 @@ '{0}' '{1}' üçün əlavə olundu + + Fayllar Şablonlara əlavə edildi + + + Fayllar Şablonlardan silindi + {0} ədəd sənəd uğurla yükləndi @@ -286,9 +442,36 @@ Təqdimatlar + + {0} faylını göstərmək üçün yükləmək alınmadı + + + Şəxsi Otaq + Layihə sənədləri + + reviziya: {0} + + + Sizə {0} sənədinə giriş icazəsi verilib. Sənədi indi açmaq üçün aşağıdakı linkə klikləyin: {1} + + + {0} sənədinə giriş icazəsi aldınız + + + {0} + + + İstifadəçilər və ya qruplar əlavə edin.{0} Onlara sənədləri oxumaq, nəzərdən keçirmək və ya redaktə etmək üçün icazəsi verin. + + + {0} üçün Parametrlər Paylaşılır + + + Qısaldılıb + Cədvəllər @@ -325,6 +508,21 @@ Bu, indi: {0} tərəfindən redaktə olunur + + Kilidlənib + + + Kilidlənib: {0} + + + Əlavə Funksiyalar + + + Yeni Forma şablonu + + + Yeni Master Forması + Yeni təqdimat @@ -337,22 +535,61 @@ Yeni qovluq + + Admin Parametrləri + + + Ümumi Parametrlər + + + Yardım + + + Əlaqələndirilmiş Buludlar + Box + + DocuSign + Dropbox Google Drive + + kDrive + + + SharePoint + OneDrive + + WebDAV + + + Yandex.Disk + + + Bilinməyən xəta... + + + Yükləmə tamamlandı + Yüklənir + + {0} fayl yüklənir ({1}% tamamlandı) + + + {0} / {1} istifadə edilib + İstifadəçilər - \ No newline at end of file + diff --git a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesJSResource.resx b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesJSResource.resx index 59441d01f..cf73d452b 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesJSResource.resx +++ b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesJSResource.resx @@ -1,5 +1,64 @@  + @@ -53,10 +112,10 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Access Settings for {0} @@ -169,12 +228,21 @@ You are about to delete this file? Please note, that if you have shared it with someone, it will become unavailable. Are you sure you want to continue? + + You are about to delete this file? Please note, that if you have shared it with someone, it will become unavailable. File will be deleted permanently in {0}. + You are about to delete this folder? Please note, that if you have shared it with someone, it will become unavailable. Are you sure you want to continue? + + You are about to delete this folder? Please note, that if you have shared it with someone, it will become unavailable. Folder will be deleted permanently in {0}. + You are about to delete these elements? Please note, that if you have shared these files/folders with someone, they will become unavailable. Are you sure you want to continue? + + You are about to delete these elements? Please note, that if you have shared these files/folders with someone, they will become unavailable. Files/folders will be deleted permanently in {0}. + You are about to move elements from the {1} directory. In such a case, they will be deleted from your {1} account and will no longer be accessible to other users, in case these elements have previously been shared. {0}Are you sure you want to move the elements? @@ -187,6 +255,21 @@ Groups + + 1 month + + + 1 week + + + 3 months + + + 2 months + + + 2 weeks + Archiving Data @@ -280,6 +363,9 @@ The portal storage has run out of free space. You cannot upload files larger than {0}. + + External link + The file with the name {0} already exists in the folder {1}. @@ -374,13 +460,13 @@ '{0}' moved - The document '{0}' is deleted + The document '{0}' is deleted. - The folder '{0}' is deleted + The folder '{0}' is deleted. - It was successfully deleted {0} from {1} + It was successfully deleted {0} from {1}. Third party '{0}' is disconnected @@ -559,4 +645,13 @@ Users + + Shortened + + + Individual rights + + + Disabled + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.Designer.cs b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.Designer.cs index 4dae83e0d..ba874eee5 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.Designer.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.Designer.cs @@ -19,7 +19,7 @@ namespace ASC.Web.Files.Resources { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class FilesUCResource { @@ -132,6 +132,15 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to Add message. + /// + public static string AddMessage { + get { + return ResourceManager.GetString("AddMessage", resourceCulture); + } + } + /// /// Looks up a localized string similar to Admin Settings. /// @@ -141,6 +150,33 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to Advanced settings. + /// + public static string AdvancedSettings { + get { + return ResourceManager.GetString("AdvancedSettings", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Trash bin auto-clearing - Delete files older than. + /// + public static string AutomaticallyCleanUp { + get { + return ResourceManager.GetString("AutomaticallyCleanUp", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The files will be deleted some time after you activate the setting. + /// + public static string AutomaticallyCleanUpInfo { + get { + return ResourceManager.GetString("AutomaticallyCleanUpInfo", resourceCulture); + } + } + /// /// Looks up a localized string similar to Add Box account. /// @@ -177,6 +213,15 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to Add link. + /// + public static string ButtonAddLink { + get { + return ResourceManager.GetString("ButtonAddLink", resourceCulture); + } + } + /// /// Looks up a localized string similar to Add Nextcloud account. /// @@ -204,6 +249,15 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to Add users. + /// + public static string ButtonAddUsers { + get { + return ResourceManager.GetString("ButtonAddUsers", resourceCulture); + } + } + /// /// Looks up a localized string similar to Other account. /// @@ -231,6 +285,15 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to Change access rights. + /// + public static string ButtonChangeAccessRights { + get { + return ResourceManager.GetString("ButtonChangeAccessRights", resourceCulture); + } + } + /// /// Looks up a localized string similar to Change owner. /// @@ -474,6 +537,15 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to Embedding settings. + /// + public static string ButtonEmbeddingSettings { + get { + return ResourceManager.GetString("ButtonEmbeddingSettings", resourceCulture); + } + } + /// /// Looks up a localized string similar to Empty Trash. /// @@ -654,6 +726,15 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to Remove. + /// + public static string ButtonRemove { + get { + return ResourceManager.GetString("ButtonRemove", resourceCulture); + } + } + /// /// Looks up a localized string similar to Remove from Favorites. /// @@ -663,15 +744,6 @@ namespace ASC.Web.Files.Resources { } } - /// - /// Looks up a localized string similar to Remove. - /// - public static string ButtonRemoveFavoriteShort { - get { - return ResourceManager.GetString("ButtonRemoveFavoriteShort", resourceCulture); - } - } - /// /// Looks up a localized string similar to Remove from templates. /// @@ -681,15 +753,6 @@ namespace ASC.Web.Files.Resources { } } - /// - /// Looks up a localized string similar to Remove. - /// - public static string ButtonRemoveTemplateShort { - get { - return ResourceManager.GetString("ButtonRemoveTemplateShort", resourceCulture); - } - } - /// /// Looks up a localized string similar to Rename. /// @@ -717,6 +780,33 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to Save and Copy direct link. + /// + public static string ButtonSaveAndCopyLink { + get { + return ResourceManager.GetString("ButtonSaveAndCopyLink", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save and Send. + /// + public static string ButtonSaveAndSend { + get { + return ResourceManager.GetString("ButtonSaveAndSend", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save without notification. + /// + public static string ButtonSaveWithoutNotification { + get { + return ResourceManager.GetString("ButtonSaveWithoutNotification", resourceCulture); + } + } + /// /// Looks up a localized string similar to All. /// @@ -726,6 +816,33 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to Groups. + /// + public static string ButtonSelectGroups { + get { + return ResourceManager.GetString("ButtonSelectGroups", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Links. + /// + public static string ButtonSelectLinks { + get { + return ResourceManager.GetString("ButtonSelectLinks", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Users. + /// + public static string ButtonSelectUsers { + get { + return ResourceManager.GetString("ButtonSelectUsers", resourceCulture); + } + } + /// /// Looks up a localized string similar to Sign with DocuSign. /// @@ -771,6 +888,15 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to Shorten the link. + /// + public static string ButtonShortenLink { + get { + return ResourceManager.GetString("ButtonShortenLink", resourceCulture); + } + } + /// /// Looks up a localized string similar to Show version history. /// @@ -1014,6 +1140,33 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to Enable external link access. + /// + public static string ConfirmExternalShare { + get { + return ResourceManager.GetString("ConfirmExternalShare", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show social media buttons for external sharing. + /// + public static string ConfirmExternalShareSocialMedia { + get { + return ResourceManager.GetString("ConfirmExternalShareSocialMedia", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show or hide buttons to quickly share external links to your files in social networks, e.g. Twitter or Facebook. + /// + public static string ConfirmExternalShareSocialMediaHelper { + get { + return ResourceManager.GetString("ConfirmExternalShareSocialMediaHelper", resourceCulture); + } + } + /// /// Looks up a localized string similar to Overwrite confirmation. /// @@ -1059,6 +1212,15 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to Are you sure you want to continue?. + /// + public static string ConfirmRemoveToContinue { + get { + return ResourceManager.GetString("ConfirmRemoveToContinue", resourceCulture); + } + } + /// /// Looks up a localized string similar to No file will be copied. The original file will be retained in the destination folder.. /// @@ -1221,6 +1383,78 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to Default access rights in sharing settings. + /// + public static string DefaultSharingAccessRightsSetting { + get { + return ResourceManager.GetString("DefaultSharingAccessRightsSetting", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to If not, then "Read Only". + /// + public static string DefaultSharingAccessRightsSettingCommentInfo { + get { + return ResourceManager.GetString("DefaultSharingAccessRightsSettingCommentInfo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Spreadsheet only. + /// + public static string DefaultSharingAccessRightsSettingCustomFilterInfo { + get { + return ResourceManager.GetString("DefaultSharingAccessRightsSettingCustomFilterInfo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Forms only. + /// + public static string DefaultSharingAccessRightsSettingFillFormsInfo { + get { + return ResourceManager.GetString("DefaultSharingAccessRightsSettingFillFormsInfo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Documents only. + /// + public static string DefaultSharingAccessRightsSettingReviewInfo { + get { + return ResourceManager.GetString("DefaultSharingAccessRightsSettingReviewInfo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Default access rights for a specific file type. + /// + public static string DefaultSharingAccessRightsSettingSpecific { + get { + return ResourceManager.GetString("DefaultSharingAccessRightsSettingSpecific", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Can't print, download and copy file (for Read Only & Comment). + /// + public static string DenyDownload { + get { + return ResourceManager.GetString("DenyDownload", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Can't change sharing settings (for Full Access). + /// + public static string DenySharing { + get { + return ResourceManager.GetString("DenySharing", resourceCulture); + } + } + /// /// Looks up a localized string similar to You have just created your ONLYOFFICE portal.{0} Upload your offline files to the portal to collaborate on them without leaving the app.{0}{0} Open your portal in browser to find more features, including {1}Projects{2}, {1}CRM{2}, {1}Calendar{2}, {1}Mail{2} and more.. /// @@ -1716,6 +1950,168 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to Collect filled forms. + /// + public static string FormFillingCollect { + get { + return ResourceManager.GetString("FormFillingCollect", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to By default. + /// + public static string FormFillingDialogByDefault { + get { + return ResourceManager.GetString("FormFillingDialogByDefault", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Collect in. + /// + public static string FormFillingDialogCollectIn { + get { + return ResourceManager.GetString("FormFillingDialogCollectIn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create subfolder. + /// + public static string FormFillingDialogCreateSubfolder { + get { + return ResourceManager.GetString("FormFillingDialogCreateSubfolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Vacation applications 2021. + /// + public static string FormFillingDialogCreateSubfolderExample { + get { + return ResourceManager.GetString("FormFillingDialogCreateSubfolderExample", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Create a subfolder for each type of form. + /// + public static string FormFillingDialogCreateUniqueSubfolder { + get { + return ResourceManager.GetString("FormFillingDialogCreateUniqueSubfolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The subfolder name will be inherited from the document name. + /// + public static string FormFillingDialogCreateUniqueSubfolderDscr { + get { + return ResourceManager.GetString("FormFillingDialogCreateUniqueSubfolderDscr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Customize. + /// + public static string FormFillingDialogCustomize { + get { + return ResourceManager.GetString("FormFillingDialogCustomize", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Date. + /// + public static string FormFillingDialogDate { + get { + return ResourceManager.GetString("FormFillingDialogDate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Document Title - Username (MM/DD/YYYY) .docx. + /// + public static string FormFillingDialogDefaultTitleFormat { + get { + return ResourceManager.GetString("FormFillingDialogDefaultTitleFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enable collection of filled forms. + /// + public static string FormFillingDialogEnableCollection { + get { + return ResourceManager.GetString("FormFillingDialogEnableCollection", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to File Name. + /// + public static string FormFillingDialogFileName { + get { + return ResourceManager.GetString("FormFillingDialogFileName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Title setting. + /// + public static string FormFillingDialogTitleSetting { + get { + return ResourceManager.GetString("FormFillingDialogTitleSetting", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to User Name. + /// + public static string FormFillingDialogUserName { + get { + return ResourceManager.GetString("FormFillingDialogUserName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to At the moment, the collection of filled forms is configured in a folder where you do not have permission to view the content. Want to reset these settings?. + /// + public static string FormFillingOwnerDialogBody { + get { + return ResourceManager.GetString("FormFillingOwnerDialogBody", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Attention!. + /// + public static string FormFillingOwnerDialogHeader { + get { + return ResourceManager.GetString("FormFillingOwnerDialogHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Yes, reset. + /// + public static string FormFillingOwnerDialogResetBtn { + get { + return ResourceManager.GetString("FormFillingOwnerDialogResetBtn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Form filling settings. + /// + public static string FormFillingSettings { + get { + return ResourceManager.GetString("FormFillingSettings", resourceCulture); + } + } + /// /// Looks up a localized string similar to Start Now. /// @@ -1905,6 +2301,15 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to Notify with changes in rights and new ones.. + /// + public static string NotifyWithChangesInRights { + get { + return ResourceManager.GetString("NotifyWithChangesInRights", resourceCulture); + } + } + /// /// Looks up a localized string similar to Preview. /// @@ -2148,6 +2553,15 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to Share to. + /// + public static string ShareTo { + get { + return ResourceManager.GetString("ShareTo", resourceCulture); + } + } + /// /// Looks up a localized string similar to External link access. /// @@ -2166,6 +2580,24 @@ namespace ASC.Web.Files.Resources { } } + /// + /// Looks up a localized string similar to Temporarily disabled on the portal by adminstration. + /// + public static string SharingLinkEnableDescr { + get { + return ResourceManager.GetString("SharingLinkEnableDescr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sharing settings. + /// + public static string SharingSettings { + get { + return ResourceManager.GetString("SharingSettings", resourceCulture); + } + } + /// /// Looks up a localized string similar to Show more. /// diff --git a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.az-Latn-AZ.resx b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.az-Latn-AZ.resx index 24aaec8bf..a81c7a0c6 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.az-Latn-AZ.resx +++ b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.az-Latn-AZ.resx @@ -58,6 +58,15 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Şərh + + + Fərdi Filter + + + Formanın Doldurulması + Yalnız oxumaq üçün @@ -67,21 +76,66 @@ Girişi yasaqla + + Nəzərdən keçirin + + + Əlaqələndirin + + + Mesaj əlavə et + + + Admin Parametrləri + + + Təkmilləşdirilmiş parametrlər + + + Sonra istifadəçi qutularını təmizləyin + Box hesabı əlavə et Dropbox hesabı əlavə et + + Sevimli kimi işarələ + Google hesabı əlavə et + + Link əlavə et + + + Nextcloud hesabı əlavə et + OneDrive hesabı əlavə et + + Şablon kimi istifadə et + + + İstifadəçi əlavə et + + + Başqa hesab + Ləğv + + Hamısını ləğv edin + + + Giriş hüquqlarını dəyiş + + + Sahibi dəyiş + Üçüncü tərəf bilgisini dəyişdir @@ -91,21 +145,57 @@ Qapat + + Kopyasını yarat + + + Xarici linki kopyala + Köçür + + Kopyala + Yarat + + Şablondan yarat + + + Şablondan + + + Sənəd Yarat + Qovluq + + Forma şablonu + + + Master Forma + + + Boş fayldan + + + Mövcud Mətn faylından + Təqdimat + + Təqdimat Yarat + Cədvəl + + Cədvəl Yarat + Sənəd @@ -118,33 +208,66 @@ Endir + + Faylın kopyasını yarat + Redaktə et + + Sənəd yerləşdirilir + + + Parametrlər yerləşdirilir + Səbəti boşalt + + Formanı doldurun + + + Arxivlər + Sənədlər + + Bütün fayllar + Qovluqlar Şəkillər + + Media + Təqdimatlar Cədvəllər + + Keçin: '{0}' + Sənədlərimə keç Daha çox öyrənin... + + Ortaq müəlliflər üçün blok edin + + + Formanı yarat + + + Köçür və ya Kopyala + Yerləşdir @@ -154,6 +277,24 @@ Açıq + + Versiya yeniləməsi ilə üzərinə yazın + + + Silin + + + Sevimlilərdən silin + + + Silin + + + Şablonlardan silin + + + Silin + Yenidən adlandır @@ -163,12 +304,45 @@ Qeyd et + + Birbaşa linki Yadda Saxlayıb Kopyalayın + + + Yadda Saxlayıb Göndərin + + + Bildiriş olmadan yadda saxlayın + Bütün + + Qruplar + + + Linklər + + + İstifadəçilər + + + DocuSign ilə imzalayın + + + Göndər + + + E-poçt vasitəsi ilə göndər + Paylaşma ayarları + + Paylaşım Parametrləri + + + Linki qısaldın + Versiyaları göstər @@ -178,27 +352,84 @@ Geri qovluğa qayıt + + Blokdan çıxarın + Yuxarı Yüklə + + Yükləməni ləğv edib bağlayın + + + Faylları yüklə + + + Qovluğu yüklə + + + Dialoq pəncərəsini böyüdün + + + Dialoq pəncərəsini kiçildin + + + Versiya tarixçəsi + + + Versiyanı tamamlayın + Facebook Twitter + + Ləğv edildi + + + Fayl parolla qorunur. Faylı çevirmək üçün parolu daxil edin. + Sənədi çevir və aç + + Sənəd yadda saxlanılır + + + Sənəd Yüklənir + + + Dəyişikliklər tətbiq edildikdən sonra, cari fayl/qovluq sahibi aid olduğu qrupun giriş parametrlərinə və ya standart qovluq icazələrinə uyğun olaraq giriş əldə edir. + + + Sahibi dəyiş + Endirilməli olan fayllardan hər birinin formatını seçin + + Ümumi Parametrlər + + + Elementi zibil qutusuna köçürərkən bildirişi göstər + + + Bu mesajı bir daha göstərmə + + + Qovluqda iki fərqli fayl olacaq. + Üzərinə yazmanın təsdiqlənməsi + + Yeni fayl yeni versiya kimi mövcud faylı əvəz edəcək. + Təsdiq @@ -208,6 +439,12 @@ Diqqət: Hesabınızın silinməsini geri döndərmək olmayacaq. + + Davam etmək istədiyinizə əminsiniz? + + + Fayl kopyalanmayacaq. Orijinal fayl təyinat qovluğunda qalacaq. + Həmçinin faylın nüsxəsini original formatda qeyd et @@ -217,21 +454,36 @@ Faylın nüsxəsi {0}Sənədlərim{1} qovluğunda yaradılacaq + + Yüklədiyiniz bütün sənədlər sürətli redaktə üçün müvafiq Office Open XML formatına (docx, xlsx və ya pptx) çevriləcək. + Həmçinin sənədləri original formatda endir + + Köçürmənin təsdiqi + Bu elementləri siyahıdan silmək istədiyinizə əminsiniz mi? Eyni adlı faylın üstünə yaz + + Bağlantı URL + + + Çevir + Çevir və endir Çevrilmə uğurla tamamlandı + + Fayl '{0}' qovluğunda yaradılıb + Çevrilmə xətası @@ -241,9 +493,75 @@ Ortaq sənədlər + + Fayldan forma şablonu yaradın + + + Fayldan Master Forma yaradın + + + Faylı çap etmək, endirmək və kopyalamaq mümkün deyil (Yalnız Oxumaq və Şərh üçün) + + + Paylaşım parametrlərini dəyişdirmək mümkün deyil (Tam Giriş üçün) + + + Siz indicə ONLYOFFICE portalını yaratdınız. {0} Tətbiqdən çıxmadan onlarla işləmək üçün oflayn faylları portala yükləyin. {0}{0} {1} Layihələr {2}, {1} CRM {2}, {1} Təqvim {2}, {1} Poçt {2} və s. kimi daha çox funksiya üçün portalı brauzerinizdə açın. + + + Brauzerdə aç + + + Satınalma ilə bağlı suallarınızı, e-poçtumuza yazın: {0} + + + Texniki problemləriniz olarsa, {0} ünvanı vasitəsilə bizimləəlaqə saxlayın. + + + Portalınıza xoş gəlmisiniz! + + + Təbriklər + Sənədlər + + Bunu etmək üçün portal parametrlərinə keçin və İnteqrasiya bölməsini açın -> Üçüncü Tərəf Xidmətləri {0} Parametrlərə keçin {1} + + + DocuSign + + + Sənədi imzalamaq üçün DocuSign xidmətinə qoşulun. + + + Qovluqda yadda saxlayın: + + + Ətraflı məlumat üçün {0}Yardım Mərkəzi{1} bölməmizə daxil olun + + + Mesaj + + + Mesaj əlavə et + + + Mesajı silin + + + Sənəd adı + + + {!user} əlavə et + + + Alıcı siyahısı + + + Bunu etmək üçün portal admininizlə əlaqə saxlayın. + Çevir @@ -253,21 +571,63 @@ Endir + + Faylları .tar.gz formatında endirin. Əks halda, fayllar .zip formatında endiriləcək. + + + Ölçü + + + Avtomatik + + + Hündürlük + + + En + 'Ümumi Sənədlər' bölməsində portal administratoru tərəfindən ümumi giriş üçün açılmış bütün sənədlər göstərilir. Yalnız portalın administratoru bu bölmədə qovluqlar yarada bilər, amma giriş hüququ almış portal istifadəçiləri də buraya öz fayllarını yükləyə bilərlər. Faylları buraya kompüterdən daşıyın ki, onları portala daha asan yol ilə yükləmək olsun. + + Faylları sevimlilərə əlavə etmək və ya onları bu siyahıdan silmək üçün kontekst menyusundan istifadə edin. + 'Mənə açıq' bölməsi dostlarınızın və ya həmkarlarınızın Sizə giriş verdiyi faylların göstərişi üçün istifadə olunur. Əgər siz bu sənədlərdə sonuncu dəyişikliyi görməmisinizsə, onlar 'yeni' işarəsi ilə işarələnirlər. Siz uyğun düyməni basmaqla, faylları siyahıdan silə bilərsiniz. 'Sənədlərim' bölməsində, Sizin {0}yaratdığınız{1} və ya portala {2}yüklədiyiniz{3} sənədlər və şəkillər saxlanılırlar. Siz onları ONLYOFFICE™ redaktoru vasitəsi ilə {4}aça{5} və {6}redaktə edə{7}, dostlara və ya həmkarlara keçid verə, qovluqlara görə düzənləyə bilərsiniz. + + Portala {2} yüklədiyiniz {3} sənədlər və şəkillər "Sənədlərim" bölməsində saxlanılır. Siz onları dostlarınız və həmkarlarınızla paylaşa, onları qovluqlarda təşkil edə bilərsiniz. + + + Admininizdən Şəxsi Otağı aktiv etməsini istəyin. + + + Şəxsi Otaqda işləmək ONLYOFFICE masaüstü tətbiqi vasitəsilə mümkündür. {0} Təlimatlar {1} + + + {0}Şəxsi Otağı Aktiv Edin {1} + + + {0} Docx, xlsx və pptx faylları üçün ən təhlükəsiz yaddaş. {1} {0} Etibarlı komanda üzvləri ilə təhlükəsiz paylaşım. {1} {0} Şifrələnmiş redaktə və real vaxtda əməkdaşlıq. {1} {0} Davamlı AES- 256 alqoritmi. {1} + Bu bölmədə Sizin yaratdığınız və ya layihə üçün yüklədiyiniz sənədlər və şəkillər saxlanılırlar. Siz onları aça və ya ONLYOFFICE™ redaktorunun köməyi ilə redaktə edə, qovluqlara görə düzənləyə bilərsiniz. + + Bu bölmədə layihə üçün yüklənmiş sənədlər və şəkillər saxlanılır. Onlara ONLYOFFICE portalında redaktordan istifadə etməklə baxmaq olar. + + + "Sonuncular" bölməsində bu yaxınlarda açdığınız fayllar göstərilir. + + + Faylları şablon kimi qeyd etmək və ya onları bu siyahıdan çıxarmaq üçün kontekst menyusundan istifadə edin. + 'Səbət' bölməsinə bütün silinmiş fayllar yerləşdirilirlər. Əgər onlar səhvən siliniblərsə, Siz onları bərpa edə bilərsiniz, və ya onları ömürlük silə bilərsiniz. Lütfən, nəzərə alın ki, 'Səbətdən' silinən fayllar bir də bərpa oluna bilməyəcəklər. @@ -277,60 +637,225 @@ Bu bölmədə Sizin filtrə uyğun olan fayl və ya qovluq yoxdur. Letfən, digər filtr seçənəklərini seçin və ya bütün faylları görmək üçün filtri təmizləyin. Sizə gərək olan faylı həmçinin digər bölmələrdə axtara bilərsiniz. + + Bu qovluqda fayl yoxdur + Burada göstəriləsi heç bir fayl yoxdur Birləşmiş hesablar yoxdur + + Xəta + + + Sevimlilər + + + {0} formatında olan faylı seçin + + + Bu növ faylları seçin: {0} + + + Fayllar {0} .tar.gz faylına {1} sıxılacaq + + + Fayllar {0}.zip faylına{1} sıxılacaq + Filtr Qovluqlar + + İndi Başlat + + + Qısaldın + elektron poçt vasitəsi ilə + + Faylı orijinaldan fərqli formata çevirmək qərarına gəlsəniz, bəzi məlumatlar itə bilər. + Hamısını seç + + Maksimum fayl ölçüsü: {0} + + + Google Play-dən ONLYOFFICE Sənədlərini əldə edin + + + Tətbiqləri yükləyin + + + App Store-da ONLYOFFICE Sənədləri endirin + + + Linux üçün ONLYOFFICE Masaüstü Redaktorları endirin + + + Mac OS üçün ONLYOFFICE Masaüstü Redaktorları endirin + + + Windows üçün ONLYOFFICE Masaüstü Redaktorları endirin + + + İşləmək üçün {0} komandanız var? {0} {1} Ətraflı məlumat {2} + + + Sənəd İdarəetməsi + + + CRM və Faktura + + + Tapşırıq idarəetməsi və Gantt Diaqramı + + + E-poçt Aqqreqatoru + + + İşləmək üçün layihəniz və komandanız var? {0} Sizin üçün bir şeylər hazırlamışıq. {1} + + + Həmkarlarınızı və ya subpodratçıları dəvət edin və əlavə imkanlardan yararlanın: + + + Komandalar üçün olan ONLYOFFICE™-ə xoş gəlmisiniz! + Sənədlərim + + Hüquqlarla bağlı dəyişiklikləri və yenilərini bildirin. + Öncədən bax Açıq + + Məkanı aç + + + Sənəd siyahısını gizlət + + + Sənəd siyahısını göstər + Parol + + Şəxsi Otaq + + + Daxil etdiyiniz hər simvolun şifrələndiyi ONLYOFFICE Şəxsi Otağına xoş gəlmisiniz + Layihə sənədləri + + Son + Oxunmuş kimi işarələ + + Hamısını oxunmuş kimi işarələ + + + Oxunmuş kimi işarələ + + + Fayl adı + + + Yadda saxlanmış sənədi yeni tabda açın + Qovluq seç + + Əməliyyatı seçin: + + + Sevimliləri göstər + + + Redaktə edərkən ara versiyaları yadda saxlayın + + + Sonuncuları Göstər + + + Əlavə hissələr + + + Bütün yadda saxlanılmış ara versiyaları saxlayın + + + Şablonları Göstər + + + Ümumi Parametrlər + + + Fayl versiyalarının saxlanması + + + Paylaş + Mənimlə paylaşılmış + + Paylaş: + Portaldan kənar adamlara giriş verin + + Link və ya yerləşdirmə kodunu paylaşın + + + Daha çoxunu göstər + + + Bu pəncərəni kiçildilmiş şəkildə göstərin + Ayarlar + + Eskiz rejiminə keçin + Yığcam görüntüyə keç Adi görüntüyə keç + + Şablonlar + + + Şablon yoxdur + + + Yüklənir... + + + Üçüncü tərəf hesabları + Box @@ -343,12 +868,21 @@ Siz növbəti hesabları ONLYOFFICE™ Sənədlər moduluna birləşdirə bilərsiniz. Onlar 'Sənədlərim' qovluğunda göstəriləcəklər və Siz onları birbaşa portalda, hamısını bir yerdə redaktə edə və qeyd edə bilərsiniz. + + Uğurla qoşulmaq üçün tələb olunan məlumatları {0} bu səhifəyə {1} daxil edin. + Hesablar birləşdirilir + + Dəyişmək üçün doldurun + Üçüncü tərəfı sil + + DocuSign + Dropbox @@ -361,12 +895,48 @@ Google Drive + + kDrive + + + Nextcloud + + + ownCloud + + + Təkrar qoşulun + + + Hesab + Paylaşılan et və Ümumi Sənədlərə qoy + + SharePoint + + + Biznes üçün SharePoint / OneDrive + OneDrive + + WebDAV + + + Yandex + + + Axtarın: + + + və {0}bütün alt-qovluqlar{1} + + + Alt-qovluqlar olmadan axtarış etmək üçün klikləyin + Seç @@ -388,4 +958,10 @@ Siyahıdan yığışdır - \ No newline at end of file + + Eyni adlı mövcud faylın üzərinə yazın + + + Planınızı yeniləyin + + diff --git a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.de.resx b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.de.resx index 8f9e716a5..06d214349 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.de.resx +++ b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.de.resx @@ -244,15 +244,9 @@ Aus Favoriten entfernen - - Entfernen - Aus Vorlagen entfernen - - Entfernen - Umbenennen diff --git a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.el.resx b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.el.resx index 3b6bf8671..0bb33d8cf 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.el.resx +++ b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.el.resx @@ -205,12 +205,6 @@ Άνοιγμα - - Αφαίρεση - - - Αφαίρεση - Μετονομασία diff --git a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.es.resx b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.es.resx index d2922e4f6..bf25c62c0 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.es.resx +++ b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.es.resx @@ -244,15 +244,9 @@ Eliminar de Favoritos - - Eliminar - Eliminar de plantillas - - Eliminar - Renombrar diff --git a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.fr.resx b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.fr.resx index 730b4d7be..4452c1858 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.fr.resx +++ b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.fr.resx @@ -244,15 +244,9 @@ Retirer des favoris - - Retirer - Suuprimer des Modèles - - Supprimer - Renommer diff --git a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.it.resx b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.it.resx index 70ec1a22c..5bab3bfdc 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.it.resx +++ b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.it.resx @@ -244,15 +244,9 @@ Rimuovi dai preferiti - - Elimina - Rimuovi dai modelli - - Elimina - Rinomina diff --git a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.ja.resx b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.ja.resx index 278103f4e..c24dc71f5 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.ja.resx +++ b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.ja.resx @@ -244,15 +244,9 @@ お気に入りから削除 - - 削除する - テンプレートから削除する - - 削除する - リネーム diff --git a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.nl.resx b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.nl.resx index b4a0ac539..9dffd80f9 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.nl.resx +++ b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.nl.resx @@ -238,15 +238,9 @@ Verwijder uit favorieten - - Verwijder - Verwijder uit sjablonen - - Verwijder - Hernoemen diff --git a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.pt-BR.resx b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.pt-BR.resx index 4316507bd..94c6f86a2 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.pt-BR.resx +++ b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.pt-BR.resx @@ -244,15 +244,9 @@ Remover dos Favoritos - - Remover - Remover dos Modelos - - Remover - Renomear diff --git a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.resx b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.resx index ec07616f0..c85ff1c82 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.resx +++ b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.resx @@ -1,5 +1,64 @@  + @@ -53,10 +112,10 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Comment @@ -82,9 +141,21 @@ Connect + + Add message + Admin Settings + + Advanced settings + + + Trash bin auto-clearing - Delete files older than + + + The files will be deleted some time after you activate the setting + Add Box account @@ -97,6 +168,9 @@ Add Google account + + Add link + Add Nextcloud account @@ -106,6 +180,9 @@ Use as template + + Add users + Other account @@ -115,6 +192,9 @@ Cancel all + + Change access rights + Change owner @@ -190,6 +270,9 @@ Embedding document + + Embedding settings + Empty Trash @@ -247,18 +330,15 @@ Overwrite with version update + + Remove + Remove from Favorites - - Remove - Remove from templates - - Remove - Rename @@ -268,9 +348,27 @@ Save + + Save and Copy direct link + + + Save and Send + + + Save without notification + All + + Groups + + + Links + + + Users + Sign with DocuSign @@ -286,6 +384,9 @@ Sharing Settings + + Shorten the link + Show version history @@ -367,6 +468,9 @@ There will be two different files in the folder. + + Enable external link access + Overwrite confirmation @@ -382,6 +486,9 @@ Note: removal from your account can not be undone. + + Are you sure you want to continue? + No file will be copied. The original file will be retained in the destination folder. @@ -433,6 +540,12 @@ Common + + Can't print, download and copy file (for Read Only & Comment) + + + Can't change sharing settings (for Full Access) + You have just created your ONLYOFFICE portal.{0} Upload your offline files to the portal to collaborate on them without leaving the app.{0}{0} Open your portal in browser to find more features, including {1}Projects{2}, {1}CRM{2}, {1}Calendar{2}, {1}Mail{2} and more. @@ -598,6 +711,60 @@ Folders + + Collect filled forms + + + By default + + + Collect in + + + Create subfolder + + + Vacation applications 2021 + + + Create a subfolder for each type of form + + + The subfolder name will be inherited from the document name + + + Customize + + + Date + + + Document Title - Username (MM/DD/YYYY) .docx + + + Enable collection of filled forms + + + File Name + + + Title setting + + + User Name + + + At the moment, the collection of filled forms is configured in a folder where you do not have permission to view the content. Want to reset these settings? + + + Attention! + + + Yes, reset + + + Form filling settings + Start Now @@ -661,6 +828,9 @@ My documents + + Notify with changes in rights and new ones. + Preview @@ -742,12 +912,18 @@ Shared with me + + Share to + External link access Share via the link or embed + + Temporarily disabled on the portal by adminstration + Show more @@ -898,4 +1074,31 @@ Save as oform + + Default access rights in sharing settings + + + If not, then "Read Only" + + + Spreadsheet only + + + Forms only + + + Documents only + + + Default access rights for a specific file type + + + Show social media buttons for external sharing + + + Show or hide buttons to quickly share external links to your files in social networks, e.g. Twitter or Facebook + + + Sharing settings + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.ru.resx b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.ru.resx index 40a5bd93c..b04758802 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.ru.resx +++ b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.ru.resx @@ -85,6 +85,12 @@ Настройки администратора + + Автоотчистка корзины - удалять файлы старше: + + + Файлы удалятся через некоторое время после включения настройки + Добавить аккаунт Box @@ -244,15 +250,9 @@ Удалить из избранного - - Удалить - Удалить из шаблонов - - Удалить - Переименовать diff --git a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.sv.resx b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.sv.resx index 3f49d7ed4..a23df3bc6 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.sv.resx +++ b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.sv.resx @@ -226,9 +226,6 @@ Ta bort från favoriter - - Ta bort - Döp om diff --git a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.zh-CN.resx b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.zh-CN.resx index 2f5e77a5e..43df779c2 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.zh-CN.resx +++ b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.zh-CN.resx @@ -229,9 +229,6 @@ 从收藏夹中删除 - - 删除 - 重命名 diff --git a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.zh-TW.resx b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.zh-TW.resx index cf7fa9429..9f414971b 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.zh-TW.resx +++ b/web/studio/ASC.Web.Studio/Products/Files/Resources/FilesUCResource.zh-TW.resx @@ -253,18 +253,11 @@ 用版本更新覆蓋 - -從最愛收藏夾中刪除 - - - 移除 + 從最愛收藏夾中刪除 從模板中刪除 - - 移除 - 重新命名 diff --git a/web/studio/ASC.Web.Studio/Products/Files/SaveAs.aspx.cs b/web/studio/ASC.Web.Studio/Products/Files/SaveAs.aspx.cs index fb498eab2..270e7f684 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/SaveAs.aspx.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/SaveAs.aspx.cs @@ -57,6 +57,11 @@ namespace ASC.Web.Files get { return Request[FilesLinkUtility.FileUri]; } } + private bool DisplayPrivacy + { + get { return !string.IsNullOrEmpty(Request["displayPrivacy"]); } + } + protected void Page_Load(object sender, EventArgs e) { Master.Master.DisabledSidePanel = true; @@ -84,9 +89,10 @@ namespace ASC.Web.Files } var script = new StringBuilder(); - script.AppendFormat("ASC.Files.FileChoice.init(\"{0}\", \"{1}\");", + script.AppendFormat("ASC.Files.FileChoice.init(\"{0}\", \"{1}\", ({2} == true));", originForPost, - (Request[FilesLinkUtility.FolderId] ?? "").Replace("\"", "\\\"")); + (Request[FilesLinkUtility.FolderId] ?? "").Replace("\"", "\\\""), + DisplayPrivacy.ToString().ToLower()); Page.RegisterInlineScript(script.ToString()); } diff --git a/web/studio/ASC.Web.Studio/Products/Files/Services/DocumentService/Configuration.cs b/web/studio/ASC.Web.Studio/Products/Files/Services/DocumentService/Configuration.cs index 1805a88b8..5ccba235a 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Services/DocumentService/Configuration.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Services/DocumentService/Configuration.cs @@ -17,7 +17,6 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Linq; using System.Runtime.Serialization; @@ -49,9 +48,9 @@ namespace ASC.Web.Files.Services.DocumentService { public static readonly Dictionary DocType = new Dictionary { - { FileType.Document, "text" }, - { FileType.Spreadsheet, "spreadsheet" }, - { FileType.Presentation, "presentation" } + { FileType.Document, "word" }, + { FileType.Spreadsheet, "cell" }, + { FileType.Presentation, "slide" } }; public enum EditorType @@ -140,6 +139,7 @@ namespace ASC.Web.Files.Services.DocumentService } } + #region Nested Classes [DataContract(Name = "document", Namespace = "")] @@ -209,16 +209,24 @@ namespace ASC.Web.Files.Services.DocumentService public EditorType Type = EditorType.Desktop; private string _breadCrumbs; + private bool? _favorite; + private bool _favoriteIsSet; [DataMember(Name = "favorite", EmitDefaultValue = false)] public bool? Favorite { - set { } + set + { + _favoriteIsSet = true; + _favorite = value; + } get { + if (_favoriteIsSet) return _favorite; if (!SecurityContext.IsAuthenticated || CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID).IsVisitor()) return null; - if (File.Encrypted) return null; + if (File.FolderID == null + || File.Encrypted) return null; return File.IsFavorite; } } @@ -281,7 +289,6 @@ namespace ASC.Web.Files.Services.DocumentService [DataContract(Name = "permissions", Namespace = "")] public class PermissionsConfig { - //todo: obsolete since DS v5.5 [DataMember(Name = "changeHistory")] public bool ChangeHistory = false; @@ -303,7 +310,6 @@ namespace ASC.Web.Files.Services.DocumentService [DataMember(Name = "modifyFilter")] public bool ModifyFilter = true; - //todo: obsolete since DS v6.0 [DataMember(Name = "rename")] public bool Rename = false; @@ -347,6 +353,22 @@ namespace ASC.Web.Files.Services.DocumentService [DataMember(Name = "actionLink", EmitDefaultValue = false)] public ActionLinkConfig ActionLink; + [DataMember(Name = "coEditing", EmitDefaultValue = false)] + public CoEditingConfig CoEditing + { + set { } + get + { + return !ModeWrite && User == null + ? new CoEditingConfig + { + Fast = false, + Change = false + } + : null; + } + } + public string ActionLinkString { get { return null; } @@ -400,7 +422,6 @@ namespace ASC.Web.Files.Services.DocumentService new TemplatesConfig { Image = CommonLinkUtility.GetFullAbsolutePath("skins/default/images/filetype/thumb/" + extension + ".png"), - Name = file.Title, Title = file.Title, Url = CommonLinkUtility.GetFullAbsolutePath(FilesLinkUtility.GetFileWebEditorUrl(file.ID)) }; @@ -410,7 +431,11 @@ namespace ASC.Web.Files.Services.DocumentService } [DataMember(Name = "callbackUrl", EmitDefaultValue = false)] - public string CallbackUrl; + public string CallbackUrl + { + set { } + get { return ModeWrite ? DocumentServiceTracker.GetCallbackUrl(_configuration.Document.Info.File.ID.ToString()) : null; } + } [DataMember(Name = "createUrl", EmitDefaultValue = false)] public string CreateUrl @@ -536,6 +561,7 @@ namespace ASC.Web.Files.Services.DocumentService + "&" + FilesLinkUtility.FileTitle + "=" + HttpUtility.UrlEncode(title); } + #region Nested Classes [DataContract(Name = "actionLink", Namespace = "")] @@ -545,6 +571,8 @@ namespace ASC.Web.Files.Services.DocumentService public ActionConfig Action; + #region Nested Classes + [DataContract(Name = "action", Namespace = "")] public class ActionConfig { @@ -555,6 +583,7 @@ namespace ASC.Web.Files.Services.DocumentService public string Data; } + #endregion public static string Serialize(ActionLinkConfig actionLinkConfig) { @@ -568,6 +597,22 @@ namespace ASC.Web.Files.Services.DocumentService } } + [DataContract(Name = "coEditing", Namespace = "")] + public class CoEditingConfig + { + public bool Fast; + + [DataMember(Name = "mode", EmitDefaultValue = false)] + public string Mode + { + set { } + get { return Fast ? "fast" : "strict"; } + } + + [DataMember(Name = "change", EmitDefaultValue = false)] + public bool Change; + } + [DataContract(Name = "embedded", Namespace = "")] public class EmbeddedConfig { @@ -652,7 +697,9 @@ namespace ASC.Web.Files.Services.DocumentService { _configuration = configuration; - Customer = new CustomerConfig(_configuration); + if (CoreContext.Configuration.Standalone) + Customer = new CustomerConfig(_configuration); + Logo = new LogoConfig(_configuration); } @@ -668,7 +715,7 @@ namespace ASC.Web.Files.Services.DocumentService get { return !CoreContext.Configuration.Standalone || CoreContext.Configuration.CustomMode; } } - [DataMember(Name = "customer")] + [DataMember(Name = "customer", EmitDefaultValue = false)] public CustomerConfig Customer; [DataMember(Name = "feedback", EmitDefaultValue = false)] @@ -678,13 +725,14 @@ namespace ASC.Web.Files.Services.DocumentService get { if (CoreContext.Configuration.Standalone) return null; - if (!AdditionalWhiteLabelSettings.Instance.FeedbackAndSupportEnabled) return null; + + var link = CommonLinkUtility.GetFeedbackAndSupportLink(); + + if (string.IsNullOrEmpty(link)) return null; return new FeedbackConfig { - Url = CommonLinkUtility.GetRegionalUrl( - AdditionalWhiteLabelSettings.Instance.FeedbackAndSupportUrl, - CultureInfo.CurrentCulture.TwoLetterISOLanguageName), + Url = link }; } } @@ -780,6 +828,34 @@ namespace ASC.Web.Files.Services.DocumentService get { return _configuration.EditorConfig.ModeWrite ? null : "markup"; } } + [DataMember(Name = "submitForm", EmitDefaultValue = false)] + public bool SubmitForm + { + set { } + get + { + if (_configuration.EditorConfig.ModeWrite + && _configuration.Document.Info.File.Access == ASC.Files.Core.Security.FileShare.FillForms) + { + using (var linkDao = Global.GetLinkDao()) + using (var fileDao = Global.DaoFactory.GetFileDao()) + { + var sourceId = linkDao.GetSource(_configuration.Document.Info.File.ID); + if (sourceId != null) + { + var properties = fileDao.GetProperties(sourceId); + return properties != null + && properties.FormFilling != null + && properties.FormFilling.CollectFillForm; + } + } + } + return false; + } + } + + + #region Nested Classes [DataContract(Name = "customer", Namespace = "")] public class CustomerConfig @@ -792,22 +868,39 @@ namespace ASC.Web.Files.Services.DocumentService private readonly Configuration _configuration; + [DataMember(Name = "address")] + public string Address + { + set { } + get { return CompanyWhiteLabelSettings.Instance.Address; } + } + [DataMember(Name = "logo")] public string Logo { set { } - get { return CommonLinkUtility.GetFullAbsolutePath(TenantLogoHelper.GetLogo(WhiteLabelLogoTypeEnum.Dark, !_configuration.EditorConfig.Customization.IsRetina)); } + get { return CommonLinkUtility.GetFullAbsolutePath(TenantWhiteLabelSettings.GetAbsoluteDefaultLogoPath(WhiteLabelLogoTypeEnum.Dark, !_configuration.EditorConfig.Customization.IsRetina)); } + } + + [DataMember(Name = "mail")] + public string Mail + { + set { } + get { return CompanyWhiteLabelSettings.Instance.Email; } } [DataMember(Name = "name")] public string Name { set { } - get - { - return (TenantWhiteLabelSettings.Load().LogoText ?? "") - .Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("/", "\\/"); - } + get { return CompanyWhiteLabelSettings.Instance.CompanyName; } + } + + [DataMember(Name = "www")] + public string Www + { + set { } + get { return CompanyWhiteLabelSettings.Instance.Site; } } } @@ -885,6 +978,8 @@ namespace ASC.Web.Files.Services.DocumentService get { return CommonLinkUtility.GetFullAbsolutePath(CommonLinkUtility.GetDefault()); } } } + + #endregion } [DataContract(Name = "recentconfig", Namespace = "")] @@ -906,10 +1001,6 @@ namespace ASC.Web.Files.Services.DocumentService [DataMember(Name = "image", EmitDefaultValue = false)] public string Image; - //todo: obsolete since DS v6.0 - [DataMember(Name = "name", EmitDefaultValue = false)] - public string Name; - [DataMember(Name = "title", EmitDefaultValue = false)] public string Title; diff --git a/web/studio/ASC.Web.Studio/Products/Files/Services/DocumentService/DocbuilderReportsUtility.cs b/web/studio/ASC.Web.Studio/Products/Files/Services/DocumentService/DocbuilderReportsUtility.cs index a140e03d6..03ce560cc 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Services/DocumentService/DocbuilderReportsUtility.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Services/DocumentService/DocbuilderReportsUtility.cs @@ -118,7 +118,7 @@ namespace ASC.Web.Files.Services.DocumentService } CoreContext.TenantManager.SetCurrentTenant(TenantId); - SecurityContext.AuthenticateMe(UserId); + SecurityContext.CurrentUser = UserId; Dictionary urls; BuilderKey = DocumentServiceConnector.DocbuilderRequest(null, Script, true, out urls); diff --git a/web/studio/ASC.Web.Studio/Products/Files/Services/DocumentService/DocumentServiceConnector.cs b/web/studio/ASC.Web.Studio/Products/Files/Services/DocumentService/DocumentServiceConnector.cs index 8f04791a9..edd811db1 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Services/DocumentService/DocumentServiceConnector.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Services/DocumentService/DocumentServiceConnector.cs @@ -47,6 +47,7 @@ namespace ASC.Web.Files.Services.DocumentService string toExtension, string documentRevisionId, string password, + string region, ThumbnailData thumbnail, SpreadsheetLayout spreadsheetLayout, bool isAsync, @@ -62,6 +63,7 @@ namespace ASC.Web.Files.Services.DocumentService toExtension, GenerateRevisionId(documentRevisionId), password, + region, thumbnail, spreadsheetLayout, isAsync, @@ -208,7 +210,7 @@ namespace ASC.Web.Files.Services.DocumentService var fileUri = ReplaceCommunityAdress(url); var key = GenerateRevisionId(Guid.NewGuid().ToString()); - Web.Core.Files.DocumentService.GetConvertedUri(FilesLinkUtility.DocServiceConverterUrl, fileUri, fileExtension, toExtension, key, null, null, null, false, FileUtility.SignatureSecret, out convertedFileUri); + Web.Core.Files.DocumentService.GetConvertedUri(FilesLinkUtility.DocServiceConverterUrl, fileUri, fileExtension, toExtension, key, null, null, null, null, false, FileUtility.SignatureSecret, out convertedFileUri); } catch (Exception ex) { diff --git a/web/studio/ASC.Web.Studio/Products/Files/Services/DocumentService/DocumentServiceHelper.cs b/web/studio/ASC.Web.Studio/Products/Files/Services/DocumentService/DocumentServiceHelper.cs index 397fdde7b..61d49f8ff 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Services/DocumentService/DocumentServiceHelper.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Services/DocumentService/DocumentServiceHelper.cs @@ -32,9 +32,11 @@ using ASC.Web.Files.Utils; using ASC.Web.Studio.Core; using JWT; +using JWT.Algorithms; using File = ASC.Files.Core.File; using FileShare = ASC.Files.Core.Security.FileShare; +using FileSecurity = ASC.Files.Core.Security.FileSecurity; using SecurityContext = ASC.Core.SecurityContext; namespace ASC.Web.Files.Services.DocumentService @@ -231,6 +233,8 @@ namespace ASC.Web.Files.Services.DocumentService EntryManager.SetFileStatus(file); } + var rightToDownload = CanDownload(fileSecurity, file, linkRight); + configuration = new Configuration(file) { Document = @@ -244,7 +248,9 @@ namespace ASC.Web.Files.Services.DocumentService FillForms = rightToFillForms && lastVersion, Comment = rightToComment && lastVersion, ChangeHistory = rightChangeHistory, - ModifyFilter = rightModifyFilter + ModifyFilter = rightModifyFilter, + Print = rightToDownload, + Download = rightToDownload } }, EditorConfig = @@ -263,12 +269,40 @@ namespace ASC.Web.Files.Services.DocumentService } + private static bool CanDownload(FileSecurity fileSecurity, File file, FileShare linkRight) + { + if (!file.DenyDownload) return true; + + var canDownload = linkRight != FileShare.Restrict && linkRight != FileShare.Read && linkRight != FileShare.Comment; + + if (canDownload || SecurityContext.CurrentAccount.ID.Equals(ASC.Core.Configuration.Constants.Guest.ID)) + { + return canDownload; + } + + if (linkRight == FileShare.Read || linkRight == FileShare.Comment) + { + using (var fileDao = Global.DaoFactory.GetFileDao()) + { + file = fileDao.GetFile(file.ID); // reset Access prop + } + } + + canDownload = fileSecurity.CanDownload(file); + + return canDownload; + } + public static string GetSignature(object payload) { if (string.IsNullOrEmpty(FileUtility.SignatureSecret)) return null; - JsonWebToken.JsonSerializer = new Web.Core.Files.DocumentService.JwtSerializer(); - return JsonWebToken.Encode(payload, FileUtility.SignatureSecret, JwtHashAlgorithm.HS256); + var encoder = new JwtEncoder(new HMACSHA256Algorithm(), + new Web.Core.Files.DocumentService.JwtSerializer(), + new JwtBase64UrlEncoder()); + + + return encoder.Encode(payload, FileUtility.SignatureSecret); } diff --git a/web/studio/ASC.Web.Studio/Products/Files/Services/DocumentService/DocumentServiceParams.cs b/web/studio/ASC.Web.Studio/Products/Files/Services/DocumentService/DocumentServiceParams.cs index a80065c2e..986d66cab 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Services/DocumentService/DocumentServiceParams.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Services/DocumentService/DocumentServiceParams.cs @@ -58,6 +58,9 @@ namespace ASC.Web.Files.Services.DocumentService [DataMember(Name = "serverErrorMessage")] public string ServerErrorMessage; + [DataMember(Name = "defaultType")] + public string DefaultType; + [DataMember(Name = "shareLinkParam")] public string ShareLinkParam; diff --git a/web/studio/ASC.Web.Studio/Products/Files/Services/DocumentService/DocumentServiceTracker.cs b/web/studio/ASC.Web.Studio/Products/Files/Services/DocumentService/DocumentServiceTracker.cs index efc9b9532..7739b645d 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Services/DocumentService/DocumentServiceTracker.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Services/DocumentService/DocumentServiceTracker.cs @@ -69,6 +69,7 @@ namespace ASC.Web.Files.Services.DocumentService { public List Actions; public string ChangesUrl; + public string Filetype; public ForceSaveInitiator ForceSaveType; public object History; public string Key; @@ -91,7 +92,8 @@ namespace ASC.Web.Files.Services.DocumentService { Command = 0, User = 1, - Timer = 2 + Timer = 2, + UserSubmit = 3, } } @@ -294,7 +296,7 @@ namespace ASC.Web.Files.Services.DocumentService { Global.Logger.ErrorFormat("DocService saving file {0} ({1}) with key {2}", fileId, docKey, fileData.Key); - StoringFileAfterError(fileId, userId.ToString(), DocumentServiceConnector.ReplaceDocumentAdress(fileData.Url)); + StoringFileAfterError(fileId, userId.ToString(), DocumentServiceConnector.ReplaceDocumentAdress(fileData.Url), fileData.Filetype); return new TrackResponse { Message = "Expected key " + docKey }; } } @@ -302,7 +304,7 @@ namespace ASC.Web.Files.Services.DocumentService UserInfo user = null; try { - SecurityContext.AuthenticateMe(userId); + SecurityContext.CurrentUser = userId; user = CoreContext.UserManager.GetUsers(userId); var culture = string.IsNullOrEmpty(user.CultureName) ? CoreContext.TenantManager.GetCurrentTenant().GetCulture() : CultureInfo.GetCultureInfo(user.CultureName); @@ -356,22 +358,26 @@ namespace ASC.Web.Files.Services.DocumentService { case TrackerData.ForceSaveInitiator.Command: forcesaveType = ForcesaveType.Command; + comments.Add(FilesCommonResource.CommentAutosave); break; case TrackerData.ForceSaveInitiator.Timer: forcesaveType = ForcesaveType.Timer; + comments.Add(FilesCommonResource.CommentAutosave); break; case TrackerData.ForceSaveInitiator.User: forcesaveType = ForcesaveType.User; + comments.Add(FilesCommonResource.CommentForcesave); + break; + case TrackerData.ForceSaveInitiator.UserSubmit: + forcesaveType = ForcesaveType.UserSubmit; + comments.Add(FilesCommonResource.CommentSubmitFillForm); break; } - comments.Add(fileData.ForceSaveType == TrackerData.ForceSaveInitiator.User - ? FilesCommonResource.CommentForcesave - : FilesCommonResource.CommentAutosave); } try { - file = EntryManager.SaveEditing(fileId, null, DocumentServiceConnector.ReplaceDocumentAdress(fileData.Url), null, string.Empty, string.Join("; ", comments), false, fileData.Encrypted, forcesaveType, true); + file = EntryManager.SaveEditing(fileId, fileData.Filetype, DocumentServiceConnector.ReplaceDocumentAdress(fileData.Url), null, string.Empty, string.Join("; ", comments), false, fileData.Encrypted, forcesaveType, true); saveMessage = fileData.Status == TrackerStatus.MustSave || fileData.Status == TrackerStatus.ForceSave ? null : "Status " + fileData.Status; } catch (Exception ex) @@ -379,7 +385,7 @@ namespace ASC.Web.Files.Services.DocumentService Global.Logger.Error(string.Format("DocService save error. File id: '{0}'. UserId: {1}. DocKey '{2}'. DownloadUri: {3}", fileId, userId, fileData.Key, fileData.Url), ex); saveMessage = ex.Message; - StoringFileAfterError(fileId, userId.ToString(), DocumentServiceConnector.ReplaceDocumentAdress(fileData.Url)); + StoringFileAfterError(fileId, userId.ToString(), DocumentServiceConnector.ReplaceDocumentAdress(fileData.Url), fileData.Filetype); } } @@ -393,6 +399,10 @@ namespace ASC.Web.Files.Services.DocumentService if (!forcesave) SaveHistory(file, (fileData.History ?? "").ToString(), DocumentServiceConnector.ReplaceDocumentAdress(fileData.ChangesUrl)); + + if (fileData.Status == TrackerStatus.ForceSave + && fileData.ForceSaveType == TrackerData.ForceSaveInitiator.UserSubmit) + EntryManager.SubmitFillForm(file); } Global.SocketManager.FilesChangeEditors(fileId, !forcesave); @@ -413,7 +423,7 @@ namespace ASC.Web.Files.Services.DocumentService try { - SecurityContext.AuthenticateMe(userId); + SecurityContext.CurrentUser = userId; var user = CoreContext.UserManager.GetUsers(userId); var culture = string.IsNullOrEmpty(user.CultureName) ? CoreContext.TenantManager.GetCurrentTenant().GetCulture() : CultureInfo.GetCultureInfo(user.CultureName); @@ -524,13 +534,14 @@ namespace ASC.Web.Files.Services.DocumentService } - private static void StoringFileAfterError(string fileId, string userId, string downloadUri) + private static void StoringFileAfterError(string fileId, string userId, string downloadUri, string downloadType) { if (string.IsNullOrEmpty(downloadUri)) return; try { - var fileName = Global.ReplaceInvalidCharsAndTruncate(fileId + FileUtility.GetFileExtension(downloadUri)); + if (string.IsNullOrEmpty(downloadType)) downloadType = FileUtility.GetFileExtension(downloadUri).Trim('.'); + var fileName = Global.ReplaceInvalidCharsAndTruncate(fileId + "." + downloadType); var path = string.Format(@"save_crash\{0}\{1}_{2}", DateTime.UtcNow.ToString("yyyy_MM_dd"), userId, diff --git a/web/studio/ASC.Web.Studio/Products/Files/Services/NotifyService/FilesPatternResource.az-Latn-AZ.resx b/web/studio/ASC.Web.Studio/Products/Files/Services/NotifyService/FilesPatternResource.az-Latn-AZ.resx index 994a25134..02ad95aac 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Services/NotifyService/FilesPatternResource.az-Latn-AZ.resx +++ b/web/studio/ASC.Web.Studio/Products/Files/Services/NotifyService/FilesPatternResource.az-Latn-AZ.resx @@ -58,10 +58,49 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + h1. Bütün imzalayanlar tərəfindən $DocumentTitle tamamlandı + +Bu e-poçt bildirişi bütün imzalayanların "$DocumentTitle":"$DocumentURL"sənədini tamamladığını bildirmək üçün göndərilib. + +^Bu e-məktubu "${__VirtualRootPath}":"${__VirtualRootPath}" portalının qeydiyyatdan keçmiş istifadəçisi olduğunuz üçün alırsınız.^ + + + h1. $Message: $DocumentTitle + +DocuSign hesabınıza daxil olmaqla ətraflı məlumat əldə edə bilərsiniz. + +^Bu e-məktubu, "${__VirtualRootPath}":"${__VirtualRootPath}" portalının qeydiyyatdan keçmiş istifadəçisi olduğunuz üçün alırsınız.^ + + + h1. "$__AuthorName":"$__AuthorUrl" sənəd şərhində sizi qeyd etdi + +Bu, "$DocumentTitle":"$DocumentURL" sənədinin şərhində "$__AuthorName":"$__AuthorUrl" tərəfindən qeyd olunduğunuzu bildirən poçt mesajıdır: + +$Message + +^"${__VirtualRootPath}":"${__VirtualRootPath}" portalının qeydiyyatdan keçmiş istifadəçisi olduğunuz üçün bu e-məktubu alırsınız.^ + + + h1. E-poçt göndərişi tamamlandı + +Bu e-poçt bildirişi $MailsCount mesajlarını poçtla göndərmək istəyiniz barədə sizə məlumat vermək üçün göndərilib və proses başa çatıb. $Message + +Uğurla göndərilmiş e-poçt mesajlarına E-poçt modulunda "Göndərilmiş":"$__VirtualRootPath/addons/mail/#sent" qovluğundan daxil ola bilərsiniz. + +^Bu e-məktubu "${__VirtualRootPath}":"${__VirtualRootPath}" portalının qeydiyyatdan keçmiş istifadəçisi olduğunuz üçün alırsınız.^ + h1.$__DateTime, "$__AuthorName":"$__AuthorUrl" istifadəçisi "$DocumentTitle":"$DocumentURL" sənədinə girişinizə bu giriş hüquqları ilə: "$AccessRights" icazə verdi. $Message + + + h1. Şifrələnmiş sənədə giriş icazəsi verildi: + +h1. $Image "$DocumentTitle":"$DocumentURL" + +Bu fayl şifrələnib və yalnız "ONLYOFFICE Desktop Redaktorları" vasitəsilə açıla bilər:"https://www.onlyoffice.com/desktop.aspx". Daha ətraflı məlumat üçün "bu təlimata" müraciət edə bilərsiniz: "https://helpcenter.onlyoffice.com/installation/desktop-private-room.aspx". h1.$__DateTime, "$__AuthorName":"$__AuthorUrl" istifadəçisi @@ -69,10 +108,40 @@ $Message $Message + + Sənədlər. Bütün imzalayanlar tərəfindən $DocumentTitle tamamlandı + + + Sənədlər. Bütün imzalayanlar tərəfindən [$DocumentTitle]($DocumentURL) tamamlandı + + + Sənədlər. İmza statusu dəyişdi + + + Sənədlər. Sənəddə qeyd edildi + + + Sənədlər. Sənəddə qeyd edildi:[$DocumentTitle]($DocumentURL) + + + Sənədlər. E-poçt göndərilməsi tamamlandı. + Sənədlər. $DocumentTitle 'a Giriş Verildi + + Sənədlər. Sənədə giriş icazəsi verildi: [$DocumentTitle]($DocumentURL) + + + Sənədlər. Şifrələnmiş sənədə giriş icazəsi verildi:$DocumentTitle + + + Sənədlər.Şifrələnmiş sənədə giriş icazəsi verildi:[$DocumentTitle]($DocumentURL) + Sənədlər. $DocumentTitle 'a Giriş Verildi - \ No newline at end of file + + Sənədlər. Qovluğa girişicazəsi verildi:[$DocumentTitle]($DocumentURL) + + diff --git a/web/studio/ASC.Web.Studio/Products/Files/Services/NotifyService/patterns.xml b/web/studio/ASC.Web.Studio/Products/Files/Services/NotifyService/patterns.xml index bb27e200a..762143c0d 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Services/NotifyService/patterns.xml +++ b/web/studio/ASC.Web.Studio/Products/Files/Services/NotifyService/patterns.xml @@ -11,7 +11,7 @@ - + @@ -24,7 +24,7 @@ - $Message: $DocumentTitle + $Message: $DocumentTitle @@ -40,7 +40,7 @@ $Message - $__AuthorName + $__AuthorName $Message @@ -63,7 +63,7 @@ $DocumentURL - $__AuthorName + $__AuthorName $AccessRights @@ -89,12 +89,12 @@ $Message - - $__AuthorName + +$__AuthorName - $AccessRights +$AccessRights - $Message +$Message @@ -115,7 +115,7 @@ $DocumentURL - $__AuthorName + $__AuthorName $AccessRights @@ -140,9 +140,9 @@ $DocumentURL - $__AuthorName + $__AuthorName - $Message +$Message \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/FileOperations/FileDeleteOperation.cs b/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/FileOperations/FileDeleteOperation.cs index 194c4dd58..d43fc110a 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/FileOperations/FileDeleteOperation.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/FileOperations/FileDeleteOperation.cs @@ -32,6 +32,7 @@ namespace ASC.Web.Files.Services.WCFService.FileOperations private object _trashId; private readonly bool _ignoreException; private readonly bool _immediately; + private readonly bool _isEmptyTrash; private readonly Dictionary _headers; public override FileOperationType OperationType @@ -40,12 +41,13 @@ namespace ASC.Web.Files.Services.WCFService.FileOperations } - public FileDeleteOperation(List folders, List files, bool ignoreException = false, bool holdResult = true, bool immediately = false, Dictionary headers = null) + public FileDeleteOperation(List folders, List files, bool ignoreException = false, bool holdResult = true, bool immediately = false, Dictionary headers = null, bool isEmptyTrash = false) : base(folders, files, holdResult) { _ignoreException = ignoreException; _immediately = immediately; _headers = headers; + _isEmptyTrash = isEmptyTrash; } @@ -65,12 +67,20 @@ namespace ASC.Web.Files.Services.WCFService.FileOperations { Status += string.Format("folder_{0}{1}", root.ID, SPLIT_CHAR); } - - DeleteFiles(Files); - DeleteFolders(Folders); + if (_isEmptyTrash) + { + DeleteFiles(Files); + DeleteFolders(Folders); + MessageService.Send(_headers, MessageAction.TrashEmptied); + } + else + { + DeleteFiles(Files, true); + DeleteFolders(Folders, true); + } } - private void DeleteFolders(IEnumerable folderIds) + private void DeleteFolders(IEnumerable folderIds, bool isNeedSendActions = false) { foreach (var folderId in folderIds) { @@ -102,7 +112,10 @@ namespace ASC.Web.Files.Services.WCFService.FileOperations if (ProviderDao != null) { ProviderDao.RemoveProviderInfo(folder.ProviderId); - FilesMessageService.Send(folder, _headers, MessageAction.ThirdPartyDeleted, folder.ID.ToString(), folder.ProviderKey); + if (isNeedSendActions) + { + FilesMessageService.Send(folder, _headers, MessageAction.ThirdPartyDeleted, folder.ID.ToString(), folder.ProviderKey); + } } ProcessedFolder(folderId); @@ -136,12 +149,18 @@ namespace ASC.Web.Files.Services.WCFService.FileOperations if (immediately) { FolderDao.DeleteFolder(folder.ID); - FilesMessageService.Send(folder, _headers, MessageAction.FolderDeleted, folder.Title); + if (isNeedSendActions) + { + FilesMessageService.Send(folder, _headers, MessageAction.FolderDeleted, folder.Title); + } } else { FolderDao.MoveFolder(folder.ID, _trashId, CancellationToken); - FilesMessageService.Send(folder, _headers, MessageAction.FolderMovedToTrash, folder.Title); + if (isNeedSendActions) + { + FilesMessageService.Send(folder, _headers, MessageAction.FolderMovedToTrash, folder.Title); + } } ProcessedFolder(folderId); @@ -153,7 +172,7 @@ namespace ASC.Web.Files.Services.WCFService.FileOperations } } - private void DeleteFiles(IEnumerable fileIds) + private void DeleteFiles(IEnumerable fileIds, bool isNeedSendActions = false) { foreach (var fileId in fileIds) { @@ -175,8 +194,10 @@ namespace ASC.Web.Files.Services.WCFService.FileOperations if (!_immediately && FileDao.UseTrashForRemove(file)) { FileDao.MoveFile(file.ID, _trashId); - FilesMessageService.Send(file, _headers, MessageAction.FileMovedToTrash, file.Title); - + if (isNeedSendActions) + { + FilesMessageService.Send(file, _headers, MessageAction.FileMovedToTrash, file.Title); + } if (file.ThumbnailStatus == Thumbnail.Waiting) { file.ThumbnailStatus = Thumbnail.NotRequired; @@ -188,7 +209,17 @@ namespace ASC.Web.Files.Services.WCFService.FileOperations try { FileDao.DeleteFile(file.ID); - FilesMessageService.Send(file, _headers, MessageAction.FileDeleted, file.Title); + if (_headers != null) + { + if (isNeedSendActions) + { + FilesMessageService.Send(file, _headers, MessageAction.FileDeleted, file.Title); + } + } + else + { + FilesMessageService.Send(file, MessageInitiator.AutoCleanUp, MessageAction.FileDeleted, file.Title); + } } catch (Exception ex) { diff --git a/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/FileOperations/FileDownloadOperation.cs b/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/FileOperations/FileDownloadOperation.cs index 10a8edb0d..a54b0ff46 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/FileOperations/FileDownloadOperation.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/FileOperations/FileDownloadOperation.cs @@ -1,352 +1,418 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; - -using ASC.Common; -using ASC.Common.Security.Authentication; -using ASC.Common.Web; -using ASC.Files.Core; -using ASC.MessagingSystem; -using ASC.Web.Core.Files; -using ASC.Web.Files.Classes; -using ASC.Web.Files.Core.Compress; -using ASC.Web.Files.Helpers; -using ASC.Web.Files.Resources; -using ASC.Web.Files.Utils; - -using File = ASC.Files.Core.File; - -namespace ASC.Web.Files.Services.WCFService.FileOperations -{ - class FileDownloadOperation : FileOperation - { - private readonly Dictionary files; - private readonly Dictionary headers; - - public override FileOperationType OperationType +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; + +using ASC.Common; +using ASC.Common.Security.Authentication; +using ASC.Common.Web; +using ASC.Core; +using ASC.Files.Core; +using ASC.MessagingSystem; +using ASC.Security.Cryptography; +using ASC.Web.Core.Files; +using ASC.Web.Files.Classes; +using ASC.Web.Files.Core.Compress; +using ASC.Web.Files.Helpers; +using ASC.Web.Files.Resources; +using ASC.Web.Files.Utils; + +using File = ASC.Files.Core.File; + +namespace ASC.Web.Files.Services.WCFService.FileOperations +{ + class FileDownloadOperation : FileOperation + { + private readonly Dictionary files; + private readonly Dictionary headers; + + public override FileOperationType OperationType + { + get { return FileOperationType.Download; } + } + + + public FileDownloadOperation(Dictionary folders, Dictionary files, Dictionary headers) + : base(folders.Select(f => f.Key).ToList(), files.Select(f => f.Key).ToList()) + { + this.files = files; + this.headers = headers; + } + + + protected override void Do() { - get { return FileOperationType.Download; } - } - - - public FileDownloadOperation(Dictionary folders, Dictionary files, Dictionary headers) - : base(folders.Select(f => f.Key).ToList(), files.Select(f => f.Key).ToList()) - { - this.files = files; - this.headers = headers; - } - - - protected override void Do() - { - var entriesPathId = GetEntriesPathId(); - if (entriesPathId == null || entriesPathId.Count == 0) - { - if (0 < Files.Count) - throw new FileNotFoundException(FilesCommonResource.ErrorMassage_FileNotFound); - throw new DirectoryNotFoundException(FilesCommonResource.ErrorMassage_FolderNotFound); + List filesForSend; + List folderForSend; + + var entriesPathId = GetEntriesPathId(out filesForSend, out folderForSend); + + if (entriesPathId == null || entriesPathId.Count == 0) + { + if (0 < Files.Count) + throw new FileNotFoundException(FilesCommonResource.ErrorMassage_FileNotFound); + throw new DirectoryNotFoundException(FilesCommonResource.ErrorMassage_FolderNotFound); + } + + Total = entriesPathId.Count + 1; + + ReplaceLongPath(entriesPathId); + + using (var stream = CompressTo(entriesPathId)) + { + if (stream != null) + { + stream.Position = 0; + + string fileName; + + if (Folders.Count == 1 && Files.Count == 0) + { + fileName = String.Format(@"{0}{1}", FolderDao.GetFolder(Folders[0]).Title, CompressToArchive.Instance.ArchiveExtension); + } + else + { + fileName = String.Format(@"{0}-{1}-{2}{3}", CoreContext.TenantManager.GetCurrentTenant().TenantAlias.ToLower(), FileConstant.DownloadTitle, DateTime.UtcNow.ToString("yyyy-MM-dd"), CompressToArchive.Instance.ArchiveExtension); + } + + var store = Global.GetStore(); + + var path = string.Format(@"{0}\{1}", ((IAccount)Thread.CurrentPrincipal.Identity).ID, fileName); + + if (store.IsFile(FileConstant.StorageDomainTmp, path)) + { + store.Delete(FileConstant.StorageDomainTmp, path); + } + + store.Save( + FileConstant.StorageDomainTmp, + path, + stream, + MimeMapping.GetMimeMapping(path), + "attachment; filename=\"" + Uri.EscapeDataString(fileName) + "\""); + + Status = string.Format("{0}?{1}=bulk&filename={2}", FilesLinkUtility.FileHandlerPath, FilesLinkUtility.Action, Uri.EscapeDataString(InstanceCrypto.Encrypt(fileName))); + + ProgressStep(); + } } - ReplaceLongPath(entriesPathId); - - using (var stream = CompressTo(entriesPathId)) + foreach (var file in filesForSend) { - if (stream != null) + var key = file.ID.ToString(); + if (files.ContainsKey(key) && !string.IsNullOrEmpty(files[key])) { - stream.Position = 0; - string fileName = FileConstant.DownloadTitle + CompressToArchive.Instance.ArchiveExtension; - var store = Global.GetStore(); - var path = string.Format(@"{0}\{1}", ((IAccount)Thread.CurrentPrincipal.Identity).ID, fileName); - - if (store.IsFile(FileConstant.StorageDomainTmp, path)) - { - store.Delete(FileConstant.StorageDomainTmp, path); - } - - store.Save( - FileConstant.StorageDomainTmp, - path, - stream, - MimeMapping.GetMimeMapping(path), - "attachment; filename=\"" + fileName + "\""); - Status = string.Format("{0}?{1}=bulk&ext={2}", FilesLinkUtility.FileHandlerPath, FilesLinkUtility.Action, CompressToArchive.Instance.ArchiveExtension); + FilesMessageService.Send(file, headers, MessageAction.FileDownloadedAs, file.Title, files[key]); } - } - } - - private ItemNameValueCollection ExecPathFromFile(File file, string path) - { - FileMarker.RemoveMarkAsNew(file); - - var title = file.Title; - - if (files.ContainsKey(file.ID.ToString())) - { - var convertToExt = files[file.ID.ToString()]; - - if (!string.IsNullOrEmpty(convertToExt)) + else { - title = FileUtility.ReplaceFileExtension(title, convertToExt); + FilesMessageService.Send(file, headers, MessageAction.FileDownloaded, file.Title); } } + foreach (var folder in folderForSend) + { + FilesMessageService.Send(folder, headers, MessageAction.FolderDownloaded, folder.Title); + } + } + + private ItemNameValueCollection ExecPathFromFile(File file, string path) + { + FileMarker.RemoveMarkAsNew(file); + + var title = file.Title; + + if (files.ContainsKey(file.ID.ToString())) + { + var convertToExt = files[file.ID.ToString()]; + + if (!string.IsNullOrEmpty(convertToExt)) + { + title = FileUtility.ReplaceFileExtension(title, convertToExt); + } + } + + var entriesPathId = new ItemNameValueCollection(); + entriesPathId.Add(path + title, file.ID.ToString()); + + return entriesPathId; + } + + private ItemNameValueCollection GetEntriesPathId(out List filesForSend, out List folderForSend) + { + filesForSend = new List(); + folderForSend = new List(); + var entriesPathId = new ItemNameValueCollection(); - entriesPathId.Add(path + title, file.ID.ToString()); - - return entriesPathId; - } - - private ItemNameValueCollection GetEntriesPathId() - { - var entriesPathId = new ItemNameValueCollection(); - if (0 < Files.Count) + + if (0 < Files.Count) { - var files = FileDao.GetFiles(Files); - files = FilesSecurity.FilterRead(files); - files.ForEach(file => entriesPathId.Add(ExecPathFromFile(file, string.Empty))); - } - if (0 < Folders.Count) - { - FilesSecurity.FilterRead(FolderDao.GetFolders(Files)) - .ForEach(folder => FileMarker.RemoveMarkAsNew(folder)); + filesForSend = FilesSecurity.FilterDownload(FileDao.GetFiles(Files)); + filesForSend.ForEach(file => entriesPathId.Add(ExecPathFromFile(file, string.Empty))); + } + if (0 < Folders.Count) + { + folderForSend = FolderDao.GetFolders(Folders); + folderForSend = FilesSecurity.FilterDownload(folderForSend); + folderForSend.ForEach(folder => FileMarker.RemoveMarkAsNew(folder)); + + var filesInFolder = GetFilesInFolders(folderForSend.Select(x => x.ID), string.Empty); + entriesPathId.Add(filesInFolder); + } + + if (Folders.Count == 1 && Files.Count == 0) + { + var entriesPathIdWithoutRoot = new ItemNameValueCollection(); - var filesInFolder = GetFilesInFolders(Folders, string.Empty); - entriesPathId.Add(filesInFolder); - } - return entriesPathId; - } - - private ItemNameValueCollection GetFilesInFolders(IEnumerable folderIds, string path) - { - CancellationToken.ThrowIfCancellationRequested(); - - var entriesPathId = new ItemNameValueCollection(); - foreach (var folderId in folderIds) - { - CancellationToken.ThrowIfCancellationRequested(); - - var folder = FolderDao.GetFolder(folderId); - if (folder == null || !FilesSecurity.CanRead(folder)) continue; - var folderPath = path + folder.Title + "/"; - - var files = FileDao.GetFiles(folder.ID, null, FilterType.None, false, Guid.Empty, string.Empty, true); - files = FilesSecurity.FilterRead(files); - files.ForEach(file => entriesPathId.Add(ExecPathFromFile(file, folderPath))); - - FileMarker.RemoveMarkAsNew(folder); - - var nestedFolders = FolderDao.GetFolders(folder.ID); - nestedFolders = FilesSecurity.FilterRead(nestedFolders); - if (files.Count == 0 && nestedFolders.Count == 0) - { - entriesPathId.Add(folderPath, String.Empty); - } - - var filesInFolder = GetFilesInFolders(nestedFolders.ConvertAll(f => f.ID), folderPath); - entriesPathId.Add(filesInFolder); - } - return entriesPathId; - } - - private Stream CompressTo(ItemNameValueCollection entriesPathId) - { - var stream = TempStream.Create(); - - using (ICompress compressTo = new CompressToArchive(stream)) - { - foreach (var path in entriesPathId.AllKeys) - { - var counter = 0; - foreach (var entryId in entriesPathId[path]) + foreach (var path in entriesPathId.AllKeys) + { + entriesPathIdWithoutRoot.Add(path.Remove(0, path.IndexOf('/') + 1), entriesPathId[path]); + } + + return entriesPathIdWithoutRoot; + } + + return entriesPathId; + } + + private ItemNameValueCollection GetFilesInFolders(IEnumerable folderIds, string path) + { + CancellationToken.ThrowIfCancellationRequested(); + + var entriesPathId = new ItemNameValueCollection(); + foreach (var folderId in folderIds) + { + CancellationToken.ThrowIfCancellationRequested(); + + var folder = FolderDao.GetFolder(folderId); + if (folder == null || !FilesSecurity.CanDownload(folder)) continue; + var folderPath = path + folder.Title + "/"; + + var files = FileDao.GetFiles(folder.ID, null, FilterType.None, false, Guid.Empty, string.Empty, true); + files = FilesSecurity.FilterDownload(files); + files.ForEach(file => entriesPathId.Add(ExecPathFromFile(file, folderPath))); + + FileMarker.RemoveMarkAsNew(folder); + + var nestedFolders = FolderDao.GetFolders(folder.ID); + nestedFolders = FilesSecurity.FilterDownload(nestedFolders); + if (files.Count == 0 && nestedFolders.Count == 0) + { + entriesPathId.Add(folderPath, String.Empty); + } + + var filesInFolder = GetFilesInFolders(nestedFolders.ConvertAll(f => f.ID), folderPath); + entriesPathId.Add(filesInFolder); + } + return entriesPathId; + } + + private Stream CompressTo(ItemNameValueCollection entriesPathId) + { + var stream = TempStream.Create(); + + using (ICompress compressTo = new CompressToArchive(stream)) + { + foreach (var path in entriesPathId.AllKeys) + { + if (string.IsNullOrEmpty(path)) { - if (CancellationToken.IsCancellationRequested) - { - compressTo.Dispose(); - stream.Dispose(); - CancellationToken.ThrowIfCancellationRequested(); - } - - var newtitle = path; - - File file = null; - var convertToExt = string.Empty; - - if (!string.IsNullOrEmpty(entryId)) - { - FileDao.InvalidateCache(entryId); - file = FileDao.GetFile(entryId); - - if (file == null) - { - Error = FilesCommonResource.ErrorMassage_FileNotFound; - continue; - } - - if (files.ContainsKey(file.ID.ToString())) - { - convertToExt = files[file.ID.ToString()]; - if (!string.IsNullOrEmpty(convertToExt)) - { - newtitle = FileUtility.ReplaceFileExtension(path, convertToExt); - } - } - } - - if (0 < counter) - { - var suffix = " (" + counter + ")"; - - if (!string.IsNullOrEmpty(entryId)) - { - newtitle = 0 < newtitle.IndexOf('.') ? newtitle.Insert(newtitle.LastIndexOf('.'), suffix) : newtitle + suffix; - } - else - { - break; - } - } - - compressTo.CreateEntry(newtitle); - - if (!string.IsNullOrEmpty(entryId) && file != null) - { - try - { - if (FileConverter.EnableConvert(file, convertToExt)) - { - //Take from converter - using (var readStream = FileConverter.Exec(file, convertToExt)) - { - compressTo.PutStream(readStream); - - if (!string.IsNullOrEmpty(convertToExt)) - { - FilesMessageService.Send(file, headers, MessageAction.FileDownloadedAs, file.Title, convertToExt); - } - else - { - FilesMessageService.Send(file, headers, MessageAction.FileDownloaded, file.Title); - } - } - } - else - { - using (var readStream = FileDao.GetFileStream(file)) - { - compressTo.PutStream(readStream); - - FilesMessageService.Send(file, headers, MessageAction.FileDownloaded, file.Title); - } - } - } - catch (Exception ex) - { - Error = ex.Message; - Logger.Error(Error, ex); - } - } - else - { - compressTo.PutNextEntry(); - } - compressTo.CloseEntry(); - counter++; + ProgressStep(); + continue; } - ProgressStep(); - } - } - return stream; - } - - private void ReplaceLongPath(ItemNameValueCollection entriesPathId) - { - foreach (var path in new List(entriesPathId.AllKeys)) - { - CancellationToken.ThrowIfCancellationRequested(); - - if (200 >= path.Length || 0 >= path.IndexOf('/')) continue; - - var ids = entriesPathId[path]; - entriesPathId.Remove(path); - - var newtitle = "LONG_FOLDER_NAME" + path.Substring(path.LastIndexOf('/')); - entriesPathId.Add(newtitle, ids); - } - } - - - class ItemNameValueCollection - { - private readonly Dictionary> dic = new Dictionary>(); - - - public IEnumerable AllKeys - { - get { return dic.Keys; } - } - - public IEnumerable this[string name] - { - get { return dic[name].ToArray(); } - } - - public int Count - { - get { return dic.Count; } - } - - public void Add(string name, string value) - { - if (!dic.ContainsKey(name)) - { - dic.Add(name, new List()); - } - dic[name].Add(value); - } - - public void Add(ItemNameValueCollection collection) - { - foreach (var key in collection.AllKeys) - { - foreach (var value in collection[key]) - { - Add(key, value); - } - } - } - - public void Add(string name, IEnumerable values) - { - if (!dic.ContainsKey(name)) - { - dic.Add(name, new List()); - } - dic[name].AddRange(values); - } - - public void Remove(string name) - { - dic.Remove(name); - } - } - } + + var counter = 0; + foreach (var entryId in entriesPathId[path]) + { + if (CancellationToken.IsCancellationRequested) + { + compressTo.Dispose(); + stream.Dispose(); + CancellationToken.ThrowIfCancellationRequested(); + } + + var newtitle = path; + + File file = null; + var convertToExt = string.Empty; + + if (!string.IsNullOrEmpty(entryId)) + { + FileDao.InvalidateCache(entryId); + file = FileDao.GetFile(entryId); + + if (file == null) + { + Error = FilesCommonResource.ErrorMassage_FileNotFound; + continue; + } + + if (files.ContainsKey(file.ID.ToString())) + { + convertToExt = files[file.ID.ToString()]; + if (!string.IsNullOrEmpty(convertToExt)) + { + newtitle = FileUtility.ReplaceFileExtension(path, convertToExt); + } + } + } + + if (0 < counter) + { + var suffix = " (" + counter + ")"; + + if (!string.IsNullOrEmpty(entryId)) + { + newtitle = 0 < newtitle.IndexOf('.') ? newtitle.Insert(newtitle.LastIndexOf('.'), suffix) : newtitle + suffix; + } + else + { + break; + } + } + + if (!string.IsNullOrEmpty(entryId) && file != null) + { + compressTo.CreateEntry(newtitle, file.ModifiedOn); + + try + { + if (FileConverter.EnableConvert(file, convertToExt)) + { + //Take from converter + using (var readStream = FileConverter.Exec(file, convertToExt)) + { + compressTo.PutStream(readStream); + } + } + else + { + using (var readStream = FileDao.GetFileStream(file)) + { + compressTo.PutStream(readStream); + } + } + } + catch (Exception ex) + { + Error = ex.Message; + Logger.Error(Error, ex); + } + } + else + { + compressTo.CreateEntry(newtitle); + + compressTo.PutNextEntry(); + } + + compressTo.CloseEntry(); + counter++; + + if (!string.IsNullOrEmpty(entryId) && file != null) + { + ProcessedFile(entryId); + } + else + { + ProcessedFolder(default(object)); + } + + } + ProgressStep(); + } + } + return stream; + } + + private void ReplaceLongPath(ItemNameValueCollection entriesPathId) + { + foreach (var path in new List(entriesPathId.AllKeys)) + { + CancellationToken.ThrowIfCancellationRequested(); + + if (200 >= path.Length || 0 >= path.IndexOf('/')) continue; + + var ids = entriesPathId[path]; + entriesPathId.Remove(path); + + var newtitle = "LONG_FOLDER_NAME" + path.Substring(path.LastIndexOf('/')); + entriesPathId.Add(newtitle, ids); + } + } + + + class ItemNameValueCollection + { + private readonly Dictionary> dic = new Dictionary>(); + + + public IEnumerable AllKeys + { + get { return dic.Keys; } + } + + public IEnumerable this[string name] + { + get { return dic[name].ToArray(); } + } + + public int Count + { + get { return dic.Count; } + } + + public void Add(string name, string value) + { + if (!dic.ContainsKey(name)) + { + dic.Add(name, new List()); + } + dic[name].Add(value); + } + + public void Add(ItemNameValueCollection collection) + { + foreach (var key in collection.AllKeys) + { + foreach (var value in collection[key]) + { + Add(key, value); + } + } + } + + public void Add(string name, IEnumerable values) + { + if (!dic.ContainsKey(name)) + { + dic.Add(name, new List()); + } + dic[name].AddRange(values); + } + + public void Remove(string name) + { + dic.Remove(name); + } + } + } } \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/FileOperations/FileMarkAsReadOperation.cs b/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/FileOperations/FileMarkAsReadOperation.cs index 16ed750f4..9bf3d41a9 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/FileOperations/FileMarkAsReadOperation.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/FileOperations/FileMarkAsReadOperation.cs @@ -21,21 +21,26 @@ using System.Threading; using ASC.Common.Security.Authentication; using ASC.Files.Core; +using ASC.MessagingSystem; +using ASC.Web.Files.Helpers; using ASC.Web.Files.Utils; namespace ASC.Web.Files.Services.WCFService.FileOperations { class FileMarkAsReadOperation : FileOperation { + private readonly Dictionary headers; + public override FileOperationType OperationType { get { return FileOperationType.MarkAsRead; } } - public FileMarkAsReadOperation(List folders, List files) + public FileMarkAsReadOperation(List folders, List files, Dictionary headers) : base(folders, files) { + this.headers = headers; } @@ -55,19 +60,21 @@ namespace ASC.Web.Files.Services.WCFService.FileOperations { entries.AddRange(FileDao.GetFiles(Files)); } - entries.ForEach(x => + entries.ForEach(entry => { CancellationToken.ThrowIfCancellationRequested(); - FileMarker.RemoveMarkAsNew(x, ((IAccount)Thread.CurrentPrincipal.Identity).ID); + FileMarker.RemoveMarkAsNew(entry, ((IAccount)Thread.CurrentPrincipal.Identity).ID); - if (x.FileEntryType == FileEntryType.File) + if (entry.FileEntryType == FileEntryType.File) { - ProcessedFile(x.ID.ToString()); + ProcessedFile(entry.ID.ToString()); + FilesMessageService.Send(entry, headers, MessageAction.FileMarkedAsRead, entry.Title); } else { - ProcessedFolder(x.ID.ToString()); + ProcessedFolder(entry.ID.ToString()); + FilesMessageService.Send(entry, headers, MessageAction.FolderMarkedAsRead, entry.Title); } ProgressStep(); }); diff --git a/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/FileOperations/FileMoveCopyOperation.cs b/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/FileOperations/FileMoveCopyOperation.cs index 0b4032c2c..9694417e0 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/FileOperations/FileMoveCopyOperation.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/FileOperations/FileMoveCopyOperation.cs @@ -103,6 +103,10 @@ namespace ASC.Web.Files.Services.WCFService.FileOperations { Error = FilesCommonResource.ErrorMassage_SecurityException_ReadFolder; } + else if (!FilesSecurity.CanDownload(folder)) + { + Error = FilesCommonResource.ErrorMassage_SecurityException; + } else if (folder.RootFolderType == FolderType.Privacy && (copy || toFolder.RootFolderType != FolderType.Privacy)) { @@ -197,7 +201,7 @@ namespace ASC.Web.Files.Services.WCFService.FileOperations newFolderId = FolderDao.MoveFolder(folder.ID, toFolderId, CancellationToken); newFolder = FolderDao.GetFolder(newFolderId); - FilesMessageService.Send(folder.RootFolderType != FolderType.USER ? folder : newFolder, toFolder, _headers, MessageAction.FolderMovedWithOverwriting, folder.Title, toFolder.Title); + FilesMessageService.Send(folder, toFolder, _headers, MessageAction.FolderMovedWithOverwriting, folder.Title, toFolder.Title); if (isToFolder) _needToMark.Add(newFolder); @@ -224,10 +228,11 @@ namespace ASC.Web.Files.Services.WCFService.FileOperations else { FileMarker.RemoveMarkAsNewForAll(folder); + var parentFolder = FolderDao.GetFolder(folder.RootFolderId); var newFolderId = FolderDao.MoveFolder(folder.ID, toFolderId, CancellationToken); newFolder = FolderDao.GetFolder(newFolderId); - FilesMessageService.Send(folder.RootFolderType != FolderType.USER ? folder : newFolder, toFolder, _headers, MessageAction.FolderMoved, folder.Title, toFolder.Title); + FilesMessageService.Send(folder, toFolder, _headers, MessageAction.FolderMovedFrom, folder.Title, parentFolder.Title, toFolder.Title); if (isToFolder) _needToMark.Add(newFolder); @@ -268,6 +273,10 @@ namespace ASC.Web.Files.Services.WCFService.FileOperations { Error = FilesCommonResource.ErrorMassage_SecurityException_ReadFile; } + else if (!FilesSecurity.CanDownload(file)) + { + Error = FilesCommonResource.ErrorMassage_SecurityException; + } else if (file.RootFolderType == FolderType.Privacy && (copy || toFolder.RootFolderType != FolderType.Privacy)) { @@ -329,7 +338,7 @@ namespace ASC.Web.Files.Services.WCFService.FileOperations var newFileId = FileDao.MoveFile(file.ID, toFolderId); newFile = FileDao.GetFile(newFileId); - FilesMessageService.Send(file.RootFolderType != FolderType.USER ? file : newFile, toFolder, _headers, MessageAction.FileMoved, file.Title, parentFolder.Title, toFolder.Title); + FilesMessageService.Send(file, toFolder, _headers, MessageAction.FileMoved, file.Title, parentFolder.Title, toFolder.Title); if (file.RootFolderType == FolderType.TRASH && newFile.ThumbnailStatus == Thumbnail.NotRequired) @@ -432,7 +441,7 @@ namespace ASC.Web.Files.Services.WCFService.FileOperations LinkDao.DeleteAllLink(file.ID); - FilesMessageService.Send(file.RootFolderType != FolderType.USER ? file : newFile, toFolder, _headers, MessageAction.FileMovedWithOverwriting, file.Title, parentFolder.Title, toFolder.Title); + FilesMessageService.Send(file, toFolder, _headers, MessageAction.FileMovedWithOverwriting, file.Title, parentFolder.Title, toFolder.Title); if (ProcessedFile(fileId)) { diff --git a/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/FileOperations/FileOperation.cs b/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/FileOperations/FileOperation.cs index cb410878d..9b3c07dab 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/FileOperations/FileOperation.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/FileOperations/FileOperation.cs @@ -52,11 +52,12 @@ namespace ASC.Web.Files.Services.WCFService.FileOperations private readonly IPrincipal principal; private readonly string culture; - private int total; private int processed; private int successProcessed; + public int Total { get; set; } + protected DistributedTask TaskInfo { get; private set; } protected string Status { get; set; } @@ -124,7 +125,7 @@ namespace ASC.Web.Files.Services.WCFService.FileOperations Logger = Global.Logger; - total = InitTotalProgressSteps(); + Total = InitTotalProgressSteps(); Do(); } @@ -172,7 +173,7 @@ namespace ASC.Web.Files.Services.WCFService.FileOperations protected virtual void FillDistributedTask() { - var progress = total != 0 ? 100 * processed / total : 0; + var progress = Total != 0 ? 100 * processed / Total : 0; TaskInfo.SetProperty(SOURCE, string.Join(SPLIT_CHAR, Folders.Select(f => "folder_" + f).Concat(Files.Select(f => "file_" + f)).ToArray())); TaskInfo.SetProperty(OPERATION_TYPE, OperationType); diff --git a/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/FileOperations/FileOperationsManager.cs b/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/FileOperations/FileOperationsManager.cs index 4a0b413a8..e2001a7ed 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/FileOperations/FileOperationsManager.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/FileOperations/FileOperationsManager.cs @@ -81,9 +81,9 @@ namespace ASC.Web.Files.Services.WCFService.FileOperations } - public ItemList MarkAsRead(List folderIds, List fileIds) + public ItemList MarkAsRead(List folderIds, List fileIds, Dictionary headers) { - var op = new FileMarkAsReadOperation(folderIds, fileIds); + var op = new FileMarkAsReadOperation(folderIds, fileIds, headers); return QueueTask(op); } @@ -108,9 +108,9 @@ namespace ASC.Web.Files.Services.WCFService.FileOperations return QueueTask(op); } - public ItemList Delete(List folders, List files, bool ignoreException, bool holdResult, bool immediately, Dictionary headers) + public ItemList Delete(List folders, List files, bool ignoreException, bool holdResult, bool immediately, Dictionary headers, bool isEmptyTrash = false) { - var op = new FileDeleteOperation(folders, files, ignoreException, holdResult, immediately, headers); + var op = new FileDeleteOperation(folders, files, ignoreException, holdResult, immediately, headers, isEmptyTrash); return QueueTask(op); } diff --git a/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/FileStorageServiceController.cs b/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/FileStorageServiceController.cs index 46d441372..cac8e7f9d 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/FileStorageServiceController.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/FileStorageServiceController.cs @@ -79,9 +79,9 @@ namespace ASC.Web.Files.Services.WCFService using (var folderDao = GetFolderDao()) { var folder = folderDao.GetFolder(folderId); - ErrorIf(folder == null, FilesCommonResource.ErrorMassage_FolderNotFound); ErrorIf(!FileSecurity.CanRead(folder), FilesCommonResource.ErrorMassage_SecurityException_ReadFolder); + EntryManager.SetIsFavoriteFolder(folder); return folder; } @@ -245,6 +245,7 @@ namespace ASC.Web.Files.Services.WCFService } EntryManager.SetFileStatus(entries.OfType().Where(r => r.ID != null).ToList()); + EntryManager.SetIsFavoriteFolders(entries.OfType().Where(r => r.ID != null).ToList()); return new ItemList(entries); } @@ -314,6 +315,8 @@ namespace ASC.Web.Files.Services.WCFService && !Global.GetFilesSecurity().CanRead(folderDao.GetFolder(folder.ParentFolderID))) folder.FolderIdDisplay = Global.FolderShare; + EntryManager.SetIsFavoriteFolder(folder); + return folder; } } @@ -421,7 +424,7 @@ namespace ASC.Web.Files.Services.WCFService } [ActionName("folders-files-createfile"), HttpGet] - public File CreateNewFile(String parentId, String title, String templateId) + public File CreateNewFile(String parentId, String title, String templateId, bool enableExternalExt = false) { using (var fileDao = GetFileDao()) using (var folderDao = GetFolderDao()) @@ -453,7 +456,7 @@ namespace ASC.Web.Files.Services.WCFService } var fileExt = FileUtility.GetFileExtension(title); - if (fileExt != FileUtility.MasterFormExtension) + if (!enableExternalExt && fileExt != FileUtility.MasterFormExtension) { fileExt = FileUtility.GetInternalExtension(title); if (!FileUtility.InternalExtension.Values.Contains(fileExt)) @@ -485,10 +488,21 @@ namespace ASC.Web.Files.Services.WCFService try { var pathNew = path + "new" + fileExt; - using (var stream = storeTemplate.GetReadStream("", pathNew)) + + if (!enableExternalExt) { - file.ContentLength = stream.CanSeek ? stream.Length : storeTemplate.GetFileSize(pathNew); - file = fileDao.SaveFile(file, stream); + using (var stream = storeTemplate.GetReadStream("", pathNew)) + { + file.ContentLength = stream.CanSeek ? stream.Length : storeTemplate.GetFileSize(pathNew); + file = fileDao.SaveFile(file, stream); + } + } + else + { + using (var stream = new MemoryStream()) + { + file = fileDao.SaveFile(file, stream); + } } var pathThumb = path + fileExt.Trim('.') + "." + Global.ThumbnailExtension; @@ -896,8 +910,7 @@ namespace ASC.Web.Files.Services.WCFService { using (var fileDao = GetFileDao()) { - File file; - var readLink = FileShareLink.Check(doc, true, fileDao, out file); + var readLink = FileShareLink.Check(doc, true, fileDao, out File file, out FileShare linkShare); if (file == null) file = fileDao.GetFile(fileId); @@ -914,8 +927,7 @@ namespace ASC.Web.Files.Services.WCFService { using (var fileDao = GetFileDao()) { - File file; - var readLink = FileShareLink.Check(doc, true, fileDao, out file); + var readLink = FileShareLink.Check(doc, true, fileDao, out File file, out FileShare linkShare); if (file != null) { @@ -936,6 +948,7 @@ namespace ASC.Web.Files.Services.WCFService var result = new EditHistoryData { + FileType = file.ConvertedExtension.Trim('.'), Key = DocumentServiceHelper.GetDocKey(file), Url = DocumentServiceConnector.ReplaceCommunityAdress(PathProvider.GetFileStreamUrl(file, doc)), Version = version, @@ -945,12 +958,14 @@ namespace ASC.Web.Files.Services.WCFService { string previouseKey; string sourceFileUrl; + string sourceExt; if (file.Version > 1) { var previousFileStable = fileDao.GetFileStable(file.ID, file.Version - 1); ErrorIf(previousFileStable == null, FilesCommonResource.ErrorMassage_FileNotFound); sourceFileUrl = PathProvider.GetFileStreamUrl(previousFileStable, doc); + sourceExt = previousFileStable.ConvertedExtension; previouseKey = DocumentServiceHelper.GetDocKey(previousFileStable); } @@ -971,16 +986,18 @@ namespace ASC.Web.Files.Services.WCFService sourceFileUrl = storeTemplate.GetUri("", path).ToString(); sourceFileUrl = CommonLinkUtility.GetFullAbsolutePath(sourceFileUrl); + sourceExt = fileExt.Trim('.'); previouseKey = DocumentServiceConnector.GenerateRevisionId(Guid.NewGuid().ToString()); } result.Previous = new EditHistoryUrl { + FileType = sourceExt.Trim('.'), Key = previouseKey, Url = DocumentServiceConnector.ReplaceCommunityAdress(sourceFileUrl), }; - result.ChangesUrl = PathProvider.GetFileChangesUrl(file, doc); + result.ChangesUrl = DocumentServiceConnector.ReplaceCommunityAdress(PathProvider.GetFileChangesUrl(file, doc)); } result.Token = DocumentServiceHelper.GetSignature(result); @@ -1031,6 +1048,104 @@ namespace ASC.Web.Files.Services.WCFService return result; } + public EntryProperties GetFileProperties(String fileId) + { + using (var fileDao = GetFileDao()) + { + fileDao.InvalidateCache(fileId); + + var file = fileDao.GetFile(fileId); + ErrorIf(file == null, FilesCommonResource.ErrorMassage_FileNotFound); + ErrorIf(!FileSecurity.CanRead(file), FilesCommonResource.ErrorMassage_SecurityException_ReadFile); + + var properties = fileDao.GetProperties(fileId) ?? new EntryProperties(); + + if (properties.FormFilling != null) + { + if (!FileSharing.CanSetAccess(file) + || !FileUtility.CanWebRestrictedEditing(file.Title)) + { + properties.FormFilling = null; + } + else + { + if (!string.IsNullOrEmpty(properties.FormFilling.ToFolderId)) + { + using (var folderDao = GetFolderDao()) + { + var folder = folderDao.GetFolder(properties.FormFilling.ToFolderId); + + if (folder == null) + { + properties.FormFilling.ToFolderId = null; + } + else if (FileSecurity.CanCreate(folder)) + { + properties.FormFilling.ToFolderPath = null; + var breadCrumbs = EntryManager.GetBreadCrumbs(folder.ID, folderDao); + properties.FormFilling.ToFolderPath = string.Join("/", breadCrumbs.Select(f => f.Title)); + } + } + } + } + } + + return properties; + } + } + + public EntryProperties SetFileProperties(String fileId, EntryProperties fileProperties) + { + using (var fileDao = GetFileDao()) + { + var file = fileDao.GetFile(fileId); + if (file == null) throw new FileNotFoundException(FilesCommonResource.ErrorMassage_FileNotFound); + if (!Global.GetFilesSecurity().CanEdit(file)) throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException_RenameFile); + if (EntryManager.FileLockedForMe(file.ID)) throw new Exception(FilesCommonResource.ErrorMassage_LockedFile); + if (file.ProviderEntry) throw new Exception(FilesCommonResource.ErrorMassage_BadRequest); + if (file.RootFolderType == FolderType.TRASH) throw new Exception(FilesCommonResource.ErrorMassage_ViewTrashItem); + + var currentProperies = fileDao.GetProperties(fileId) ?? new EntryProperties(); + if (fileProperties != null) + { + if (fileProperties.FormFilling != null) + { + if (!FileSharing.CanSetAccess(file)) throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException); + if (!FileUtility.CanWebRestrictedEditing(file.Title)) throw new Exception(FilesCommonResource.ErrorMassage_NotSupportedFormat); + + if (currentProperies.FormFilling == null) + { + currentProperies.FormFilling = new EntryProperties.FormFillingProperties(); + } + + currentProperies.FormFilling.CollectFillForm = fileProperties.FormFilling.CollectFillForm; + + if (!string.IsNullOrEmpty(fileProperties.FormFilling.ToFolderId)) + { + using (var folderDao = GetFolderDao()) + { + var folder = folderDao.GetFolder(fileProperties.FormFilling.ToFolderId); + + ErrorIf(folder == null, FilesCommonResource.ErrorMassage_FolderNotFound); + ErrorIf(!FileSecurity.CanCreate(folder), FilesCommonResource.ErrorMassage_SecurityException_Create); + + currentProperies.FormFilling.ToFolderId = folder.ID.ToString(); + } + } + + currentProperies.FormFilling.CreateFolderTitle = Global.ReplaceInvalidCharsAndTruncate(fileProperties.FormFilling.CreateFolderTitle); + + currentProperies.FormFilling.CreateFileMask = fileProperties.FormFilling.CreateFileMask; + currentProperies.FormFilling.FixFileMask(); + } + + fileDao.SaveProperties(file.ID, currentProperies); + } + + return currentProperies; + } + } + #endregion #region News @@ -1075,7 +1190,7 @@ namespace ASC.Web.Files.Services.WCFService List filesId; ParseArrayItems(items, out foldersId, out filesId); - return fileOperations.MarkAsRead(foldersId, filesId); + return fileOperations.MarkAsRead(foldersId, filesId, GetHttpHeaders()); } #endregion @@ -1116,11 +1231,11 @@ namespace ASC.Web.Files.Services.WCFService var providersInfo = providerDao.GetProvidersInfo((FolderType)folderType); var folders = providersInfo.Select(providerInfo => - { - var folder = EntryManager.GetFakeThirdpartyFolder(providerInfo); - folder.NewForMe = folder.RootFolderType == FolderType.COMMON ? 1 : 0; - return folder; - }); + { + var folder = EntryManager.GetFakeThirdpartyFolder(providerInfo); + folder.NewForMe = folder.RootFolderType == FolderType.COMMON ? 1 : 0; + return folder; + }); return new ItemList(folders); } @@ -1236,7 +1351,7 @@ namespace ASC.Web.Files.Services.WCFService ErrorIf(!Global.IsAdministrator, FilesCommonResource.ErrorMassage_SecurityException); FilesSettings.EnableThirdParty = enable; - FilesMessageService.Send(GetHttpHeaders(), MessageAction.DocumentsThirdPartySettingsUpdated); + MessageService.Send(GetHttpHeaders(), MessageAction.DocumentsThirdPartySettingsUpdated); return FilesSettings.EnableThirdParty; } @@ -1383,6 +1498,8 @@ namespace ASC.Web.Files.Services.WCFService [ActionName("moveorcopy"), HttpPost] public ItemList MoveOrCopyItems([FromBody] ItemList items, string destFolderId, FileConflictResolveType resolve, bool ic, bool deleteAfter = false) { + ErrorIf(resolve == FileConflictResolveType.Overwrite && CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID).IsVisitor(), FilesCommonResource.ErrorMassage_SecurityException); + ItemList result; if (items.Count != 0) { @@ -1419,7 +1536,7 @@ namespace ASC.Web.Files.Services.WCFService var foldersId = folderDao.GetFolders(trashId).Select(f => f.ID).ToList(); var filesId = fileDao.GetFiles(trashId).ToList(); - return fileOperations.Delete(foldersId, filesId, false, true, false, GetHttpHeaders()); + return fileOperations.Delete(foldersId, filesId, false, true, false, GetHttpHeaders(), true); } } @@ -1652,6 +1769,10 @@ namespace ASC.Web.Files.Services.WCFService var tags = entries.Select(entry => Tag.Favorite(SecurityContext.CurrentAccount.ID, entry)); tagDao.SaveTags(tags); + foreach (var entry in entries) + { + FilesMessageService.Send(entry, HttpContext.Current.Request, MessageAction.FileMarkedAsFavorite, entry.Title); + } return new ItemList(entries); } @@ -1677,6 +1798,11 @@ namespace ASC.Web.Files.Services.WCFService tagDao.RemoveTags(tags); + foreach (var entry in entries) + { + FilesMessageService.Send(entry, HttpContext.Current.Request, MessageAction.FileRemovedFromFavorite, entry.Title); + } + return new ItemList(entries); } } @@ -1901,12 +2027,16 @@ namespace ASC.Web.Files.Services.WCFService try { - var changed = FileSharing.SetAceObject(aceCollection.Aces, entry, notify, aceCollection.Message); + var changed = FileSharing.SetAceObject(aceCollection.Aces, entry, notify, aceCollection.Message, aceCollection.AdvancedSettings); if (changed) { - FilesMessageService.Send(entry, GetHttpHeaders(), - entryType == FileEntryType.Folder ? MessageAction.FolderUpdatedAccess : MessageAction.FileUpdatedAccess, - entry.Title); + foreach (var ace in aceCollection.Aces) + { + var name = ace.SubjectGroup ? CoreContext.UserManager.GetGroupInfo(ace.SubjectId).Name : CoreContext.UserManager.GetUsers(ace.SubjectId).DisplayUserName(false); + FilesMessageService.Send(entry, GetHttpHeaders(), + entryType == FileEntryType.Folder ? MessageAction.FolderUpdatedAccessFor : MessageAction.FileUpdatedAccessFor, + entry.Title, name, GetAccessString(ace.Share)); + } } } catch (Exception e) @@ -1943,6 +2073,17 @@ namespace ASC.Web.Files.Services.WCFService entries.AddRange(foldersId.Select(folderDao.GetFolder)); FileSharing.RemoveAce(entries); + foreach (var entry in entries) + { + if (entry.FileEntryType == FileEntryType.File) + { + FilesMessageService.Send(entry, GetHttpHeaders(), MessageAction.FileRemovedFromList, entry.Title); + } + else + { + FilesMessageService.Send(entry, GetHttpHeaders(), MessageAction.FolderRemovedFromList, entry.Title); + } + } } } @@ -1987,11 +2128,10 @@ namespace ASC.Web.Files.Services.WCFService try { - - var changed = FileSharing.SetAceObject(aces, file, false, null); + var changed = FileSharing.SetAceObject(aces, file, false, null, null); if (changed) { - FilesMessageService.Send(file, GetHttpHeaders(), MessageAction.FileUpdatedAccess, file.Title); + FilesMessageService.Send(file, GetHttpHeaders(), MessageAction.FileExternalLinkAccessUpdated, file.Title, GetAccessString(share)); } } catch (Exception e) @@ -2106,8 +2246,14 @@ namespace ASC.Web.Files.Services.WCFService } }; - showSharingSettings |= FileSharing.SetAceObject(aces, file, false, null); - + showSharingSettings |= FileSharing.SetAceObject(aces, file, false, null, null); + if (showSharingSettings) + { + foreach (var ace in aces) + { + FilesMessageService.Send(file, GetHttpHeaders(), MessageAction.FileUpdatedAccessFor, file.Title, CoreContext.UserManager.GetUsers(ace.SubjectId).DisplayUserName(false), GetAccessString(ace.Share)); + } + } recipients.Add(recipient.ID); } catch (Exception e) @@ -2121,11 +2267,6 @@ namespace ASC.Web.Files.Services.WCFService } } - if (showSharingSettings) - { - FilesMessageService.Send(file, GetHttpHeaders(), MessageAction.FileUpdatedAccess, file.Title); - } - var fileLink = FilesLinkUtility.GetFileWebEditorUrl(file.ID); if (mentionMessage.ActionLink != null) { @@ -2154,6 +2295,35 @@ namespace ASC.Web.Files.Services.WCFService return new ItemList(fileKeyPair); } + [ActionName("external"), HttpGet] + public bool ChangeExternalShareSettings(bool enable) + { + ErrorIf(!Global.IsAdministrator, FilesCommonResource.ErrorMassage_SecurityException); + + FilesSettings.ExternalShare = enable; + + if (!enable) + { + FilesSettings.ExternalShareSocialMedia = false; + } + + MessageService.Send(GetHttpHeaders(), MessageAction.DocumentsExternalShareSettingsUpdated); + + return FilesSettings.ExternalShare; + } + + [ActionName("externalsocialmedia"), HttpGet] + public bool ChangeExternalShareSocialMediaSettings(bool enable) + { + ErrorIf(!Global.IsAdministrator, FilesCommonResource.ErrorMassage_SecurityException); + + FilesSettings.ExternalShareSocialMedia = FilesSettings.ExternalShare && enable; + + MessageService.Send(GetHttpHeaders(), MessageAction.DocumentsExternalShareSettingsUpdated); + + return FilesSettings.ExternalShareSocialMedia; + } + #endregion #region MailMerge @@ -2222,6 +2392,8 @@ namespace ASC.Web.Files.Services.WCFService newFolder = folderDao.GetFolder(newFolderID); newFolder.Access = folderAccess; + EntryManager.SetIsFavoriteFolder(folder); + FilesMessageService.Send(newFolder, GetHttpHeaders(), MessageAction.FileChangeOwner, new[] { newFolder.Title, userInfo.DisplayUserName(false) }); } entries.Add(newFolder); @@ -2289,7 +2461,7 @@ namespace ASC.Web.Files.Services.WCFService public bool StoreOriginal(bool set) { FilesSettings.StoreOriginalFiles = set; - FilesMessageService.Send(GetHttpHeaders(), MessageAction.DocumentsUploadingFormatsSettingsUpdated); + MessageService.Send(GetHttpHeaders(), MessageAction.DocumentsUploadingFormatsSettingsUpdated); return FilesSettings.StoreOriginalFiles; } @@ -2311,8 +2483,10 @@ namespace ASC.Web.Files.Services.WCFService [ActionName("updateifexist"), HttpGet] public bool UpdateIfExist(bool set) { + ErrorIf(CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID).IsVisitor(), FilesCommonResource.ErrorMassage_SecurityException); + FilesSettings.UpdateIfExist = set; - FilesMessageService.Send(GetHttpHeaders(), MessageAction.DocumentsOverwritingSettingsUpdated); + MessageService.Send(GetHttpHeaders(), MessageAction.DocumentsOverwritingSettingsUpdated); return FilesSettings.UpdateIfExist; } @@ -2321,7 +2495,7 @@ namespace ASC.Web.Files.Services.WCFService public bool Forcesave(bool set) { FilesSettings.Forcesave = set; - FilesMessageService.Send(GetHttpHeaders(), MessageAction.DocumentsForcesave); + MessageService.Send(GetHttpHeaders(), MessageAction.DocumentsForcesave); return FilesSettings.Forcesave; } @@ -2332,7 +2506,7 @@ namespace ASC.Web.Files.Services.WCFService ErrorIf(!Global.IsAdministrator, FilesCommonResource.ErrorMassage_SecurityException); FilesSettings.StoreForcesave = set; - FilesMessageService.Send(GetHttpHeaders(), MessageAction.DocumentsStoreForcesave); + MessageService.Send(GetHttpHeaders(), MessageAction.DocumentsStoreForcesave); return FilesSettings.StoreForcesave; } @@ -2372,6 +2546,26 @@ namespace ASC.Web.Files.Services.WCFService return FilesSettings.ConfirmDelete; } + public AutoCleanUpData ChangeAutomaticallyCleanUp(bool set, DateToAutoCleanUp gap) + { + FilesSettings.AutomaticallyCleanUp = new AutoCleanUpData() { IsAutoCleanUp = set, Gap = gap }; + + return FilesSettings.AutomaticallyCleanUp; + } + + [ActionName("autocleanup"), HttpGet] + public AutoCleanUpData GetSettingsAutomaticallyCleanUp() + { + return FilesSettings.AutomaticallyCleanUp; + } + + public List ChangeDafaultAccessRights(List value) + { + FilesSettings.DefaultSharingAccessRights = value; + + return FilesSettings.DefaultSharingAccessRights; + } + [ActionName("downloadtargz"), HttpGet] public ICompress ChangeDownloadTarGz(bool set) { @@ -2487,6 +2681,24 @@ namespace ASC.Web.Files.Services.WCFService return null; } + private static string GetAccessString(FileShare fileShare) + { + switch (fileShare) + { + case FileShare.Read: + case FileShare.ReadWrite: + case FileShare.CustomFilter: + case FileShare.Review: + case FileShare.FillForms: + case FileShare.Comment: + case FileShare.Restrict: + case FileShare.None: + return FilesCommonResource.ResourceManager.GetString("AceStatusEnum_" + fileShare.ToString()); + default: + return string.Empty; + } + } + #endregion } } \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/IFileStorageService.cs b/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/IFileStorageService.cs index 7ea44ccc4..c17c3d6e4 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/IFileStorageService.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/IFileStorageService.cs @@ -66,7 +66,7 @@ namespace ASC.Web.Files.Services.WCFService File GetFile(String fileId, int version); - File CreateNewFile(String parentId, String fileTitle, string templateId); + File CreateNewFile(String parentId, String fileTitle, string templateId, bool enableExternalExt); File FileRename(String fileId, String title); @@ -102,6 +102,10 @@ namespace ASC.Web.Files.Services.WCFService Web.Core.Files.DocumentService.FileLink GetPresignedUri(String fileId); + EntryProperties GetFileProperties(String fileId); + + EntryProperties SetFileProperties(String fileId, EntryProperties fileProperties); + #endregion #region Favorites Manager @@ -156,6 +160,12 @@ namespace ASC.Web.Files.Services.WCFService bool ChangeDeleteConfrim(bool update); + AutoCleanUpData ChangeAutomaticallyCleanUp(bool set, DateToAutoCleanUp gap); + + AutoCleanUpData GetSettingsAutomaticallyCleanUp(); + + List ChangeDafaultAccessRights(List value); + ICompress ChangeDownloadTarGz(bool update); String GetHelpCenter(); diff --git a/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/Wrappers/AceWrapper.cs b/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/Wrappers/AceWrapper.cs index 9038fd3d9..68a98a648 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/Wrappers/AceWrapper.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Services/WCFService/Wrappers/AceWrapper.cs @@ -35,6 +35,9 @@ namespace ASC.Web.Files.Services.WCFService [DataMember(Name = "message", Order = 3, IsRequired = false)] public string Message { get; set; } + + [DataMember(Name = "advancedSettings", Order = 4, IsRequired = false)] + public AceAdvancedSettingsWrapper AdvancedSettings { get; set; } } [DataContract(Name = "ace_wrapper", Namespace = "")] @@ -115,4 +118,14 @@ namespace ASC.Web.Files.Services.WCFService Permissions = permission; } } + + [DataContract(Name = "advancedSettings", Namespace = "")] + public class AceAdvancedSettingsWrapper + { + [DataMember(Name = "denyDownload")] + public bool DenyDownload { get; set; } + + [DataMember(Name = "denySharing")] + public bool DenySharing { get; set; } + } } \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Files/Share.aspx b/web/studio/ASC.Web.Studio/Products/Files/Share.aspx index 07f04bc98..616123f5a 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Share.aspx +++ b/web/studio/ASC.Web.Studio/Products/Files/Share.aspx @@ -6,8 +6,28 @@ diff --git a/web/studio/ASC.Web.Studio/Products/Files/Share.aspx.cs b/web/studio/ASC.Web.Studio/Products/Files/Share.aspx.cs index 37b20847d..c31782dd4 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Share.aspx.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Share.aspx.cs @@ -16,6 +16,7 @@ using System; +using System.Collections.Generic; using System.Net; using System.Text; using System.Web; @@ -28,12 +29,16 @@ using ASC.Web.Files.Controls; using ASC.Web.Files.Resources; using ASC.Web.Studio; +using Newtonsoft.Json; + using Global = ASC.Web.Files.Classes.Global; namespace ASC.Web.Files { public partial class Share : MainPage, IStaticBundle { + private bool shareDialogV115 = Global.EnableShareDialogV115; + public static string Location { get { return FilesLinkUtility.FilesBaseAbsolutePath + "Share.aspx"; } @@ -59,9 +64,15 @@ namespace ASC.Web.Files .AddStaticStyles(GetStaticStyleSheet()) .AddStaticBodyScripts(GetStaticJavaScript()); - var accessRights = (AccessRights)LoadControl(AccessRights.Location); - accessRights.IsPopup = false; - CommonContainerHolder.Controls.Add(accessRights); + if (shareDialogV115) { + var accessRights = (AccessRights)LoadControl(AccessRights.Location); + accessRights.IsPopup = false; + CommonContainerHolder.Controls.Add(accessRights); + } else { + var sharingDialog = (SharingDialog)LoadControl(SharingDialog.Location); + sharingDialog.IsPopup = false; + CommonContainerHolder.Controls.Add(sharingDialog); + } InitScript(); } @@ -104,11 +115,15 @@ namespace ASC.Web.Files } var script = new StringBuilder(); - script.AppendFormat("ASC.Files.Share.getSharedInfo(\"file_{0}\", \"{1}\", true, {2} === true, \"{3}\");", + script.AppendFormat("ASC.Files.Share.getSharedInfo(\"file_{0}\", \"{1}\", true, {2}, \"{3}\", {4}, {5}, {6}, {7});", file.ID, file.Title, (file.RootFolderType == FolderType.COMMON).ToString().ToLower(), - originForPost); + originForPost, + file.DenyDownload.ToString().ToLower(), + file.DenySharing.ToString().ToLower(), + file.ProviderEntry.ToString().ToLower(), + JsonConvert.SerializeObject(FilesSettings.DefaultSharingAccessRights)); //todo: change hardcode url script.AppendFormat("\r\nASC.Controls.JabberClient.pathWebTalk = \"{0}\";", @@ -119,6 +134,10 @@ namespace ASC.Web.Files public ScriptBundleData GetStaticJavaScript() { + var src = shareDialogV115 + ? new List { "Controls/AccessRights/accessrights.js", "Controls/AccessRights/formfilling.js" } + : new List { "Controls/SharingDialog/sharingdialog.js" }; + return (ScriptBundleData) new ScriptBundleData("filesshare", "files") .AddSource(PathProvider.GetFileStaticRelativePath, @@ -131,19 +150,19 @@ namespace ASC.Web.Files "~/js/third-party/clipboard.js", "~/Products/Files/Controls/Desktop/desktop.js" ) - .AddSource(r => FilesLinkUtility.FilesBaseAbsolutePath + r, - "Controls/AccessRights/accessrights.js" - ); + .AddSource(r => FilesLinkUtility.FilesBaseAbsolutePath + r, src.ToArray()); } public StyleBundleData GetStaticStyleSheet() { + var src = shareDialogV115 + ? new List { "Controls/AccessRights/accessrights.css", "Controls/AccessRights/formfilling.css" } + : new List { "Controls/SharingDialog/sharingdialog.css" }; + return (StyleBundleData) new StyleBundleData("filesshare", "files") .AddSource(PathProvider.GetFileStaticRelativePath, "common.css") - .AddSource(r => FilesLinkUtility.FilesBaseAbsolutePath + r, - "Controls/AccessRights/accessrights.css" - ); + .AddSource(r => FilesLinkUtility.FilesBaseAbsolutePath + r, src.ToArray()); } } } \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Files/Templates/getfolderitem.xsl b/web/studio/ASC.Web.Studio/Products/Files/Templates/getfolderitem.xsl index f5c0088d7..2e582ee10 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Templates/getfolderitem.xsl +++ b/web/studio/ASC.Web.Studio/Products/Files/Templates/getfolderitem.xsl @@ -8,7 +8,7 @@
  57. file-row folder-row new-folder item-row - + on-favorite @@ -70,6 +70,11 @@ +
    + + + +
    @@ -105,12 +110,18 @@ + + +   + + + | @@ -137,6 +148,12 @@ + + + + + + @@ -176,6 +193,12 @@ + + + + + +
    @@ -373,9 +396,15 @@ + + +   + + + | @@ -395,6 +424,12 @@ + + + + + + @@ -452,6 +487,9 @@ + + + diff --git a/web/studio/ASC.Web.Studio/Products/Files/Templates/getfolderitems.xsl b/web/studio/ASC.Web.Studio/Products/Files/Templates/getfolderitems.xsl index b59776134..529365ed7 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Templates/getfolderitems.xsl +++ b/web/studio/ASC.Web.Studio/Products/Files/Templates/getfolderitems.xsl @@ -13,7 +13,7 @@
  58. file-row folder-row item-row - + on-favorite @@ -75,6 +75,11 @@ +
    + + + +
    @@ -110,12 +115,18 @@ + + +   + + + | @@ -142,6 +153,12 @@ + + + + + + @@ -181,6 +198,12 @@ + + + + + +
    @@ -380,9 +403,15 @@ -   + + + +   + + + | @@ -402,6 +431,12 @@ + + + + + + @@ -459,6 +494,9 @@ + + + diff --git a/web/studio/ASC.Web.Studio/Products/Files/Templates/getnewitems.xsl b/web/studio/ASC.Web.Studio/Products/Files/Templates/getnewitems.xsl index 417ce2024..e26567cb9 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Templates/getnewitems.xsl +++ b/web/studio/ASC.Web.Studio/Products/Files/Templates/getnewitems.xsl @@ -56,6 +56,12 @@ + + + + + + @@ -113,6 +119,9 @@ + +
    +
  59. diff --git a/web/studio/ASC.Web.Studio/Products/Files/Templates/getthirdpartyitem.xsl b/web/studio/ASC.Web.Studio/Products/Files/Templates/getthirdpartyitem.xsl index dcbea59d3..ab36286e0 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Templates/getthirdpartyitem.xsl +++ b/web/studio/ASC.Web.Studio/Products/Files/Templates/getthirdpartyitem.xsl @@ -35,9 +35,7 @@ _blank - - # - + # @@ -159,6 +157,14 @@ + + + + + + + + diff --git a/web/studio/ASC.Web.Studio/Products/Files/ThirdPartyApp/BoxApp.cs b/web/studio/ASC.Web.Studio/Products/Files/ThirdPartyApp/BoxApp.cs index 7192f407c..a72a0c17d 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/ThirdPartyApp/BoxApp.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/ThirdPartyApp/BoxApp.cs @@ -17,6 +17,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Net; @@ -211,7 +212,7 @@ namespace ASC.Web.Files.ThirdPartyApp Global.Logger.Debug("BoxApp: GetConvertedUri from " + fileType + " to " + currentType + " - " + downloadUrl); var key = DocumentServiceConnector.GenerateRevisionId(downloadUrl); - DocumentServiceConnector.GetConvertedUri(downloadUrl, fileType, currentType, key, null, null, null, false, out downloadUrl); + DocumentServiceConnector.GetConvertedUri(downloadUrl, fileType, currentType, key, null, CultureInfo.CurrentUICulture.Name, null, null, false, out downloadUrl); stream = null; } catch (Exception e) @@ -327,9 +328,7 @@ namespace ASC.Web.Files.ThirdPartyApp throw new Exception("Profile is null"); } - var cookiesKey = SecurityContext.AuthenticateMe(userInfo.ID); - CookiesManager.SetCookies(CookiesType.AuthKey, cookiesKey); - MessageService.Send(HttpContext.Current.Request, MessageAction.LoginSuccessViaSocialApp); + CookiesManager.AuthenticateMeAndSetCookies(userInfo.Tenant, userInfo.ID, MessageAction.LoginSuccessViaSocialApp); if (isNew) { @@ -485,7 +484,7 @@ namespace ASC.Web.Files.ThirdPartyApp try { - SecurityContext.AuthenticateMe(ASC.Core.Configuration.Constants.CoreSystem); + SecurityContext.CurrentAccount = ASC.Core.Configuration.Constants.CoreSystem; userInfo = UserManagerWrapper.AddUser(userInfo, UserManagerWrapper.GeneratePassword()); } finally diff --git a/web/studio/ASC.Web.Studio/Products/Files/ThirdPartyApp/GoogleDriveApp.cs b/web/studio/ASC.Web.Studio/Products/Files/ThirdPartyApp/GoogleDriveApp.cs index d5bcee71e..b3655e65a 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/ThirdPartyApp/GoogleDriveApp.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/ThirdPartyApp/GoogleDriveApp.cs @@ -17,6 +17,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Net; @@ -206,7 +207,7 @@ namespace ASC.Web.Files.ThirdPartyApp Global.Logger.Debug("GoogleDriveApp: GetConvertedUri from " + fileType + " to " + currentType + " - " + downloadUrl); var key = DocumentServiceConnector.GenerateRevisionId(downloadUrl); - DocumentServiceConnector.GetConvertedUri(downloadUrl, fileType, currentType, key, null, null, null, false, out downloadUrl); + DocumentServiceConnector.GetConvertedUri(downloadUrl, fileType, currentType, key, null, CultureInfo.CurrentUICulture.Name, null, null, false, out downloadUrl); stream = null; } catch (Exception e) @@ -322,9 +323,7 @@ namespace ASC.Web.Files.ThirdPartyApp throw new Exception("Profile is null"); } - var cookiesKey = SecurityContext.AuthenticateMe(userInfo.ID); - CookiesManager.SetCookies(CookiesType.AuthKey, cookiesKey); - MessageService.Send(HttpContext.Current.Request, MessageAction.LoginSuccessViaSocialApp); + CookiesManager.AuthenticateMeAndSetCookies(userInfo.Tenant, userInfo.ID, MessageAction.LoginSuccessViaSocialApp); if (isNew) { @@ -529,7 +528,6 @@ namespace ASC.Web.Files.ThirdPartyApp { var linker = new AccountLinker("webstudio"); var linkedProfiles = linker.GetLinkedObjectsByHashId(HashHelper.MD5(string.Format("{0}/{1}", ProviderConstants.Google, googleId))); - linkedProfiles = linkedProfiles.Concat(linker.GetLinkedObjectsByHashId(HashHelper.MD5(string.Format("{0}/{1}", ProviderConstants.OpenId, googleId)))); Guid tmp; return linkedProfiles.Any(profileId => Guid.TryParse(profileId, out tmp) && tmp == SecurityContext.CurrentAccount.ID); @@ -588,7 +586,7 @@ namespace ASC.Web.Files.ThirdPartyApp try { - SecurityContext.AuthenticateMe(ASC.Core.Configuration.Constants.CoreSystem); + SecurityContext.CurrentAccount = ASC.Core.Configuration.Constants.CoreSystem; userInfo = UserManagerWrapper.AddUser(userInfo, UserManagerWrapper.GeneratePassword()); } finally @@ -729,7 +727,7 @@ namespace ASC.Web.Files.ThirdPartyApp Global.Logger.Debug("GoogleDriveApp: GetConvertedUri- " + downloadUrl); var key = DocumentServiceConnector.GenerateRevisionId(downloadUrl); - DocumentServiceConnector.GetConvertedUri(downloadUrl, fromExt, toExt, key, null, null, null, false, out downloadUrl); + DocumentServiceConnector.GetConvertedUri(downloadUrl, fromExt, toExt, key, null, CultureInfo.CurrentUICulture.Name, null, null, false, out downloadUrl); } catch (Exception e) { diff --git a/web/studio/ASC.Web.Studio/Products/Files/Utils/ChunkedUploadSessionHolder.cs b/web/studio/ASC.Web.Studio/Products/Files/Utils/ChunkedUploadSessionHolder.cs index 2240d1cf8..fd5856b95 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Utils/ChunkedUploadSessionHolder.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Utils/ChunkedUploadSessionHolder.cs @@ -61,7 +61,7 @@ namespace ASC.Web.Files.Utils public static ChunkedUploadSession GetSession(string sessionId) { - return (ChunkedUploadSession)CommonSessionHolder(false).Get(sessionId); + return CommonSessionHolder(false).Get(sessionId); } public static ChunkedUploadSession CreateUploadSession(File file, long contentLength) diff --git a/web/studio/ASC.Web.Studio/Products/Files/Utils/EntryManager.cs b/web/studio/ASC.Web.Studio/Products/Files/Utils/EntryManager.cs index c5e57fa39..c18dd7900 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Utils/EntryManager.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Utils/EntryManager.cs @@ -17,6 +17,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Net; @@ -30,6 +31,7 @@ using ASC.Common.Caching; using ASC.Core; using ASC.Core.Users; using ASC.Files.Core; +using ASC.MessagingSystem; using ASC.Web.Core.Files; using ASC.Web.Files.Api; using ASC.Web.Files.Classes; @@ -283,6 +285,7 @@ namespace ASC.Web.Files.Utils } SetFileStatus(entries.Where(r => r != null && r.ID != null && r.FileEntryType == FileEntryType.File).Select(r => r as File).ToList()); + SetIsFavoriteFolders(entries.Where(r => r != null && r.ID != null && r.FileEntryType == FileEntryType.Folder).Select(r => r as Folder).ToList()); return entries; } @@ -682,6 +685,29 @@ namespace ASC.Web.Files.Utils } } + public static void SetIsFavoriteFolder(Folder folder) + { + if (folder == null || folder.ID == null) return; + + SetIsFavoriteFolders(new List(1) { folder }); + } + + public static void SetIsFavoriteFolders(IEnumerable folders) + { + using (var tagDao = Global.DaoFactory.GetTagDao()) + { + var tagsFavorite = tagDao.GetTags(SecurityContext.CurrentAccount.ID, TagType.Favorite, folders); + + foreach (var folder in folders) + { + if (tagsFavorite.Any(r => r.EntryId.Equals(folder.ID))) + { + folder.IsFavorite = true; + } + } + } + } + public static bool FileLockedForMe(object fileId, Guid userId = default(Guid)) { var app = ThirdPartySelector.GetAppByFileId(fileId.ToString()); @@ -773,6 +799,8 @@ namespace ASC.Web.Files.Utils using (var fileDao = Global.DaoFactory.GetFileDao()) { var sourceId = linkDao.GetSource(linkedFile.ID); + if (sourceId == null) return false; + var sourceFile = fileDao.GetFile(sourceId); var fileSecurity = Global.GetFilesSecurity(); @@ -788,12 +816,101 @@ namespace ASC.Web.Files.Utils return true; } + public static bool SubmitFillForm(File draft) + { + if (draft == null) return false; + try + { + using (var linkDao = Global.GetLinkDao()) + using (var fileDao = Global.DaoFactory.GetFileDao()) + using (var folderDao = Global.DaoFactory.GetFolderDao()) + { + var sourceId = linkDao.GetSource(draft.ID); + if (sourceId == null) throw new Exception("Link source is not found"); + + var sourceFile = fileDao.GetFile(sourceId); + if (sourceFile == null) throw new FileNotFoundException(FilesCommonResource.ErrorMassage_FileNotFound, draft.ID.ToString()); + + if (!FileUtility.CanWebRestrictedEditing(sourceFile.Title)) throw new Exception(FilesCommonResource.ErrorMassage_NotSupportedFormat); + + var properties = fileDao.GetProperties(sourceFile.ID); + if (properties == null + || properties.FormFilling == null + || !properties.FormFilling.CollectFillForm) throw new Exception(FilesCommonResource.ErrorMassage_BadRequest); + + var folderId = properties.FormFilling.ToFolderId; + if (!string.IsNullOrEmpty(folderId)) + { + var folder = folderDao.GetFolder(folderId); + if (folder == null) + { + folderId = sourceFile.FolderID.ToString(); + } + } + else + { + folderId = sourceFile.FolderID.ToString(); + } + //todo: think about right to create in folder + + if (!string.IsNullOrEmpty(properties.FormFilling.CreateFolderTitle)) + { + var newFolderTitle = Global.ReplaceInvalidCharsAndTruncate(properties.FormFilling.CreateFolderTitle); + + var folder = folderDao.GetFolder(newFolderTitle, folderId); + if (folder == null) + { + folder = new Folder { Title = newFolderTitle, ParentFolderID = folderId }; + folderId = folderDao.SaveFolder(folder).ToString(); + + folder = folderDao.GetFolder(folderId); + FilesMessageService.Send(folder, MessageInitiator.DocsService, MessageAction.FolderCreated, folder.Title); + } + + folderId = folder.ID.ToString(); + } + //todo: think about right to create in folder + + var title = properties.FormFilling.GetTitleByMask(sourceFile.Title); + + var submitFile = new File + { + Title = title, + FolderID = folderId, + FileStatus = draft.FileStatus, + ConvertedType = draft.ConvertedType, + Comment = FilesCommonResource.CommentSubmitFillForm, + Encrypted = draft.Encrypted, + }; + + using (var stream = fileDao.GetFileStream(draft)) + { + submitFile.ContentLength = stream.CanSeek ? stream.Length : draft.ContentLength; + submitFile = fileDao.SaveFile(submitFile, stream); + } + + FilesMessageService.Send(submitFile, MessageInitiator.DocsService, MessageAction.FileCreated, submitFile.Title); + + FileMarker.MarkAsNew(submitFile); + + return true; + } + } + catch (Exception e) + { + Global.Logger.Error(string.Format("Error on submit form {0}", draft.ID), e); + return false; + } + } + public static File SaveEditing(String fileId, string fileExtension, string downloadUri, Stream stream, String doc, string comment = null, bool checkRight = true, bool encrypted = false, ForcesaveType? forcesave = null, bool keepLink = false) { var newExtension = string.IsNullOrEmpty(fileExtension) ? FileUtility.GetFileExtension(downloadUri) : fileExtension; + if (!string.IsNullOrEmpty(newExtension)) + newExtension = "." + newExtension.Trim('.'); var app = ThirdPartySelector.GetAppByFileId(fileId); if (app != null) @@ -805,7 +922,7 @@ namespace ASC.Web.Files.Utils File file; using (var fileDao = Global.DaoFactory.GetFileDao()) { - var editLink = FileShareLink.Check(doc, false, fileDao, out file); + var editLink = FileShareLink.Check(doc, false, fileDao, out file, out FileShare linkShare); if (file == null) { file = fileDao.GetFile(fileId); @@ -836,7 +953,7 @@ namespace ASC.Web.Files.Utils } else { - if (file.Version != 1) + if (file.Version != 1 || string.IsNullOrEmpty(currentExt)) { file.VersionGroup++; } @@ -856,7 +973,8 @@ namespace ASC.Web.Files.Utils path += "new" + fileExt; //todo: think about the criteria for saving after creation - if (file.ContentLength != storeTemplate.GetFileSize("", path)) + if (!storeTemplate.IsFile("", path) + || file.ContentLength != storeTemplate.GetFileSize("", path)) { file.VersionGroup++; } @@ -885,7 +1003,7 @@ namespace ASC.Web.Files.Utils } var key = DocumentServiceConnector.GenerateRevisionId(downloadUri); - DocumentServiceConnector.GetConvertedUri(downloadUri, newExtension, currentExt, key, null, null, null, false, out downloadUri); + DocumentServiceConnector.GetConvertedUri(downloadUri, newExtension, currentExt, key, null, CultureInfo.CurrentUICulture.Name, null, null, false, out downloadUri); stream = null; } @@ -961,7 +1079,7 @@ namespace ASC.Web.Files.Utils bool editLink; using (var fileDao = Global.DaoFactory.GetFileDao()) { - editLink = FileShareLink.Check(doc, false, fileDao, out file); + editLink = FileShareLink.Check(doc, false, fileDao, out file, out FileShare linkShare); if (file == null) file = fileDao.GetFile(fileId); } @@ -996,7 +1114,7 @@ namespace ASC.Web.Files.Utils if (version < 1) throw new ArgumentNullException("version"); File fromFile; - var editLink = FileShareLink.Check(doc, false, fileDao, out fromFile); + var editLink = FileShareLink.Check(doc, false, fileDao, out fromFile, out FileShare linkShare); if (fromFile == null) fromFile = fileDao.GetFile(fileId); diff --git a/web/studio/ASC.Web.Studio/Products/Files/Utils/FileConverter.cs b/web/studio/ASC.Web.Studio/Products/Files/Utils/FileConverter.cs index a9ee79020..aa6b4470d 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Utils/FileConverter.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Utils/FileConverter.cs @@ -121,7 +121,7 @@ namespace ASC.Web.Files.Utils var docKey = DocumentServiceHelper.GetDocKey(file); string convertUri; fileUri = DocumentServiceConnector.ReplaceCommunityAdress(fileUri); - DocumentServiceConnector.GetConvertedUri(fileUri, file.ConvertedExtension, toExtension, docKey, null, null, null, false, out convertUri); + DocumentServiceConnector.GetConvertedUri(fileUri, file.ConvertedExtension, toExtension, docKey, null, CultureInfo.CurrentUICulture.Name, null, null, false, out convertUri); if (WorkContext.IsMono && ServicePointManager.ServerCertificateValidationCallback == null) { @@ -137,7 +137,7 @@ namespace ASC.Web.Files.Utils var fileSecurity = Global.GetFilesSecurity(); if (!fileSecurity.CanRead(file)) { - var readLink = FileShareLink.Check(doc, true, fileDao, out file); + var readLink = FileShareLink.Check(doc, true, fileDao, out file, out ASC.Files.Core.Security.FileShare linkShare); if (file == null) { throw new ArgumentNullException("file", FilesCommonResource.ErrorMassage_FileNotFound); @@ -156,7 +156,7 @@ namespace ASC.Web.Files.Utils string convertUri; fileUri = DocumentServiceConnector.ReplaceCommunityAdress(fileUri); - DocumentServiceConnector.GetConvertedUri(fileUri, fileExtension, toExtension, docKey, null, null, null, false, out convertUri); + DocumentServiceConnector.GetConvertedUri(fileUri, fileExtension, toExtension, docKey, null, CultureInfo.CurrentUICulture.Name, null, null, false, out convertUri); return SaveConvertedFile(file, convertUri); } @@ -336,7 +336,7 @@ namespace ASC.Web.Files.Utils } CoreContext.TenantManager.SetCurrentTenant(tenantId); - SecurityContext.AuthenticateMe(account); + SecurityContext.CurrentAccount = account; var user = CoreContext.UserManager.GetUsers(account.ID); var culture = string.IsNullOrEmpty(user.CultureName) ? CoreContext.TenantManager.GetCurrentTenant().GetCulture() : CultureInfo.GetCultureInfo(user.CultureName); @@ -360,7 +360,7 @@ namespace ASC.Web.Files.Utils var docKey = DocumentServiceHelper.GetDocKey(file); fileUri = DocumentServiceConnector.ReplaceCommunityAdress(fileUri); - operationResultProgress = DocumentServiceConnector.GetConvertedUri(fileUri, fileExtension, toExtension, docKey, password, null, null, true, out convertedFileUrl); + operationResultProgress = DocumentServiceConnector.GetConvertedUri(fileUri, fileExtension, toExtension, docKey, password, CultureInfo.CurrentUICulture.Name, null, null, true, out convertedFileUrl); } catch (Exception exception) { diff --git a/web/studio/ASC.Web.Studio/Products/Files/Utils/FileShareLink.cs b/web/studio/ASC.Web.Studio/Products/Files/Utils/FileShareLink.cs index 49affa6e7..355649118 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Utils/FileShareLink.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Utils/FileShareLink.cs @@ -57,9 +57,9 @@ namespace ASC.Web.Files.Utils return Signature.Read(doc ?? String.Empty, Global.GetDocDbKey()); } - public static bool Check(string doc, bool checkRead, IFileDao fileDao, out File file) + public static bool Check(string doc, bool checkRead, IFileDao fileDao, out File file, out FileShare fileShare) { - var fileShare = Check(doc, fileDao, out file); + fileShare = Check(doc, fileDao, out file); return (!checkRead && (fileShare == FileShare.ReadWrite || fileShare == FileShare.CustomFilter @@ -72,6 +72,7 @@ namespace ASC.Web.Files.Utils public static FileShare Check(string doc, IFileDao fileDao, out File file) { file = null; + if (!FilesSettings.ExternalShare) return FileShare.Restrict; if (string.IsNullOrEmpty(doc)) return FileShare.Restrict; var fileId = Parse(doc); file = fileDao.GetFile(fileId); diff --git a/web/studio/ASC.Web.Studio/Products/Files/Utils/FileSharing.cs b/web/studio/ASC.Web.Studio/Products/Files/Utils/FileSharing.cs index 373203c30..91c62376f 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Utils/FileSharing.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Utils/FileSharing.cs @@ -45,10 +45,10 @@ namespace ASC.Web.Files.Utils && (entry.RootFolderType == FolderType.COMMON && Global.IsAdministrator || !CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID).IsVisitor() && (entry.RootFolderType == FolderType.USER - && (Equals(entry.RootFolderId, Global.FolderMy) || Global.GetFilesSecurity().CanEdit(entry)) + && (Equals(entry.RootFolderId, Global.FolderMy) || Global.GetFilesSecurity().CanShare(entry)) || entry.RootFolderType == FolderType.Privacy && entry is File - && (Equals(entry.RootFolderId, Global.FolderPrivacy) || Global.GetFilesSecurity().CanEdit(entry)))); + && (Equals(entry.RootFolderId, Global.FolderPrivacy) || Global.GetFilesSecurity().CanShare(entry)))); } public static List GetSharedInfo(FileEntry entry) @@ -80,6 +80,11 @@ namespace ASC.Web.Files.Utils continue; } + if (r.Subject == FileConstant.DenyDownloadId || r.Subject == FileConstant.DenySharingId) + { + continue; + } + var u = CoreContext.UserManager.GetUsers(r.Subject); var isgroup = false; var title = u.DisplayUserName(false); @@ -131,7 +136,7 @@ namespace ASC.Web.Files.Utils var w = new AceWrapper { SubjectId = FileConstant.ShareLinkId, - Link = FileShareLink.GetLink((File)entry), + Link = FilesSettings.ExternalShare ? FileShareLink.GetLink((File)entry) : string.Empty, SubjectGroup = true, Share = linkAccess, Owner = false @@ -173,7 +178,8 @@ namespace ASC.Web.Files.Utils }; result.Add(w); } - if (result.All(w => w.SubjectId != Constants.GroupEveryone.ID)) + var index = result.FindIndex(w => w.SubjectId == Constants.GroupEveryone.ID); + if (index == -1) { var w = new AceWrapper { @@ -186,18 +192,22 @@ namespace ASC.Web.Files.Utils }; result.Add(w); } + else + { + result[index].DisableRemove = true; + } } return result; } - public static bool SetAceObject(List aceWrappers, FileEntry entry, bool notify, string message) + public static bool SetAceObject(List aceWrappers, FileEntry entry, bool notify, string message, AceAdvancedSettingsWrapper advancedSettings) { if (entry == null) throw new ArgumentNullException(FilesCommonResource.ErrorMassage_BadRequest); if (!CanSetAccess(entry)) throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException); var fileSecurity = Global.GetFilesSecurity(); - + var ownerId = entry.RootFolderType == FolderType.USER ? entry.RootFolderCreator : entry.CreateBy; var entryType = entry.FileEntryType; var recipients = new Dictionary(); var usersWithoutRight = new List(); @@ -207,7 +217,6 @@ namespace ASC.Web.Files.Utils { var subjects = fileSecurity.GetUserSubjects(w.SubjectId); - var ownerId = entry.RootFolderType == FolderType.USER ? entry.RootFolderCreator : entry.CreateBy; if (entry.RootFolderType == FolderType.COMMON && subjects.Contains(Constants.GroupAdmin.ID) || ownerId == w.SubjectId) continue; @@ -227,7 +236,10 @@ namespace ASC.Web.Files.Utils if (w.Share != FileShare.Restrict && !CoreContext.Configuration.Standalone && TenantExtra.GetTenantQuota().Trial && !FileUtility.CanWebView(entry.Title)) throw new SecurityException(FilesCommonResource.ErrorMassage_BadRequest); - share = w.Share == FileShare.Restrict ? FileShare.None : w.Share; + share = w.Share == FileShare.Restrict + || !FilesSettings.ExternalShare + ? FileShare.None + : w.Share; } fileSecurity.Share(entry.ID, entryType, w.SubjectId, share); @@ -296,6 +308,12 @@ namespace ASC.Web.Files.Utils } } + if (advancedSettings != null && entryType == FileEntryType.File && ownerId == SecurityContext.CurrentAccount.ID && FileUtility.CanWebView(entry.Title) && !entry.ProviderEntry) + { + fileSecurity.Share(entry.ID, entryType, FileConstant.DenyDownloadId, advancedSettings.DenyDownload ? FileShare.Restrict : FileShare.None); + fileSecurity.Share(entry.ID, entryType, FileConstant.DenySharingId, advancedSettings.DenySharing ? FileShare.Restrict : FileShare.None); + } + usersWithoutRight.ForEach(userId => FileMarker.RemoveMarkAsNew(entry, userId)); return changed; diff --git a/web/studio/ASC.Web.Studio/Products/Files/Utils/FileUploader.cs b/web/studio/ASC.Web.Studio/Products/Files/Utils/FileUploader.cs index e86021a6a..ce7103241 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/Utils/FileUploader.cs +++ b/web/studio/ASC.Web.Studio/Products/Files/Utils/FileUploader.cs @@ -180,7 +180,41 @@ namespace ASC.Web.Files.Utils return file; } - public static ChunkedUploadSession InitiateUpload(string folderId, string fileId, string fileName, long contentLength, bool encrypted) + public static File VerifyChunkedUploadForEditing(object fileId, long fileSize) + { + using (var fileDao = Global.DaoFactory.GetFileDao()) + { + var file = fileDao.GetFile(fileId); + + if (file == null) + { + throw new FileNotFoundException(FilesCommonResource.ErrorMassage_FileNotFound); + } + + var maxUploadSize = GetMaxFileSize(file.FolderID, true); + + if (fileSize > maxUploadSize) + { + throw FileSizeComment.GetFileSizeException(maxUploadSize); + } + + if (!CanEdit(file)) + { + throw new SecurityException(FilesCommonResource.ErrorMassage_SecurityException_EditFile); + } + + file.ConvertedType = null; + file.Comment = FilesCommonResource.CommentUpload; + file.Encrypted = false; + file.ThumbnailStatus = Thumbnail.Waiting; + + file.ContentLength = fileSize; + + return file; + } + } + + public static ChunkedUploadSession InitiateUpload(string folderId, string fileId, string fileName, long contentLength, bool encrypted, bool keepVersion = false) { if (string.IsNullOrEmpty(folderId)) folderId = null; @@ -207,6 +241,7 @@ namespace ASC.Web.Files.Utils uploadSession.FolderId = folderId; uploadSession.CultureName = Thread.CurrentThread.CurrentUICulture.Name; uploadSession.Encrypted = encrypted; + uploadSession.KeepVersion = keepVersion; ChunkedUploadSessionHolder.StoreSession(uploadSession); diff --git a/web/studio/ASC.Web.Studio/Products/Files/js/actionmanager.js b/web/studio/ASC.Web.Studio/Products/Files/js/actionmanager.js index 589176b2b..762884e63 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/js/actionmanager.js +++ b/web/studio/ASC.Web.Studio/Products/Files/js/actionmanager.js @@ -1,4 +1,4 @@ -/* +/* * * (c) Copyright Ascensio System Limited 2010-2021 * @@ -26,7 +26,7 @@ window.ASC.Files.Actions = (function () { if (isInit === false) { isInit = true; - jq(document).click(function (event) { + jq(document).on("click", function (event) { jq.dropdownToggle().registerAutoHide(event, "#filesActionsPanel", "#filesActionsPanel", function () { ASC.Files.Mouse.disableHover = false; @@ -160,11 +160,19 @@ window.ASC.Files.Actions = (function () { return; } + var showFormFillingSettings = jsonData.some(function (item) { + return item.ace_status == ASC.Files.Constants.AceStatusEnum.FillForms; + }) + + if (false && showFormFillingSettings) { + jq("#filesFormFillingSettings").show().removeClass("display-none"); + } + var data = jsonData[0]; var toggleSwitcher = jq("#filesGetExternalLink .toggle"); - toggleSwitcher.toggleClass("off", data.ace_status == 3); + toggleSwitcher.toggleClass("off", data.ace_status == ASC.Files.Constants.AceStatusEnum.Restrict); ASC.Files.Actions.clipGetExternalLink = ASC.Clipboard.destroy(ASC.Files.Actions.clipGetExternalLink); @@ -185,11 +193,64 @@ window.ASC.Files.Actions = (function () { }); }; + var getLinkDefaultAccesssRigth = function (entryTitle) { + + var getLinkAccessRights = function (title) { + var accessRights = [ASC.Files.Constants.AceStatusEnum.Read]; + + if (ASC.Files.Utility.CanWebEdit(title) && !ASC.Files.Utility.MustConvert(title)) { + accessRights.push(ASC.Files.Constants.AceStatusEnum.ReadWrite); + } + + if (ASC.Files.Utility.CanWebCustomFilterEditing(title)) { + accessRights.push(ASC.Files.Constants.AceStatusEnum.CustomFilter); + } + + if (ASC.Files.Utility.CanWebReview(title)) { + accessRights.push(ASC.Files.Constants.AceStatusEnum.Review); + } + + if (ASC.Files.Utility.CanWebRestrictedEditing(title)) { + accessRights.push(ASC.Files.Constants.AceStatusEnum.FillForms); + } + + if (ASC.Files.Utility.CanWebComment(title)) { + accessRights.push(ASC.Files.Constants.AceStatusEnum.Comment); + } + + return accessRights; + } + + var $settingsObj = jq("#defaultAccessRightsSetting"); + + if ($settingsObj.length) { + var common = +$settingsObj.find("input[type=radio]:checked").val(); + + var specific = []; + $settingsObj.find("input[type=checkbox]:checked").each(function () { + specific.push(+this.value); + }); + + var accessRights = getLinkAccessRights(entryTitle); + for (var access of accessRights) { + if (specific.indexOf(access) != -1) { + return access; + } + } + + if (accessRights.indexOf(common) != -1) { + return common; + } + } + + return ASC.Files.Constants.AceStatusEnum.Read; + } + var setAceFileLink = function () { ASC.Files.ServiceManager.setAceLink(ASC.Files.ServiceManager.events.SetAceFileLink, { showLoading: true, fileId: ASC.Files.Actions.currentEntryData.entryId, - share: jq("#filesGetExternalLink .toggle").hasClass("off") ? ASC.Files.Constants.AceStatusEnum.Read : ASC.Files.Constants.AceStatusEnum.Restrict + share: jq("#filesGetExternalLink .toggle").hasClass("off") ? getLinkDefaultAccesssRigth(ASC.Files.Actions.currentEntryData.title) : ASC.Files.Constants.AceStatusEnum.Restrict }); }; @@ -205,16 +266,25 @@ window.ASC.Files.Actions = (function () { var showActionsViewPanel = function (event) { - jq("#buttonUnsubscribe, #buttonDelete, #buttonMoveto, #buttonCopyto, #buttonShare, #buttonMarkRead, #buttonSendInEmail, #buttonAddFaforite").hide(); + jq("#buttonUnsubscribe, #buttonDelete, #buttonMoveto, #buttonCopyto, #buttonShare, #buttonMarkRead, #buttonSendInEmail, #buttonAddFaforite, #buttonDownload, #buttonConvert").hide(); jq("#mainContentHeader .unlockAction").removeClass("unlockAction"); - var count = jq("#filesMainContent .file-row:not(.checkloading):not(.new-folder):not(.new-file):not(.error-entry):has(.checkbox input:checked)").length; - var filesCount = jq("#filesMainContent .file-row:not(.checkloading):not(.new-folder):not(.new-file):not(.error-entry):not(.folder-row):has(.checkbox input:checked)").length; + + var selectedItems = jq("#filesMainContent .file-row:not(.checkloading):not(.new-folder):not(.new-file):not(.error-entry):has(.checkbox input:checked)"); + var count = selectedItems.length; + var filesCount = selectedItems.filter(":not(.folder-row)").length; var countNotFavorite = 0; if (ASC.Files.Tree.displayFavorites() && ASC.Files.Folders.folderContainer != "trash" && ASC.Files.Folders.folderContainer != "privacy") { - countNotFavorite = jq("#filesMainContent .file-row:not(.checkloading):not(.new-folder):not(.new-file):not(.error-entry):not(.on-favorite):not(.folder-row):has(.checkbox input:checked)").length; + countNotFavorite = selectedItems.filter(":not(.on-favorite)").length; + } + + var countFavorite = 0; + if (ASC.Files.Tree.displayFavorites() + && ASC.Files.Folders.folderContainer != "trash" + && ASC.Files.Folders.folderContainer != "privacy") { + countFavorite = selectedItems.filter(".on-favorite").length; } var countWithRights = count; @@ -223,8 +293,9 @@ window.ASC.Files.Actions = (function () { var countThirdParty = 0; var canConvert = false; var countCanShare = 0; + var countCannotDownload = 0; - jq("#filesMainContent .file-row:not(.checkloading):not(.new-folder):not(.new-file):not(.error-entry):has(.checkbox input:checked)").each(function () { + selectedItems.each(function () { var entryObj = jq(this); if (ASC.Files.UI.editingFile(entryObj) || !ASC.Files.UI.accessDelete(entryObj) || ASC.Files.UI.lockedForMe(entryObj)) { @@ -246,14 +317,20 @@ window.ASC.Files.Actions = (function () { onlyThirdParty = false; } + var denyDownload = ASC.Files.UI.denyDownload(entryData); + if (denyDownload) { + countCannotDownload++; + } + if (!canConvert && !entryObj.hasClass("folder-row")) { var entryTitle = entryData.title; var formats = ASC.Files.Utility.GetConvertFormats(entryTitle); - canConvert = formats.length > 0 && !entryData.encrypted && entryData.content_length <= ASC.Files.Constants.AvailableFileSize; + canConvert = formats.length > 0 && !entryData.encrypted && entryData.content_length <= ASC.Files.Constants.AvailableFileSize && !denyDownload; } if (!ASC.Resources.Master.Personal && entryObj.is(":not(.without-share)") && ASC.Files.Share && ASC.Files.Folders.currentFolder.shareable + && !ASC.Files.UI.denySharing(entryData) && (!entryData.encrypted || ASC.Files.Folders.folderContainer == "privacy" && ASC.Files.Utility.CanWebEncrypt(entryData.title) @@ -263,8 +340,12 @@ window.ASC.Files.Actions = (function () { }); if (count > 0) { - jq("#buttonDownload, #buttonRemoveFavorite, #buttonRemoveTemplate, #buttonUnsubscribe, #buttonRestore, #buttonCopyto").show().find("span").html(count); - jq("#mainDownload, #mainRemoveFavorite, #mainRemoveTemplate, #mainUnsubscribe, #mainRestore, #mainCopy").addClass("unlockAction"); + if (count - countCannotDownload > 0) { + jq("#buttonDownload, #buttonCopyto").show().find("span").html(count - countCannotDownload); + jq("#mainDownload, #mainCopy").addClass("unlockAction"); + } + jq("#buttonRemoveTemplate, #buttonUnsubscribe, #buttonRestore").show().find("span").html(count); + jq("#mainRemoveFavorite, #mainRemoveTemplate, #mainUnsubscribe, #mainRestore").addClass("unlockAction"); if (ASC.Files.Folders.folderContainer == "privacy") { jq("#buttonCopyto").hide(); } @@ -273,9 +354,7 @@ window.ASC.Files.Actions = (function () { } if (ASC.Files.Folders.folderContainer == "favorites") { jq("#buttonMoveto").hide(); - } else { - jq("#buttonRemoveFavorite").hide(); - } + } if (ASC.Files.Folders.folderContainer == "templates") { jq("#buttonMoveto").hide(); } else { @@ -283,8 +362,9 @@ window.ASC.Files.Actions = (function () { } } - if (filesCount > 0 && countCanShare > 0 || ASC.Files.Folders.folderContainer == "project" && filesCount > 0) { - jq("#buttonSendInEmail").show().find("span").html(filesCount); + var countCanSendInEmail = filesCount - countCannotDownload; + if (countCanSendInEmail > 0 && (countCanShare > 0 || ASC.Files.Folders.folderContainer == "project")) { + jq("#buttonSendInEmail").show().find("span").html(countCanSendInEmail); } if (countNotFavorite > 0) { @@ -293,8 +373,14 @@ window.ASC.Files.Actions = (function () { jq("#buttonAddFavorite").hide(); } + if (countFavorite > 0) { + jq("#buttonRemoveFavorite").show().find("span").html(countFavorite); + } else { + jq("#buttonRemoveFavorite").hide(); + } + if (canConvert) { - jq("#buttonConvert").show().find("span").html(count); + jq("#buttonConvert").show().find("span").html(count - countCannotDownload); } else { jq("#buttonConvert").hide(); } @@ -422,6 +508,10 @@ window.ASC.Files.Actions = (function () { var entryTitle = entryData.title; + if (ASC.Files.UI.denyDownload(entryData)) { + jq("#filesDownload, #filesConvert, #filesSendInEmail, #filesMoveto, #filesCopyto, #filesCopy").hide().addClass("display-none"); + } + if (!ASC.Files.Utility.GetConvertFormats(entryTitle).length || entryData.encrypted || entryData.content_length > ASC.Files.Constants.AvailableFileSize) { jq("#filesConvert").hide().addClass("display-none"); } @@ -594,7 +684,7 @@ window.ASC.Files.Actions = (function () { #filesRemove").hide().addClass("display-none"); } - if (entryObj.is(".without-share *, .without-share")) { + if (entryObj.is(".without-share *:not(.can-share), .without-share") || ASC.Files.UI.denySharing(entryData)) { jq("#filesShareAccess").hide().addClass("display-none"); jq("#filesGetExternalLink").hide().addClass("display-none"); } else if (jq("#filesGetExternalLink").data("trial") @@ -633,6 +723,8 @@ window.ASC.Files.Actions = (function () { jq("#filesMove").hide().addClass("display-none"); } + jq("#filesFormFillingSettings").hide().addClass("display-none"); + jq("#actionPanelFiles").show(); } else { @@ -648,13 +740,28 @@ window.ASC.Files.Actions = (function () { #foldersCopyto,\ #foldersMarkRead,\ #foldersRename,\ + #foldersAddFavorite,\ + #foldersRemoveFavorite,\ #foldersRestore,\ #foldersRemove,\ #foldersRemoveThirdparty,\ #foldersChangeThirdparty").show().removeClass("display-none"); + if (ASC.Files.UI.denyDownload(entryData)) { + jq("#foldersDownload, #foldersMove, #foldersMoveto, #foldersCopyto").hide().addClass("display-none"); + } + if (ASC.Files.Folders.folderContainer == "privacy") { - jq("#foldersCopyto").hide().addClass("display-none"); + jq("#foldersCopyto, #foldersAddFavorite, #foldersRemoveFavorite, #foldersGetLink").hide().addClass("display-none"); + } + if (entryObj.hasClass("on-favorite")) { + jq("#foldersAddFavorite").hide().addClass("display-none"); + } else { + jq("#foldersRemoveFavorite").hide().addClass("display-none"); + + if (!ASC.Files.Tree.displayFavorites()) { + jq("#foldersAddFavorite").hide().addClass("display-none"); + } } if (ASC.Files.Folders.folderContainer == "trash") { @@ -666,6 +773,8 @@ window.ASC.Files.Actions = (function () { #foldersMoveto,\ #foldersCopyto,\ #foldersMarkRead,\ + #foldersAddFavorite,\ + #foldersRemoveFavorite,\ #foldersRename").hide().addClass("display-none"); } else { jq("#foldersRestore").hide().addClass("display-none"); @@ -692,7 +801,7 @@ window.ASC.Files.Actions = (function () { } } - if (entryObj.is(".without-share *, .without-share")) { + if (entryObj.is(".without-share *:not(.can-share), .without-share") || ASC.Files.UI.denySharing(entryData)) { jq("#foldersShareAccess").hide().addClass("display-none"); } @@ -741,6 +850,10 @@ window.ASC.Files.Actions = (function () { jq("#foldersGotoParent").hide().addClass("display-none"); } + if (ASC.Files.Folders.folderContainer == "favorites") { + jq("#foldersGotoParent").show().removeClass("display-none"); + } + if (!jq("#foldersMovePanel li:not(.display-none)").length) { jq("#foldersMove").hide().addClass("display-none"); } @@ -829,18 +942,55 @@ window.ASC.Files.Actions = (function () { ASC.Files.Mouse.disableHover = false; }; + var createIframe = function (target, url) { + var iframe = target.createElement("iframe"); + iframe.src = url; + iframe.id = "hiddenIframe"; + iframe.style.display = "none"; + target.body.appendChild(iframe); + return iframe; + }; + + var openCustomProtocolInIframe = function (uri) { + var iframe = document.querySelector("#hiddenIframe"); + if (!iframe) { + iframe = createIframe(document, "about:blank"); + } + iframe.contentWindow.location.href = uri; + }; + + var openDocumentPrivacyCheck = function (fileId, winEditor, fileObj) { + var urlForFileOpenWebEditor = ASC.Files.Utility.GetFileWebEditorUrl(fileId); + var customUrlForFileOpenDesktopEditor = ASC.Files.Utility.GetFileCustomProtocolEditorUrl(fileId); + var urlForOpenPrivate = ASC.Files.Utility.GetOpenPrivate(fileId); + if (!fileObj.length) { + fileObj = jq("#filesNewsList " + ASC.Files.UI.getSelectorId(fileId)); + } + var entryData = ASC.Files.UI.getObjectData(fileObj); + if (!ASC.Desktop && entryData && entryData.encrypted) { + if (winEditor && winEditor.location) { + winEditor.location.href = urlForFileOpenWebEditor; + } else { + if (localStorage.getItem("protocoldetector") == 1) { + openCustomProtocolInIframe(customUrlForFileOpenDesktopEditor); + } else { + window.open(urlForOpenPrivate, "_blank"); + } + } + } else { + if (winEditor && winEditor.location) { + winEditor.location.href = urlForFileOpenWebEditor; + } else { + winEditor = window.open(urlForFileOpenWebEditor, "_blank"); + } + } + }; + var checkEditFile = function (fileId, winEditor) { var fileObj = ASC.Files.UI.getEntryObject("file", fileId); ASC.Files.UI.lockEditFile(fileObj, true); - - var url = ASC.Files.Utility.GetFileWebEditorUrl(fileId); - - if (winEditor && winEditor.location) { - winEditor.location.href = url; - } else { - winEditor = window.open(url, "_blank"); - } - + openDocumentPrivacyCheck(fileId, winEditor, fileObj); + var onloadFunction = function () { var fileIdLocal = fileId; if (fileIdLocal) { @@ -890,6 +1040,25 @@ window.ASC.Files.Actions = (function () { return ASC.Files.Marker.removeNewIcon("file", fileId); }; + var checkViewFile = function (fileId, version) { + var viewerUrl = ASC.Files.Utility.GetFileWebViewerUrl(fileId, version); + var fileObj = ASC.Files.UI.getEntryObject("file", fileId); + var entryData = ASC.Files.UI.getObjectData(fileObj); + if (!ASC.Desktop && entryData && entryData.encrypted) { + var viewerParameters = viewerUrl.slice(viewerUrl.indexOf("&")); + if (localStorage.getItem("protocoldetector") == 1) { + var customProtocolViewerUrl = ASC.Files.Utility.GetFileCustomProtocolEditorUrl(fileId) + viewerParameters; + openCustomProtocolInIframe(customProtocolViewerUrl); + } else { + var openPrivateUrl = ASC.Files.Utility.GetOpenPrivate(fileId) + viewerParameters; + window.open(openPrivateUrl, "_blank"); + } + } else { + window.open(viewerUrl, "_blank"); + } + return ASC.Files.Marker.removeNewIcon("file", fileId); + }; + var showMoveToSelector = function (isCopy) { ASC.Files.Folders.isCopyTo = (isCopy === true); ASC.Files.Actions.hideAllActionPanels(); @@ -939,7 +1108,7 @@ window.ASC.Files.Actions = (function () { jq("#treeViewPanelSelector").scrollTo(jq("#treeViewPanelSelector").find(".tree-node" + ASC.Files.UI.getSelectorId(ASC.Files.Folders.currentFolder.id))); - jq("body").bind("click", ASC.Files.Actions.registerHideTree); + jq("body").on("click", ASC.Files.Actions.registerHideTree); }; var registerHideTree = function (event) { @@ -948,7 +1117,7 @@ window.ASC.Files.Actions = (function () { #foldersRestore, #filesRestore, #buttonMoveto, #buttonCopyto, #buttonRestore,\ #mainMove, #mainCopy, #mainRestore")) { ASC.Files.Actions.hideAllActionPanels(); - jq("body").unbind("click", ASC.Files.Actions.registerHideTree); + jq("body").off("click", ASC.Files.Actions.registerHideTree); } }; @@ -959,6 +1128,7 @@ window.ASC.Files.Actions = (function () { onContextMenu: onContextMenu, checkEditFile: checkEditFile, + checkViewFile: checkViewFile, hideAllActionPanels: hideAllActionPanels, @@ -978,69 +1148,80 @@ window.ASC.Files.Actions = (function () { ASC.Files.Actions.init(); $(function () { - jq("#filesSelectAll").click(function () { + jq("#filesSelectAll").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); ASC.Files.UI.checkSelectAll(true); }); jq("#filesSelectFile, #filesSelectFolder, #filesSelectDocument,\ - #filesSelectPresentation, #filesSelectSpreadsheet, #filesSelectImage, #filesSelectMedia, #filesSelectArchive").click(function () { + #filesSelectPresentation, #filesSelectSpreadsheet, #filesSelectImage, #filesSelectMedia, #filesSelectArchive").on("click", function () { var filter = this.id.replace("filesSelect", "").toLowerCase(); ASC.Files.UI.checkSelect(filter); }); - jq("#filesDownload, #foldersDownload").click(function () { + jq("#filesDownload, #foldersDownload").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); ASC.Files.Folders.download(ASC.Files.Actions.currentEntryData.entryType, ASC.Files.Actions.currentEntryData.id); }); - jq("#filesRename, #foldersRename").click(function () { + jq("#filesRename, #foldersRename").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); ASC.Files.Folders.rename(ASC.Files.Actions.currentEntryData.entryType, ASC.Files.Actions.currentEntryData.id); }); - jq("#filesAddFavorite, #filesRemoveFavorite").click(function () { + jq("#filesAddFavorite, #filesRemoveFavorite, #foldersAddFavorite, #foldersRemoveFavorite").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); ASC.Files.Folders.toggleFavorite(ASC.Files.Actions.currentEntryData.entryObject); }); - jq("#filesAddTemplate, #filesRemoveTemplate").click(function () { + jq("#filesAddTemplate, #filesRemoveTemplate").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); ASC.Files.Folders.toggleTemplate(ASC.Files.Actions.currentEntryData.entryObject); }); - jq("#filesRemove, #foldersRemove").click(function () { + jq("#filesRemove, #foldersRemove").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); ASC.Files.Folders.deleteItem(ASC.Files.Actions.currentEntryData.entryType, ASC.Files.Actions.currentEntryData.id); }); - jq("#filesShareAccess, #foldersShareAccess").click(function () { + jq("#filesShareAccess, #foldersShareAccess").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); - ASC.Files.Share.getSharedInfo(ASC.Files.Actions.currentEntryData.entryType + "_" + ASC.Files.Actions.currentEntryData.id, ASC.Files.Actions.currentEntryData.title); - }); - - jq("#filesGetExternalLink .toggle").click(function(e) { - if (jq(this).hasClass("off")) { - ASC.Files.Actions.clipGetExternalLink.fromToggleBtn = true; - } else { - e.stopPropagation(); - ASC.Files.Actions.setAceFileLink(); + if (!ASC.Files.UI.denySharing(ASC.Files.Actions.currentEntryData)) { + ASC.Files.Share.getSharedInfo(ASC.Files.Actions.currentEntryData.entryType + "_" + ASC.Files.Actions.currentEntryData.id, ASC.Files.Actions.currentEntryData.title); } }); - jq("#filesMarkRead, #foldersMarkRead").click(function () { + jq("#filesFormFillingSettings").on("click", function () { + window.ASC.Files.FormFilling.setFileId(ASC.Files.Actions.currentEntryData.id); + window.ASC.Files.FormFilling.getPropertiesAndShowDialog(); + }); + + jq("#filesGetExternalLink .toggle").on("click", function(e) { + if (!ASC.Files.UI.denySharing(ASC.Files.Actions.currentEntryData)) { + if (jq(this).hasClass("off")) { + ASC.Files.Actions.clipGetExternalLink.fromToggleBtn = true; + } else { + e.stopPropagation(); + ASC.Files.Actions.setAceFileLink(); + } + } + }); + + jq("#filesMarkRead, #foldersMarkRead").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); ASC.Files.Marker.markAsRead(ASC.Files.Actions.currentEntryData); }); - jq("#filesChangeOwner, #foldersChangeOwner").click(function () { + jq("#filesChangeOwner, #foldersChangeOwner").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); ASC.Files.Folders.changeOwnerDialog(ASC.Files.Actions.currentEntryData); }); - jq("#filesSendInEmail").click(function () { + jq("#filesSendInEmail").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); - window.location.href = ASC.Mail.Utility.GetDraftUrl(ASC.Files.Actions.currentEntryData.entryId); + if (!ASC.Files.UI.denyDownload(ASC.Files.Actions.currentEntryData)) { + window.location.href = ASC.Mail.Utility.GetDraftUrl(ASC.Files.Actions.currentEntryData.entryId); + } }); jq("#studioPageContent").on("click", "#buttonSendInEmail", function () { @@ -1048,92 +1229,92 @@ window.ASC.Files.Actions = (function () { var fileIds = jq("#filesMainContent .file-row:not(.checkloading):not(.new-folder):not(.new-file):not(.error-entry):not(.folder-row):has(.checkbox input:checked)").map(function () { var entryRowData = ASC.Files.UI.getObjectData(this); - var entryRowId = entryRowData.entryId; - - return entryRowId; + return ASC.Files.UI.denyDownload(entryRowData) ? 0 : entryRowData.entryId; + }).filter(function (index, item) { + return item > 0 }); window.location.href = ASC.Mail.Utility.GetDraftUrl(fileIds.get()); }); - jq("#filesUnsubscribe, #foldersUnsubscribe").click(function () { + jq("#filesUnsubscribe, #foldersUnsubscribe").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); ASC.Files.Share.unSubscribeMe(ASC.Files.Actions.currentEntryData.entryType, ASC.Files.Actions.currentEntryData.id); }); - jq("#filesConvert").click(function () { + jq("#filesConvert").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); ASC.Files.Converter.showToConvert(ASC.Files.Actions.currentEntryData.entryObject); }); - jq("#filesOpen").click(function () { + jq("#filesOpen").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); ASC.Files.Folders.clickOnFile(ASC.Files.Actions.currentEntryData, false); return false; }); - jq("#filesLock, #filesUnlock").click(function () { + jq("#filesLock, #filesUnlock").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); ASC.Files.Folders.lockFile(ASC.Files.Actions.currentEntryData.entryObject, ASC.Files.Actions.currentEntryData.id); }); - jq("#filesLock .toggle, #filesUnlock .toggle").click(function (e) { + jq("#filesLock .toggle, #filesUnlock .toggle").on("click", function (e) { e.stopPropagation(); jq("#filesLock, #filesUnlock").toggle(); ASC.Files.Folders.lockFile(ASC.Files.Actions.currentEntryData.entryObject, ASC.Files.Actions.currentEntryData.id); }); - jq("#filesCompleteVersion").click(function () { + jq("#filesCompleteVersion").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); ASC.Files.Folders.versionComplete(ASC.Files.Actions.currentEntryData.id, 0, false); }); - jq("#filesVersions").click(function () { + jq("#filesVersions").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); ASC.Files.Folders.showVersions(ASC.Files.Actions.currentEntryData.entryObject, ASC.Files.Actions.currentEntryData.id); }); - jq("#filesEdit, #filesFillForm").click(function () { + jq("#filesEdit, #filesFillForm").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); ASC.Files.Folders.clickOnFile(ASC.Files.Actions.currentEntryData, true); }); - jq("#filesCreateForm").click(function () { + jq("#filesCreateForm").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); ASC.Files.Folders.createNewForm(ASC.Files.Actions.currentEntryData); }); - jq("#filesByTemplate").click(function () { + jq("#filesByTemplate").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); ASC.Files.Folders.createNewDoc(ASC.Files.Actions.currentEntryData); }); - jq("#filesCopy").click(function () { + jq("#filesCopy").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); ASC.Files.Folders.createDuplicate(ASC.Files.Actions.currentEntryData); }); - jq("#foldersOpen").click(function () { + jq("#foldersOpen").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); ASC.Files.Folders.clickOnFolder(ASC.Files.Actions.currentEntryData.id); }); - jq("#filesGotoParent, #foldersGotoParent").click(function () { + jq("#filesGotoParent, #foldersGotoParent").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); ASC.Files.Folders.clickOnFolder(ASC.Files.Actions.currentEntryData.folder_id); }); - jq("#foldersRemoveThirdparty").click(function () { + jq("#foldersRemoveThirdparty").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); ASC.Files.ThirdParty.showDeleteDialog(null, null, null, ASC.Files.Actions.currentEntryData.title, ASC.Files.Actions.currentEntryData); }); - jq("#foldersChangeThirdparty").click(function () { + jq("#foldersChangeThirdparty").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); ASC.Files.ThirdParty.showChangeDialog(ASC.Files.Actions.currentEntryData); }); - jq("body").bind("contextmenu", function (event) { + jq("body").on("contextmenu", function (event) { return ASC.Files.Actions.onContextMenu(event); }); @@ -1148,7 +1329,7 @@ window.ASC.Files.Actions = (function () { ASC.Files.Actions.showMoveToSelector(this.id == "buttonCopyto" || this.id == "mainCopy" || this.id == "filesCopyto" || this.id == "foldersCopyto"); }); - jq("#filesDocuSign").click(function () { + jq("#filesDocuSign").on("click", function () { ASC.Files.ThirdParty.showDocuSignDialog(ASC.Files.Actions.currentEntryData); ASC.Files.Actions.hideAllActionPanels(); return false; diff --git a/web/studio/ASC.Web.Studio/Products/Files/js/anchormanager.js b/web/studio/ASC.Web.Studio/Products/Files/js/anchormanager.js index 465b9f7a0..953fcd291 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/js/anchormanager.js +++ b/web/studio/ASC.Web.Studio/Products/Files/js/anchormanager.js @@ -123,7 +123,7 @@ window.ASC.Files.Anchor = (function () { var hash = ASC.Files.MediaPlayer.getPlayHash(fileId); ASC.Files.Anchor.move(hash, true); }, - downloadAction: ASC.Files.Utility.GetFileDownloadUrl, + downloadAction: ASC.Files.Utility.GetFileViewUrl, canDelete: function (fileId) { var entryObj = ASC.Files.UI.getEntryObject("file", fileId); if (entryObj.length <= 0) { diff --git a/web/studio/ASC.Web.Studio/Products/Files/js/editor.js b/web/studio/ASC.Web.Studio/Products/Files/js/editor.js index 949b795ca..31695cf41 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/js/editor.js +++ b/web/studio/ASC.Web.Studio/Products/Files/js/editor.js @@ -27,9 +27,6 @@ window.ASC.Files.Editor = (function () { var docServiceParams = {}; var configurationParams = null; - var trackEditTimeout = null; - var doStartEdit = true; - var init = function () { if (isInit === false) { isInit = true; @@ -37,15 +34,10 @@ window.ASC.Files.Editor = (function () { jq("body").css("overflow-y", "hidden"); - window.onbeforeunload = ASC.Files.Editor.finishEdit; - - ASC.Files.ServiceManager.bind(ASC.Files.ServiceManager.events.TrackEditFile, completeTrack); - ASC.Files.ServiceManager.bind(ASC.Files.ServiceManager.events.StartEdit, onStartEdit); ASC.Files.ServiceManager.bind(ASC.Files.ServiceManager.events.GetEditHistory, completeGetEditHistory); ASC.Files.ServiceManager.bind(ASC.Files.ServiceManager.events.GetDiffUrl, completeGetDiffUrl); ASC.Files.ServiceManager.bind(ASC.Files.ServiceManager.events.RestoreVersion, completeGetEditHistory); ASC.Files.ServiceManager.bind(ASC.Files.ServiceManager.events.GetMails, completeGetMails); - ASC.Files.ServiceManager.bind(ASC.Files.ServiceManager.events.StartMailMerge, completeStartMailMerge); ASC.Files.ServiceManager.bind(ASC.Files.ServiceManager.events.FileRename, completeRename); ASC.Files.ServiceManager.bind(ASC.Files.ServiceManager.events.GetUsers, completeGetUsers); ASC.Files.ServiceManager.bind(ASC.Files.ServiceManager.events.SendEditorNotify, completeSendEditorNotify); @@ -93,10 +85,8 @@ window.ASC.Files.Editor = (function () { if (ASC.Files.Editor.docServiceParams.linkToEdit) { eventsConfig.onRequestEditRights = ASC.Files.Editor.requestEditRightsEditor; } - eventsConfig.onError = ASC.Files.Editor.errorEditor; eventsConfig.onDocumentReady = ASC.Files.Editor.readyDocument; eventsConfig.onOutdatedVersion = ASC.Files.Editor.reloadPage; - eventsConfig.onInfo = ASC.Files.Editor.infoEditor; if (documentConfig.permissions.rename) { eventsConfig.onRequestRename = ASC.Files.Editor.rename; @@ -108,6 +98,7 @@ window.ASC.Files.Editor = (function () { if (ASC.Files.Constants.URL_MAIL_ACCOUNTS) { eventsConfig.onRequestEmailAddresses = ASC.Files.Editor.getMails; + //todo: remove after release 12.0 eventsConfig.onRequestStartMailMerge = ASC.Files.Editor.requestStartMailMerge; } @@ -136,7 +127,7 @@ window.ASC.Files.Editor = (function () { width: "100%", height: "100%", - type: typeConfig || "desktop", + type: typeConfig || ASC.Files.Editor.docServiceParams.defaultType || "desktop", documentType: documentTypeConfig, document: documentConfig, editorConfig: editorConfig, @@ -149,7 +140,7 @@ window.ASC.Files.Editor = (function () { try { var param = /(?:\?|\&)options=([^&]*)/g.exec(location.search); if (param && param[1]) { - return jq.parseJSON(decodeURIComponent(param[1])); + return JSON.parse(decodeURIComponent(param[1])); } } catch (e) { } @@ -175,10 +166,6 @@ window.ASC.Files.Editor = (function () { location.hash = ""; return; } - - if (ASC.Files.Editor.configurationParams && ASC.Files.Editor.configurationParams.editorConfig.mode === "edit") { - ASC.Files.Editor.trackEdit(); - } }; var readyDocument = function () { @@ -203,10 +190,6 @@ window.ASC.Files.Editor = (function () { docIsChangedTimeout = setTimeout(titleChange, 500); } } - - if (event.data) { - subscribeEdit(ASC.Files.ServiceManager.events.StartEdit); - } }; var updateDocumentTitle = function (changed) { @@ -215,25 +198,6 @@ window.ASC.Files.Editor = (function () { + ASC.Files.Editor.docServiceParams.pageTitlePostfix; }; - var subscribeEdit = function (event) { - if (!ASC.Files.Editor.configurationParams.editorConfig.callbackUrl - && doStartEdit) { - doStartEdit = false; - - ASC.Files.ServiceManager.startEdit(event, { - fileID: ASC.Files.Editor.docServiceParams.fileId, - shareLinkParam: ASC.Files.Editor.docServiceParams.shareLinkParam, - }); - - return true; - } - return false; - }; - - var errorEditor = function () { - ASC.Files.Editor.finishEdit(); - }; - var requestEditRightsEditor = function () { location.href = ASC.Files.Editor.docServiceParams.linkToEdit + ASC.Files.Editor.docServiceParams.shareLinkParam; }; @@ -243,8 +207,6 @@ window.ASC.Files.Editor = (function () { return; } - ASC.Files.Editor.finishEdit(); - ASC.Files.ServiceManager.getEditHistory(ASC.Files.ServiceManager.events.GetEditHistory, { fileID: ASC.Files.Editor.docServiceParams.fileId, @@ -303,20 +265,6 @@ window.ASC.Files.Editor = (function () { ASC.Files.ServiceManager.getMailAccounts(ASC.Files.ServiceManager.events.GetMails); }; - var requestStartMailMerge = function () { - if (!subscribeEdit(ASC.Files.ServiceManager.events.StartMailMerge)) { - completeStartMailMerge(); - } - }; - - var infoEditor = function (event) { - if (!!ASC.Files.Editor.configurationParams.editorConfig.callbackUrl - || event && event.data && event.data.mode == "view") { - clearTimeout(trackEditTimeout); - trackEditTimeout = null; - } - }; - var requestUsers = function () { ASC.Files.ServiceManager.getUsers(ASC.Files.ServiceManager.events.GetUsers, { fileId: ASC.Files.Editor.docServiceParams.fileId @@ -399,77 +347,6 @@ window.ASC.Files.Editor = (function () { location.reload(true); }; - var trackEdit = function () { - clearTimeout(trackEditTimeout); - trackEditTimeout = null; - if (ASC.Files.Editor.docServiceParams.editByUrl || ASC.Files.Editor.docServiceParams.thirdPartyApp) { - return; - } - if (!doStartEdit) { - return; - } - if (!!ASC.Files.Editor.configurationParams.editorConfig.callbackUrl) { - return; - } - - ASC.Files.ServiceManager.trackEditFile(ASC.Files.ServiceManager.events.TrackEditFile, - { - fileID: ASC.Files.Editor.docServiceParams.fileId, - tabId: ASC.Files.Editor.docServiceParams.tabId, - docKeyForTrack: ASC.Files.Editor.docServiceParams.docKeyForTrack, - shareLinkParam: ASC.Files.Editor.docServiceParams.shareLinkParam, - }); - }; - - var finishEdit = function () { - if (trackEditTimeout !== null && doStartEdit) { - clearTimeout(trackEditTimeout); - trackEditTimeout = null; - ASC.Files.ServiceManager.trackEditFile("FinishTrackEditFile", - { - fileID: ASC.Files.Editor.docServiceParams.fileId, - tabId: ASC.Files.Editor.docServiceParams.tabId, - docKeyForTrack: ASC.Files.Editor.docServiceParams.docKeyForTrack, - shareLinkParam: ASC.Files.Editor.docServiceParams.shareLinkParam, - finish: true, - ajaxsync: true - }); - } - }; - - var completeTrack = function (jsonData, params, errorMessage) { - clearTimeout(trackEditTimeout); - trackEditTimeout = null; - if (typeof errorMessage != "undefined") { - if (errorMessage == null || !errorMessage.length) { - setTimeout(function () { - ASC.Files.Editor.docEditor.showMessage("Connection is lost"); - }, 500); - } else { - ASC.Files.Editor.docEditor.showMessage(errorMessage || "Connection is lost"); - } - return; - } - - if (jsonData.key == true) { - trackEditTimeout = setTimeout(ASC.Files.Editor.trackEdit, 5000); - } else { - errorMessage = jsonData.value; - denyEditingRights(errorMessage); - } - }; - - var onStartEdit = function (jsonData, params, errorMessage) { - if (typeof errorMessage != "undefined") { - denyEditingRights(errorMessage); - return; - } - }; - - var denyEditingRights = function (message) { - ASC.Files.Editor.docEditor.denyEditingRights(message || "Connection is lost"); - }; - var checkMessageFromHash = function () { var regExpError = /^#error\/(\S+)?/; if (regExpError.test(location.hash)) { @@ -555,9 +432,6 @@ window.ASC.Files.Editor = (function () { error: errorMessage || "Connection is lost" }; } else { - clearTimeout(trackEditTimeout); - trackEditTimeout = null; - var currentVersion = params.setLast ? jsonData.length : ASC.Files.Editor.docServiceParams.fileVersion; @@ -626,17 +500,8 @@ window.ASC.Files.Editor = (function () { ASC.Files.Editor.docEditor.setEmailAddresses(data); }; - var completeStartMailMerge = function (jsonData, params, errorMessage) { - if (typeof ASC.Files.Editor.docEditor.processMailMerge != "function") { - if (typeof errorMessage != "undefined") { - ASC.Files.Editor.docEditor.showMessage(errorMessage || "Connection is lost"); - } else { - ASC.Files.Editor.docEditor.showMessage("Function is not available"); - } - return; - } - - ASC.Files.Editor.docEditor.processMailMerge(typeof errorMessage == "undefined", errorMessage); + var requestStartMailMerge = function () { + ASC.Files.Editor.docEditor.processMailMerge(true); }; var completeRename = function (xmlData, params, errorMessage) { @@ -685,15 +550,11 @@ window.ASC.Files.Editor = (function () { docServiceParams: docServiceParams, configurationParams: configurationParams, - trackEdit: trackEdit, - finishEdit: finishEdit, - //event readyEditor: readyEditor, readyDocument: readyDocument, documentStateChangeEditor: documentStateChangeEditor, requestEditRightsEditor: requestEditRightsEditor, - errorEditor: errorEditor, reloadPage: reloadPage, requestHistory: requestHistory, historyClose: historyClose, @@ -701,7 +562,6 @@ window.ASC.Files.Editor = (function () { restoreVersion: restoreVersion, getMails: getMails, requestStartMailMerge: requestStartMailMerge, - infoEditor: infoEditor, rename: rename, metaChange: metaChange, requestClose: requestClose, @@ -722,11 +582,18 @@ window.ASC.Files.Editor = (function () { $(function () { if (typeof DocsAPI === "undefined") { alert(ASC.Files.Constants.DocsAPIundefined); - ASC.Files.Editor.errorEditor(); return; } + if (ASC.Files.Editor.configurationParams + && (ASC.Files.Editor.configurationParams.document.fileType === "docxf" + || ASC.Files.Editor.configurationParams.document.fileType === "oform") + && DocsAPI.DocEditor.version().split(".")[0] < 7) { + alert("Please update Docs to version 7.0"); + return; + } + var fixPageCaching = function (delta) { if (location.hash.indexOf("reload") == -1) { var openingDateParse = Date.parse(ASC.Files.Editor.docServiceParams.openinigDate); diff --git a/web/studio/ASC.Web.Studio/Products/Files/js/eventhandler.js b/web/studio/ASC.Web.Studio/Products/Files/js/eventhandler.js index aef70471f..f0e524423 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/js/eventhandler.js +++ b/web/studio/ASC.Web.Studio/Products/Files/js/eventhandler.js @@ -56,6 +56,9 @@ window.ASC.Files.EventHandler = (function () { ASC.Files.ServiceManager.bind(ASC.Files.ServiceManager.events.Download, ASC.Files.EventHandler.onGetTasksStatuses); ASC.Files.ServiceManager.bind(ASC.Files.ServiceManager.events.TerminateTasks, ASC.Files.EventHandler.onGetTasksStatuses); ASC.Files.ServiceManager.bind(ASC.Files.ServiceManager.events.GetTasksStatuses, ASC.Files.EventHandler.onGetTasksStatuses); + + ASC.Files.ServiceManager.bind(ASC.Files.ServiceManager.events.ChangeExternalShareSettings, ASC.Files.EventHandler.onChangeExternalShareSettings); + ASC.Files.ServiceManager.bind(ASC.Files.ServiceManager.events.ChangeExternalShareSocialMediaSettings, ASC.Files.EventHandler.onChangeExternalShareSocialMediaSettings); } }; @@ -134,7 +137,11 @@ window.ASC.Files.EventHandler = (function () { } if (ASC.Files.CreateMenu) { - ASC.Files.CreateMenu.disableMenu(ASC.Files.UI.accessEdit()); + if (!ASC.Desktop && (ASC.Files.Folders.currentFolder.id == ASC.Files.Constants.FOLDER_ID_PRIVACY || ASC.Files.Folders.folderContainer == "privacy")) { + ASC.Files.CreateMenu.disableMenu(false); + } else { + ASC.Files.CreateMenu.disableMenu(ASC.Files.UI.accessEdit()); + } } ASC.Files.UI.stickContentHeader(); @@ -159,7 +166,7 @@ window.ASC.Files.EventHandler = (function () { ASC.Files.UI.removeEntryObject(replaceWith); } else { jq("#filesMainContent").prepend(htmlXML); - newFolderItems = jq("#filesMainContent .file-row[name=\"addRow\"]"); + newFolderItems = jq("#filesMainContent .file-row[name=\"addRow\"][data-id!=\"folder_0\"][data-id!=\"file_0\"]"); } } else { jq("#filesMainContent").append(htmlXML); @@ -377,58 +384,6 @@ window.ASC.Files.EventHandler = (function () { return true; }; - var onGetPrivacy = function () { - //fake duplicate from onGetFolderItems - - ASC.Files.Folders.currentFolder = { - access: ASC.Files.Constants.AceStatusEnum.Read, - entryId: ASC.Files.Constants.FOLDER_ID_PRIVACY, - entryType: "folder", - id: ASC.Files.Constants.FOLDER_ID_PRIVACY, - shareable: false, - title: ASC.Files.FilesJSResource.PrivacyRoom, - }; - ASC.Files.UI.setDocumentTitle(ASC.Files.Folders.currentFolder.title); - - jq("#filesMainContent") - .removeClass("myFiles") - .removeClass("corporateFiles") - .removeClass("shareformeFiles") - .removeClass("recentFiles") - .removeClass("favoritesFiles") - .removeClass("templatesFiles") - .removeClass("privacyFiles") - .removeClass("trashFiles") - .removeClass("projectFiles"); - - ASC.Files.Folders.folderContainer = "privacy"; - jq("#filesMainContent").addClass("privacyFiles"); - - if (ASC.Files.Tree) { - ASC.Files.Tree.pathParts = [ASC.Files.Constants.FOLDER_ID_PRIVACY]; - ASC.Files.Tree.updateTreePath(); - } - - if (ASC.Files.Filter) { - ASC.Files.Filter.disableFilter(); - } - - ASC.Files.UI.countTotal = 0; - - jq("#filesMainContent").empty(); - jq(document).scrollTop(0); - - jq("#emptyContainer_privacy .emptyScrBttnPnl").remove(); - ASC.Files.EmptyScreen.displayEmptyScreen(); - - ASC.Files.UI.lastSelectedEntry = null; - ASC.Files.Folders.eventAfter = null; - - if (ASC.Files.CreateMenu) { - ASC.Files.CreateMenu.disableMenu(false); - } - }; - var onGetItems = function (jsonData, params, errorMessage) { if (typeof errorMessage != "undefined") { ASC.Files.UI.displayInfoPanel(errorMessage, true); @@ -746,6 +701,10 @@ window.ASC.Files.EventHandler = (function () { jq("#contentVersions").addClass("version-edit"); } + if (fileData.deny_download) { + jq(".version-download").remove(); + } + jq("#contentVersions").addClass("version-highlight"); if (ASC.Files.Utility.CanWebView(fileData.title)) { @@ -864,7 +823,7 @@ window.ASC.Files.EventHandler = (function () { } }; - var onMoveItemsFinish = function (listData, isCopyOperation, countProcessed) { + var onMoveItemsFinish = function (listData, isCopyOperation, countProcessed, listSource) { var folderToId = ASC.Files.UI.parseItemId(listData[0]).entryId; listData = listData.slice(1); var listFromId = new Array(); @@ -932,8 +891,8 @@ window.ASC.Files.EventHandler = (function () { if (filesCount > 0) { var fileCountObj = folderToObj.find(".countFiles"); - - fileCountObj.html((parseInt(fileCountObj.html()) || 0) + filesCount); + var countOverwrites = ASC.Files.Folders.owerwriteManager.get(isCopyOperation, listSource); + fileCountObj.html((parseInt(fileCountObj.html()) || 0) + filesCount - countOverwrites); } if (listFromId.length > 0 && ASC.Files.Folders.currentFolder.id != folderToId) { @@ -1259,11 +1218,11 @@ window.ASC.Files.EventHandler = (function () { switch (data[i].operation) { case 0: //move - onMoveItemsFinish(listResult, false, data[i].processed); + onMoveItemsFinish(listResult, false, data[i].processed, listSource); break; case 1: //copy - onMoveItemsFinish(listResult, true, data[i].processed); + onMoveItemsFinish(listResult, true, data[i].processed, listSource); break; case 2: //delete @@ -1271,7 +1230,7 @@ window.ASC.Files.EventHandler = (function () { break; case 3: //download - if (listResult[0]) { + if (listResult[0] && data[i].error == null) { location.href = listResult[0]; } ASC.Files.Folders.bulkStatuses = false; @@ -1317,6 +1276,39 @@ window.ASC.Files.EventHandler = (function () { } }; + var onChangeExternalShareSettings = function (jsonData, params, errorMessage) { + if (typeof errorMessage != "undefined") { + ASC.Files.UI.displayInfoPanel(errorMessage, true); + return; + } + + var enable = jsonData === true; + jq("#cbxExternalShare").prop("checked", enable); + + if (enable) { + jq("#cbxExternalShareSocialMedia").prop("disabled", false).next().removeClass("gray-text"); + } else { + jq("#cbxExternalShareSocialMedia").prop("disabled", true).prop("checked", false).next().addClass("gray-text"); + } + + if (ASC.Files.Share) { + ASC.Files.Share.changeExternalShareSettings(); + } + }; + + var onChangeExternalShareSocialMediaSettings = function (jsonData, params, errorMessage) { + if (typeof errorMessage != "undefined") { + ASC.Files.UI.displayInfoPanel(errorMessage, true); + return; + } + + jq("#cbxExternalShareSocialMedia").prop("checked", jsonData === true); + + if (ASC.Files.Share) { + ASC.Files.Share.changeExternalShareSettings(); + } + }; + var onThumbnailError = function () { if (!jq.browser.msie) { this.parentElement.style.backgroundSize = "96px"; @@ -1328,7 +1320,6 @@ window.ASC.Files.EventHandler = (function () { init: init, onGetFolderItems: onGetFolderItems, - onGetPrivacy: onGetPrivacy, onGetItems: onGetItems, onGetFile: onGetFile, onCreateNewFile: onCreateNewFile, @@ -1349,6 +1340,9 @@ window.ASC.Files.EventHandler = (function () { onMoveFilesCheck: onMoveFilesCheck, onGetTasksStatuses: onGetTasksStatuses, + onChangeExternalShareSettings: onChangeExternalShareSettings, + onChangeExternalShareSocialMediaSettings: onChangeExternalShareSocialMediaSettings, + onThumbnailError: onThumbnailError }; })(); diff --git a/web/studio/ASC.Web.Studio/Products/Files/js/filechoice.js b/web/studio/ASC.Web.Studio/Products/Files/js/filechoice.js index 6eff8da04..f002e88c7 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/js/filechoice.js +++ b/web/studio/ASC.Web.Studio/Products/Files/js/filechoice.js @@ -17,14 +17,22 @@ window.ASC.Files.FileChoice = (function () { var isInit = false; + var isTriggered = false; - var init = function (folderId, onlyFolder, thirdParty, fromEditor, originForPost) { + var init = function (folderId, onlyFolder, thirdParty, fromEditor, originForPost, displayPrivacy) { if (fromEditor) { thirdParty = undefined; } if (isInit === false) { isInit = true; + if (!displayPrivacy && ASC.Files.FileSelector.fileSelectorTree) { + var privacyFolderData = ASC.Files.FileSelector.fileSelectorTree.getFolderData(ASC.Files.Constants.FOLDER_ID_PRIVACY); + if (privacyFolderData) { + privacyFolderData.entryObject.addClass("privacy-node"); + } + } + if (typeof thirdParty == "string") { if (thirdParty == "") { thirdParty = undefined; @@ -123,6 +131,12 @@ window.ASC.Files.FileChoice = (function () { window.parent.postMessage(message, originForPost); }; + jq(document).on("keyup", function (event) { + if (event.keyCode == 27) { + ASC.Files.FileSelector.onCancel(); + } + }); + if (!thirdParty) { callback(); } @@ -130,11 +144,18 @@ window.ASC.Files.FileChoice = (function () { }; var eventAfter = function () { + console.log("ASC.Files.FileChoice.eventAfter"); + isTriggered = true; + }; + + var isEventAfterTriggered = function () { + return isTriggered; }; return { init: init, - eventAfter:eventAfter + eventAfter: eventAfter, + isEventAfterTriggered: isEventAfterTriggered }; })(); diff --git a/web/studio/ASC.Web.Studio/Products/Files/js/filter.js b/web/studio/ASC.Web.Studio/Products/Files/js/filter.js index 08c00e7cf..4884b7da6 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/js/filter.js +++ b/web/studio/ASC.Web.Studio/Products/Files/js/filter.js @@ -35,8 +35,8 @@ window.ASC.Files.Filter = (function () { ASC.Files.Filter.advansedFilter = jq(".files-filter div") .advansedFilter(filterOptions) - .bind("setfilter", ASC.Files.Filter.setFilter) - .bind("resetfilter", ASC.Files.Filter.setFilter); + .on("setfilter", ASC.Files.Filter.setFilter) + .on("resetfilter", ASC.Files.Filter.setFilter); } }; @@ -426,12 +426,12 @@ window.ASC.Files.Filter = (function () { $(function () { ASC.Files.Filter.init(); - jq(".files-clear-filter").click(function () { + jq(".files-clear-filter").on("click", function () { ASC.Files.Filter.clearFilter(); return false; }); - jq("#filesWithoutSubfolders").click(function () { + jq("#filesWithoutSubfolders").on("click", function () { ASC.Files.Filter.setWithoutSubfolders(); return false; }); diff --git a/web/studio/ASC.Web.Studio/Products/Files/js/foldermanager.js b/web/studio/ASC.Web.Studio/Products/Files/js/foldermanager.js index 6bd85ffbb..f4878403e 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/js/foldermanager.js +++ b/web/studio/ASC.Web.Studio/Products/Files/js/foldermanager.js @@ -1,4 +1,4 @@ -/* +/* * * (c) Copyright Ascensio System Limited 2010-2021 * @@ -32,15 +32,6 @@ window.ASC.Files.Folders = (function () { /* Methods*/ var getFolderItems = function (isAppend, countAppend) { - if (ASC.Files.Folders.currentFolder.id == ASC.Files.Constants.FOLDER_ID_PRIVACY) { - if (!ASC.Desktop || !ASC.Desktop.encryptionSupport()) { - ASC.Files.EventHandler.onGetPrivacy(); - return; - } else { - jq("#privacyForDesktop").remove(); - } - } - var filterSettings = ASC.Files.Filter.getFilterSettings(); ASC.Files.ServiceManager.getFolderItems(ASC.Files.ServiceManager.events.GetFolderItems, { @@ -111,19 +102,27 @@ window.ASC.Files.Folders = (function () { } if (ASC.Files.Utility.CanWebView(fileTitle)) { - if (ASC.Files.Utility.MustConvert(fileTitle) && fileData.encrypted) { + if (ASC.Files.Utility.MustConvert(fileTitle) && (fileData.encrypted || ASC.Files.UI.denyDownload(fileData))) { forEdit = false; } return ASC.Files.Converter.checkCanOpenEditor(fileId, fileTitle, version, forEdit != false); } if (typeof ASC.Files.ImageViewer != "undefined" && ASC.Files.Utility.CanImageView(fileTitle)) { + if (ASC.Files.UI.denyDownload(fileData)) { + ASC.Files.UI.displayInfoPanel(ASC.Files.FilesJSResource.ErrorMassage_SecurityException, true); + return false; + } var hash = ASC.Files.ImageViewer.getPreviewHash(fileId); ASC.Files.Anchor.move(hash); return false; } if (typeof ASC.Files.MediaPlayer != "undefined" && ASC.Files.MediaPlayer.canPlay(fileTitle, true)) { + if (ASC.Files.UI.denyDownload(fileData)) { + ASC.Files.UI.displayInfoPanel(ASC.Files.FilesJSResource.ErrorMassage_SecurityException, true); + return false; + } hash = ASC.Files.MediaPlayer.getPlayHash(fileId); ASC.Files.Anchor.move(hash); return false; @@ -149,10 +148,15 @@ window.ASC.Files.Folders = (function () { listObj = fileObj; } else { - listObj = jq("#filesMainContent .file-row:not(.checkloading):not(.new-folder):not(.new-file):has(.checkbox input:checked)"); + if (toFavorite) { + listObj = jq("#filesMainContent .file-row:not(.checkloading):not(.new-file):has(.checkbox input:checked):not(.on-favorite)"); + } else { + listObj = jq("#filesMainContent .file-row:not(.checkloading):not(.new-file):has(.checkbox input:checked).on-favorite"); + } } var fileIds = []; + var folderIds = []; var isFavorite; listObj = listObj.filter(function () { var entryRowData = ASC.Files.UI.getObjectData(this); @@ -170,6 +174,18 @@ window.ASC.Files.Folders = (function () { return true; } } + + if (entryRowType == "folder") { + var curIsFavorite = entryObject.hasClass("on-favorite"); + + if (toFavorite !== true || !curIsFavorite) { + isFavorite = curIsFavorite; + var entryRowId = entryRowData.entryId; + folderIds.push(entryRowId); + + return true; + } + } return false; }); @@ -179,7 +195,8 @@ window.ASC.Files.Folders = (function () { method( {}, { - fileIds: fileIds + fileIds: fileIds, + folderIds: folderIds }, function () { listObj.toggleClass("on-favorite", !isFavorite); @@ -358,13 +375,13 @@ window.ASC.Files.Folders = (function () { var emptyFolder = { folder: - { - id: "0", - title: ASC.Files.FilesJSResource.TitleNewFolder, - access: 0, - shared: false, - isnew: 0, - } + { + id: "0", + title: ASC.Files.FilesJSResource.TitleNewFolder, + access: 0, + shared: false, + isnew: 0, + } }; var stringData = ASC.Files.Common.jsonToXml(emptyFolder); @@ -397,9 +414,9 @@ window.ASC.Files.Folders = (function () { newContainer.insertAfter(obj); newContainer.show(); obj.hide(); - newContainer.focus(); + newContainer.trigger("focus"); if (!jq.browser.mobile) { - newContainer.select(); + newContainer.trigger("select"); } ASC.Files.UI.checkCharacter(newContainer); @@ -424,9 +441,9 @@ window.ASC.Files.Folders = (function () { }; newFolderObj.append(getActionHtml()); - newFolderObj.find(".name-aplly").click(saveFolder); + newFolderObj.find(".name-aplly").on("click", saveFolder); - jq("#promptCreateFolder").bind("keydown", function (event) { + jq("#promptCreateFolder").on("keydown", function (event) { if (jq("#promptCreateFolder").length == 0) { return; } @@ -589,17 +606,17 @@ window.ASC.Files.Folders = (function () { newContainer.addClass("textEdit input-rename"); newContainer.val(titleNewDoc); newContainer.insertAfter(obj); - newContainer.show().focus(); + newContainer.show().trigger("focus"); if (!jq.browser.mobile) { - newContainer.select(); + newContainer.trigger("select"); } ASC.Files.UI.checkCharacter(newContainer); newFileObj.append(getActionHtml()); - newFileObj.find(".name-aplly").click(saveFile); + newFileObj.find(".name-aplly").on("click", saveFile); - jq("#promptCreateFile").bind("keydown", function (event) { + jq("#promptCreateFile").on("keydown", function (event) { if (jq("#promptCreateFile").length == 0) { return; } @@ -616,7 +633,7 @@ window.ASC.Files.Folders = (function () { } }); - jq("#promptCreateFile").bind("keypress", function (event) { + jq("#promptCreateFile").on("keypress", function (event) { if (jq("#promptCreateFile").length == 0) { return; } @@ -664,10 +681,10 @@ window.ASC.Files.Folders = (function () { var replaceFileStream = function (fileId, fileTitle, file, encrypted, winEditor, forcesave) { Teamlab.updateFileStream({ - fileId: fileId, - encrypted: !!encrypted, - forcesave: !!forcesave, - }, + fileId: fileId, + encrypted: !!encrypted, + forcesave: !!forcesave, + }, file, { success: function () { @@ -713,7 +730,7 @@ window.ASC.Files.Folders = (function () { var lenExt = 0; if (jq("#promptRename").length != 0) { - jq("#filesMainContent .file-row.row-rename .name-cancel").click(); + jq("#filesMainContent .file-row.row-rename .name-cancel").trigger("click"); } if (entryObj.find("#contentVersions").length) { @@ -742,9 +759,9 @@ window.ASC.Files.Folders = (function () { newContainer.addClass("textEdit input-rename"); newContainer.val(entryTitle); newContainer.insertAfter(entryObj.find(".entry-title .name a")); - newContainer.show().focus(); + newContainer.show().trigger("focus"); if (!jq.browser.mobile) { - newContainer.select(); + newContainer.trigger("select"); } ASC.Files.UI.checkCharacter(newContainer); @@ -795,11 +812,11 @@ window.ASC.Files.Folders = (function () { }; entryObj.append(getActionHtml()); - entryObj.find(".name-aplly").click(saveRename); + entryObj.find(".name-aplly").on("click", saveRename); entryObj.removeClass("row-selected"); - jq("#promptRename").bind("keydown", function (event) { + jq("#promptRename").on("keydown", function (event) { if (jq("#promptRename").length === 0) { return; } @@ -932,9 +949,9 @@ window.ASC.Files.Folders = (function () { newContainer.val(comment); commentObj.append(newContainer); newContainer.show(); - newContainer.focus(); + newContainer.trigger("focus"); if (!jq.browser.mobile) { - newContainer.select(); + newContainer.trigger("select"); } var saveComment = function () { @@ -953,8 +970,8 @@ window.ASC.Files.Folders = (function () { }; commentObj.append(getActionHtml()); - commentObj.find(".name-aplly").click(saveComment); - jq("#promptVersionComment").bind("keydown", function (event) { + commentObj.find(".name-aplly").on("click", saveComment); + jq("#promptVersionComment").on("keydown", function (event) { if (jq("#promptVersionComment").length == 0) { return; } @@ -1029,7 +1046,7 @@ window.ASC.Files.Folders = (function () { jq("#overwriteMessage").html(message); - jq("#buttonConfirmResolve").unbind("click").click(function () { + jq("#buttonConfirmResolve").off("click").on("click", function () { var resolve = jq(".overwrite-resolve input:checked").val(); if (resolve == ASC.Files.Constants.ConflictResolveType.Skip) { @@ -1045,17 +1062,24 @@ window.ASC.Files.Folders = (function () { PopupKeyUpActionProvider.CloseDialogAction = ""; PopupKeyUpActionProvider.CloseDialog(); + + isCopyOperation = isCopyOperation == true; + + if (resolve == ASC.Files.Constants.ConflictResolveType.Overwrite) { + owerwriteManager.set(isCopyOperation, listData.entry, data.length); + } + ASC.Files.ServiceManager.moveItems(ASC.Files.ServiceManager.events.MoveItems, { folderToId: folderId, resolve: resolve, - isCopyOperation: (isCopyOperation == true), + isCopyOperation: isCopyOperation, doNow: true }, { stringList: listData }); }); - jq("#buttonCancelOverwrite").unbind("click").click(function () { + jq("#buttonCancelOverwrite").off("click").on("click", function () { for (i = 0; i < listData.entry.length; i++) { var itemId = ASC.Files.UI.parseItemId(listData.entry[i]); ASC.Files.UI.blockObjectById(itemId.entryType, itemId.entryId, false, null, true); @@ -1068,10 +1092,46 @@ window.ASC.Files.Folders = (function () { ASC.Files.UI.blockUI("#confirmOverwriteFiles", 465); - PopupKeyUpActionProvider.EnterAction = "jq(\"#buttonConfirmResolve\").click();"; - PopupKeyUpActionProvider.CloseDialogAction = "jq(\"#buttonCancelOverwrite\").click();"; + PopupKeyUpActionProvider.EnterAction = "jq(\"#buttonConfirmResolve\").trigger('click');"; + PopupKeyUpActionProvider.CloseDialogAction = "jq(\"#buttonCancelOverwrite\").trigger('click');"; }; + var owerwriteManager = function () { + function strHash(str) { + var hash = 0, i, l; + if (str.length == 0) { + return hash; + } + for (i = 0, l = str.length; i < l; i++) { + hash = ((hash << 5) - hash) + str.charCodeAt(i); + hash |= 0; + } + return hash; + } + + function getKey(isCopyOperation, entries) { + var sortedArrayCopy = entries.concat().sort(); + return (isCopyOperation ? "copy" : "move") + strHash(sortedArrayCopy.toString()); + } + + function get(isCopyOperation, entries) { + var key = getKey(isCopyOperation, entries); + var value = +sessionStorage.getItem(key); + sessionStorage.removeItem(key) + return value; + } + + function set(isCopyOperation, entries, value) { + var key = getKey(isCopyOperation, entries); + sessionStorage.setItem(key, value) + } + + return { + get: get, + set: set + } + }(); + var curItemFolderMoveTo = function (folderToId, folderToTitle, pathDest, confirmedThirdParty) { ASC.Files.Actions.hideAllActionPanels(); if (folderToId === ASC.Files.Folders.currentFolder.entryId) { @@ -1177,12 +1237,28 @@ window.ASC.Files.Folders = (function () { }; var changeOwnerDialog = function (entryData) { - jq("#ownerSelector .change-owner-selector").text(entryData.create_by); - jq("#ownerSelector") - .attr("data-id", entryData.create_by_id) - .useradvancedSelector("reset") - .useradvancedSelector("disable", [entryData.create_by_id]); + var ownerSelectorObj = jq("#ownerSelector").attr("data-id", entryData.create_by_id); + + var ownerSelectorInnerObj = ownerSelectorObj.find(".change-owner-selector").text(entryData.create_by); + + if (!ownerSelectorObj.data().useradvancedSelector) { + ownerSelectorObj.useradvancedSelector({ + inPopup: true, + itemsSelectedIds: [entryData.create_by_id], + canadd: false, + showGroups: true, + onechosen: true, + withGuests: false, + }) + .on("showList", function (event, item) { + jq("#buttonSaveChangeOwner").removeClass("disable"); + ownerSelectorInnerObj.html(item.title); + ownerSelectorObj.attr("data-id", item.id); + }); + } else { + ownerSelectorObj.useradvancedSelector("select", [entryData.create_by_id]); + } jq("#changeOwnerTitle").text(entryData.title); jq("#buttonSaveChangeOwner").addClass("disable"); @@ -1194,13 +1270,13 @@ window.ASC.Files.Folders = (function () { ASC.Files.Folders.changeOwner([entryData.entryType + "_" + entryData.entryId], userId); }); - PopupKeyUpActionProvider.EnterAction = "jq(\"#buttonSaveChangeOwner\").click();"; + PopupKeyUpActionProvider.EnterAction = "jq(\"#buttonSaveChangeOwner\").trigger('click');"; ASC.Files.UI.blockUI("#changeOwner", 420); }; var changeOwner = function (entries, userId) { - var data = {entry: entries}; + var data = { entry: entries }; ASC.Files.ServiceManager.changeOwner(ASC.Files.ServiceManager.events.ChangeOwner, { @@ -1208,7 +1284,7 @@ window.ASC.Files.Folders = (function () { userId: userId, parentFolderID: ASC.Files.Folders.currentFolder.id, }, - {stringList: data}); + { stringList: data }); }; var emptyTrash = function () { @@ -1221,23 +1297,42 @@ window.ASC.Files.Folders = (function () { ASC.Files.UI.checkSelectAll(true); jq("#confirmRemoveText").html(ASC.Files.FilesJSResource.ConfirmEmptyTrash); + jq("#confirmRemoveList").hide(); + jq("#confirmRemoveTextToContinue").hide(); + jq("#confirmRemoveSharpBoxTextDescription").hide(); + jq("#confirmRemoveTextDescription").show(); - jq("#removeConfirmBtn").unbind("click").click(function () { + jq("#removeConfirmBtn").off("click").on("click", function () { PopupKeyUpActionProvider.CloseDialog(); ASC.Files.ServiceManager.emptyTrash(ASC.Files.ServiceManager.events.EmptyTrash, { doNow: true }); }); ASC.Files.UI.blockUI("#confirmRemove", 420); - PopupKeyUpActionProvider.EnterAction = "jq(\"#removeConfirmBtn\").click();"; + PopupKeyUpActionProvider.EnterAction = "jq(\"#removeConfirmBtn\").trigger('click');"; }; var deleteItem = function (entryType, entryId, successfulDeletion) { ASC.Files.Actions.hideAllActionPanels(); - var caption = ASC.Files.FilesJSResource.ConfirmRemoveList; + var data = ASC.Files.UI.autoCleanUpSetting.gap; + var settingAutoCleanUp = null; + switch (data) { + case -1: settingAutoCleanUp = null; break; + case 1: settingAutoCleanUp = ASC.Files.FilesJSResource.DateOneWeek; break; + case 2: settingAutoCleanUp = ASC.Files.FilesJSResource.DateTwoWeeks; break; + case 3: settingAutoCleanUp = ASC.Files.FilesJSResource.DateOneMonth; break; + case 4: settingAutoCleanUp = ASC.Files.FilesJSResource.DateTwoMonths; break; + case 5: settingAutoCleanUp = ASC.Files.FilesJSResource.DateThreeMonths; break; + } + var folderContainer = ASC.Files.Folders.folderContainer; + var isThirdParty = ASC.Files.ThirdParty && ASC.Files.ThirdParty.isThirdParty(); + var isAutoCleanUpOn = settingAutoCleanUp != null && folderContainer != "trash" && folderContainer != "privacy" && !isThirdParty; + var caption = isAutoCleanUpOn + ? ASC.Files.FilesJSResource.ConfirmRemoveListPermanently.format(settingAutoCleanUp) + : ASC.Files.FilesJSResource.ConfirmRemoveList; var list = new Array(); if (entryType && entryId) { @@ -1274,10 +1369,19 @@ window.ASC.Files.Folders = (function () { } if (list.length == 1) { - if (list[0].entryType == "file") { - caption = ASC.Files.FilesJSResource.ConfirmRemoveFile; - } else { - caption = ASC.Files.FilesJSResource.ConfirmRemoveFolder; + if (isAutoCleanUpOn) { + if (list[0].entryType == "file") { + caption = ASC.Files.FilesJSResource.ConfirmRemoveFilePermanently.format(settingAutoCleanUp); + } else { + caption = ASC.Files.FilesJSResource.ConfirmRemoveFolderPermanently.format(settingAutoCleanUp); + } + } + else { + if (list[0].entryType == "file") { + caption = ASC.Files.FilesJSResource.ConfirmRemoveFile; + } else { + caption = ASC.Files.FilesJSResource.ConfirmRemoveFolder; + } } } @@ -1287,6 +1391,12 @@ window.ASC.Files.Folders = (function () { for (var i = 0; i < list.length; i++) { var entryRowTitle = ASC.Files.UI.getEntryTitle(list[i].entryType, list[i].entryId); if (list[i].entryType == "file") { + if (entryRowTitle == null && ASC.Files.ChunkUploads) { + var uploadData = ASC.Files.ChunkUploads.getUploadDataByFileId(list[i].entryId, 1); + if (uploadData) { + entryRowTitle = uploadData.name; + } + } textFile += strHtml.format(entryRowTitle, list[i].entryType, list[i].entryId); } else { textFolder += strHtml.format(entryRowTitle, list[i].entryType, list[i].entryId); @@ -1306,11 +1416,15 @@ window.ASC.Files.Folders = (function () { } var checkRemoveItem = function () { - jq("#confirmRemoveList .confirm-remove-folders-count").text(jq("#confirmRemoveList dd.confirm-remove-folders :checked").length); - jq("#confirmRemoveList .confirm-remove-files-count").text(jq("#confirmRemoveList dd.confirm-remove-files :checked").length); + jq("#confirmRemoveList").show(); + var foldersCount = jq("#confirmRemoveList dd.confirm-remove-folders :checked").length; + jq("#confirmRemoveList .confirm-remove-folders-count").text(foldersCount); + var filesCount = jq("#confirmRemoveList dd.confirm-remove-files :checked").length; + jq("#confirmRemoveList .confirm-remove-files-count").text(filesCount); + jq("#removeConfirmBtn").toggleClass("disable", foldersCount + filesCount == 0) }; checkRemoveItem(); - jq("#confirmRemoveList dd [type='checkbox']").change(checkRemoveItem); + jq("#confirmRemoveList dd [type='checkbox']").on("change", checkRemoveItem); var mustConfirm = jq(".files-content-panel").attr("data-deleteConfirm"); if (jq("#cbxDeleteConfirm").length) { @@ -1331,7 +1445,16 @@ window.ASC.Files.Folders = (function () { jq("#confirmRemoveTextDescription").hide(); } + if (isAutoCleanUpOn) { + jq("#confirmRemoveTextToContinue").show(); + } + else { + jq("#confirmRemoveTextToContinue").hide(); + } + var doDeletion = function () { + if (jq(this).hasClass("disable")) return; + PopupKeyUpActionProvider.CloseDialog(); var data = {}; @@ -1356,11 +1479,11 @@ window.ASC.Files.Folders = (function () { }; if (mustConfirm) { - jq("#removeConfirmBtn").unbind("click").click(doDeletion); + jq("#removeConfirmBtn").off("click").on("click", doDeletion); ASC.Files.UI.blockUI("#confirmRemove", 420); - PopupKeyUpActionProvider.EnterAction = "jq(\"#removeConfirmBtn\").click();"; + PopupKeyUpActionProvider.EnterAction = "jq(\"#removeConfirmBtn\").trigger('click');"; } else { doDeletion(); } @@ -1427,6 +1550,7 @@ window.ASC.Files.Folders = (function () { versionComplete: versionComplete, showOverwriteMessage: showOverwriteMessage, + owerwriteManager: owerwriteManager, curItemFolderMoveTo: curItemFolderMoveTo, createDuplicate: createDuplicate, @@ -1473,21 +1597,21 @@ window.ASC.Files.Folders = (function () { (function ($) { $(function () { - jq("#pageNavigatorHolder a").click(function () { + jq("#pageNavigatorHolder a").on("click", function () { ASC.Files.Folders.showMore(); return false; }); - jq("#topNewFolder a").click(function () { + jq("#topNewFolder a").on("click", function () { ASC.Files.Folders.createFolder(); }); - jq("#buttonDelete, #mainDelete").click(function () { + jq("#buttonDelete, #mainDelete").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); ASC.Files.Folders.deleteItem(); }); - jq("#buttonEmptyTrash, #mainEmptyTrash").click(function () { + jq("#buttonEmptyTrash, #mainEmptyTrash").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); ASC.Files.Folders.emptyTrash(); }); @@ -1497,12 +1621,12 @@ window.ASC.Files.Folders = (function () { ASC.Files.Folders.download(); }); - jq("#filesSelectAllCheck").click(function (e) { + jq("#filesSelectAllCheck").on("click", function (e) { e.stopPropagation(); ASC.Files.Actions.hideAllActionPanels(); ASC.Files.UI.checkSelectAll(jq("#filesSelectAllCheck").prop("checked") == true); - jq(this).blur(); + jq(this).trigger("blur"); }); jq("#filesMainContent").on("click", ".file-lock", function () { @@ -1539,12 +1663,12 @@ window.ASC.Files.Folders = (function () { return false; }); - jq("#buttonRemoveFavorite, #mainRemoveFavorite, #buttonAddFavorite").click(function () { + jq("#buttonRemoveFavorite, #mainRemoveFavorite, #buttonAddFavorite").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); ASC.Files.Folders.toggleFavorite(null, this.id == "buttonAddFavorite"); }); - jq("#buttonRemoveTemplate, #mainRemoveTemplate").click(function () { + jq("#buttonRemoveTemplate, #mainRemoveTemplate").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); ASC.Files.Folders.toggleTemplate(); }); @@ -1678,12 +1802,12 @@ window.ASC.Files.Folders = (function () { jq(this).closest(".overwrite-resolve").addClass("selected"); }); - jq("#overwriteListShow").click(function () { + jq("#overwriteListShow").on("click", function () { jq("#overwriteList, #overwriteListHide").show(); jq("#overwriteListShow").hide(); }); - jq("#overwriteListHide").click(function () { + jq("#overwriteListHide").on("click", function () { jq("#overwriteListShow").show(); jq("#overwriteList, #overwriteListHide").hide(); }); @@ -1696,7 +1820,7 @@ window.ASC.Files.Folders = (function () { }); jq("#cbxDownloadTarGz").on("change", function () { - var value = jq(this).prop("checked") == true; + var value = jq(this).prop("checked"); Teamlab.filesDownloadTarGz(value, { success: function (_, data) { jq("#convertFileZip").html(data.title.format("", "")); @@ -1709,6 +1833,45 @@ window.ASC.Files.Folders = (function () { return false; }); + jq("#cbxAutomaticallyCleanUp").on("change", function () { + var value = jq(this).prop("checked"); + var dataValue = jq("#selectGapToAutoCleanUp").val(); + Teamlab.changeAutomaticallyCleanUp(value, value ? dataValue : -1, { + success: function (_, data) { + ASC.Files.UI.autoCleanUpSetting = data; + if (data.isAutoCleanUp) { + jq("#selectGapToAutoCleanUp").removeClass("disabled"); + jq("#selectGapToAutoCleanUp").tlcombobox(true); + } + else { + jq("#selectGapToAutoCleanUp").tlcombobox(false); + } + }, + error: function (params, error) { + ASC.Files.UI.displayInfoPanel(error[0], true); + } + }); + return false; + }); + + jq("#selectGapToAutoCleanUp").on("change", function () { + jq("#selectGapToAutoCleanUp").tlcombobox(); + var value = jq("#cbxAutomaticallyCleanUp").prop("checked"); + var dataValue = jq("#selectGapToAutoCleanUp").val(); + Teamlab.changeAutomaticallyCleanUp(value, value ? dataValue : -1, { + success: function (_, data) { + ASC.Files.UI.autoCleanUpSetting = data; + }, + error: function (params, error) { + ASC.Files.UI.displayInfoPanel(error[0], true); + } + }); + + return false; + }); + + jq("#selectGapToAutoCleanUp").tlcombobox(); + jq("#cbxFavorites").on("change", function () { var value = jq(this).prop("checked") == true; @@ -1759,26 +1922,23 @@ window.ASC.Files.Folders = (function () { return false; }); - jq("#ownerSelector") - .useradvancedSelector({ - inPopup: true, - itemsChoose: [], - canadd: false, - showGroups: true, - onechosen: true, - withGuests: false, - }) - .on("showList", function (event, item) { - jq(this) - .attr("data-id", item.id) - .find(".change-owner-selector").html(item.title); - jq("#buttonSaveChangeOwner").removeClass("disable"); - - jq("#ownerSelector").useradvancedSelector("reset"); - jq("#ownerSelector").useradvancedSelector("disable", [item.id]); + jq("#defaultAccessRightsSetting input").on("change", function () { + var data = []; + jq("#defaultAccessRightsSetting input:checked").each(function () { + data.push(+this.value); }); - jq(".update-if-exist").change(function () { + Teamlab.filesChangeDafaultAccessRightsSetting(data, { + success: function (_, res) {}, + error: function (_, error) { + ASC.Files.UI.displayInfoPanel(error[0], true); + } + }); + + return false; + }); + + jq(".update-if-exist").on("change", function () { ASC.Files.Folders.updateIfExist(this); }); @@ -1796,23 +1956,37 @@ window.ASC.Files.Folders = (function () { return false; }); + jq("#cbxExternalShare").on("change", function changeExternalShareSettings() { + ASC.Files.Actions.hideAllActionPanels(); + var enable = jq(this).prop("checked") === true; + ASC.Files.ServiceManager.changeExternalShareSettings(ASC.Files.ServiceManager.events.ChangeExternalShareSettings, { enable }); + return false; + }); + + jq("#cbxExternalShareSocialMedia").on("change", function changeExternalShareSocialMediaSettings() { + ASC.Files.Actions.hideAllActionPanels(); + var enable = jq(this).prop("checked") === true; + ASC.Files.ServiceManager.changeExternalShareSocialMediaSettings(ASC.Files.ServiceManager.events.ChangeExternalShareSocialMediaSettings, { enable }); + return false; + }); + if (typeof ASC.Files.Share == "undefined") { jq("#files_shareaccess_folders, #filesShareAccess,\ #filesUnsubscribe, #foldersUnsubscribe").remove(); } - jq(window).scroll(function () { + jq(window).on("scroll", function () { ASC.Files.UI.stickContentHeader(); ASC.Files.UI.trackShowMore(false); return true; }); - jq(".mainPageContent").scroll(function () { + jq(".mainPageContent").on("scroll", function () { ASC.Files.UI.trackShowMore(true); return true; }); - jq("#filesTemplateList").scroll(function () { + jq("#filesTemplateList").on("scroll", function () { ASC.Files.UI.trackShowMoreTemplates(); }); diff --git a/web/studio/ASC.Web.Studio/Products/Files/js/markernew.js b/web/studio/ASC.Web.Studio/Products/Files/js/markernew.js index e502fcc6f..631cdd91b 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/js/markernew.js +++ b/web/studio/ASC.Web.Studio/Products/Files/js/markernew.js @@ -26,7 +26,7 @@ window.ASC.Files.Marker = (function () { ASC.Files.ServiceManager.bind(ASC.Files.ServiceManager.events.GetNews, ASC.Files.Marker.onGetNews); - jq(document).click(function (event) { + jq(document).on("click", function (event) { jq.dropdownToggle().registerAutoHide(event, ".is-new", "#filesNewsPanel"); }); } @@ -292,7 +292,7 @@ window.ASC.Files.Marker = (function () { ASC.Files.Marker.markAsRead(); }); - jq("#filesNewsMarkRead").click(function () { + jq("#filesNewsMarkRead").on("click", function () { ASC.Files.Actions.hideAllActionPanels(); var folderId = jq("#filesNewsPanel").attr("data-id"); ASC.Files.Marker.markAsRead(folderId); diff --git a/web/studio/ASC.Web.Studio/Products/Files/js/mousemanager.js b/web/studio/ASC.Web.Studio/Products/Files/js/mousemanager.js index 7d00f89d5..40f37afc4 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/js/mousemanager.js +++ b/web/studio/ASC.Web.Studio/Products/Files/js/mousemanager.js @@ -69,16 +69,13 @@ window.ASC.Files.Mouse = (function () { } if (!jq(target).is( - "nav,\ - .nav-content,\ - .page-menu,\ - main,\ + "main,\ .filter-content,\ - .mainPageTableSidePanel,\ .mainPageContent,\ .files-content-panel,\ #mainContentHeader,\ #mainContent,\ + .mainPageContent .layout-bottom-spacer,\ #filesMainContent.thumbnails,\ .file-row:not(.row-rename):not(.row-selected),\ .file-row:not(.row-rename):not(.row-selected) *")) { @@ -110,6 +107,12 @@ window.ASC.Files.Mouse = (function () { mainContentArea.bottom = mainContentArea.top + jq("#filesMainContent")[0].offsetHeight; mainContentArea.documentWidth = jq(document).width(); mainContentArea.documentHeight = jq(document).height(); + + var mainOffset = jq("main").offset(); + mainContentArea.minX = mainOffset.left; + mainContentArea.minY = mainOffset.top; + + mainContentArea.minVisibleY = jq(".mainPageContent").offset().top; }; var intersectionLines = function (line1, line2) { @@ -165,9 +168,9 @@ window.ASC.Files.Mouse = (function () { var windowFix = (jq.browser.msie && jq.browser.version < 9 ? jq("body") : jq(window)); windowFix - .unbind("mousemove.MouseSelect mouseup.MouseSelect") - .bind("mousemove.MouseSelect", ASC.Files.Mouse.continueSelecting) - .bind("mouseup.MouseSelect", ASC.Files.Mouse.finishSelecting); + .off("mousemove.MouseSelect mouseup.MouseSelect") + .on("mousemove.MouseSelect", ASC.Files.Mouse.continueSelecting) + .on("mouseup.MouseSelect", ASC.Files.Mouse.finishSelecting); if (typeof SmallChat != "undefined" && SmallChat.minimizeAllWindowsIfLoseFocus) { SmallChat.minimizeAllWindowsIfLoseFocus(e, target); @@ -185,8 +188,8 @@ window.ASC.Files.Mouse = (function () { } var selectDelta = 2; - var posXnew = Math.min(e.pageX, mainContentArea.documentWidth - selectDelta); - var posYnew = Math.min(e.pageY, mainContentArea.documentHeight - selectDelta); + var posXnew = Math.max(Math.min(e.pageX, mainContentArea.documentWidth - selectDelta), mainContentArea.minX); + var posYnew = Math.max(Math.min(e.pageY, mainContentArea.documentHeight - selectDelta), mainContentArea.minY); var width = Math.abs(posXnew - ASC.Files.Mouse.mouseSelector.startX); var height = Math.abs(posYnew - ASC.Files.Mouse.mouseSelector.startY); @@ -229,7 +232,9 @@ window.ASC.Files.Mouse = (function () { for (var i = itemsCount; i; i--) { itemObj = ASC.Files.Mouse.mouseSelector.entryItems[itemsCount - i]; if (intersectionRectangles(itemObj, selectObj)) { - selectionChanged = ASC.Files.UI.selectRow(itemObj.entryObj, true) || selectionChanged; + if (posYnew >= mainContentArea.minVisibleY && itemObj.bottom >= mainContentArea.minVisibleY) { + selectionChanged = ASC.Files.UI.selectRow(itemObj.entryObj, true) || selectionChanged; + } } else { if (!e.ctrlKey) { selectionChanged = ASC.Files.UI.selectRow(itemObj.entryObj, false) || selectionChanged; @@ -268,19 +273,20 @@ window.ASC.Files.Mouse = (function () { ASC.Files.Mouse.disableHover = false; var windowFix = (jq.browser.msie && jq.browser.version < 9 ? jq("body") : jq(window)); - windowFix.unbind("mousemove.MouseSelect mouseup.MouseSelect"); + windowFix.off("mousemove.MouseSelect mouseup.MouseSelect"); jq("body").removeClass("select-action"); }; var highlightFolderTo = function (cssClass, withoutTrash) { - var listArea = - [ - "#treeViewContainer li:not(.access-read) > a", - ".to-parent-folder" - ]; - if (ASC.Files.Folders.currentFolder.id != ASC.Files.Constants.FOLDER_ID_TRASH) { - listArea.push("#filesMainContent .folder-row:not(.checkloading):not(.new-folder):not(.error-entry):not(.row-selected)"); + var listArea = ["#treeViewContainer li:not(.access-read) > a"]; + + if (ASC.Files.Folders.folderContainer != "privacy") { + listArea.push(".to-parent-folder"); + + if (ASC.Files.Folders.currentFolder.id != ASC.Files.Constants.FOLDER_ID_TRASH) { + listArea.push("#filesMainContent .folder-row:not(.checkloading):not(.new-folder):not(.error-entry):not(.row-selected)"); + } } var searchArea = listArea.join(","); @@ -364,17 +370,36 @@ window.ASC.Files.Mouse = (function () { ASC.Files.Mouse.moveToY = e.pageY; jq("body") - .unbind("mouseout.MouseMove mousemove.MouseMove keyup.MouseMove keydown.MouseMove") - .bind("mouseout.MouseMove mousemove.MouseMove", ASC.Files.Mouse.beginMoveTo); + .off("mouseout.MouseMove mousemove.MouseMove keyup.MouseMove keydown.MouseMove") + .on("mouseout.MouseMove mousemove.MouseMove", ASC.Files.Mouse.beginMoveTo); return true; }; + var movingData = null; + + var initMovingData = function () { + movingData = { + entries: jq("#filesMainContent .file-row:has(.checkbox input:checked)"), + isCopyTo: !ASC.Files.UI.accessEdit() || !ASC.Files.UI.accessDelete() + }; + + if (!movingData.isCopyTo && ASC.Files.ThirdParty && !ASC.Files.ThirdParty.isThirdParty()) { + movingData.entries.each(function () { + var entryData = ASC.Files.UI.getObjectData(this); + if (ASC.Files.ThirdParty.isThirdParty(entryData)) { + movingData.isCopyTo = true; + return false; + } + }); + } + } + var beginMoveTo = function (e) { e = jq.fixEvent(e); if (!(e.button == 0 || (jq.browser.msie && e.button == 1)) || ASC.Files.Mouse.mouseBtn == false) { - jq("body").unbind("mouseout.MouseMove mousemove.MouseMove keyup.MouseMove keydown.MouseMove"); + jq("body").off("mouseout.MouseMove mousemove.MouseMove keyup.MouseMove keydown.MouseMove"); return false; } @@ -385,6 +410,8 @@ window.ASC.Files.Mouse = (function () { ASC.Files.Actions.hideAllActionPanels(); + initMovingData(); + jq("#contentVersions").removeClass("version-highlight"); jq("#filesMainContent .row-hover").removeClass("row-hover"); jq(".may-row-to").removeClass("may-row-to"); @@ -394,10 +421,10 @@ window.ASC.Files.Mouse = (function () { ASC.Files.Mouse.highlightFolderTo("may-row-to"); jq("body") - .unbind("mouseout.MouseMove mousemove.MouseMove mouseup.MouseMove keyup.MouseMove keydown.MouseMove") - .bind("mouseout.MouseMove mousemove.MouseMove", ASC.Files.Mouse.continueMoveTo) - .bind("mouseup.MouseMove", ASC.Files.Mouse.finishMoveTo) - .bind("keyup.MouseMove keydown.MouseMove", function (ev) { + .off("mouseout.MouseMove mousemove.MouseMove mouseup.MouseMove keyup.MouseMove keydown.MouseMove") + .on("mouseout.MouseMove mousemove.MouseMove", ASC.Files.Mouse.continueMoveTo) + .on("mouseup.MouseMove", ASC.Files.Mouse.finishMoveTo) + .on("keyup.MouseMove keydown.MouseMove", function (ev) { ev = jq.fixEvent(ev); var code = ev.keyCode || ev.which; if (code == ASC.Files.Common.keyCode.ctrl) { @@ -417,7 +444,7 @@ window.ASC.Files.Mouse = (function () { return true; } - if (!ASC.Files.UI.accessEdit() || !ASC.Files.UI.accessDelete() || e.ctrlKey) { + if (movingData.isCopyTo || e.ctrlKey) { var textFormat = ASC.Files.FilesJSResource.InfoCopyDescribe; jq("body").addClass("file-mouse-copy"); } else { @@ -430,11 +457,10 @@ window.ASC.Files.Mouse = (function () { } var textInfo; - var list = jq("#filesMainContent .file-row:has(.checkbox input:checked)"); - if (list.length == 1) { - textInfo = ASC.Files.UI.getObjectTitle(list[0]); + if (movingData.entries.length == 1) { + textInfo = ASC.Files.UI.getObjectTitle(movingData.entries[0]); } else { - textInfo = ASC.Files.FilesJSResource.InfoCountDescribe.format(list.length); + textInfo = ASC.Files.FilesJSResource.InfoCountDescribe.format(movingData.entries.length); } textInfo = textFormat.format(textInfo, "", "", "
    "); jq("#filesMovingTooltip").html(textInfo); @@ -467,10 +493,10 @@ window.ASC.Files.Mouse = (function () { } try { - el.focus(); + el.trigger("focus"); } catch (e) { el.disabled = false; - el.focus(); + el.trigger("focus"); el.disabled = true; } } @@ -498,7 +524,7 @@ window.ASC.Files.Mouse = (function () { if (folderToId == ASC.Files.Constants.FOLDER_ID_TRASH) { ASC.Files.Folders.deleteItem(); } else { - ASC.Files.Folders.isCopyTo = !ASC.Files.UI.accessEdit() || !ASC.Files.UI.accessDelete() || e && e.ctrlKey === true; + ASC.Files.Folders.isCopyTo = movingData.isCopyTo || e.ctrlKey; var folderToTitle = ASC.Files.UI.getEntryTitle("folder", folderToId); @@ -507,9 +533,11 @@ window.ASC.Files.Mouse = (function () { } ASC.Files.Folders.isCopyTo = false; - jq("body").unbind("mouseout.MouseMove mousemove.MouseMove mouseup.MouseMove mouseenter.MouseMove mouseleave.MouseMove keyup.MouseMove keydown.MouseMove"); + jq("body").off("mouseout.MouseMove mousemove.MouseMove mouseup.MouseMove mouseenter.MouseMove mouseleave.MouseMove keyup.MouseMove keydown.MouseMove"); jq("#filesMovingTooltip").remove(); + + movingData = null; }; var overCompactTitle = function () { @@ -572,7 +600,7 @@ window.ASC.Files.Mouse = (function () { jq("#mainContent").on("mouseover", "#filesMainContent.compact .file-row:not(.checkloading):not(.new-folder):not(.new-file) .entry-title .name a, #filesMainContent.thumbnails .file-row:not(.checkloading):not(.new-folder):not(.new-file) .name a", ASC.Files.Mouse.overCompactTitle); jq(document).on("mousedown.MouseSelect", "#studioPageContent:has(#filesMainContent .file-row:visible:not(.checkloading):not(.new-folder):not(.new-file))", ASC.Files.Mouse.beginSelecting); - jq(document).bind("mouseup.Mouse", function () { + jq(document).on("mouseup.Mouse", function () { ASC.Files.Mouse.mouseBtn = false; }); diff --git a/web/studio/ASC.Web.Studio/Products/Files/js/saveas.js b/web/studio/ASC.Web.Studio/Products/Files/js/saveas.js index 264e5a159..8f8e2cd00 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/js/saveas.js +++ b/web/studio/ASC.Web.Studio/Products/Files/js/saveas.js @@ -18,10 +18,17 @@ window.ASC.Files.FileChoice = (function () { var isInit = false; - var init = function (originForPost, folderId) { + var init = function (originForPost, folderId, displayPrivacy) { if (isInit === false) { isInit = true; + if (!displayPrivacy && ASC.Files.FileSelector.fileSelectorTree) { + var privacyFolderData = ASC.Files.FileSelector.fileSelectorTree.getFolderData(ASC.Files.Constants.FOLDER_ID_PRIVACY); + if (privacyFolderData) { + privacyFolderData.entryObject.addClass("privacy-node"); + } + } + if (ASC.Files.Utility.CanWebView(jq("#saveAsTitle").data("title"))) { jq("#saveAsOpenTabPanel").insertBefore(".middle-button-container"); } else { @@ -82,6 +89,12 @@ window.ASC.Files.FileChoice = (function () { finishSubmit({}); }; + jq(document).on("keyup", function (event) { + if (event.keyCode == 27) { + ASC.Files.FileSelector.onCancel(); + } + }); + ASC.Files.FileSelector.openDialog(folderId || null, true); ASC.Files.UI.checkCharacter(jq("#saveAsTitle")); diff --git a/web/studio/ASC.Web.Studio/Products/Files/js/servicemanager.js b/web/studio/ASC.Web.Studio/Products/Files/js/servicemanager.js index eb33be738..a96ae46fe 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/js/servicemanager.js +++ b/web/studio/ASC.Web.Studio/Products/Files/js/servicemanager.js @@ -261,12 +261,12 @@ window.ASC.Files.ServiceManager = (function () { data = ASC.Files.TemplateManager.createXML(xmlHttpRequest.responseText); break; case "json": - data = jq.parseJSON(xmlHttpRequest.responseText); + data = JSON.parse(xmlHttpRequest.responseText); break; default: if (xmlHttpRequest.responseXML.xml.indexOf(ignorResponse) != 0) { data = ASC.Files.TemplateManager.createXML(xmlHttpRequest.responseXML.xml) - || jq.parseJSON(xmlHttpRequest.responseText); + || JSON.parse(xmlHttpRequest.responseText); } } } catch (e) { @@ -422,6 +422,8 @@ window.ASC.Files.ServiceManager = (function () { UnSubscribeMe: "unsubscribeme", GetShortenLink: "getshortenlink", GetPresignedUri: "getpresigneduri", + ChangeExternalShareSettings: "changeexternalsharesettings", + ChangeExternalShareSocialMediaSettings: "changeexternalsharesocialmediasettings", GetUsers: "getusers", SendEditorNotify: "sendeditornotify", @@ -474,7 +476,6 @@ window.ASC.Files.ServiceManager = (function () { ChunkUploadGetFileFromServer: "chunkuploadgetfilefromserver", TrackEditFile: "trackeditfile", - StartEdit: "startedit", LockFile: "lockfile", @@ -584,18 +585,10 @@ window.ASC.Files.ServiceManager = (function () { request("get", "json", eventType, params, "tasks"); }; - var trackEditFile = function (eventType, params) { - request("get", "json", eventType, params, "trackeditfile?fileId=" + encodeURIComponent(params.fileID) + "&tabId=" + params.tabId + "&docKeyForTrack=" + params.docKeyForTrack + "&isFinish=" + (params.finish == true) + params.shareLinkParam); - }; - var checkEditing = function (eventType, params, data) { request("post", "json", eventType, params, data, "checkediting"); }; - var startEdit = function (eventType, params) { - request("get", "json", eventType, params, "startEdit?fileId=" + encodeURIComponent(params.fileID) + params.shareLinkParam); - }; - var setAceLink = function (eventType, params) { request("get", "json", eventType, params, "setacelink?fileId=" + encodeURIComponent(params.fileId) + "&share=" + params.share); }; @@ -632,6 +625,14 @@ window.ASC.Files.ServiceManager = (function () { request("post", "json", eventType, params, data, "sendeditornotify?fileId=" + encodeURIComponent(params.fileId)); }; + var changeExternalShareSettings = function (eventType, params) { + request("get", "json", eventType, params, "external?enable=" + (params.enable === true)); + }; + + var changeExternalShareSocialMediaSettings = function (eventType, params) { + request("get", "json", eventType, params, "externalsocialmedia?enable=" + (params.enable === true)); + }; + var checkConversion = function (eventType, params, data) { request("post", "json", eventType, params, data, "checkconversion"); }; @@ -764,9 +765,7 @@ window.ASC.Files.ServiceManager = (function () { getTasksStatuses: getTasksStatuses, terminateTasks: terminateTasks, - trackEditFile: trackEditFile, checkEditing: checkEditing, - startEdit: startEdit, setAceLink: setAceLink, getSharedInfo: getSharedInfo, @@ -775,6 +774,8 @@ window.ASC.Files.ServiceManager = (function () { unSubscribeMe: unSubscribeMe, getShortenLink: getShortenLink, getEncryptionAccess: getEncryptionAccess, + changeExternalShareSettings: changeExternalShareSettings, + changeExternalShareSocialMediaSettings: changeExternalShareSocialMediaSettings, getUsers: getUsers, sendEditorNotify: sendEditorNotify, diff --git a/web/studio/ASC.Web.Studio/Products/Files/js/templatemanager.js b/web/studio/ASC.Web.Studio/Products/Files/js/templatemanager.js index 3ae80ab3b..2760bb0ba 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/js/templatemanager.js +++ b/web/studio/ASC.Web.Studio/Products/Files/js/templatemanager.js @@ -137,7 +137,6 @@ window.ASC.Files.TemplateManager = (function () { } var xmlDocument; var xsltProcessor; - var xmlSerializer; try { xsltProcessor = new XSLTProcessor(); xsltProcessor.importStylesheet(xsl); @@ -146,8 +145,7 @@ window.ASC.Files.TemplateManager = (function () { throw 'Can\'t translate xml : ' + err; } try { - xmlSerializer = new XMLSerializer(); - xmlstr = xmlSerializer.serializeToString(xmlDocument); + xmlstr = jq('
    ').append(xmlDocument).html(); } catch (err) { throw 'Can\'t serialized xml : ' + err; } diff --git a/web/studio/ASC.Web.Studio/Products/Files/js/ui.js b/web/studio/ASC.Web.Studio/Products/Files/js/ui.js index e50f9125f..abc09bac8 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/js/ui.js +++ b/web/studio/ASC.Web.Studio/Products/Files/js/ui.js @@ -28,6 +28,8 @@ window.ASC.Files.UI = (function () { var amountPage = 0; var countTotal = 0; + var autoCleanUpSetting = null; + var lastSelectedEntry = null; var filesUserProfileInfo = new PopupBox("pb_filesUserProfileInfo", 320, 140, "tintLight", "borderBaseShadow", "", @@ -63,17 +65,29 @@ window.ASC.Files.UI = (function () { isInit = true; } - jq(window).resize(function () { + jq(window).on("resize", function () { fixContentHeaderLeft(); ASC.Files.UI.fixContentHeaderWidth(); resizeVersionsBlock(); }); - jq(window).bind("resizeWinTimerWithMaxDelay", function (event) { + jq(window).on("resizeWinTimerWithMaxDelay", function (event) { fixContentHeaderLeft(); ASC.Files.UI.fixContentHeaderWidth(); }); + + isAutoCleanUpOn = jq("#cbxAutomaticallyCleanUp").is(":checked"); + autoCleanUpGap = isAutoCleanUpOn ? parseInt(jq("#selectGapToAutoCleanUp").val()) : -1; + + ASC.Files.UI.autoCleanUpSetting = { + isAutoCleanUp: isAutoCleanUpOn, + gap: autoCleanUpGap + }; + + if (!isAutoCleanUpOn) { + jq("#selectGapToAutoCleanUp").val("1").trigger("change"); + } }; var resizeVersionsBlock = function () { @@ -123,6 +137,8 @@ window.ASC.Files.UI = (function () { if (entryObject.is(".jstree *")) { selectorObject = ".tree-node"; selectorData = "> a input:hidden[name=\"entry_data\"]"; + } else if (entryObject.closest("#contentVersions").length) { + entryObject = jq(".display-versions"); } entryObject = entryObject.closest(selectorObject); } @@ -160,17 +176,37 @@ window.ASC.Files.UI = (function () { result.provider_id = dataObject.attr("data-provider_id") | 0; result.provider_key = dataObject.attr("data-provider_key"); result.shared = dataObject.attr("data-shared") === "true"; + result.deny_download = dataObject.attr("data-deny_download") === "true"; + result.deny_sharing = dataObject.attr("data-deny_sharing") === "true"; result.title = (dataObject.attr("data-title") || "").trim(); result.version = dataObject.attr("data-version") | 0; result.version_group = dataObject.attr("data-version_group") | 0; result.folderUrl = dataObject.attr("data-folder_url") ; result.folder_id = dataObject.attr("data-folder_id"); + result.folder_is_favorite = dataObject.attr("data-folder_is_favorite"); result.encrypted = dataObject.attr("data-encrypted") === "true"; result.thumbnail_status = dataObject.attr("data-thumbnail_status"); + result.deleted_permanently_date = dataObject.attr("data-deleted_permanently_date"); return result; }; + var setObjectData = function (entryObject, key, value) { + entryObject = jq(entryObject); + + if (entryObject.length == 0) { + return; + } + + var selectorData = "input:hidden[name=\"entry_data\"]"; + var dataObject = entryObject.find(selectorData); + if (!dataObject.length) { + return; + } + + dataObject.attr(key, value); + }; + var parseItemId = function (itemId) { if (typeof itemId == "undefined" || itemId == null) { return null; @@ -453,6 +489,8 @@ window.ASC.Files.UI = (function () { awaitingThumbnails.setFolder(ASC.Files.Folders.currentFolder.id); + var isAutoCleanUp = ASC.Files.UI.autoCleanUpSetting.isAutoCleanUp; + listEntry.each(function () { var entryData = ASC.Files.UI.getObjectData(this); @@ -462,6 +500,7 @@ window.ASC.Files.UI = (function () { var entryTitle = entryData.title; if (ASC.Files.Folders.folderContainer == "trash") { + entryObj.find(".file-lock").remove(); entryObj.find(".template-action").remove(); entryObj.find(".entry-descr .title-created").remove(); @@ -470,8 +509,22 @@ window.ASC.Files.UI = (function () { entryObj.find(".create-date").remove(); entryObj.find(".modified-date").show(); } + if (isAutoCleanUp) { + entryObj.find(".entry-descr .title-removed").remove(); + entryObj.find(".create-date").remove(); + entryObj.find(".modified-date").remove(); + entryObj.find(".deleted-permanently").show(); + entryObj.find(".entry-descr .title-removed-permanently").show(); + } else { + entryObj.find(".entry-descr .title-removed-permanently").remove(); + entryObj.find(".entry-descr .title-removed").show(); + entryObj.find(".deleted-permanently-date").remove(); + entryObj.find(".modified-date").show(); + } } else { entryObj.find(".entry-descr .title-removed").remove(); + entryObj.find(".entry-descr .title-removed-permanently").remove(); + entryObj.find(".deleted-permanently-date").remove(); if (entryType == "folder") { entryObj.find(".modified-date").remove(); } @@ -531,6 +584,7 @@ window.ASC.Files.UI = (function () { if (ASC.Files.Utility.MustConvert(entryTitle) && !entryData.encrypted) { entryObj.find(".pencil:not(.convert-action)").remove(); if (Teamlab.profile.isVisitor && !ASC.Files.UI.accessEdit() + || ASC.Files.UI.denyDownload(entryData) || ASC.Files.Folders.folderContainer == "trash") { entryObj.find(".convert-action").remove(); } else { @@ -541,9 +595,14 @@ window.ASC.Files.UI = (function () { entryObj.find(".fillform-action").show(); } else { if (entryData.encrypted) { - entryUrl = ASC.Files.Utility.GetFileDownloadUrl(entryId); - rowLink.attr("href", entryUrl); - entryObj.find(".file-edit").attr("href", entryUrl); + if (!ASC.Desktop) { + rowLink.removeAttr("href"); + entryObj.find(".file-edit").removeAttr("href"); + } else { + entryUrl = ASC.Files.Utility.GetFileWebEditorUrl(entryId); + rowLink.attr("href", entryUrl); + entryObj.find(".file-edit").attr("href", entryUrl); + } } if (ASC.Files.UI.editableFile(entryData) @@ -588,12 +647,17 @@ window.ASC.Files.UI = (function () { rowLink.attr("href", ASC.Files.UI.getEntryLink("folder", ASC.Files.Folders.currentFolder.id)); } - if (!jq("#filesMainContent").hasClass("without-share") - && (ASC.Files.Folders.folderContainer == "forme" && !ASC.Files.UI.accessEdit(entryData, entryObj) - || ASC.Files.Folders.folderContainer == "privacy" && (entryType == "folder" || !ASC.Files.UI.accessEdit(entryData, entryObj)) + if (!jq("#filesMainContent").hasClass("without-share")) { + if (ASC.Files.Folders.folderContainer == "forme" && (!ASC.Files.UI.accessEdit(entryData, entryObj) || ASC.Files.UI.denySharing(entryData)) + || ASC.Files.Folders.folderContainer == "privacy" && (entryType == "folder" || (!ASC.Files.UI.accessEdit(entryData, entryObj) || ASC.Files.UI.denySharing(entryData))) || ASC.Resources.Master.Personal && entryType == "folder" - || Teamlab.profile.isVisitor === true)) { - entryObj.addClass("without-share"); + || Teamlab.profile.isVisitor === true) { + entryObj.addClass("without-share"); + } + } else { + if (ASC.Files.Folders.folderContainer == "forme" && entryData.access == ASC.Files.Constants.AceStatusEnum.ReadWrite && !entryData.deny_sharing && !Teamlab.profile.isVisitor) { + entryObj.addClass("can-share"); + } } if (jq.browser.msie) { @@ -601,7 +665,7 @@ window.ASC.Files.UI = (function () { } } }); - + if (jq("#switchToNormal").length) { ASC.Files.UI.switchFolderView(); } @@ -900,6 +964,25 @@ window.ASC.Files.UI = (function () { }; + var denyDownload = function (entryData) { + if (!entryData || !entryData.deny_download) + return false; + + return entryData.access == ASC.Files.Constants.AceStatusEnum.Read + || entryData.access == ASC.Files.Constants.AceStatusEnum.Comment; + } + + var denySharing = function (entryData) { + if (!entryData) + return false; + + if (entryData.access != ASC.Files.Constants.AceStatusEnum.None + && entryData.access != ASC.Files.Constants.AceStatusEnum.ReadWrite) + return true; + + return entryData.access == ASC.Files.Constants.AceStatusEnum.ReadWrite && entryData.deny_sharing; + } + var lockEditFileById = function (fileId, edit, listBy) { var fileObj = ASC.Files.UI.getEntryObject("file", fileId); return ASC.Files.UI.lockEditFile(fileObj, edit, listBy); @@ -1041,7 +1124,7 @@ window.ASC.Files.UI = (function () { }; var checkCharacter = function (input) { - jq(input).unbind("keyup").bind("keyup", function () { + jq(input).off("keyup").on("keyup", function () { var str = jq(this).val(); if (str.search(ASC.Files.Common.characterRegExp) != -1) { jq(this).val(ASC.Files.Common.replaceSpecCharacter(str)); @@ -1219,6 +1302,7 @@ window.ASC.Files.UI = (function () { removeEntryObject: removeEntryObject, getEntryObject: getEntryObject, getObjectData: getObjectData, + setObjectData: setObjectData, getSelectorId: getSelectorId, getEntryTitle: getEntryTitle, @@ -1229,6 +1313,7 @@ window.ASC.Files.UI = (function () { currentPage: currentPage, amountPage: amountPage, countTotal: countTotal, + autoCleanUpSetting: autoCleanUpSetting, updateFolderView: updateFolderView, switchFolderView: switchFolderView, @@ -1266,6 +1351,9 @@ window.ASC.Files.UI = (function () { accessEdit: accessEdit, accessDelete: accessDelete, + denyDownload: denyDownload, + denySharing: denySharing, + checkCharacter: checkCharacter, canSetDocumentTitle: canSetDocumentTitle, setDocumentTitle: setDocumentTitle, @@ -1308,14 +1396,14 @@ window.ASC.Files.UI = (function () { ASC.Files.UI.init(); $(function () { - jq("#switchToNormal").click(function () { + jq("#switchToNormal").on("click", function () { ASC.Files.UI.switchFolderView("normal"); }); - jq("#switchToCompact").click(function () { + jq("#switchToCompact").on("click", function () { ASC.Files.UI.switchFolderView("compact"); }); - jq("#switchToThumbnails").click(function () { + jq("#switchToThumbnails").on("click", function () { ASC.Files.UI.switchFolderView("thumbnails"); }); jq("#filesMainContent").on("click", ".file-row", ASC.Files.UI.clickRow); @@ -1334,16 +1422,16 @@ window.ASC.Files.UI = (function () { } ASC.Files.UI.clickRow(event, jq(this)); - jq(this).blur(); + jq(this).trigger("blur"); ASC.Files.Common.cancelBubble(event); }); - jq(".menu-action-on-top").click(function () { + jq(".menu-action-on-top").on("click", function () { jq(document).scrollTop(0); return false; }); - jq(document).keydown(function (event) { + jq(document).on("keydown", function (event) { if (jq(".blockUI:visible").length != 0 || jq("#fileViewerDialog:visible,#mediaPlayer:visible").length != 0 || jq(".studio-action-panel:visible").length != 0 || @@ -1373,7 +1461,7 @@ window.ASC.Files.UI = (function () { if (code == ASC.Files.Common.keyCode.A && e.ctrlKey) { if (jq.browser.opera) { setTimeout(function () { - jq("#filesSelectAllCheck").focus(); + jq("#filesSelectAllCheck").trigger("focus"); }, 1); } ASC.Files.UI.checkSelectAll(true); @@ -1383,7 +1471,7 @@ window.ASC.Files.UI = (function () { return true; }); - jq(document).keyup(function (event) { + jq(document).on("keyup", function (event) { if (jq(".blockUI:visible").length != 0 || jq("#fileViewerDialog:visible,#mediaPlayer:visible").length != 0 || jq("#promptRename").length != 0 || diff --git a/web/studio/ASC.Web.Studio/Products/Files/web.config b/web/studio/ASC.Web.Studio/Products/Files/web.config index fdaec8985..b378ea1a9 100644 --- a/web/studio/ASC.Web.Studio/Products/Files/web.config +++ b/web/studio/ASC.Web.Studio/Products/Files/web.config @@ -1,91 +1,103 @@ - + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + @@ -99,4 +111,4 @@ - \ No newline at end of file + diff --git a/web/studio/ASC.Web.Studio/Products/People/ASC.Web.People.csproj b/web/studio/ASC.Web.Studio/Products/People/ASC.Web.People.csproj index c1a778fb7..5a3940181 100644 --- a/web/studio/ASC.Web.Studio/Products/People/ASC.Web.People.csproj +++ b/web/studio/ASC.Web.Studio/Products/People/ASC.Web.People.csproj @@ -15,8 +15,10 @@ ASC.Web.People ASC.Web.People v4.8 - - + + + + 4.0 ..\..\bin\ @@ -42,6 +44,15 @@ + + CardDavSettings.aspx + ASPXCodeBehind + + + CardDavSettings.aspx + + + @@ -58,6 +69,14 @@ Import.aspx + + + Birthdays.aspx + ASPXCodeBehind + + + Birthdays.aspx + Reassigns.aspx ASPXCodeBehind @@ -135,11 +154,16 @@ + + + + + @@ -158,21 +182,39 @@ + + ASPXCodeBehind + - + + ASPXCodeBehind + + + + + + + ASPXCodeBehind + - + + ASPXCodeBehind + - + + ASPXCodeBehind + - + + ASPXCodeBehind + @@ -184,9 +226,205 @@ - + + ASPXCodeBehind + + + BirthdayPatternResource.resx + + + BirthdayPatternResource.resx + + + BirthdayPatternResource.resx + + + BirthdayPatternResource.resx + + + BirthdayPatternResource.resx + + + BirthdayPatternResource.resx + + + BirthdayPatternResource.resx + + + BirthdayPatternResource.resx + + + BirthdayPatternResource.resx + + + BirthdayPatternResource.resx + + + BirthdayPatternResource.resx + + + BirthdayPatternResource.resx + + + BirthdayPatternResource.resx + + + BirthdayPatternResource.resx + + + BirthdayPatternResource.resx + + + BirthdayPatternResource.resx + + + BirthdayPatternResource.resx + + + BirthdayPatternResource.resx + + + BirthdayPatternResource.resx + + + PublicResXFileCodeGenerator + BirthdayPatternResource.Designer.cs + Designer + + + BirthdayPatternResource.resx + + + BirthdayPatternResource.resx + + + BirthdayPatternResource.resx + + + BirthdayPatternResource.resx + + + BirthdayPatternResource.resx + + + BirthdayPatternResource.resx + + + BirthdayPatternResource.resx + + + BirthdayPatternResource.resx + + + BirthdayPatternResource.resx + + + True + True + BirthdayPatternResource.resx + + + BirthdaysResource.resx + + + BirthdaysResource.resx + + + BirthdaysResource.resx + + + BirthdaysResource.resx + + + BirthdaysResource.resx + + + BirthdaysResource.resx + + + BirthdaysResource.resx + + + BirthdaysResource.resx + + + BirthdaysResource.resx + + + BirthdaysResource.resx + + + BirthdaysResource.resx + + + BirthdaysResource.resx + + + BirthdaysResource.resx + + + BirthdaysResource.resx + + + BirthdaysResource.resx + + + BirthdaysResource.resx + + + BirthdaysResource.resx + + + BirthdaysResource.resx + + + BirthdaysResource.resx + + + BirthdaysResource.resx + + + PublicResXFileCodeGenerator + BirthdaysResource.Designer.cs + Designer + + + BirthdaysResource.resx + + + BirthdaysResource.resx + + + BirthdaysResource.resx + + + BirthdaysResource.resx + + + BirthdaysResource.resx + + + BirthdaysResource.resx + + + BirthdaysResource.resx + + + BirthdaysResource.resx + + + BirthdaysResource.resx + + + True + True + BirthdaysResource.resx + + + PeopleJSResource.resx + PeopleJSResource.resx @@ -435,9 +673,15 @@ - - - + + ASPXCodeBehind + + + ASPXCodeBehind + + + ASPXCodeBehind + @@ -473,10 +717,10 @@ - 2.9.17.2 + 21.12.22.2 - 12.0.3 + 13.0.1 diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/App_Themes/default/birthdays.css b/web/studio/ASC.Web.Studio/Products/People/App_Themes/default/css/birthdays.css similarity index 92% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/App_Themes/default/birthdays.css rename to web/studio/ASC.Web.Studio/Products/People/App_Themes/default/css/birthdays.css index f5b60dcf1..615a23cf8 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/App_Themes/default/birthdays.css +++ b/web/studio/ASC.Web.Studio/Products/People/App_Themes/default/css/birthdays.css @@ -1,95 +1,95 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -/* Birthdays page */ -.birthday-content-splitter { - margin-bottom: 16px; -} -.birthday-header { - color: #333333; - font-size: 18px; - font-weight: bold; -} -.birthday-date { - text-align: center; -} -.user-card-list { - border-left: 1px dotted #D1D1D1; - display: inline-block; - padding-left: 8px; -} -.user-card, -.small-user-card { - float: left; - margin: 4px; - width: 330px; -} -.user-card .user-avatar { - padding: 4px; - float: left; -} -.user-card .user-info { - float: left; - margin: 8px 0 0 16px; - width: 200px; - overflow: hidden; -} -.user-card .user-info-title { - margin: 8px 0; -} -.small-user-card .user-avatar { - float: left; -} -.small-user-card .user-info { - float: left; - margin: 4px 0 0 16px; - width: 260px; - overflow: hidden; -} -.small-user-card .user-info .link { - white-space: nowrap; -} -.small-user-card .user-info-title { - margin-top: 6px; -} -.birthday-remind { - display: none; - padding-top: 2px; -} -.birthday-remind-active { - background-color: #F7E3AC; - color: #DA9038; - padding: 2px 8px; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - display: none; -} -.birthday-remind-active a { - background: url("images/white_cross.png") no-repeat scroll 0 0 transparent; - cursor: pointer; - display: inline-block; - height: 8px; - margin-left: 4px; - width: 8px; -} -.tableBase tr:hover .small-user-card:not(.active) .birthday-remind { - display: inline-block; -} -.small-user-card.active .birthday-remind-active { - display: inline-block; +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +/* Birthdays page */ +.birthday-content-splitter { + margin-bottom: 16px; +} +.birthday-header { + color: #333333; + font-size: 18px; + font-weight: bold; +} +.birthday-date { + text-align: center; +} +.user-card-list { + border-left: 1px dotted #D1D1D1; + display: inline-block; + padding-left: 8px; +} +.user-card, +.small-user-card { + float: left; + margin: 4px; + width: 330px; +} +.user-card .user-avatar { + padding: 4px; + float: left; +} +.user-card .user-info { + float: left; + margin: 8px 0 0 16px; + width: 200px; + overflow: hidden; +} +.user-card .user-info-title { + margin: 8px 0; +} +.small-user-card .user-avatar { + float: left; +} +.small-user-card .user-info { + float: left; + margin: 4px 0 0 16px; + width: 260px; + overflow: hidden; +} +.small-user-card .user-info .link { + white-space: nowrap; +} +.small-user-card .user-info-title { + margin-top: 6px; +} +.birthday-remind { + display: none; + padding-top: 2px; +} +.birthday-remind-active { + background-color: #F7E3AC; + color: #DA9038; + padding: 2px 8px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + display: none; +} +.birthday-remind-active a { + background: url("../images/white_cross.png") no-repeat scroll 0 0 transparent; + cursor: pointer; + display: inline-block; + height: 8px; + margin-left: 4px; + width: 8px; +} +.tableBase tr:hover .small-user-card:not(.active) .birthday-remind { + display: inline-block; +} +.small-user-card.active .birthday-remind-active { + display: inline-block; } \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/People/App_Themes/default/css/carddav.css b/web/studio/ASC.Web.Studio/Products/People/App_Themes/default/css/carddav.css new file mode 100644 index 000000000..efa71298f --- /dev/null +++ b/web/studio/ASC.Web.Studio/Products/People/App_Themes/default/css/carddav.css @@ -0,0 +1,66 @@ + +.url-link.cardDav { + margin-bottom: 0.5em; +} + +.url-link { + word-wrap: break-word; + margin-bottom: 2.5em; + position: relative; +} + + .url-link .title { + font-weight: bold; + margin: 1em 0 0.5em; + } + +input[type="text" i] { + width: 100%; + margin-bottom: 10px; +} + +.url-link .control.disabled .button { + background-color: #efefef; + color: #666562; +} + +.cardDav-settings-label-checkbox { + margin: 0 2px 22px 0; + float: left; + clear: both; +} + +.settings-checkbox-text { + display: inline-block; + width: calc(100% - 50px); + vertical-align: top; + margin-left: 5px; +} + +.url-link .button { + border-radius: 0; + color: #333333; + font-size: 11px; + font-weight: normal; + background: #fff; + cursor: pointer; + padding: 0; +} + + .url-link .button span.text { + text-decoration: none !important; + border-bottom: 1px dotted; + } + + + .url-link .button:hover { + background-color: #fff; + color: #333333; + } + +span.move-or-copy { + padding: 0 20px 0 17px; + background-repeat: no-repeat; + background-position: -3px center; + background-image: url("/skins/default/images/svg/context/icon_move_or_copy.svg"); +} diff --git a/web/studio/ASC.Web.Studio/Products/People/App_Themes/default/css/people.master.less b/web/studio/ASC.Web.Studio/Products/People/App_Themes/default/css/people.master.less index 49cd6a876..822404623 100644 --- a/web/studio/ASC.Web.Studio/Products/People/App_Themes/default/css/people.master.less +++ b/web/studio/ASC.Web.Studio/Products/People/App_Themes/default/css/people.master.less @@ -197,10 +197,11 @@ table.table-list tbody tr:hover .link.dotted { } -#peopleData .group, +#peopleData .group, #peopleData .info { white-space: nowrap; - max-width:115px; + max-width: 115px; + overflow: hidden; } #peopleData .info .red-text { float:left; @@ -211,14 +212,14 @@ table.table-list tbody tr:hover .link.dotted { #peopleData .info .email .email { display:inline-block; height: 18px; - max-width:250px; + max-width:200px; overflow: hidden; padding-right: 13px; position:relative; text-overflow:ellipsis; } #peopleData .info .red-text + .email .email { - max-width: 200px; + max-width: 150px; } #peopleData .profile.hover{ @@ -235,11 +236,12 @@ table.table-list tbody tr:hover .link.dotted { } #peopleData .profile .birthday { - background: url(../imgs/birthday.png) 0 0 no-repeat; - color: #ff9000; + background: url(../images/svg/birthday-orange.svg) 0 0 no-repeat; + background-position: left center; + color: #EE831A; display: inline-block; - margin-left: 2px; - padding: 4px 0 4px 20px; + margin-left: 4px; + padding: 4px 0 4px 18px; } #peopleData td.name, @@ -280,40 +282,57 @@ table.table-list tbody tr:hover .link.dotted { white-space: nowrap; } } + +body.media-width-0-1650 { + #peopleData .info .btn.email, + #peopleData .info .email .email { + max-width: 170px; + } + + #peopleData .info .red-text + .email .email { + max-width: 120px; + } + + #peopleData td.name, + #peopleData td.group, + #peopleData td.location { + max-width: 170px; + } +} + body.media-width-0-1450 { #peopleData .info .btn.email, - #peopleData .info .email .email{ + #peopleData .info .email .email { max-width: 150px; } + #peopleData .info .red-text + .email .email { max-width: 100px; } + + #peopleData td.name, + #peopleData td.group, + #peopleData td.location { + max-width: 150px; + } } body.media-width-0-1200 { - #peopleData td.name, - #peopleData td.group, - #peopleData td.location - { - max-width:120px; - } -} - -body.media-width-0-1100 { #peopleData .info .btn.email, - #peopleData .info .email .email{ + #peopleData .info .email .email { max-width: 120px; } + #peopleData .info .red-text + .email .email { max-width: 70px; } + #peopleData td.name, #peopleData td.group, #peopleData td.location { max-width: 120px; } } - /******************************************************************************* header menu elements for group operations *******************************************************************************/ @@ -442,7 +461,7 @@ header menu elements for group operations width: 223px; &:hover { - background-position: center bottom; + background-position-y: bottom; } } @@ -586,4 +605,8 @@ header menu elements for group operations .menu-item-label { white-space: nowrap; } +} + +#connectionsBlockContainer { + margin: 16px 0 0 0; } \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/App_Themes/default/images/birthday.png b/web/studio/ASC.Web.Studio/Products/People/App_Themes/default/images/birthdayEmpScr.png similarity index 100% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/App_Themes/default/images/birthday.png rename to web/studio/ASC.Web.Studio/Products/People/App_Themes/default/images/birthdayEmpScr.png diff --git a/web/studio/ASC.Web.Studio/Products/People/App_Themes/default/images/svg/birthday-orange.svg b/web/studio/ASC.Web.Studio/Products/People/App_Themes/default/images/svg/birthday-orange.svg new file mode 100644 index 000000000..65f10cb51 --- /dev/null +++ b/web/studio/ASC.Web.Studio/Products/People/App_Themes/default/images/svg/birthday-orange.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/web/studio/ASC.Web.Studio/Products/People/App_Themes/default/images/svg/birthday.svg b/web/studio/ASC.Web.Studio/Products/People/App_Themes/default/images/svg/birthday.svg new file mode 100644 index 000000000..d6fae5f45 --- /dev/null +++ b/web/studio/ASC.Web.Studio/Products/People/App_Themes/default/images/svg/birthday.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/App_Themes/default/images/white_cross.png b/web/studio/ASC.Web.Studio/Products/People/App_Themes/default/images/white_cross.png similarity index 100% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/App_Themes/default/images/white_cross.png rename to web/studio/ASC.Web.Studio/Products/People/App_Themes/default/images/white_cross.png diff --git a/web/studio/ASC.Web.Studio/Products/People/App_Themes/default/imgs/people_import.png b/web/studio/ASC.Web.Studio/Products/People/App_Themes/default/imgs/people_import.png index 61fb77a8e381f910e82f76271c9006c181e9d94d..5457611c9c3d8e1fefde4015fb4cf251a9a9bc2e 100644 GIT binary patch delta 2585 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 PJ1(@f6RO_wLgK#xuUO7P delta 2757 zcmZ8jXIK(!8%<4n@y#-AqNX{>ZA(;2OmmwPSDI#SEO75ZDW5qqbC1B0Cgv(rz;cvI znWW;xf!h$1a3-dPeY{=Q`~CQSJkN8^eV_B={CTd!5wARZF4@Y$NJtPUc;LVRAroVL z8}2*9J^FtgN@W4plY8ZX7_U;jF$b;+)({t=>6FZtV5e_Z6Zk%QE{b<$U* zd(u1PLgThKpv+e_FO~6S9Y=aWA4gtZoUKVkffiYBgupM4h1;*TU^j~s3ayyvb*pH= z4&-}qS7fW9Y~#|P9|`xQ7bd4POiPk!P+gaKMH#|NN(#n0F?U2ZyiNy?6DpSg<4rIp-yy4NQ~ep zT6^~mdywzHtkC>j+6VaJ3c|&RRl=~GseMwDXr?{Q@55Zno)yj&59mw`s>xxGDSP+u zYfQlu;I(OB$YRo=qkSVMGg#S;Z zEg5GM*H^V!b3HU!)YGwaXs$$0CT^iQj2Q^CA!a=s+U88Wr!8ipl)`S&b*4IbW=6|< zx9z5~^?lh^VJUp{{u5(KVT^@$WEC8x8WZ9?H?fd}}3R!7j2hl^=7>oxdss zL7Hg0%EYPSP}E!u6*~V0?CetCo4}5l7^3>n6Y|ppUfVR6|7{_5xOMsF0;x$O)>*q~ zboIy>GX0$Yp#>{^@5F~43^G<)9&0vftLcYjti|=MC#XiZ3#jd4!#$f`&u-O z*E!P-_5AY4?w~Pn1Gz-@-6fwIWJvk`Xz?HW`dDYN=D8v(gbLVb`SQ9$@Qbp&3SzjV zN6O}|!fxu5zJp@R@sHdhjm--KXKLN5hcoY~vXn8;siPjBrSAk?6gze%^tj2ZGoz2r zsXM*lySjZ5eK*aXq0j$`Z|@j_(8S0$VDDr<54Yqan@PcKe#H1fWF$1jN4Ii*u&H#VU2M12o(Qn=^(LDvdr223`L}n(Y z=h}^FP4Vk59OU9zH;tYG7`6+C!C%GS3*vaAqx&}d(F>=4DEZWErL6AsAJUS$jMzEM zK8gA$Dp*hpLkaEAP?|;XVZd#SzZ8+|uj> zIk{#|bIO%lDN4G=Q8&|JMfP;ZF!wj8Nnp6k2cc&pQ61(tjQuW%+YF?82Y99gHn#_~ zCygL-wlRUzDiT0!Hq{-ko?EK-0>bAz=w1tgb3iTxCv2DysN4=dyrQMF71ecc1%jh0 zGPU6ih@wHSG_mRRGmcf%{EIpB1`pHSi^Co{hBFWns*YJ*9XfWV6Ldg8FG^yeN(H=9 zl{J_&?vo{^ngkD*@tJQK4I^@T%khaDdj2O|sfsL0-$M>nEOY{%kURDYpum@ikuP+- zkoT9tVnG|-A#y)v^u%w8`>{q@H=tXE`hCFgNwu}d)Eau>HVIFwloM+&2$?#=*dSF7 zRavWlnxsB!cs+Bk+0}mu1!_L4!Wz1%-7|;D@k|ui&~3G|uTu9Od%)xuO??5Drfdk>9DUge9`FpNf%e&f%e;i9(6|}?2VVg!D`+=FL)w5UKjJzOept6G?okb zN;=IFe~pT}*Ii5ms#y&Y0}HBQk%Bsu zzpwQ^DR91n2ig^~{wB;^@*dRAu@@-&o=;hOUJjZZU@qMl_S~2M@$J#F=~*$*^o(v> zUdT^_zLpKMun6L8?Z~e+HG;{h|0Jp%M`M-Z;btIeBqU^no$ikH3F2LRi%!+(?Go9% zar;nYYJ6Fka{pcv>V_^lGzz3wCYydJFDTce!J}QruJA)4hJ|Qq=|9Cmb@G16V;^a& zb8bL;PeC|w7XITh6&??-TB=5LTDXAETmjLEx~DbVp^gDNO*Zog2U3}6Y=tvbehMR95rgtFm%VdKw42M{veM63BcsQK>4ZFWF36@8 zS!UDr1Go!o@m5fLaqWM(DocrV9QX{Tw`!nJw zpl~YWL!61uTAq}pHmvq#f)0M#bgxwi_&vZvO|_|}ck~wzYIuTa+HZKMHB~Z&{#Xkh z@3qxMsN8(D-=!{V_XuP}3CqnT?^BLm`EJ8PxoA|3$32Vny`3sh{lOa`({??p{3Hqg zcA+FsJxaGac-OpY^nP{uw42HBLS7LYomZY_Ty2+D4LuC$G0d@P$11rtQDPTwohCo# zXkO_#Mko0A#-M%$)6C-FC+P&8+4Ww;o!O)O+?<0wM#w$hFaIPhk|)L*KA&W3arJ7l z+>o~#W<4aUS;NvNz7izUf@q8kd17l#GIU7gPtp3#cl+0_pkV&{b#5~~U}9jQU#I65 F|6icvbG-ln diff --git a/web/studio/ASC.Web.Studio/Products/People/Birthdays.aspx b/web/studio/ASC.Web.Studio/Products/People/Birthdays.aspx new file mode 100644 index 000000000..29d3b544b --- /dev/null +++ b/web/studio/ASC.Web.Studio/Products/People/Birthdays.aspx @@ -0,0 +1,118 @@ +<%@ Assembly Name="ASC.Web.People" %> +<%@ Assembly Name="ASC.Web.Core" %> + +<%@ Page Language="C#" MasterPageFile="~/Products/People/Masters/PeopleBaseTemplate.Master" AutoEventWireup="true" + CodeBehind="Birthdays.aspx.cs" Inherits="ASC.Web.People.Birthdays" %> + +<%@ Import Namespace="ASC.Core.Users" %> +<%@ Import Namespace="ASC.Core" %> +<%@ Import Namespace="ASC.Web.People.Resources" %> +<%@ Import Namespace="ASC.Web.Core.Users" %> +<%@ Import Namespace="ASC.Web.Studio.Utility" %> + + + + + <% if (todayBirthdays != null && todayBirthdays.Count > 0) %> + <% { %> +
    + <%= BirthdaysResource.BirthdaysTodayTitle%> +
    + + + + + + + + + + + +
    + <%= DateTime.UtcNow.ToString("dd MMMM").ToLower()%> + + +
    + <% } %> + + + + + + + <% if (upcomingBirthdays != null && upcomingBirthdays.Count > 0) %> + <% { %> +
    + <%= BirthdaysResource.BirthdaysUpcomingTitle%> +
    + + + + + + + <% foreach (var bday in upcomingBirthdays) %> + <% { %> + + + + + <% } %> + +
    +
    + <%= bday.Date.ToShortDayMonth()%> +
    +
    +
    + <% foreach (var usr in bday.Users) %> + <% { %> +
    "> +
    + +
    + +
    + <% } %> +
    +
    + <% } %> + +
    \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/People/Birthdays.aspx.cs b/web/studio/ASC.Web.Studio/Products/People/Birthdays.aspx.cs new file mode 100644 index 000000000..96f39e10a --- /dev/null +++ b/web/studio/ASC.Web.Studio/Products/People/Birthdays.aspx.cs @@ -0,0 +1,127 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; + +using ASC.Core; +using ASC.Core.Tenants; +using ASC.Core.Users; +using ASC.Web.Core; +using ASC.Web.People.Core; +using ASC.Web.People.Resources; +using ASC.Web.Studio; +using ASC.Web.Studio.Controls.Common; +using ASC.Web.Studio.Utility; + +namespace ASC.Web.People +{ + public partial class Birthdays : MainPage + { + protected List todayBirthdays; + protected List upcomingBirthdays; + + + + protected void Page_Load(object sender, EventArgs e) + { + var isBirthdaysAvailable = WebItemManager.Instance.GetSubItems(PeopleProduct.ID).Any(item => item.ID == WebItemManager.BirthdaysProductID); + + if (!isBirthdaysAvailable) + { + Response.Redirect(PeopleProduct.GetStartURL()); + } + + RenderScripts(); + + todayBirthdays = GetTodayBirthdays(); + upcomingBirthdays = GetUpcomingBirthdays().ToList(); + + if (upcomingBirthdays == null || !upcomingBirthdays.Any()) + { + upcomingEmptyContent.Controls.Add(new EmptyScreenControl + { + ImgSrc = VirtualPathUtility.ToAbsolute("~/Products/People/App_Themes/default/images/birthdayEmpScr.png"), + Header = BirthdaysResource.BirthdayEmptyScreenCaption, + Describe = BirthdaysResource.BirthdaysEmptyModuleDescription + }); + } + + Title = HeaderStringHelper.GetPageTitle(BirthdaysResource.BirthdaysModuleTitle); + } + + protected void RenderScripts() + { + Page + .RegisterStyle("~/Products/People/App_Themes/default/css/birthdays.css") + .RegisterBodyScripts("~/Products/People/js/birthdays.js"); + } + + private static List GetTodayBirthdays() + { + var today = TenantUtil.DateTimeNow(); + return (from u in CoreContext.UserManager.GetUsers(EmployeeStatus.Active, EmployeeType.User) + where u.BirthDate.HasValue && u.BirthDate.Value.Month.Equals(today.Month) && u.BirthDate.Value.Day.Equals(today.Day) + orderby u.DisplayUserName() + select u) + .ToList(); + } + + private class BirthDateComparer : IComparer + { + public int Compare(DateTime x, DateTime y) + { + var today = TenantUtil.DateTimeNow(); + var leap = new DateTime(2000, today.Month, today.Day); + + var dx = new DateTime(2000, x.Month, x.Day).DayOfYear - leap.DayOfYear; + var dy = new DateTime(2000, y.Month, y.Day).DayOfYear - leap.DayOfYear; + + if (dx < 0 && dy >= 0) return 1; + if (dx >= 0 && dy < 0) return -1; + + return dx.CompareTo(dy); + } + } + + private static IEnumerable GetUpcomingBirthdays() + { + var today = TenantUtil.DateTimeNow(); + + return CoreContext.UserManager.GetUsers(EmployeeStatus.Active, EmployeeType.User) + .Where(x => x.BirthDate.HasValue) + .OrderBy(x => x.BirthDate.Value, new BirthDateComparer()) + .GroupBy(x => new DateTime(2000, x.BirthDate.Value.Month, x.BirthDate.Value.Day)) // 29 february + .Select(x => new BirthdayWrapper { Date = x.Key, Users = x.ToList() }) + .SkipWhile(x => x.Date.Month.Equals(today.Month) && x.Date.Day.Equals(today.Day)) + .Take(10); + } + + protected bool IsInRemindList(Guid userID) + { + return BirthdaysNotifyClient.Instance.IsSubscribe(SecurityContext.CurrentAccount.ID, userID); + } + + protected class BirthdayWrapper + { + public DateTime Date; + public List Users; + } + } +} \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/People/Birthdays.aspx.designer.cs b/web/studio/ASC.Web.Studio/Products/People/Birthdays.aspx.designer.cs new file mode 100644 index 000000000..f246eb5ad --- /dev/null +++ b/web/studio/ASC.Web.Studio/Products/People/Birthdays.aspx.designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ASC.Web.People +{ + + + public partial class Birthdays + { + + /// + /// upcomingEmptyContent control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.PlaceHolder upcomingEmptyContent; + } +} diff --git a/web/studio/ASC.Web.Studio/Products/People/CardDavSettings.aspx b/web/studio/ASC.Web.Studio/Products/People/CardDavSettings.aspx new file mode 100644 index 000000000..fecb26508 --- /dev/null +++ b/web/studio/ASC.Web.Studio/Products/People/CardDavSettings.aspx @@ -0,0 +1,61 @@ +<%@ Assembly Name="ASC.Web.People" %> +<%@ Assembly Name="ASC.Web.Core" %> + +<%@ Page Language="C#" MasterPageFile="~/Products/People/Masters/PeopleBaseTemplate.Master" AutoEventWireup="true" + CodeBehind="CardDavSettings.aspx.cs" Inherits="ASC.Web.People.CardDavSettings" %> + +<%@ Import Namespace="ASC.Core.Users" %> +<%@ Import Namespace="ASC.Core" %> +<%@ Import Namespace="ASC.Web.People.Resources" %> +<%@ Import Namespace="ASC.Web.Core.Users" %> +<%@ Import Namespace="ASC.Web.Studio.Utility" %> + + + + +
    +
    +
    + +
    <%= PeopleResource.CardDavSettingsHeader %>
    + +
    +
    + +
    + + + + +
    + +
    +
    +

    <%: PeopleResource.CardDavSettingsDescription %>

    +
    +
    +
    + + +
    diff --git a/web/studio/ASC.Web.Studio/Products/People/CardDavSettings.aspx.cs b/web/studio/ASC.Web.Studio/Products/People/CardDavSettings.aspx.cs new file mode 100644 index 000000000..b8baf8094 --- /dev/null +++ b/web/studio/ASC.Web.Studio/Products/People/CardDavSettings.aspx.cs @@ -0,0 +1,63 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System; +using System.Web; + +using ASC.Common.Radicale; +using ASC.Common.Radicale.Core; +using ASC.Core; +using ASC.Web.Core; +using ASC.Web.Studio; + +namespace ASC.Web.People +{ + public partial class CardDavSettings : MainPage + { + protected bool IsDisabled { get; set; } + protected bool IsEnabledLink { get; set; } + + protected string CardDavLink { get; set; } + protected void Page_Load(object sender, EventArgs e) + { + + IsDisabled = WebItemManager.Instance[WebItemManager.PeopleProductID].IsDisabled(); + if (IsDisabled) return; + + RenderScripts(); + var dao = new DbRadicale(); + + IsEnabledLink = dao.IsExistCardDavUser(CoreContext.TenantManager.GetCurrentTenant().TenantId, SecurityContext.CurrentAccount.ID.ToString()); + + if (IsEnabledLink) + { + var myUri = (HttpContext.Current != null) ? HttpContext.Current.Request.GetUrlRewriter().ToString() : "http://localhost"; + var cardDavAddBook = new CardDavAddressbook(); + CardDavLink = cardDavAddBook.GetRadicaleUrl(myUri, CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID).Email, true, true, true); + } + + + } + + protected void RenderScripts() + { + Page + .RegisterStyle("~/Products/People/App_Themes/default/css/carddav.css") + .RegisterBodyScripts("~/Products/People/js/carddav.js"); + } + } +} \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/People/CardDavSettings.aspx.designer.cs b/web/studio/ASC.Web.Studio/Products/People/CardDavSettings.aspx.designer.cs new file mode 100644 index 000000000..5befe1d69 --- /dev/null +++ b/web/studio/ASC.Web.Studio/Products/People/CardDavSettings.aspx.designer.cs @@ -0,0 +1,17 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ASC.Web.People +{ + + + public partial class CardDavSettings + { + } +} diff --git a/web/studio/ASC.Web.Studio/Products/People/Classes/BirthdaysModule.cs b/web/studio/ASC.Web.Studio/Products/People/Classes/BirthdaysModule.cs new file mode 100644 index 000000000..0cb930850 --- /dev/null +++ b/web/studio/ASC.Web.Studio/Products/People/Classes/BirthdaysModule.cs @@ -0,0 +1,83 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System; + +using ASC.Web.Core; +using ASC.Web.Core.ModuleManagement; +using ASC.Web.People.Core; +using ASC.Web.People.Resources; + + +namespace ASC.Web.People +{ + public class BirthdaysModule : Module + { + private static readonly object locker = new object(); + private static bool registered; + + public override Guid ID + { + get { return WebItemManager.BirthdaysProductID; } + } + + public override Guid ProjectId + { + get { return PeopleProduct.ID; } + } + + public override string Name + { + get { return BirthdaysResource.BirthdaysModuleTitle; } + } + + public override string Description + { + get { return BirthdaysResource.BirthdaysModuleDescription; } + } + + public override string StartURL + { + get { return "~/Products/People/Birthdays.aspx?sysname=/modules/birthdays"; } + } + + public BirthdaysModule() + { + Context = new ModuleContext + { + DefaultSortOrder = 51, + SmallIconFileName = string.Empty, + IconFileName = string.Empty + }; + } + + public static void RegisterSendMethod() + { + if (!registered) + { + lock (locker) + { + if (!registered) + { + registered = true; + BirthdaysNotifyClient.Instance.RegisterSendMethod(); + } + } + } + } + } +} \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Classes/BirthdaysNotifyClient.cs b/web/studio/ASC.Web.Studio/Products/People/Classes/BirthdaysNotifyClient.cs similarity index 86% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Classes/BirthdaysNotifyClient.cs rename to web/studio/ASC.Web.Studio/Products/People/Classes/BirthdaysNotifyClient.cs index 0811d1b83..3c47b731e 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Classes/BirthdaysNotifyClient.cs +++ b/web/studio/ASC.Web.Studio/Products/People/Classes/BirthdaysNotifyClient.cs @@ -1,200 +1,199 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.Linq; - -using ASC.Common.Data; -using ASC.Common.Data.Sql; -using ASC.Common.Data.Sql.Expressions; -using ASC.Common.Logging; -using ASC.Core; -using ASC.Core.Billing; -using ASC.Core.Notify; -using ASC.Core.Tenants; -using ASC.Core.Users; -using ASC.Notify; -using ASC.Notify.Model; -using ASC.Notify.Patterns; -using ASC.Notify.Recipients; -using ASC.Web.Community.Modules.Birthdays.Resources; -using ASC.Web.Core; -using ASC.Web.Studio.Core.Notify; -using ASC.Web.Studio.Utility; - - -namespace ASC.Web.Community.Birthdays -{ - public class BirthdaysNotifyClient - { - private readonly INotifyClient client; - - public static INotifyAction Event_BirthdayReminder = new NotifyAction("BirthdayReminder"); - - public static BirthdaysNotifyClient Instance - { - get; - private set; - } - - public static INotifySource NotifySource - { - get; - private set; - } - - static BirthdaysNotifyClient() - { - NotifySource = new BirthdaysNotifySource(); - Instance = new BirthdaysNotifyClient(WorkContext.NotifyContext.NotifyService.RegisterClient(NotifySource)); - } - - private BirthdaysNotifyClient(INotifyClient client) - { - this.client = client; - } - - public void RegisterSendMethod() - { - client.RegisterSendMethod(SendBirthdayReminders, "0 0 12 ? * *"); - } - - public void SetSubscription(Guid subscriber, Guid to, bool subscribe) - { - var subscriptions = NotifySource.GetSubscriptionProvider(); - var recipient = ToRecipient(subscriber); - if (subscribe) - { - subscriptions.Subscribe(Event_BirthdayReminder, to.ToString(), recipient); - } - else - { - subscriptions.UnSubscribe(Event_BirthdayReminder, to.ToString(), recipient); - } - } - - public bool IsSubscribe(Guid subscriber, Guid to) - { - return NotifySource.GetSubscriptionProvider() - .IsSubscribed(Event_BirthdayReminder, ToRecipient(subscriber), to.ToString()); - } - - public void SendBirthdayReminders(DateTime scheduleDate) - { - try - { - scheduleDate = scheduleDate.AddDays(1); - List tenants; - using (var db = DbManager.FromHttpContext("core")) - using (var command = db.Connection.CreateCommand()) - { - command.CommandTimeout = 30 * 10; - var q = new SqlQuery("core_user") - .Select("tenant") - .Where(!Exp.Eq("bithdate", null)) - .Where("date_format(bithdate, '%m%d')", scheduleDate.ToString("MMdd")) - .Where("removed", 0) - .Where("status", EmployeeStatus.Active) - .GroupBy(1); - tenants = command.ExecuteList(q, DbRegistry.GetSqlDialect(db.DatabaseId)) - .ConvertAll(r => Convert.ToInt32(r[0])) - .Select(id => CoreContext.TenantManager.GetTenant(id)) - .ToList(); - } - - foreach (var tenant in tenants) - { - if (tenant == null || - tenant.Status != TenantStatus.Active || - TariffState.NotPaid <= CoreContext.PaymentManager.GetTariff(tenant.TenantId).State) - { - continue; - } - - CoreContext.TenantManager.SetCurrentTenant(tenant); - - if (!WebItemSecurity.GetSecurityInfo(WebItemManager.CommunityProductID.ToString()).Enabled || - !WebItemSecurity.GetSecurityInfo(WebItemManager.BirthdaysProductID.ToString()).Enabled) - { - continue; - } - - var users = CoreContext.UserManager.GetUsers(EmployeeStatus.Active, EmployeeType.User); - var birthdays = users.Where(u => u.BirthDate.HasValue && u.BirthDate.Value.Month == scheduleDate.Month && u.BirthDate.Value.Day == scheduleDate.Day); - var subscriptionProvider = NotifySource.GetSubscriptionProvider(); - - foreach (var user in users) - { - if (WebItemManager.Instance[WebItemManager.CommunityProductID].IsDisabled(user.ID)) - { - continue; - } - - var allSubscription = subscriptionProvider.IsSubscribed(Event_BirthdayReminder, user, null); - foreach (var birthday in birthdays) - { - if (user.Equals(birthday)) continue; - - if ((allSubscription && !subscriptionProvider.IsUnsubscribe(user, Event_BirthdayReminder, birthday.ID.ToString())) || - (!allSubscription && subscriptionProvider.IsSubscribed(Event_BirthdayReminder, user, birthday.ID.ToString()))) - { - client.SendNoticeToAsync( - Event_BirthdayReminder, - birthday.ID.ToString(), - new[] { user }, - true, - new TagValue("BirthdayUserName", birthday.DisplayUserName(false)), - new TagValue("BirthdayUserUrl", CommonLinkUtility.GetUserProfile(birthday.ID)), - new TagValue("BirthdayDate", birthday.BirthDate.Value.ToShortDayMonth()), - new TagValue(CommonTags.Priority, 1)); - } - } - } - } - } - catch (Exception ex) - { - LogManager.GetLogger("ASC").Error(ex); - } - } - - private IRecipient ToRecipient(Guid userID) - { - return NotifySource.GetRecipientsProvider().GetRecipient(userID.ToString()); - } - - - private class BirthdaysNotifySource : NotifySource - { - public BirthdaysNotifySource() - : base(BirthdaysModule.ModuleId) - { - } - - protected override IActionProvider CreateActionProvider() - { - return new ConstActionProvider(Event_BirthdayReminder); - } - - protected override IPatternProvider CreatePatternsProvider() - { - return new XmlPatternProvider2(BirthdayPatternResource.patterns); - } - } - } -} +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System; +using System.Collections.Generic; +using System.Linq; + +using ASC.Common.Data; +using ASC.Common.Data.Sql; +using ASC.Common.Data.Sql.Expressions; +using ASC.Common.Logging; +using ASC.Core; +using ASC.Core.Billing; +using ASC.Core.Tenants; +using ASC.Core.Users; +using ASC.Notify; +using ASC.Notify.Model; +using ASC.Notify.Patterns; +using ASC.Notify.Recipients; +using ASC.Web.Core; +using ASC.Web.People.Resources; +using ASC.Web.Studio.Core.Notify; +using ASC.Web.Studio.Utility; + +using NotifySourceBase = ASC.Core.Notify.NotifySource; + +namespace ASC.Web.People +{ + public class BirthdaysNotifyClient + { + private readonly INotifyClient client; + public static INotifyAction Event_BirthdayReminder = new NotifyAction("BirthdayReminder"); + + public static BirthdaysNotifyClient Instance + { + get; + private set; + } + + public static INotifySource NotifySource + { + get; + private set; + } + + static BirthdaysNotifyClient() + { + NotifySource = new BirthdaysNotifySource(); + Instance = new BirthdaysNotifyClient(WorkContext.NotifyContext.NotifyService.RegisterClient(NotifySource)); + } + + private BirthdaysNotifyClient(INotifyClient client) + { + this.client = client; + } + + public void RegisterSendMethod() + { + client.RegisterSendMethod(SendBirthdayReminders, "0 0 12 ? * *"); + } + + public void SetSubscription(Guid subscriber, Guid to, bool subscribe) + { + var subscriptions = NotifySource.GetSubscriptionProvider(); + var recipient = ToRecipient(subscriber); + if (subscribe) + { + subscriptions.Subscribe(Event_BirthdayReminder, to.ToString(), recipient); + } + else + { + subscriptions.UnSubscribe(Event_BirthdayReminder, to.ToString(), recipient); + } + } + + public bool IsSubscribe(Guid subscriber, Guid to) + { + return NotifySource.GetSubscriptionProvider() + .IsSubscribed(Event_BirthdayReminder, ToRecipient(subscriber), to.ToString()); + } + + public void SendBirthdayReminders(DateTime scheduleDate) + { + try + { + scheduleDate = scheduleDate.AddDays(1); + List tenants; + using (var db = DbManager.FromHttpContext("core")) + using (var command = db.Connection.CreateCommand()) + { + command.CommandTimeout = 30 * 10; + var q = new SqlQuery("core_user u") + .Select("u.tenant") + .InnerJoin("tenants_tenants tt", Exp.EqColumns("u.tenant", "tt.id")) + .Where(!Exp.Eq("u.bithdate", null)) + .Where("date_format(u.bithdate, '%m%d')", scheduleDate.ToString("MMdd")) + .Where("u.removed", 0) + .Where("u.status", EmployeeStatus.Active) + .Where("tt.status", TenantStatus.Active) + .GroupBy(1); + tenants = command.ExecuteList(q, DbRegistry.GetSqlDialect(db.DatabaseId)) + .ConvertAll(r => Convert.ToInt32(r[0])) + .Select(id => CoreContext.TenantManager.GetTenant(id)) + .ToList(); + } + + foreach (var tenant in tenants) + { + if (tenant == null || + tenant.Status != TenantStatus.Active || + TariffState.NotPaid <= CoreContext.PaymentManager.GetTariff(tenant.TenantId).State) + { + continue; + } + + CoreContext.TenantManager.SetCurrentTenant(tenant); + + if (!WebItemSecurity.GetSecurityInfo(WebItemManager.PeopleProductID.ToString()).Enabled) + { + continue; + } + + var users = CoreContext.UserManager.GetUsers(EmployeeStatus.Active, EmployeeType.User); + var birthdays = users.Where(u => u.BirthDate.HasValue && u.BirthDate.Value.Month == scheduleDate.Month && u.BirthDate.Value.Day == scheduleDate.Day); + var subscriptionProvider = NotifySource.GetSubscriptionProvider(); + + foreach (var user in users) + { + if (WebItemManager.Instance[WebItemManager.PeopleProductID].IsDisabled(user.ID)) + { + continue; + } + + var allSubscription = subscriptionProvider.IsSubscribed(Event_BirthdayReminder, user, null); + foreach (var birthday in birthdays) + { + if (user.Equals(birthday)) continue; + + if ((allSubscription && !subscriptionProvider.IsUnsubscribe(user, Event_BirthdayReminder, birthday.ID.ToString())) || + (!allSubscription && subscriptionProvider.IsSubscribed(Event_BirthdayReminder, user, birthday.ID.ToString()))) + { + client.SendNoticeToAsync( + Event_BirthdayReminder, + birthday.ID.ToString(), + new[] { user }, + true, + new TagValue("BirthdayUserName", birthday.DisplayUserName(false)), + new TagValue("BirthdayUserUrl", CommonLinkUtility.GetUserProfile(birthday.ID)), + new TagValue("BirthdayDate", birthday.BirthDate.Value.ToShortDayMonth()), + new TagValue(CommonTags.Priority, 1)); + } + } + } + } + } + catch (Exception ex) + { + LogManager.GetLogger("ASC").Error(ex); + } + } + + private IRecipient ToRecipient(Guid userID) + { + return NotifySource.GetRecipientsProvider().GetRecipient(userID.ToString()); + } + + private class BirthdaysNotifySource : NotifySourceBase + { + public BirthdaysNotifySource() : base(WebItemManager.BirthdaysProductID) + { + + } + + protected override IActionProvider CreateActionProvider() + { + return new ConstActionProvider(Event_BirthdayReminder); + } + + protected override IPatternProvider CreatePatternsProvider() + { + return new XmlPatternProvider2(BirthdayPatternResource.patterns); + } + } + } +} diff --git a/web/studio/ASC.Web.Studio/Products/People/Classes/PeopleSubscriptionManager.cs b/web/studio/ASC.Web.Studio/Products/People/Classes/PeopleSubscriptionManager.cs new file mode 100644 index 000000000..ef29434a3 --- /dev/null +++ b/web/studio/ASC.Web.Studio/Products/People/Classes/PeopleSubscriptionManager.cs @@ -0,0 +1,70 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System; +using System.Collections.Generic; + +using ASC.Notify.Model; +using ASC.Web.People.Resources; +using ASC.Web.Core.Subscriptions; + +namespace ASC.Web.People +{ + public class PeopleSubscriptionManager : IProductSubscriptionManager + { + public List GetSubscriptionObjects(Guid subItem) + { + return new List(); + } + + public List GetSubscriptionTypes() + { + var subscriptionTypes = new List(); + + subscriptionTypes.Add(new SubscriptionType() + { + ID = new Guid("{3177E937-9189-45db-BA7C-916C69C4A574}"), + Name = BirthdaysResource.BirthdaysSubscribeAll, + NotifyAction = BirthdaysNotifyClient.Event_BirthdayReminder, + Single = true, + IsEmptySubscriptionType = new IsEmptySubscriptionTypeDelegate(IsEmptySubscriptionType), + CanSubscribe = true + }); + return subscriptionTypes; + } + + private bool IsEmptySubscriptionType(Guid productID, Guid moduleID, Guid typeID) + { + return false; + } + + public ISubscriptionProvider SubscriptionProvider + { + get { return BirthdaysNotifyClient.NotifySource.GetSubscriptionProvider(); } + } + + public GroupByType GroupByType + { + get { return GroupByType.Simple; } + } + + public List GetSubscriptionGroups() + { + return new List(); + } + } +} diff --git a/web/studio/ASC.Web.Studio/Products/People/Core/Import/EncodingEnum.cs b/web/studio/ASC.Web.Studio/Products/People/Core/Import/EncodingEnum.cs index 03a98eda1..e0b66261d 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Core/Import/EncodingEnum.cs +++ b/web/studio/ASC.Web.Studio/Products/People/Core/Import/EncodingEnum.cs @@ -41,7 +41,6 @@ namespace ASC.Web.People.Core.Import public enum ImportFromWhatEnum { FromFile, - Google, - Yahoo + Google } } \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/People/Core/Import/ImportUsersTask.cs b/web/studio/ASC.Web.Studio/Products/People/Core/Import/ImportUsersTask.cs index 060a43a0b..03e84b3c9 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Core/Import/ImportUsersTask.cs +++ b/web/studio/ASC.Web.Studio/Products/People/Core/Import/ImportUsersTask.cs @@ -78,7 +78,7 @@ namespace ASC.Web.People.Core.Import { Status = (int)Operation.Success; CoreContext.TenantManager.SetCurrentTenant((int)Id); - SecurityContext.AuthenticateMe(UserId); + SecurityContext.CurrentUser = UserId; if (!SecurityContext.CheckPermissions(Constants.Action_AddRemoveUser)) { @@ -140,7 +140,7 @@ namespace ASC.Web.People.Core.Import LastName = userData.LastName }; - UserManagerWrapper.AddUser(userInfo, UserManagerWrapper.GeneratePassword(), false, true, isGuest); + UserManagerWrapper.AddUser(userInfo, UserManagerWrapper.GeneratePassword(), false, true, isGuest, false, true, true); var messageAction = isGuest ? MessageAction.GuestImported : MessageAction.UserImported; MessageService.Send(httpHeaders, messageAction, MessageTarget.Create(userInfo.ID), string.Format("{0} ({1})", userInfo.DisplayUserName(false), userInfo.Email)); @@ -157,5 +157,6 @@ namespace ASC.Web.People.Core.Import IsCompleted = true; } + } } \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/People/Core/PeopleProduct.cs b/web/studio/ASC.Web.Studio/Products/People/Core/PeopleProduct.cs index 5f391fe12..bf09f78a6 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Core/PeopleProduct.cs +++ b/web/studio/ASC.Web.Studio/Products/People/Core/PeopleProduct.cs @@ -96,7 +96,8 @@ namespace ASC.Web.People.Core LargeIconFileName = "product_logolarge.svg", DefaultSortOrder = 50, AdminOpportunities = () => PeopleResource.ProductAdminOpportunities.Split('|').ToList(), - UserOpportunities = () => PeopleResource.ProductUserOpportunities.Split('|').ToList() + UserOpportunities = () => PeopleResource.ProductUserOpportunities.Split('|').ToList(), + SubscriptionManager = new PeopleSubscriptionManager() }; SearchHandlerManager.Registry(new SearchHandler()); diff --git a/web/studio/ASC.Web.Studio/Products/People/Default.aspx.cs b/web/studio/ASC.Web.Studio/Products/People/Default.aspx.cs index 78117098b..dfb1a9d13 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Default.aspx.cs +++ b/web/studio/ASC.Web.Studio/Products/People/Default.aspx.cs @@ -19,7 +19,6 @@ using System; using System.Configuration; using ASC.Core; -using ASC.Core.Billing; using ASC.Core.Users; using ASC.Web.Core; using ASC.Web.People.Resources; @@ -60,7 +59,7 @@ namespace ASC.Web.People IsFreeTariff = quota.Free && !quota.Open; IsStandalone = CoreContext.Configuration.Standalone; - DisplayPayments = TenantExtra.EnableTariffSettings && (!CoreContext.Configuration.Standalone || quota.ActiveUsers != LicenseReader.MaxUserCount); + DisplayPayments = TenantExtra.EnableTariffSettings && (!CoreContext.Configuration.Standalone || quota.ActiveUsers != Constants.MaxEveryoneCount); if (DisplayPayments) { diff --git a/web/studio/ASC.Web.Studio/Products/People/Masters/ClientScripts/ClientLocalizationResources.cs b/web/studio/ASC.Web.Studio/Products/People/Masters/ClientScripts/ClientLocalizationResources.cs index d5adf7946..3b58f77a5 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Masters/ClientScripts/ClientLocalizationResources.cs +++ b/web/studio/ASC.Web.Studio/Products/People/Masters/ClientScripts/ClientLocalizationResources.cs @@ -85,11 +85,6 @@ namespace ASC.Web.People.Masters.ClientScripts importFromList.Add(new ImportDataSource(ImportFromWhatEnum.Google, PeopleResource.ImportFromGoogle)); } - if (Studio.ThirdParty.ImportContacts.Yahoo.Enable) - { - importFromList.Add(new ImportDataSource(ImportFromWhatEnum.Yahoo, PeopleResource.ImportFromYahoo)); - } - return importFromList; } diff --git a/web/studio/ASC.Web.Studio/Products/People/Masters/PeopleBaseTemplate.Master.cs b/web/studio/ASC.Web.Studio/Products/People/Masters/PeopleBaseTemplate.Master.cs index ea6a8007f..d0cf978bc 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Masters/PeopleBaseTemplate.Master.cs +++ b/web/studio/ASC.Web.Studio/Products/People/Masters/PeopleBaseTemplate.Master.cs @@ -39,6 +39,8 @@ namespace ASC.Web.People.Masters //ControlHolder.Controls.Add(new ImportUsersWebControl()); ControlHolder.Controls.Add(LoadControl(ResendInvitesControl.Location)); + ControlHolder.Controls.Add(LoadControl(Studio.UserControls.Management.TariffLimitExceed.Location)); + Master .AddClientScript( new ClientSettingsResources(), @@ -52,7 +54,7 @@ namespace ASC.Web.People.Masters .AddStaticStyles(GetStaticStyleSheet()) .AddStaticBodyScripts(GetStaticJavaScript()) .RegisterInlineScript( - "jQuery(document.body).children('form').bind('submit', function() { return false; });"); + "jQuery(document.body).children('form').on('submit', function() { return false; });"); } public ScriptBundleData GetStaticJavaScript() diff --git a/web/studio/ASC.Web.Studio/Products/People/Profile.aspx b/web/studio/ASC.Web.Studio/Products/People/Profile.aspx index f72103ee6..4e4fef264 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Profile.aspx +++ b/web/studio/ASC.Web.Studio/Products/People/Profile.aspx @@ -46,5 +46,18 @@
    + +
    +
    + <%= PeopleResource.LblActiveConnections %> + + <%= Resource.Show %> + +
    + +
    <% } %> \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/People/Profile.aspx.cs b/web/studio/ASC.Web.Studio/Products/People/Profile.aspx.cs index 504bdaa84..bbf7e46ad 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Profile.aspx.cs +++ b/web/studio/ASC.Web.Studio/Products/People/Profile.aspx.cs @@ -57,19 +57,8 @@ namespace ASC.Web.People if (ProfileHelper.UserInfo.IsMe()) { InitSubscriptionView(); + InitConnectionsView(); InitTipsSettingsView(); - - var script = new StringBuilder(); - script.Append("jq('#switcherSubscriptionButton').one('click',"); - script.Append("function() {"); - script.Append("if (!jq('#subscriptionBlockContainer').hasClass('subsLoaded') &&"); - script.Append("typeof (window.CommonSubscriptionManager) != 'undefined' &&"); - script.Append("typeof (window.CommonSubscriptionManager.LoadSubscriptions) === 'function') {"); - script.Append("window.CommonSubscriptionManager.LoadSubscriptions();"); - script.Append("jq('#subscriptionBlockContainer').addClass('subsLoaded');"); - script.Append("}});"); - - Page.RegisterInlineScript(script.ToString()); } } @@ -78,6 +67,11 @@ namespace ASC.Web.People _phSubscriptionView.Controls.Add(LoadControl(UserSubscriptions.Location)); } + private void InitConnectionsView() + { + _phConnectionsView.Controls.Add(LoadControl(UserConnections.Location)); + } + private void InitTipsSettingsView() { if (!string.IsNullOrEmpty(Studio.Core.SetupInfo.TipsAddress)) diff --git a/web/studio/ASC.Web.Studio/Products/People/Profile.aspx.designer.cs b/web/studio/ASC.Web.Studio/Products/People/Profile.aspx.designer.cs index 7624f480e..d5ddaf6f2 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Profile.aspx.designer.cs +++ b/web/studio/ASC.Web.Studio/Products/People/Profile.aspx.designer.cs @@ -7,11 +7,13 @@ // //------------------------------------------------------------------------------ -namespace ASC.Web.People { - - - public partial class Profile { - +namespace ASC.Web.People +{ + + + public partial class Profile + { + /// /// actionsHolder control. /// @@ -20,7 +22,7 @@ namespace ASC.Web.People { /// To modify move field declaration from designer file to code-behind file. /// protected global::System.Web.UI.WebControls.PlaceHolder actionsHolder; - + /// /// CommonContainerHolder control. /// @@ -29,7 +31,7 @@ namespace ASC.Web.People { /// To modify move field declaration from designer file to code-behind file. /// protected global::System.Web.UI.WebControls.PlaceHolder CommonContainerHolder; - + /// /// _phSubscriptionView control. /// @@ -38,7 +40,7 @@ namespace ASC.Web.People { /// To modify move field declaration from designer file to code-behind file. /// protected global::System.Web.UI.WebControls.PlaceHolder _phSubscriptionView; - + /// /// _phTipsSettingsView control. /// @@ -47,5 +49,14 @@ namespace ASC.Web.People { /// To modify move field declaration from designer file to code-behind file. /// protected global::System.Web.UI.WebControls.PlaceHolder _phTipsSettingsView; + + /// + /// _phConnectionsView control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.PlaceHolder _phConnectionsView; } } diff --git a/web/studio/ASC.Web.Studio/Products/People/Properties/AssemblyInfo.cs b/web/studio/ASC.Web.Studio/Products/People/Properties/AssemblyInfo.cs index 569054c0f..d6149382b 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Properties/AssemblyInfo.cs +++ b/web/studio/ASC.Web.Studio/Products/People/Properties/AssemblyInfo.cs @@ -35,3 +35,4 @@ using ASC.Web.Core; [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: Product(typeof(ASC.Web.People.Core.PeopleProduct))] +[assembly: Product(typeof(ASC.Web.People.BirthdaysModule))] diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.Designer.cs b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.Designer.cs similarity index 89% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.Designer.cs rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.Designer.cs index e7a79aed4..a151962ac 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.Designer.cs +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.Designer.cs @@ -1,112 +1,112 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace ASC.Web.Community.Modules.Birthdays.Resources { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class BirthdayPatternResource { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal BirthdayPatternResource() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ASC.Web.Community.Modules.Birthdays.Resources.BirthdayPatternResource", typeof(BirthdayPatternResource).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to h1."$BirthdayUserName":"$BirthdayUserUrl" has the Birthday $BirthdayDate - /// - ///The following "${__VirtualRootPath}":"${__VirtualRootPath}" portal user has the birthday $BirthdayDate - ///$BirthdayUserName - /// - ///Visit the "${__VirtualRootPath}":"${__VirtualRootPath}" portal to view the event or send your birthday greetings. - /// - ///^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. If you do not want to receive the notifications about the new events on the [rest of string was truncated]";. - /// - public static string pattern_BirthdayReminder { - get { - return ResourceManager.GetString("pattern_BirthdayReminder", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to <patterns> - /// <formatter type="ASC.Notify.Patterns.NVelocityPatternFormatter, ASC.Common" /> - /// - /// <!--Milestone Due Date Notification--> - /// <pattern id="BirthdayReminder" sender="email.sender"> - /// <subject resource="|subject_BirthdayReminder|ASC.Web.Community.Modules.Birthdays.Resources.BirthdayPatternResource,ASC.Web.Community" /> - /// <body styler="ASC.Notify.Textile.TextileStyler,ASC.Notify.Textile" resource="|pattern_BirthdayReminder|ASC.Web.Community.Modules.Birthdays.Resources.BirthdayPatternResourc [rest of string was truncated]";. - /// - public static string patterns { - get { - return ResourceManager.GetString("patterns", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to $BirthdayUserName has the birthday $BirthdayDate. - /// - public static string subject_BirthdayReminder { - get { - return ResourceManager.GetString("subject_BirthdayReminder", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to [$BirthdayUserName]($BirthdayUserUrl) has the birthday $BirthdayDate. - /// - public static string subject_BirthdayReminder_tg { - get { - return ResourceManager.GetString("subject_BirthdayReminder_tg", resourceCulture); - } - } - } -} +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ASC.Web.People.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class BirthdayPatternResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal BirthdayPatternResource() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ASC.Web.People.Resources.BirthdayPatternResource", typeof(BirthdayPatternResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to h1."$BirthdayUserName":"$BirthdayUserUrl" has the Birthday $BirthdayDate + /// + ///The following "${__VirtualRootPath}":"${__VirtualRootPath}" portal user has the birthday $BirthdayDate + ///$BirthdayUserName + /// + ///Visit the "${__VirtualRootPath}":"${__VirtualRootPath}" portal to view the event or send your birthday greetings. + /// + ///^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. If you do not want to receive the notifications about the new events on the [rest of string was truncated]";. + /// + public static string pattern_BirthdayReminder { + get { + return ResourceManager.GetString("pattern_BirthdayReminder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to <patterns> + /// <formatter type="ASC.Notify.Patterns.NVelocityPatternFormatter, ASC.Common" /> + /// + /// <!--Milestone Due Date Notification--> + /// <pattern id="BirthdayReminder" sender="email.sender"> + /// <subject resource="|subject_BirthdayReminder|ASC.Web.People.Resources.BirthdayPatternResource,ASC.Web.People" /> + /// <body styler="ASC.Notify.Textile.TextileStyler,ASC.Notify.Textile" resource="|pattern_BirthdayReminder|ASC.Web.People.Resources.BirthdayPatternResource,ASC.Web [rest of string was truncated]";. + /// + public static string patterns { + get { + return ResourceManager.GetString("patterns", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to $BirthdayUserName has the birthday $BirthdayDate. + /// + public static string subject_BirthdayReminder { + get { + return ResourceManager.GetString("subject_BirthdayReminder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to [$BirthdayUserName]($BirthdayUserUrl) has the birthday $BirthdayDate. + /// + public static string subject_BirthdayReminder_tg { + get { + return ResourceManager.GetString("subject_BirthdayReminder_tg", resourceCulture); + } + } + } +} diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.az-Latn-AZ.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.az-Latn-AZ.resx new file mode 100644 index 000000000..d89586410 --- /dev/null +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.az-Latn-AZ.resx @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + h1."$BirthdayUserName":"$BirthdayUserUrl" adlı istifadəçinin ad günüdür: $BirthdayDate + +"${__VirtualRootPath}":"${__VirtualRootPath}" portal istifadəçisinin ad günüdür: $BirthdayDate +Tədbirə baxmaq və ad günü arzularınızı göndərmək üçün "${__VirtualRootPath}":"${__VirtualRootPath}" portalına daxil olun. + +^Siz "${__VirtualRootPath}":"${__VirtualRootPath}" portalının qeydiyyatdan keçmiş üzvü olduğunuz üçün bu e-məktubu alırsınız. Yeni "${__VirtualRootPath}":"${__VirtualRootPath}" portal tədbirləri haqqında bu bildirişləri almaq istəmirsinizsə:, abunə "parametrlərinizi konfiqurasiya edin":"$RecipientSubscriptionConfigURL".^ + + + $BirthdayUserName adlı istifadəçinin ad günüdür: $BirthdayDate + + + [$BirthdayUserName]($BirthdayUserUrl) adlı istifadəçinin ad günüdür: $BirthdayDate + + diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.be.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.be.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.be.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.be.resx index 47023ae44..ef3db3470 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.be.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.be.resx @@ -1,67 +1,67 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Карыстальнік $BirthdayUserName адзначае дзень народзінаў $BirthdayDate - - - [$BirthdayUserName]($BirthdayUserUrl) адзначае дзень народзінаў $BirthdayDate - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Карыстальнік $BirthdayUserName адзначае дзень народзінаў $BirthdayDate + + + [$BirthdayUserName]($BirthdayUserUrl) адзначае дзень народзінаў $BirthdayDate + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.bg.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.bg.resx similarity index 98% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.bg.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.bg.resx index 69ae38481..30cad3dc4 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.bg.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.bg.resx @@ -1,67 +1,67 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - "$BirthdayUserName":"$BirthdayUserUrl" има рожден ден $BirthdayDate Следният "${__VirtualRootPath}":"${__VirtualRootPath}" потребител на портала има рожден ден $BirthdayDate $BirthdayUserName Посетете "${__VirtualRootPath}":" ${__VirtualRootPath} "портал за преглед на събитието или изпращане на поздравления за рожден ден. ^ Получавате този имейл, защото сте регистриран потребител на портала "${__VirtualRootPath}":"${__VirtualRootPath}". Ако не искате да получавате известия за новите събития в портала "${__VirtualRootPath}":"${__VirtualRootPath}", моля, управлявайте своите "настройки за абонамент":"$RecipientSubscriptionConfigURL". ^ - - - $BirthdayUserName има рождената дата за рожден ден - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + "$BirthdayUserName":"$BirthdayUserUrl" има рожден ден $BirthdayDate Следният "${__VirtualRootPath}":"${__VirtualRootPath}" потребител на портала има рожден ден $BirthdayDate $BirthdayUserName Посетете "${__VirtualRootPath}":" ${__VirtualRootPath} "портал за преглед на събитието или изпращане на поздравления за рожден ден. ^ Получавате този имейл, защото сте регистриран потребител на портала "${__VirtualRootPath}":"${__VirtualRootPath}". Ако не искате да получавате известия за новите събития в портала "${__VirtualRootPath}":"${__VirtualRootPath}", моля, управлявайте своите "настройки за абонамент":"$RecipientSubscriptionConfigURL". ^ + + + $BirthdayUserName има рождената дата за рожден ден + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.cs.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.cs.resx similarity index 98% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.cs.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.cs.resx index d81960f23..1b39b40d1 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.cs.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.cs.resx @@ -1,64 +1,64 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + h1."$BirthdayUserName":"$BirthdayUserUrl" má narozeniny $BirthdayDate Následující "${__VirtualRootPath}":"${__VirtualRootPath}" uživatel portálu má narozeniny $BirthdayDate @@ -66,9 +66,9 @@ $BirthdayUserName Navštivte "${__VirtualRootPath}":"${__VirtualRootPath}" portál pro zobrazení události nebo odeslání pozdravů k narozeninám. -^Tento e-mail jste obdržel/a, protože jste registrovaným uživatelem "${__VirtualRootPath}":"${__VirtualRootPath}" portálu. Pokud nechcete dostávat oznámení o nových událostech na "${__VirtualRootPath}":"${__VirtualRootPath}" portále, prosím upravte Vaše "nastavení odběru":"$RecipientSubscriptionConfigURL".^ - - - $BirthdayUserName má narozeniny $BirthdayDate - +^Tento e-mail jste obdržel/a, protože jste registrovaným uživatelem "${__VirtualRootPath}":"${__VirtualRootPath}" portálu. Pokud nechcete dostávat oznámení o nových událostech na "${__VirtualRootPath}":"${__VirtualRootPath}" portále, prosím upravte Vaše "nastavení odběru":"$RecipientSubscriptionConfigURL".^ + + + $BirthdayUserName má narozeniny $BirthdayDate + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.de.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.de.resx similarity index 98% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.de.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.de.resx index a1cb5509a..a3babc1ba 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.de.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.de.resx @@ -1,64 +1,64 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + h1."$BirthdayUserName":"$BirthdayUserUrl" hat Geburtstag am $BirthdayDate Das folgende Mitglied des Portals "${__VirtualRootPath}":"${__VirtualRootPath}" hat Geburtstag am $BirthdayDate: @@ -66,12 +66,12 @@ $BirthdayUserName Besuchen Sie das Portal "${__VirtualRootPath}":"${__VirtualRootPath}", um das Ereignis zu sehen oder dem Portalmitglied zum Geburtstag zu gratulieren. -^Sie erhalten diese Nachricht, weil Sie ein registrierter Benutzer bzw. Benutzerin des Portals "${__VirtualRootPath}":"${__VirtualRootPath}" sind. Wenn Sie keine Benachrichtigungen über die Geburtstage auf dem Portal "${__VirtualRootPath}":"${__VirtualRootPath}" erhalten möchten, verwalten Sie Ihre "Einstellungen Ihrer Benachrichtigungen":"$RecipientSubscriptionConfigURL".^ - - - $BirthdayUserName hat Geburtstag am $BirthdayDate - - - [$BirthdayUserName]($BirthdayUserUrl) hat am $BirthdayDate Geburtstag - +^Sie erhalten diese Nachricht, weil Sie ein registrierter Benutzer bzw. Benutzerin des Portals "${__VirtualRootPath}":"${__VirtualRootPath}" sind. Wenn Sie keine Benachrichtigungen über die Geburtstage auf dem Portal "${__VirtualRootPath}":"${__VirtualRootPath}" erhalten möchten, verwalten Sie Ihre "Einstellungen Ihrer Benachrichtigungen":"$RecipientSubscriptionConfigURL".^ + + + $BirthdayUserName hat Geburtstag am $BirthdayDate + + + [$BirthdayUserName]($BirthdayUserUrl) hat am $BirthdayDate Geburtstag + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.el.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.el.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.el.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.el.resx index b26b4b742..5093fa487 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.el.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.el.resx @@ -1,67 +1,67 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Ο/Η $BirthdayUserName έχει τα γενέθλιά του $BirthdayDate - - - Ο/Η[$BirthdayUserName]($BirthdayUserUrl) έχει γενέθλιά του/της την $BirthdayDate - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Ο/Η $BirthdayUserName έχει τα γενέθλιά του $BirthdayDate + + + Ο/Η[$BirthdayUserName]($BirthdayUserUrl) έχει γενέθλιά του/της την $BirthdayDate + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.es.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.es.resx similarity index 98% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.es.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.es.resx index e74639e47..a57329964 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.es.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.es.resx @@ -1,64 +1,64 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + h1."$BirthdayUserName":"$BirthdayUserUrl" tiene el cumpleaños el $BirthdayDate El usuario del portal "${__VirtualRootPath}":"${__VirtualRootPath}" tiene el cumpleaños el $BirthdayDate @@ -66,12 +66,12 @@ $BirthdayUserName Pase al portal "${__VirtualRootPath}":"${__VirtualRootPath}" para ver el evento o felicitar al usuario. -^Usted recibe este correo electrónico porque es un usuario registrado de portal"${__VirtualRootPath}":"${__VirtualRootPath}". Si usted no quiere recibir las notificaciones sobre los cumpleaños "${__VirtualRootPath}":"${__VirtualRootPath}", por favor, gestione sus "ajustes de suscripción":"$RecipientSubscriptionConfigURL".^ - - - $BirthdayUserName tiene cumpleaños el $BirthdayDate - - - [$BirthdayUserName]($BirthdayUserUrl) tiene el cumpleaños $BirthdayDate - +^Usted recibe este correo electrónico porque es un usuario registrado de portal"${__VirtualRootPath}":"${__VirtualRootPath}". Si usted no quiere recibir las notificaciones sobre los cumpleaños "${__VirtualRootPath}":"${__VirtualRootPath}", por favor, gestione sus "ajustes de suscripción":"$RecipientSubscriptionConfigURL".^ + + + $BirthdayUserName tiene cumpleaños el $BirthdayDate + + + [$BirthdayUserName]($BirthdayUserUrl) tiene el cumpleaños $BirthdayDate + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.fi.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.fi.resx similarity index 98% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.fi.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.fi.resx index fc6f3053a..b5199db7c 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.fi.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.fi.resx @@ -1,73 +1,73 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + h1.Käyttäjällä "$BirthdayUserName":"$BirthdayUserUrl" on syntymäpäivä $BirthdayDate Sivuston "${__VirtualRootPath}":"${__VirtualRootPath}" käyttäjällä $BirthdayUserName on syntymäpäivä $BirthdayDate Siirry sivulle "${__VirtualRootPath}":"${__VirtualRootPath}" voidaksesi lähettää syntymäpäiväonnittelut. -^Sait tämän sähköpostin, koska olet sivuston "${__VirtualRootPath}":"${__VirtualRootPath}" rekisteröity käyttäjä. Jos et halua vastaanottaa näitä ilmoituksia, muuta "tilausasetuksiasi":"$RecipientSubscriptionConfigURL".^ - - - Käyttäjällä $BirthdayUserName on syntymäpäivä $BirthdayDate - +^Sait tämän sähköpostin, koska olet sivuston "${__VirtualRootPath}":"${__VirtualRootPath}" rekisteröity käyttäjä. Jos et halua vastaanottaa näitä ilmoituksia, muuta "tilausasetuksiasi":"$RecipientSubscriptionConfigURL".^ + + + Käyttäjällä $BirthdayUserName on syntymäpäivä $BirthdayDate + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.fr.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.fr.resx similarity index 98% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.fr.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.fr.resx index 2a355d7c6..99c060f3d 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.fr.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.fr.resx @@ -1,64 +1,64 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + h1."$BirthdayUserName":"$BirthdayUserUrl" a l'anniversaire $BirthdayDate L'utilisateur du portail "${__VirtualRootPath}":"${__VirtualRootPath}" a l'anniversaire $BirthdayDate @@ -66,12 +66,12 @@ $BirthdayUserName Visitez le portail "${__VirtualRootPath}":"${__VirtualRootPath}" pour visualiser l'événement ou envoyer vos salutations d'anniversaire. -^Vous recevez ce message car vous êtes un utilisateur enregistré du portail "${__VirtualRootPath}":"${__VirtualRootPath}". Si vous ne souhaitez pas recevoir les notifications sur les nouveaux événements du portail "${__VirtualRootPath}":"${__VirtualRootPath}", gérez vos "paramètres d'abonement":"$RecipientSubscriptionConfigURL".^ - - - $BirthdayUserName a l'anniversaire $BirthdayDate - - - [$BirthdayUserName]($BirthdayUserUrl) fête son anniversaire le $BirthdayDate - +^Vous recevez ce message car vous êtes un utilisateur enregistré du portail "${__VirtualRootPath}":"${__VirtualRootPath}". Si vous ne souhaitez pas recevoir les notifications sur les nouveaux événements du portail "${__VirtualRootPath}":"${__VirtualRootPath}", gérez vos "paramètres d'abonement":"$RecipientSubscriptionConfigURL".^ + + + $BirthdayUserName a l'anniversaire $BirthdayDate + + + [$BirthdayUserName]($BirthdayUserUrl) fête son anniversaire le $BirthdayDate + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.hi.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.hi.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.hi.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.hi.resx index 3639fc41e..9c1c660b5 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.hi.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.hi.resx @@ -1,64 +1,64 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $ BirthdayUserName जन्मदिन $ BirthdayDate है - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $ BirthdayUserName जन्मदिन $ BirthdayDate है + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.hu.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.hu.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.hu.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.hu.resx index 5a0ffee38..7e4587eee 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.hu.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.hu.resx @@ -1,64 +1,64 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $BirthdayUserName születésnapja: $BirthdayDate - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $BirthdayUserName születésnapja: $BirthdayDate + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.id.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.id.resx similarity index 98% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.id.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.id.resx index 4465ec01c..fb7576bd6 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.id.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.id.resx @@ -1,73 +1,73 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + h1."$BirthdayUserName":"$BirthdayUserUrl" Berulang Tahun pada $BirthdayDate Pengguna portal "${__VirtualRootPath}":"${__VirtualRootPath}" ini berulang tahun pada $BirthdayDate $BirthdayUserName Kunjungi portal "${__VirtualRootPath}":"${__VirtualRootPath}" untuk melihat acara atau mengirim ucapan selamat ulang tahun. -^Anda menerima email ini karena Anda adalah pengguna yang terdaftar di portal "${__VirtualRootPath}":"${__VirtualRootPath}" . Jik aanda tidak ingin menerima pemberitahuan mengenai acara baru di portal "${__VirtualRootPath}":"${__VirtualRootPath}" , silakan lakukan "pengaturan status berlangganan":"$RecipientSubscriptionConfigURL".^ - - - $BirthdayUserName Berulang Tahun pada $BirthdayDate - +^Anda menerima email ini karena Anda adalah pengguna yang terdaftar di portal "${__VirtualRootPath}":"${__VirtualRootPath}" . Jik aanda tidak ingin menerima pemberitahuan mengenai acara baru di portal "${__VirtualRootPath}":"${__VirtualRootPath}" , silakan lakukan "pengaturan status berlangganan":"$RecipientSubscriptionConfigURL".^ + + + $BirthdayUserName Berulang Tahun pada $BirthdayDate + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.it.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.it.resx similarity index 98% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.it.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.it.resx index 9623ff508..ba140a12f 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.it.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.it.resx @@ -1,76 +1,76 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + h1."$BirthdayUserName":"$BirthdayUserUrl" compie gli anni $BirthdayDate $BirthdayUserName compie gli anni $BirthdayDate Visita il portale "${__VirtualRootPath}":"${__VirtualRootPath}" per inviare i tuoi auguri. -^Hai ricevuto questo messaggio in quanto sei registrato nel portale "${__VirtualRootPath}":"${__VirtualRootPath}". Se non desideri ricevere le notifiche dei compleanni degli utenti del portale "${__VirtualRootPath}":"${__VirtualRootPath}", si prega di gestire le "opzioni di iscrizione":"$RecipientSubscriptionConfigURL".^ - - - $BirthdayUserName compie gli anni $BirthdayDate - - - [$BirthdayUserName]($BirthdayUserUrl) fa il compleanno il $BirthdayDate - +^Hai ricevuto questo messaggio in quanto sei registrato nel portale "${__VirtualRootPath}":"${__VirtualRootPath}". Se non desideri ricevere le notifiche dei compleanni degli utenti del portale "${__VirtualRootPath}":"${__VirtualRootPath}", si prega di gestire le "opzioni di iscrizione":"$RecipientSubscriptionConfigURL".^ + + + $BirthdayUserName compie gli anni $BirthdayDate + + + [$BirthdayUserName]($BirthdayUserUrl) fa il compleanno il $BirthdayDate + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.ja.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.ja.resx similarity index 98% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.ja.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.ja.resx index 3683cb627..498d8c71a 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.ja.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.ja.resx @@ -1,76 +1,76 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + h1. "$BirthdayUserName":"$BirthdayUserUrl"の誕生日は $BirthdayDateです。 "${__VirtualRootPath}":"${__VirtualRootPath}"ポータルユーザーの誕生日は$BirthdayDate $BirthdayUserNameです。 イベントを表示し、挨拶文を送るように"${__VirtualRootPath}":"${__VirtualRootPath}" ポータルを訪ねてください。 -^"${__VirtualRootPath}":"${__VirtualRootPath}" ポータルの登録ユーザーであるのためにそのメールを送信されました。通知型を変更するように、”サブスクリプション設定” を管理してください:"$RecipientSubscriptionConfigURL".^ - - - $BirthdayUserNameの誕生日は$BirthdayDateです - - - [$BirthdayUserName]($BirthdayUserUrl) のお誕生日は $BirthdayDate - +^"${__VirtualRootPath}":"${__VirtualRootPath}" ポータルの登録ユーザーであるのためにそのメールを送信されました。通知型を変更するように、”サブスクリプション設定” を管理してください:"$RecipientSubscriptionConfigURL".^ + + + $BirthdayUserNameの誕生日は$BirthdayDateです + + + [$BirthdayUserName]($BirthdayUserUrl) のお誕生日は $BirthdayDate + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.kk.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.kk.resx similarity index 98% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.kk.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.kk.resx index a28e2e51c..9682070a5 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.kk.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.kk.resx @@ -1,64 +1,64 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + h1."$BirthdayUserName " пайдаланушысы:"$BirthdayUserUrl " $BirthdayDate туған күнін атап өтеді $BirthdayDate "${_VirtualRootPath}": "${_VirtualRootPath}": @@ -66,9 +66,9 @@ $BirthdayUserName Бұл оқиғаны көру немесе құттықтау жіберу үшін "${_VirtualRootPath} " порталына кіріңіз. -^Сіз Бұл хабарламаны алды, өйткені "${_VirtualRootPath} " порталында тіркелген:"${_VirtualRootPath}". Егер сіз "${__VirtualRootPath} "порталында Туған күн туралы хабарлама алғыңыз келмесе:" ${_VirtualRootPath}", "жазылу параметрлерін" өзгертіңіз: "$RecipientSubscriptionConfigURL".^ - - - $ BirthdayUserName $ BirthdayDate туған күнін атап өтеді - +^Сіз Бұл хабарламаны алды, өйткені "${_VirtualRootPath} " порталында тіркелген:"${_VirtualRootPath}". Егер сіз "${__VirtualRootPath} "порталында Туған күн туралы хабарлама алғыңыз келмесе:" ${_VirtualRootPath}", "жазылу параметрлерін" өзгертіңіз: "$RecipientSubscriptionConfigURL".^ + + + $ BirthdayUserName $ BirthdayDate туған күнін атап өтеді + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.lv.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.lv.resx similarity index 98% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.lv.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.lv.resx index 7b66fa26b..ead7a0dfb 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.lv.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.lv.resx @@ -1,64 +1,64 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + h1.Lietotāja "$BirthdayUserName":"$BirthdayUserUrl" dzimšanas diena ir $BirthdayDate Šim "${__VirtualRootPath}":"${__VirtualRootPath}" portāla lietotājam ir dzimšanas diena $BirthdayDate @@ -66,9 +66,9 @@ $BirthdayUserName Apmeklējiet "${__VirtualRootPath}":"${__VirtualRootPath}" portālu, lai aplūkotu pasākumu vai nosūtītu apsveikumu. -^Jūs saņemat šo e-pastu, jo esat portāla "${__VirtualRootPath}":"${__VirtualRootPath}" lietotājs. Ja nevēlaties saņemt ziņojumus par jauniem portāla "${__VirtualRootPath}":"${__VirtualRootPath}" notikumiem, lūdzu, mainiet savus "abonementa uzstādījumus":"$RecipientSubscriptionConfigURL".^ - - - Lietotājs $BirthdayUserName svin dzimšanas dienu $BirthdayDate - +^Jūs saņemat šo e-pastu, jo esat portāla "${__VirtualRootPath}":"${__VirtualRootPath}" lietotājs. Ja nevēlaties saņemt ziņojumus par jauniem portāla "${__VirtualRootPath}":"${__VirtualRootPath}" notikumiem, lūdzu, mainiet savus "abonementa uzstādījumus":"$RecipientSubscriptionConfigURL".^ + + + Lietotājs $BirthdayUserName svin dzimšanas dienu $BirthdayDate + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.nl.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.nl.resx similarity index 98% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.nl.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.nl.resx index 1f459e1c7..6ffc160ab 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.nl.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.nl.resx @@ -1,64 +1,64 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + h1."$BirthdayUserName":"$BirthdayUserUrl" is jarig op $BirthdayDate The following "${__VirtualRootPath}":"${__VirtualRootPath}" portaalgebruiker is jarig op $BirthdayDate @@ -66,9 +66,9 @@ $BirthdayUserName Bezoek het "${__VirtualRootPath}":"${__VirtualRootPath}" portaal om de gebeurtenis te bekijken of uw felicitaties te versturen. -^U ontvangt deze e-mail omdat u een geregistreerde gebruiker bent van het "${__VirtualRootPath}":"${__VirtualRootPath}" portaal. Als u geen notificaties wilt ontvangen over nieuwe gebeurtenissen op het "${__VirtualRootPath}":"${__VirtualRootPath}" portaal, kunt u dit in uw "abonnementsinstellingen" beheren":"$RecipientSubscriptionConfigURL".^ - - - $BirthdayUserName heeft de verjaardag $BirthdayDate - +^U ontvangt deze e-mail omdat u een geregistreerde gebruiker bent van het "${__VirtualRootPath}":"${__VirtualRootPath}" portaal. Als u geen notificaties wilt ontvangen over nieuwe gebeurtenissen op het "${__VirtualRootPath}":"${__VirtualRootPath}" portaal, kunt u dit in uw "abonnementsinstellingen" beheren":"$RecipientSubscriptionConfigURL".^ + + + $BirthdayUserName heeft de verjaardag $BirthdayDate + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.pl.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.pl.resx similarity index 98% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.pl.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.pl.resx index 2717ffb4b..b80909596 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.pl.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.pl.resx @@ -1,64 +1,64 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + h1.Użytkownik "$BirthdayUserName":"$BirthdayUserUrl" ma urodziny $BirthdayDate $ BirthdayDate to dzień urodzin użytkownika portalu "${__VirtualRootPath}":"${__VirtualRootPath}": @@ -66,9 +66,9 @@ $ BirthdayUserName Przejdź do portalu "${__VirtualRootPath}":"${__VirtualRootPath}", aby zobaczyć wydarzenie lub przesłać pozdrowienia. -^Otrzymujesz tę wiadomość, ponieważ jesteś zarejestrowany na portalu "${__VirtualRootPath}":"${__VirtualRootPath}". Jeśli nie chcesz być powiadamiany o urodzinach na portalu "${__VirtualRootPath}":"${__VirtualRootPath}", proszę zmienić "Opcje subskrypcji":"$RecipientSubscriptionConfigURL"^. - - - $BirthdayUserName na urodziny $BirthdayDate - +^Otrzymujesz tę wiadomość, ponieważ jesteś zarejestrowany na portalu "${__VirtualRootPath}":"${__VirtualRootPath}". Jeśli nie chcesz być powiadamiany o urodzinach na portalu "${__VirtualRootPath}":"${__VirtualRootPath}", proszę zmienić "Opcje subskrypcji":"$RecipientSubscriptionConfigURL"^. + + + $BirthdayUserName na urodziny $BirthdayDate + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.pt-BR.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.pt-BR.resx similarity index 98% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.pt-BR.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.pt-BR.resx index fe3f1562a..ac915a866 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.pt-BR.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.pt-BR.resx @@ -1,64 +1,64 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + h1."$BirthdayUserName":"$BirthdayUserUrl" faz aniversário em $BirthdayDate O seguinte usuário do portal "${__VirtualRootPath}":"${__VirtualRootPath}" faz aniversário em $BirthdayDate @@ -66,12 +66,12 @@ $BirthdayUserName Visite o portal "${__VirtualRootPath}":"${__VirtualRootPath}" para ver o evento ou enviar seus cumprimentos de aniversário. -^Você recebeu este e-mail por que é um usuário registrado do portal "${__VirtualRootPath}":"${__VirtualRootPath}". Se você não quer receber notificações a respeito de novos eventos do portal "${__VirtualRootPath}":"${__VirtualRootPath}", por favor, altere seus "parâmetros de subscrição":"$RecipientSubscriptionConfigURL".^ - - - $BirthdayUserName faz aniversário em $BirthdayDate - - - [$BirthdayUserName]($BirthdayUserUrl) tem o aniversário $BirthdayDate - +^Você recebeu este e-mail por que é um usuário registrado do portal "${__VirtualRootPath}":"${__VirtualRootPath}". Se você não quer receber notificações a respeito de novos eventos do portal "${__VirtualRootPath}":"${__VirtualRootPath}", por favor, altere seus "parâmetros de subscrição":"$RecipientSubscriptionConfigURL".^ + + + $BirthdayUserName faz aniversário em $BirthdayDate + + + [$BirthdayUserName]($BirthdayUserUrl) tem o aniversário $BirthdayDate + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.resx similarity index 98% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.resx index 0e8d49369..397353c3e 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.resx @@ -1,81 +1,81 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - h1."$BirthdayUserName":"$BirthdayUserUrl" has the Birthday $BirthdayDate - -The following "${__VirtualRootPath}":"${__VirtualRootPath}" portal user has the birthday $BirthdayDate -$BirthdayUserName - -Visit the "${__VirtualRootPath}":"${__VirtualRootPath}" portal to view the event or send your birthday greetings. - -^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. If you do not want to receive the notifications about the new events on the "${__VirtualRootPath}":"${__VirtualRootPath}" portal, please manage your "subscription settings":"$RecipientSubscriptionConfigURL".^ - - - $BirthdayUserName has the birthday $BirthdayDate - - - [$BirthdayUserName]($BirthdayUserUrl) has the birthday $BirthdayDate - - - - patterns.xml;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + h1."$BirthdayUserName":"$BirthdayUserUrl" has the Birthday $BirthdayDate + +The following "${__VirtualRootPath}":"${__VirtualRootPath}" portal user has the birthday $BirthdayDate +$BirthdayUserName + +Visit the "${__VirtualRootPath}":"${__VirtualRootPath}" portal to view the event or send your birthday greetings. + +^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. If you do not want to receive the notifications about the new events on the "${__VirtualRootPath}":"${__VirtualRootPath}" portal, please manage your "subscription settings":"$RecipientSubscriptionConfigURL".^ + + + $BirthdayUserName has the birthday $BirthdayDate + + + [$BirthdayUserName]($BirthdayUserUrl) has the birthday $BirthdayDate + + + + patterns.xml;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.ru.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.ru.resx similarity index 98% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.ru.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.ru.resx index 22da08553..21eadea75 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.ru.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.ru.resx @@ -1,64 +1,64 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + h1.Пользователь "$BirthdayUserName":"$BirthdayUserUrl" отмечает День рождения $BirthdayDate $BirthdayDate отмечает День рождения пользователь портала "${__VirtualRootPath}":"${__VirtualRootPath}": @@ -66,12 +66,12 @@ $BirthdayUserName Зайдите на портал "${__VirtualRootPath}":"${__VirtualRootPath}", чтобы просмотреть это событие или отправить поздравление. -^Вы получили это сообщение, так как зарегистрированы на портале "${__VirtualRootPath}":"${__VirtualRootPath}". Если Вы не хотите получать оповещения о Днях рождения на портале "${__VirtualRootPath}":"${__VirtualRootPath}", пожалуйста, измените "параметры подписки":"$RecipientSubscriptionConfigURL".^ - - - Пользователь $BirthdayUserName отмечает День рождения $BirthdayDate - - - [$BirthdayUserName]($BirthdayUserUrl) отмечает День рождения $BirthdayDate - +^Вы получили это сообщение, так как зарегистрированы на портале "${__VirtualRootPath}":"${__VirtualRootPath}". Если Вы не хотите получать оповещения о Днях рождения на портале "${__VirtualRootPath}":"${__VirtualRootPath}", пожалуйста, измените "параметры подписки":"$RecipientSubscriptionConfigURL".^ + + + Пользователь $BirthdayUserName отмечает День рождения $BirthdayDate + + + [$BirthdayUserName]($BirthdayUserUrl) отмечает День рождения $BirthdayDate + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.sk.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.sk.resx similarity index 98% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.sk.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.sk.resx index 7198f3d66..c542c24cc 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.sk.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.sk.resx @@ -1,73 +1,73 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + h1."$BirthdayUserName":"$BirthdayUserUrl" má narodeniny $BirthdayDate Nasledujúci používateľ portálu "${__VirtualRootPath}":"${__VirtualRootPath} $BirthdayUserName má narodeniny $BirthdayDate Navštívte portál "${__VirtualRootPath}":"${__VirtualRootPath}", ak chcete zobraziť udalosť alebo poslať svoje blahoželanie k narodeninám. -^Tento e-mail ste dostali, pretože ste registrovaný používateľ portálu "${__VirtualRootPath}":"${__VirtualRootPath}". Ak nechcete dostávať upozornenia o nových udalostiach na portáli "${__VirtualRootPath}":"${__VirtualRootPath}", upravte svoje "nastavenia odberu":"$RecipientSubscriptionConfigURL". ^ - - - $BirthdayUserName má narodeniny $BirthdayDate - +^Tento e-mail ste dostali, pretože ste registrovaný používateľ portálu "${__VirtualRootPath}":"${__VirtualRootPath}". Ak nechcete dostávať upozornenia o nových udalostiach na portáli "${__VirtualRootPath}":"${__VirtualRootPath}", upravte svoje "nastavenia odberu":"$RecipientSubscriptionConfigURL". ^ + + + $BirthdayUserName má narodeniny $BirthdayDate + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.sl.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.sl.resx similarity index 98% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.sl.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.sl.resx index 4ecc19c6c..32f956558 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.sl.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.sl.resx @@ -1,73 +1,73 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + h1."$BirthdayUserName":"$BirthdayUserUrl" ima rojstni dan $BirthdayDate Sledeči "${__VirtualRootPath}":"${__VirtualRootPath}" uporabnik portala ima rojstni dan $BirthdayDate $BirthdayUserName Obišči "${__VirtualRootPath}":"${__VirtualRootPath}" portal za ogled dogodka, ali pošlji svoje rojstno dnevne pozdrave. -^To e-pošto prejemate, ker ste registriran uporabnik "${__VirtualRootPath}":"${__VirtualRootPath}" portala. Če ne želite prejemati obvestil o novih dogodkih na "${__VirtualRootPath}":"${__VirtualRootPath}" portalu, prosim uredite svoje "nastavitve naročnine":"$RecipientSubscriptionConfigURL".^ - - - $BirthdayUserName Ima rojstni dan $BirthdayDate - +^To e-pošto prejemate, ker ste registriran uporabnik "${__VirtualRootPath}":"${__VirtualRootPath}" portala. Če ne želite prejemati obvestil o novih dogodkih na "${__VirtualRootPath}":"${__VirtualRootPath}" portalu, prosim uredite svoje "nastavitve naročnine":"$RecipientSubscriptionConfigURL".^ + + + $BirthdayUserName Ima rojstni dan $BirthdayDate + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.sv.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.sv.resx similarity index 98% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.sv.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.sv.resx index 02e3f32b7..276192a0e 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.sv.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.sv.resx @@ -1,64 +1,64 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + h1."$BirthdayUserName":"$BirthdayUserUrl" har sin födelsedag den $BirthdayDate Följande "${__VirtualRootPath}":"${__VirtualRootPath}" portalanvändare har sin födelsedag den $BirthdayDate @@ -66,9 +66,9 @@ $BirthdayUserName Besök "${__VirtualRootPath}":"${__VirtualRootPath}" portalen för att se evenemangen eller skicka dina födelsedagshälsningar. -^Du får detta mejl för att du är en registrerad användare av "${__VirtualRootPath}":"${__VirtualRootPath}" portalen. Om du inte vill få dessa aviseringar om de nya evenemang på "${__VirtualRootPath}":"${__VirtualRootPath}" portalen, vänligen modifiera dina "abonnemangsinställningar":"$RecipientSubscriptionConfigURL".^ - - - $BirthdayUserName har födelsedagen $BirthdayDate - +^Du får detta mejl för att du är en registrerad användare av "${__VirtualRootPath}":"${__VirtualRootPath}" portalen. Om du inte vill få dessa aviseringar om de nya evenemang på "${__VirtualRootPath}":"${__VirtualRootPath}" portalen, vänligen modifiera dina "abonnemangsinställningar":"$RecipientSubscriptionConfigURL".^ + + + $BirthdayUserName har födelsedagen $BirthdayDate + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.tr.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.tr.resx similarity index 98% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.tr.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.tr.resx index d644bfb8b..4b6754417 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.tr.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.tr.resx @@ -1,73 +1,73 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + h1."$BirthdayUserName":"$BirthdayUserUrl"'ın $BirthdayDate tarihinde doğumgünü var "${__VirtualRootPath}":"${__VirtualRootPath}" portal kullanıcısının $BirthdayDate tarihinde doğumgünü var:$BirthdayUserName Etkinlikleri görüntülemek yada doğumgünü kutlaması göndermek için "${__VirtualRootPath}":"${__VirtualRootPath}" portalını ziyaret ediniz. -^Bu espostayı alıyorsunuz çünkü "${__VirtualRootPath}":"${__VirtualRootPath}" portalının kayıtlı üyesisiniz. Yeni etkinlikler hakkında bildirim almak istemiyorsanız, lütfen "abonelik ayarlarını" değiştirin:"$RecipientSubscriptionConfigURL".^ - - - $BirthdayUserName $BirthdayDate tarihinde doğumgünü var - +^Bu espostayı alıyorsunuz çünkü "${__VirtualRootPath}":"${__VirtualRootPath}" portalının kayıtlı üyesisiniz. Yeni etkinlikler hakkında bildirim almak istemiyorsanız, lütfen "abonelik ayarlarını" değiştirin:"$RecipientSubscriptionConfigURL".^ + + + $BirthdayUserName $BirthdayDate tarihinde doğumgünü var + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.uk.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.uk.resx similarity index 98% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.uk.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.uk.resx index 829904895..a0c677c87 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.uk.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.uk.resx @@ -1,73 +1,73 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + h1."$BirthdayUserName":"$BirthdayUserUrl" день народження $BirthdayDate У користувача порталу "${__VirtualRootPath}":"${__VirtualRootPath}" день народження $BirthdayDate $BirthdayUserName Відвідайте портал "${__VirtualRootPath}":"${__VirtualRootPath}" щоб продивитись події або надіслати ваші поздоровлення. -^Ви отримали це повідомлення, оскільки ви зареєстрований користувач "${__VirtualRootPath}":"${__VirtualRootPath}" порталу. Якщо ви не бажаєте отримувати повідомлення про нові коментарі, що були додані до закладки, змініть параметр у "налаштуваннях підписки":"$RecipientSubscriptionConfigURL".^ - - - $BirthdayUserName день народження $BirthdayDate - +^Ви отримали це повідомлення, оскільки ви зареєстрований користувач "${__VirtualRootPath}":"${__VirtualRootPath}" порталу. Якщо ви не бажаєте отримувати повідомлення про нові коментарі, що були додані до закладки, змініть параметр у "налаштуваннях підписки":"$RecipientSubscriptionConfigURL".^ + + + $BirthdayUserName день народження $BirthdayDate + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.vi.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.vi.resx similarity index 98% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.vi.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.vi.resx index 59ed4d05a..2da4610f9 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.vi.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.vi.resx @@ -1,64 +1,64 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + h1."$BirthdayUserName":"$BirthdayUserUrl" có sinh nhật $BirthdayDate Người dùng sau đây "${__VirtualRootPath}":"${__VirtualRootPath}" có sinh nhật $BirthdayDate @@ -66,9 +66,9 @@ $BirthdayUserName Truy cập cổng "${__VirtualRootPath}":"${__VirtualRootPath}" để xem sự kiện hoặc gửi lời chúc mừng sinh nhật. -^Bạn nhận được email này vì bạn là người dùng đã đăng ký của cổng "${__VirtualRootPath}":"${__VirtualRootPath}". Nếu bạn không muốn nhận thông báo về sự kiện mới trên cổng thông tin "${__VirtualRootPath}":"${__VirtualRootPath}", vui lòng điều chỉnh "thiết lập đăng ký":"$RecipientSubscriptionConfigURL".^ - - - $BirthdayUserName có sinh nhật $BirthdayDate - +^Bạn nhận được email này vì bạn là người dùng đã đăng ký của cổng "${__VirtualRootPath}":"${__VirtualRootPath}". Nếu bạn không muốn nhận thông báo về sự kiện mới trên cổng thông tin "${__VirtualRootPath}":"${__VirtualRootPath}", vui lòng điều chỉnh "thiết lập đăng ký":"$RecipientSubscriptionConfigURL".^ + + + $BirthdayUserName có sinh nhật $BirthdayDate + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.zh-CN.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.zh-CN.resx similarity index 98% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.zh-CN.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.zh-CN.resx index 73adef085..519415130 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.zh-CN.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.zh-CN.resx @@ -1,64 +1,64 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + h1."$BirthdayUserName":"$BirthdayUserUrl" 的生日为 $BirthdayDate 下列"${__VirtualRootPath}":"${__VirtualRootPath}"门户用户的生日为 $BirthdayDate @@ -66,9 +66,9 @@ $BirthdayUserName 请访问 "${__VirtualRootPath}":"${__VirtualRootPath}"门户,以查看活动或发送您的生日问候。 -^您收到该电邮,因为您是"${__VirtualRootPath}":"${__VirtualRootPath}" 门户的注册用户。若您不想收到关于"${__VirtualRootPath}":"${__VirtualRootPath}"门户新活动的通知,请管理您的"订阅设置":"$RecipientSubscriptionConfigURL".^ - - - $BirthdayUserName的生日为$BirthdayDate - +^您收到该电邮,因为您是"${__VirtualRootPath}":"${__VirtualRootPath}" 门户的注册用户。若您不想收到关于"${__VirtualRootPath}":"${__VirtualRootPath}"门户新活动的通知,请管理您的"订阅设置":"$RecipientSubscriptionConfigURL".^ + + + $BirthdayUserName的生日为$BirthdayDate + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.zh-TW.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.zh-TW.resx similarity index 98% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.zh-TW.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.zh-TW.resx index b010fcd7a..fc0fd2b87 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdayPatternResource.zh-TW.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdayPatternResource.zh-TW.resx @@ -1,64 +1,64 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + h1。“ $ BirthdayUserName”:“ $ BirthdayUserUrl”具有生日$ BirthdayDate @@ -67,12 +67,12 @@ $ BirthdayUserName 訪問“ $ {__ VirtualRootPath}”:“ $ {__ VirtualRootPath}”門戶以查看事件或發送生日祝福。 -^您收到此電子郵件的原因是您是“ $ {__ VirtualRootPath}”:“ $ {__ VirtualRootPath}”門戶的註冊用戶。如果您不想在“ $ {__ VirtualRootPath}”:“ $ {__ VirtualRootPath}”門戶上收到有關新事件的通知,請管理您的“訂閱設置”:“ $ RecipientSubscriptionConfigURL”。^ - - - $ BirthdayUserName的生日為$ BirthdayDate - - - [$ BirthdayUserName]($ BirthdayUserUrl)的生日為$ BirthdayDate - +^您收到此電子郵件的原因是您是“ $ {__ VirtualRootPath}”:“ $ {__ VirtualRootPath}”門戶的註冊用戶。如果您不想在“ $ {__ VirtualRootPath}”:“ $ {__ VirtualRootPath}”門戶上收到有關新事件的通知,請管理您的“訂閱設置”:“ $ RecipientSubscriptionConfigURL”。^ + + + $ BirthdayUserName的生日為$ BirthdayDate + + + [$ BirthdayUserName]($ BirthdayUserUrl)的生日為$ BirthdayDate + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.Designer.cs b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.Designer.cs similarity index 93% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.Designer.cs rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.Designer.cs index 887ab5bfa..d27178926 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.Designer.cs +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.Designer.cs @@ -1,153 +1,153 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace ASC.Web.Community.Modules.Birthdays.Resources { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - public class BirthdaysResource { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal BirthdaysResource() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ASC.Web.Community.Modules.Birthdays.Resources.BirthdaysResource", typeof(BirthdaysResource).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to Send Your Greetings. - /// - public static string BirthdayCongratulateLinkTitle { - get { - return ResourceManager.GetString("BirthdayCongratulateLinkTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to No birthdays for the nearest dates. - /// - public static string BirthdayEmptyScreenCaption { - get { - return ResourceManager.GetString("BirthdayEmptyScreenCaption", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Remind about the birthday. - /// - public static string BirthdayRemindLinkTitle { - get { - return ResourceManager.GetString("BirthdayRemindLinkTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Reminder enabled. - /// - public static string BirthdayRemindReadyLinkTitle { - get { - return ResourceManager.GetString("BirthdayRemindReadyLinkTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Birthdays can be specified in the 'People' module. - /// - public static string BirthdaysEmptyModuleDescription { - get { - return ResourceManager.GetString("BirthdaysEmptyModuleDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Nearest birthdays of portal members. - /// - public static string BirthdaysModuleDescription { - get { - return ResourceManager.GetString("BirthdaysModuleDescription", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Birthdays. - /// - public static string BirthdaysModuleTitle { - get { - return ResourceManager.GetString("BirthdaysModuleTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to All the birthdays. - /// - public static string BirthdaysSubscribeAll { - get { - return ResourceManager.GetString("BirthdaysSubscribeAll", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Today is the birthday. - /// - public static string BirthdaysTodayTitle { - get { - return ResourceManager.GetString("BirthdaysTodayTitle", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Nearest dates. - /// - public static string BirthdaysUpcomingTitle { - get { - return ResourceManager.GetString("BirthdaysUpcomingTitle", resourceCulture); - } - } - } -} +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ASC.Web.People.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class BirthdaysResource { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal BirthdaysResource() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ASC.Web.People.Resources.BirthdaysResource", typeof(BirthdaysResource).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Send Your Greetings. + /// + public static string BirthdayCongratulateLinkTitle { + get { + return ResourceManager.GetString("BirthdayCongratulateLinkTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No birthdays for the nearest dates. + /// + public static string BirthdayEmptyScreenCaption { + get { + return ResourceManager.GetString("BirthdayEmptyScreenCaption", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Remind about the birthday. + /// + public static string BirthdayRemindLinkTitle { + get { + return ResourceManager.GetString("BirthdayRemindLinkTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Reminder enabled. + /// + public static string BirthdayRemindReadyLinkTitle { + get { + return ResourceManager.GetString("BirthdayRemindReadyLinkTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Birthdays can be specified in the 'People' module. + /// + public static string BirthdaysEmptyModuleDescription { + get { + return ResourceManager.GetString("BirthdaysEmptyModuleDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Nearest birthdays of portal members. + /// + public static string BirthdaysModuleDescription { + get { + return ResourceManager.GetString("BirthdaysModuleDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Birthdays. + /// + public static string BirthdaysModuleTitle { + get { + return ResourceManager.GetString("BirthdaysModuleTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to All the birthdays. + /// + public static string BirthdaysSubscribeAll { + get { + return ResourceManager.GetString("BirthdaysSubscribeAll", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Today is the birthday. + /// + public static string BirthdaysTodayTitle { + get { + return ResourceManager.GetString("BirthdaysTodayTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Nearest dates. + /// + public static string BirthdaysUpcomingTitle { + get { + return ResourceManager.GetString("BirthdaysUpcomingTitle", resourceCulture); + } + } + } +} diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.az-Latn-AZ.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.az-Latn-AZ.resx new file mode 100644 index 000000000..118498c81 --- /dev/null +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.az-Latn-AZ.resx @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Salamlarınızı Göndərin + + + Yaxın tarixlər üçün doğum günü yoxdur + + + Doğum gününü xatırlat + + + Xatırlatma aktiv edildi + + + Doğum günləri "Şəxslər" modulunda göstərilə bilər + + + Portal üzvləri arasında ən yaxın doğum günləri + + + Doğum günləri + + + Bütün doğum günləri + + + Bu gün ad günüdür + + + Ən yaxın tarixlər + + diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.be.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.be.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.be.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.be.resx index 1238180d8..598e1909a 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.be.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.be.resx @@ -1,91 +1,91 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Павіншаваць - - - У бліжэйшыя дні ніхто не адзначае дня народзінаў - - - Нагадаць пра дзень народзінаў - - - Напамін уключаны - - - Дні народзінаў можна вызначыць у модулі "Людзі" - - - Бліжэйшыя дні народзінаў удзельнікаў партала - - - Дні народзінаў - - - Падпісацца на ўсе дні народзінаў - - - Сённяшнія дні народзінаў - - - Бліжэйшыя даты - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Павіншаваць + + + У бліжэйшыя дні ніхто не адзначае дня народзінаў + + + Нагадаць пра дзень народзінаў + + + Напамін уключаны + + + Дні народзінаў можна вызначыць у модулі "Людзі" + + + Бліжэйшыя дні народзінаў удзельнікаў партала + + + Дні народзінаў + + + Падпісацца на ўсе дні народзінаў + + + Сённяшнія дні народзінаў + + + Бліжэйшыя даты + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.bg.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.bg.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.bg.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.bg.resx index 9ab22f8ed..76ce92cd1 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.bg.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.bg.resx @@ -1,91 +1,91 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Изпратете вашите поздрави - - - Няма рождени дни за най-близките дати - - - Напомни за рождения ден - - - Напомнянето е активирано - - - Рожденните дни могат да бъдат посочени в модула 'Хора' - - - Най-близки рождени дни на членовете на портала - - - Рождени дни - - - Всички рождени дни - - - Днес е рожден ден - - - Най-близки дати - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Изпратете вашите поздрави + + + Няма рождени дни за най-близките дати + + + Напомни за рождения ден + + + Напомнянето е активирано + + + Рожденните дни могат да бъдат посочени в модула 'Хора' + + + Най-близки рождени дни на членовете на портала + + + Рождени дни + + + Всички рождени дни + + + Днес е рожден ден + + + Най-близки дати + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.cs.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.cs.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.cs.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.cs.resx index da01059d7..88a61469f 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.cs.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.cs.resx @@ -1,85 +1,85 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Pošlete své pozdravy - - - Připomenutí narozenin - - - Připomenutí zapnuto - - - Nejbližší narozeniny členů protálu - - - Narozeniny - - - Všechny narozeniny - - - Dnes jsou narozeniny - - - Nejbližší data - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Pošlete své pozdravy + + + Připomenutí narozenin + + + Připomenutí zapnuto + + + Nejbližší narozeniny členů protálu + + + Narozeniny + + + Všechny narozeniny + + + Dnes jsou narozeniny + + + Nejbližší data + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.da.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.da.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.da.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.da.resx index cccb54d09..2ffcb7a52 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.da.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.da.resx @@ -1,67 +1,67 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Fødselsdage - - - Alle fødselsdage - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Fødselsdage + + + Alle fødselsdage + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.de.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.de.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.de.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.de.resx index 6942793b7..eb38eeb9c 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.de.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.de.resx @@ -1,91 +1,91 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Gratulieren - - - Keine Geburtstage für die nächsten Termine - - - An Geburtstag erinnern - - - Erinnerung aktiviert - - - Geburtstage können im Modul "Personen" angegeben werden - - - Kommende Geburtstage der Portalmitglieder - - - Geburtstage - - - Für alle Geburtstage anmelden - - - Heutige Geburtstage - - - Kommende Geburtstage - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Gratulieren + + + Keine Geburtstage für die nächsten Termine + + + An Geburtstag erinnern + + + Erinnerung aktiviert + + + Geburtstage können im Modul "Personen" angegeben werden + + + Kommende Geburtstage der Portalmitglieder + + + Geburtstage + + + Für alle Geburtstage anmelden + + + Heutige Geburtstage + + + Kommende Geburtstage + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.el.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.el.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.el.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.el.resx index daba5536d..9f0d395c5 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.el.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.el.resx @@ -1,88 +1,88 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Αποστολή χαιρετισμών σας - - - Δεν υπάρχουν γενέθλια στις κοντινές ημερομηνίες - - - Υπενθύμιση για τα γενέθλια - - - Η υπενθύμιση ενεργοποιήθηκε - - - Τα γενέθλια μπορούν να καθοριστούν στο άρθρωμα «Άτομα» - - - Γενέθλια - - - Όλα τα γενέθλια - - - Σήμερα είναι τα γενέθλια - - - Κοντινές ημερομηνίες - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Αποστολή χαιρετισμών σας + + + Δεν υπάρχουν γενέθλια στις κοντινές ημερομηνίες + + + Υπενθύμιση για τα γενέθλια + + + Η υπενθύμιση ενεργοποιήθηκε + + + Τα γενέθλια μπορούν να καθοριστούν στο άρθρωμα «Άτομα» + + + Γενέθλια + + + Όλα τα γενέθλια + + + Σήμερα είναι τα γενέθλια + + + Κοντινές ημερομηνίες + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.es.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.es.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.es.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.es.resx index d4eec0ddd..3c80ffd81 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.es.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.es.resx @@ -1,91 +1,91 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Enviar felicitaciones - - - No hay cumpleaños para las fechas más cercanas - - - Recordar de cumpleaños - - - Recordatorio activado - - - Cumpleaños se pueden especificarse en el módulo 'Personas' - - - Cumpleaños próximos - - - Cumpleaños - - - Recibir una notificación cuando uno tiene cumpleaños - - - Hoy es cumpleaños - - - Fechas próximas - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Enviar felicitaciones + + + No hay cumpleaños para las fechas más cercanas + + + Recordar de cumpleaños + + + Recordatorio activado + + + Cumpleaños se pueden especificarse en el módulo 'Personas' + + + Cumpleaños próximos + + + Cumpleaños + + + Recibir una notificación cuando uno tiene cumpleaños + + + Hoy es cumpleaños + + + Fechas próximas + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.fi.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.fi.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.fi.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.fi.resx index 27d55085f..3cb8c3552 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.fi.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.fi.resx @@ -1,85 +1,85 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Lähetä onnittelut - - - Muistuta syntymäpäivästä - - - Muistutus kytketty päälle - - - Sivuston käyttäjien tulevat syntymäpäivät - - - Syntymäpäivät - - - Kaikki syntymäpäivät - - - Syntymäpäiviä tänään - - - Lähimmät syntymäpäivät - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Lähetä onnittelut + + + Muistuta syntymäpäivästä + + + Muistutus kytketty päälle + + + Sivuston käyttäjien tulevat syntymäpäivät + + + Syntymäpäivät + + + Kaikki syntymäpäivät + + + Syntymäpäiviä tänään + + + Lähimmät syntymäpäivät + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.fr.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.fr.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.fr.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.fr.resx index 110849703..6de6e615c 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.fr.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.fr.resx @@ -1,91 +1,91 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Envoyer vos voeux - - - Aucun anniversaire pour les dates les plus proches - - - Rappeler l'anniversaire - - - Rappel activé - - - Anniversaires peuvent être specifiés dans le module "Personnes" - - - Anniversaires les plus proches - - - Anniversaires - - - S'abonner à tous les anniversaires - - - Aujourd'hui c'est l'anniversaire - - - Dates les plus proches - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Envoyer vos voeux + + + Aucun anniversaire pour les dates les plus proches + + + Rappeler l'anniversaire + + + Rappel activé + + + Anniversaires peuvent être specifiés dans le module "Personnes" + + + Anniversaires les plus proches + + + Anniversaires + + + S'abonner à tous les anniversaires + + + Aujourd'hui c'est l'anniversaire + + + Dates les plus proches + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.hi.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.hi.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.hi.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.hi.resx index a380e75bf..e443e9a6f 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.hi.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.hi.resx @@ -1,85 +1,85 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - अपनी बधाई भेजें - - - जन्मदिन के बारे में याद दिलाना - - - रिमाइंडर सक्षम किया - - - साइट के सदस्यों के सबसे नजदीक जन्मदिन - - - जन्मदिन - - - सभी जन्मदिन - - - आज जन्मदिन है - - - नजदीकी तिथियाँ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + अपनी बधाई भेजें + + + जन्मदिन के बारे में याद दिलाना + + + रिमाइंडर सक्षम किया + + + साइट के सदस्यों के सबसे नजदीक जन्मदिन + + + जन्मदिन + + + सभी जन्मदिन + + + आज जन्मदिन है + + + नजदीकी तिथियाँ + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.hu.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.hu.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.hu.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.hu.resx index b9a2946b2..717cc2927 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.hu.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.hu.resx @@ -1,91 +1,91 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Küldd el az üdvözletedet - - - Nincs születésnap a közelgő napokban - - - Emlékeztess a születésnapokra - - - Emlékeztető engedélyezése - - - A születésnapokat az 'Emberek' modulban lehet megadni - - - A portál tagok közeli születésnapja - - - Születésnapok - - - Valamennyi születésnap - - - Ma születésnap - - - Legközelebbi dátum - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Küldd el az üdvözletedet + + + Nincs születésnap a közelgő napokban + + + Emlékeztess a születésnapokra + + + Emlékeztető engedélyezése + + + A születésnapokat az 'Emberek' modulban lehet megadni + + + A portál tagok közeli születésnapja + + + Születésnapok + + + Valamennyi születésnap + + + Ma születésnap + + + Legközelebbi dátum + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.id.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.id.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.id.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.id.resx index ab9883f1f..31754f0ba 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.id.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.id.resx @@ -1,85 +1,85 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Kirimkan Ucapan Selamat Ulang Tahun - - - Ingatkan tanggal ulang tahun - - - Pengingat diaktifkan - - - Tanggal ulang tahun terdekat anggota portal - - - Ulang tahun - - - Semua tanggal ulang tahun - - - Hari ini adalah ulang tahun - - - Tanggal terdekat - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Kirimkan Ucapan Selamat Ulang Tahun + + + Ingatkan tanggal ulang tahun + + + Pengingat diaktifkan + + + Tanggal ulang tahun terdekat anggota portal + + + Ulang tahun + + + Semua tanggal ulang tahun + + + Hari ini adalah ulang tahun + + + Tanggal terdekat + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.it.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.it.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.it.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.it.resx index 5e6af6e2b..0a0a42d11 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.it.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.it.resx @@ -1,91 +1,91 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Invia auguri - - - Nessun compleanno - - - Promemoria compleanno - - - Promemoria attivato - - - I compleanni possono essere specificati nel modulo 'Contatti' - - - Prossimi compleanni - - - Compleanni - - - Inviami una notifica quando qualcuno compie gli anni - - - Oggi è il compleanno - - - Prossime date - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Invia auguri + + + Nessun compleanno + + + Promemoria compleanno + + + Promemoria attivato + + + I compleanni possono essere specificati nel modulo 'Contatti' + + + Prossimi compleanni + + + Compleanni + + + Inviami una notifica quando qualcuno compie gli anni + + + Oggi è il compleanno + + + Prossime date + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.ja.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.ja.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.ja.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.ja.resx index c75f29b3e..ea346ffa3 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.ja.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.ja.resx @@ -1,91 +1,91 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 挨拶文を送る - - - 近未来の誕生日はありません - - - 誕生日について思い出させる - - - リマインダーは有効です - - - 「人達」モジュールで誕生日が指定されます - - - 今度のユーザー誕生日 - - - 誕生日 - - - 全ての誕生日 - - - 今日の誕生日 - - - 今度の日付 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 挨拶文を送る + + + 近未来の誕生日はありません + + + 誕生日について思い出させる + + + リマインダーは有効です + + + 「人達」モジュールで誕生日が指定されます + + + 今度のユーザー誕生日 + + + 誕生日 + + + 全ての誕生日 + + + 今日の誕生日 + + + 今度の日付 + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.kk.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.kk.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.kk.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.kk.resx index fafed2061..d29950ad0 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.kk.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.kk.resx @@ -1,91 +1,91 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Құттықтау - - - Алдағы күндер үшін туған күндер жоқ - - - Туған күн туралы еске түсіру - - - Ескерту қосулы - - - Туған күнін 'адамдар' модулінде көрсетуге болады - - - Портал қатысушыларының жақын арадағы туған күндері - - - Туған күндер - - - Барлық туған күндерге жазылыңыз - - - Бүгінгі туған күндер - - - Алдағы күндер - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Құттықтау + + + Алдағы күндер үшін туған күндер жоқ + + + Туған күн туралы еске түсіру + + + Ескерту қосулы + + + Туған күнін 'адамдар' модулінде көрсетуге болады + + + Портал қатысушыларының жақын арадағы туған күндері + + + Туған күндер + + + Барлық туған күндерге жазылыңыз + + + Бүгінгі туған күндер + + + Алдағы күндер + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.lv.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.lv.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.lv.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.lv.resx index 2bf05c943..b78c32362 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.lv.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.lv.resx @@ -1,85 +1,85 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Sūtīt savus sveicienus - - - Atgādināt par dzimšanas dienu - - - Atgādinājums ieslēgts - - - Tuvākās portāla lietotāju dzimšanas dienas - - - Dzimšanas dienas - - - Visas dzimšanas dienas - - - Šodien ir dzimšanas diena - - - Tuvākie datumi - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Sūtīt savus sveicienus + + + Atgādināt par dzimšanas dienu + + + Atgādinājums ieslēgts + + + Tuvākās portāla lietotāju dzimšanas dienas + + + Dzimšanas dienas + + + Visas dzimšanas dienas + + + Šodien ir dzimšanas diena + + + Tuvākie datumi + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.nl.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.nl.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.nl.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.nl.resx index 0c55f6a16..46d374b66 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.nl.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.nl.resx @@ -1,85 +1,85 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Stuur uw Groet - - - Herinneren aan de verjaardag - - - Reminder ingeschakeld - - - Eerste verjaardagen van portaalleden - - - Verjaardagen - - - Alle verjaardagen - - - Vandaag is de verjaardag - - - Eerste data - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Stuur uw Groet + + + Herinneren aan de verjaardag + + + Reminder ingeschakeld + + + Eerste verjaardagen van portaalleden + + + Verjaardagen + + + Alle verjaardagen + + + Vandaag is de verjaardag + + + Eerste data + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.pl.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.pl.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.pl.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.pl.resx index 6d64a7b83..da8e385ed 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.pl.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.pl.resx @@ -1,85 +1,85 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Pogratuluj - - - Przypomnij o urodzinach - - - Przypomnienie włączone - - - Najbliższe urodziny użytkowników - - - Urodziny - - - Wszystkie urodziny - - - Dziś urodziny - - - Najbliższe daty - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Pogratuluj + + + Przypomnij o urodzinach + + + Przypomnienie włączone + + + Najbliższe urodziny użytkowników + + + Urodziny + + + Wszystkie urodziny + + + Dziś urodziny + + + Najbliższe daty + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.pt-BR.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.pt-BR.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.pt-BR.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.pt-BR.resx index a9a7ffafe..88e6d2fd9 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.pt-BR.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.pt-BR.resx @@ -1,91 +1,91 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Envie seus parabéns - - - Não há aniversários para os próximos dias - - - Lembrete sobre o aniversário - - - Lembrete habilitado - - - Os aniversários podem ser especificados no módulo "Pessoas" - - - Próximos aniversariantes membros do portal. - - - Aniversários - - - Todos os aniversários - - - Hoje é o aniversário - - - Datas mais próximas. - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Envie seus parabéns + + + Não há aniversários para os próximos dias + + + Lembrete sobre o aniversário + + + Lembrete habilitado + + + Os aniversários podem ser especificados no módulo "Pessoas" + + + Próximos aniversariantes membros do portal. + + + Aniversários + + + Todos os aniversários + + + Hoje é o aniversário + + + Datas mais próximas. + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.resx index 2dd14d952..3cd9e85c1 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.resx @@ -1,91 +1,91 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Send Your Greetings - - - No birthdays for the nearest dates - - - Remind about the birthday - - - Reminder enabled - - - Birthdays can be specified in the 'People' module - - - Nearest birthdays of portal members - - - Birthdays - - - All the birthdays - - - Today is the birthday - - - Nearest dates - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Send Your Greetings + + + No birthdays for the nearest dates + + + Remind about the birthday + + + Reminder enabled + + + Birthdays can be specified in the 'People' module + + + Nearest birthdays of portal members + + + Birthdays + + + All the birthdays + + + Today is the birthday + + + Nearest dates + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.ru.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.ru.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.ru.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.ru.resx index e9b793917..dbdcb580a 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.ru.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.ru.resx @@ -1,91 +1,91 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Поздравить - - - Нет дней рождения на ближайшие даты - - - Напомнить о дне рождения - - - Напоминание включено - - - Дни рождения можно указать в модуле 'Люди' - - - Ближайшие дни рождения участников портала - - - Дни Рождения - - - Подписаться на все дни рождения - - - Дни рождения сегодня - - - Ближайшие даты - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Поздравить + + + Нет дней рождения на ближайшие даты + + + Напомнить о дне рождения + + + Напоминание включено + + + Дни рождения можно указать в модуле 'Люди' + + + Ближайшие дни рождения участников портала + + + Дни Рождения + + + Подписаться на все дни рождения + + + Дни рождения сегодня + + + Ближайшие даты + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.sk.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.sk.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.sk.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.sk.resx index 5584d400e..14f81f688 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.sk.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.sk.resx @@ -1,85 +1,85 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Pošlite svoje blahoprianie - - - Pripomenúť narodeniny - - - Pripomenutie je povolené - - - Najbližšie narodeniny členov portálu - - - Narodeniny - - - Všetky narodeniny - - - Dnešné narodeniny - - - Najbližšie dátumy - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Pošlite svoje blahoprianie + + + Pripomenúť narodeniny + + + Pripomenutie je povolené + + + Najbližšie narodeniny členov portálu + + + Narodeniny + + + Všetky narodeniny + + + Dnešné narodeniny + + + Najbližšie dátumy + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.sl.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.sl.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.sl.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.sl.resx index 47832008a..139863f6b 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.sl.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.sl.resx @@ -1,85 +1,85 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Pošlji svoje pozdrave - - - Opomni o rojstnem dnevu - - - Opomnik omogočen - - - Najbližji rojstni dan članov portala - - - Rojstni dnevi - - - Vsi rojstni dnevi - - - Danes je rojstni dan - - - Najbližji datumi - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Pošlji svoje pozdrave + + + Opomni o rojstnem dnevu + + + Opomnik omogočen + + + Najbližji rojstni dan članov portala + + + Rojstni dnevi + + + Vsi rojstni dnevi + + + Danes je rojstni dan + + + Najbližji datumi + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.sv.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.sv.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.sv.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.sv.resx index fbaa452b7..33a4e757b 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.sv.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.sv.resx @@ -1,91 +1,91 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Skicka hälsningar - - - Inga födelsedagar de närmsta dagarna - - - Påminn om födelsedagen - - - Påminnelse aktiverad - - - Födelsedagar kan specificeras i 'People'-modulen - - - Närmsta födelsedagar av portalmedlemmar - - - Födelsedagar - - - Alla födelsedagarna - - - Idag är födelsedagen - - - Närmsta datum - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Skicka hälsningar + + + Inga födelsedagar de närmsta dagarna + + + Påminn om födelsedagen + + + Påminnelse aktiverad + + + Födelsedagar kan specificeras i 'People'-modulen + + + Närmsta födelsedagar av portalmedlemmar + + + Födelsedagar + + + Alla födelsedagarna + + + Idag är födelsedagen + + + Närmsta datum + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.tr.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.tr.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.tr.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.tr.resx index 159fc53c1..1dea5d4d3 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.tr.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.tr.resx @@ -1,91 +1,91 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Tebrikleri Gönder - - - Yakın tarihler için doğum günü yok - - - Doğumgünü hakkında hatırlatma yap - - - Hatırlatıcı etkinleştirildi - - - Doğum günleri 'Kişiler' modülünde belirtilebilir. - - - Portal üyeleri arasında en yakın doğumgünleri - - - Doğumgünleri - - - Tüm doğumgünleri - - - Bugün doğumgünü - - - En yakın tarihler - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Tebrikleri Gönder + + + Yakın tarihler için doğum günü yok + + + Doğumgünü hakkında hatırlatma yap + + + Hatırlatıcı etkinleştirildi + + + Doğum günleri 'Kişiler' modülünde belirtilebilir. + + + Portal üyeleri arasında en yakın doğumgünleri + + + Doğumgünleri + + + Tüm doğumgünleri + + + Bugün doğumgünü + + + En yakın tarihler + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.uk.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.uk.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.uk.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.uk.resx index 3cc841189..6db83f9dd 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.uk.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.uk.resx @@ -1,85 +1,85 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Відправити поздоровлення - - - Нагадати про день народження - - - Нагадування увімкнено - - - Найближчі дні народження учасників порталу - - - Дні народження - - - Всі дні народження - - - Сьогодні день народження у - - - Найближчі дати - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Відправити поздоровлення + + + Нагадати про день народження + + + Нагадування увімкнено + + + Найближчі дні народження учасників порталу + + + Дні народження + + + Всі дні народження + + + Сьогодні день народження у + + + Найближчі дати + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.vi.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.vi.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.vi.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.vi.resx index 525a19212..dda2311c1 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.vi.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.vi.resx @@ -1,85 +1,85 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Gửi Lời Chào Của Bạn - - - Nhắc nhở sinh nhật - - - Đã bật nhắc nhở - - - Sinh nhật gần nhất của các thành viên cổng thông tin - - - Sinh nhật - - - Tất cả sinh nhật - - - Hôm nay là sinh nhật - - - Ngày gần nhất - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Gửi Lời Chào Của Bạn + + + Nhắc nhở sinh nhật + + + Đã bật nhắc nhở + + + Sinh nhật gần nhất của các thành viên cổng thông tin + + + Sinh nhật + + + Tất cả sinh nhật + + + Hôm nay là sinh nhật + + + Ngày gần nhất + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.zh-CN.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.zh-CN.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.zh-CN.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.zh-CN.resx index 076e54a07..678265828 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.zh-CN.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.zh-CN.resx @@ -1,91 +1,91 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 发送您的问候 - - - 最近无人过生日 - - - 生日提醒 - - - 已启用提醒 - - - 生日可以在“人员”模块中指定 - - - 最近几天过生日的门户会员 - - - 生日 - - - 所有生日 - - - 今天生日 - - - 最近几天 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 发送您的问候 + + + 最近无人过生日 + + + 生日提醒 + + + 已启用提醒 + + + 生日可以在“人员”模块中指定 + + + 最近几天过生日的门户会员 + + + 生日 + + + 所有生日 + + + 今天生日 + + + 最近几天 + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.zh-TW.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.zh-TW.resx similarity index 97% rename from web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.zh-TW.resx rename to web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.zh-TW.resx index 35b4be228..430c489c4 100644 --- a/web/studio/ASC.Web.Studio/Products/Community/Modules/Birthdays/Resources/BirthdaysResource.zh-TW.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/BirthdaysResource.zh-TW.resx @@ -1,91 +1,91 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 發送您的問候 - - - 最近日期沒有人生日 - - - 提醒生日 - - - 開啟提醒 - - - 可以在“人物”模塊中指定生日 - - - 門戶成員最近生日 - - - 生日 - - - 所有的生日 - - - 今天生日 - - - 最近日期 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 發送您的問候 + + + 最近日期沒有人生日 + + + 提醒生日 + + + 開啟提醒 + + + 可以在“人物”模塊中指定生日 + + + 門戶成員最近生日 + + + 生日 + + + 所有的生日 + + + 今天生日 + + + 最近日期 + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleJSResource.az-Latn-AZ.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleJSResource.az-Latn-AZ.resx new file mode 100644 index 000000000..124384fe8 --- /dev/null +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleJSResource.az-Latn-AZ.resx @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + İstifadəçini seçin + + + Şəkil formatı dəstəklənmir + + + Şəkli serverə yükləyərkən xəta baş verdi + + + Aktiv + + + Növ + + + Başqa + + + Gözləmə + + + Status + + + Deaktiv + + + Şəxslər + + + {0} seçilib + + + İstifadəçi statusu dəyişdirildi + + + İstifadəçi növü dəyişdirildi + + + Dəvət göndərildi + + + Cari tarif planı {0} {1} aktiv istifadəçilərin {2} əlavə edilməsinə imkan verir. + + + Cari tarif planınız sizə {0} {1} aktiv istifadəçi {2} əlavə etməyə imkan verir (qonaqlar daxil deyil) + + + Cari tarif planınız sizə {0}{1} qonaq{2} əlavə etməyə imkan verir. + + diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.Designer.cs b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.Designer.cs index c5b0b8806..5e1932feb 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.Designer.cs +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.Designer.cs @@ -19,7 +19,7 @@ namespace ASC.Web.People.Resources { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class PeopleResource { @@ -105,6 +105,15 @@ namespace ASC.Web.People.Resources { } } + /// + /// Looks up a localized string similar to Birthdays. + /// + public static string Birthdays { + get { + return ResourceManager.GetString("Birthdays", resourceCulture); + } + } + /// /// Looks up a localized string similar to Disabled. /// @@ -123,6 +132,52 @@ namespace ASC.Web.People.Resources { } } + /// + /// Looks up a localized string similar to Copy. + /// + public static string CardDavCopyBtn { + get { + return ResourceManager.GetString("CardDavCopyBtn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to CardDav Settings. + /// + public static string CardDavSettings { + get { + return ResourceManager.GetString("CardDavSettings", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enable CardDAV to sync contacts from the People module. Copy the server address and enter it in a third-party application or device, use your valid portal login + ///and password.. + /// + public static string CardDavSettingsDescription { + get { + return ResourceManager.GetString("CardDavSettingsDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to CardDav. + /// + public static string CardDavSettingsHeader { + get { + return ResourceManager.GetString("CardDavSettingsHeader", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Try again. + /// + public static string CardDavTryAgainBtn { + get { + return ResourceManager.GetString("CardDavTryAgainBtn", resourceCulture); + } + } + /// /// Looks up a localized string similar to Change status. /// @@ -357,6 +412,15 @@ namespace ASC.Web.People.Resources { } } + /// + /// Looks up a localized string similar to Enable synchronizing contacts via CardDAV. + /// + public static string EnableCardDavLink { + get { + return ResourceManager.GetString("EnableCardDavLink", resourceCulture); + } + } + /// /// Looks up a localized string similar to Enable. /// @@ -546,15 +610,6 @@ namespace ASC.Web.People.Resources { } } - /// - /// Looks up a localized string similar to Yahoo. - /// - public static string ImportFromYahoo { - get { - return ResourceManager.GetString("ImportFromYahoo", resourceCulture); - } - } - /// /// Looks up a localized string similar to Import users. /// @@ -672,6 +727,15 @@ namespace ASC.Web.People.Resources { } } + /// + /// Looks up a localized string similar to Active connections. + /// + public static string LblActiveConnections { + get { + return ResourceManager.GetString("LblActiveConnections", resourceCulture); + } + } + /// /// Looks up a localized string similar to Cancel. /// diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.ar-AE.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.ar-AE.resx index 476072a4a..c9c746c7a 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.ar-AE.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.ar-AE.resx @@ -64,6 +64,9 @@ اجراءات + + اعياد الميلاد + معطل diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.az-Latn-AZ.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.az-Latn-AZ.resx index 2761368f1..b944c22af 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.az-Latn-AZ.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.az-Latn-AZ.resx @@ -64,24 +64,207 @@ əyləmlər + + Əlavə edin + + + {!group} əlavə edin + + + Üzvlər əlavə edin + + + Doğum günləri + Yasaqlandı + + Ləğv et + + + Statusu dəyişin + + + İstifadəçi statusunu dəyişin + + + Bu rəqəm cari qiymət planını aşırsa, planınızı yeniləməli və əməliyyatı təkrarlamalısınız, əks halda digər istifadəçilərin statusu dəyişdirilməyəcək. + + + İstifadəçi statusunu dəyişin + + + Siz portal sahibi statusunu və öz statusunuzu dəyişə bilməzsiniz + + + "Deaktiv" statuslu istifadəçilər aktiv ediləcək. + + + "Aktiv" statuslu istifadəçilər deaktiv ediləcək. + + + Növü dəyişin + + + Planınızı təkmilləşdirin və əməliyyatı təkrarlayın, əks halda digər istifadəçi növü dəyişdirilməyəcək. + + + İstifadəçi növünü dəyişin + + + Siz öz növünüzü və ya portal sahibinin növünü dəyişə bilməzsiniz + + + '{!User}' növlü istifadəçilər '{!Guest}' növünə köçürüləcək + + + '{!Guest}' növlü istifadəçilər '{!User}' növünə köçürüləcək + Təmizlə Təsdiqlənmə + + yalnız "Deaktiv" statuslu istifadəçilər və qonaqlar seçilə bilər + Sil + + Təkrar təyin etmə tamamlandıqda profili silin + + + İStifadəçiləri portaldan silin + + + Həmin istifadəçilərin başqaları ilə paylaşılan şəxsi sənədləri silinəcək. Bunun qarşısını almaq üçün silinməzdən əvvəl məlumat köçürmə prosesinə başlamalısınız. + + + Seçilmiş deaktiv istifadəçilər portaldan silinəcək. + + + {!Head} + + + Bütün seçimləri ləğv edin + + + Maksimum istifadəçi və qonaq sayı keçilib + Yasaqla + + E-poçt + İzn ver + + Ad daxil edin + + + Yüklənmiş fayl tapılmadı + + + Şəkil ölçüsü çox böyükdür + + + Şəklin fayl ölçüsü çox böyükdür + + + Naməlum şəkil faylı növü + + + Nümunə dəyərləri + + + Fayldakı sahələr + + + ONLYOFFICE portalındakı sahələr + + + Ad + + + Seçilmiş istifadəçi siyahısını gizlədin + + + Sütun + + + Cüt dırnaq + + + Tək dırnaq + + + ASCII + + + CP-866 + + + KOI8-R + + + UTF-8 + + + Windows1251 + + + Fayldan + + + Google + + + Yahoo + + + İstifadəçiləri idxal edin + + + İki nöqtə + + + Vergül + + + Nöqtəli vergül + + + Boşluq + + + Tab + + + Kontakrları idxal edin + + + Portala əlavə edin + + + Uyğunluğu təyin et + + + Kontaktları redaktə edin + + + İstifadəçilər dəvət edin + + + Soyad + + + Aktiv + Ləğv @@ -103,6 +286,12 @@ OK + + Məlumatları yenidən təyin edin + + + Şəxsi məlumatları silin + Dəvətnamələri bir də göndərin @@ -121,25 +310,196 @@ Yasaqlandı + + İpucları + + + Üzvlər + Bu bölmədə sizin filtr ilə üst-üstə düşən heç bir adam tapılmadı. Lütfən, filtri təmizləmək və bu bölmədə bütün adamları görmək üçün digər filtr seçənəklərini seçin. Sizin axtarışlarınızla üst-üstə düşən heç bir nəticə tapılmadı + + İdxal etməyin + + + Yuxarıda + + + İstifadəçinin etdiyini edin | Profillər və qruplar yaradın | Profilləri idxal edin | İstifadəçiləri dəvət edin + Yeni istifadəçilər əlavə et, onları dəvət və ya import et. İstifadəçiləri idarə et və onlar haqqında ayrıntılı bilgiyə bax. + + Yeni istifadəçilər əlavə et, onları dəvət və ya idxal et. İstifadəçiləri idarə et və onlar haqqında ətraflı məlumata bax. + Adamlar + + Profil və qruplara baxın + + + Və ya {0}qeyri-kommersiya istifadəsi{1} haqqında öyrənin. + + + Köçürməni ləğv edin + + + Məlumatların köçürülməsi ləğv edildi + + + Yenidən təyin edin + + + CRM + + + Sənədlər + + + Server xətası səbəbindən məlumatların köçürülməsi dayandırıldı + + + Poçt + + + Məlumatların yenidən təyini + + + Layihələr + + + Yenidən köçürməyə başlayın + + + Bu səhifəni bağlaya bilərsiniz. Proses başa çatdıqda, onu idarə edən administrator e-poçt vasitəsilə məlumatlandırılacaq. + + + Məlumatın {0} adlı istifadəçidən {1} adlı istifadəçiyə yenidən təyin edilməsi prosesi başlayıb, bu, uzun müddət çəkə bilər. + + + Məlumatların köçürülməsi haqqında ətraflı məlumat + + + Kəsildi, bəzi məlumatlar köçürülməyə bilər + + + Server xətası + + + Bütün məlumatlar köçürülüb + + + Verilən köçürülmədi + + + Növbədə + + + Başladıldı + + + Məlumatların köçürüləcəyi işçi - + + + Köçürüləcək: + + + Digər portal istifadəçiləri üçün mövcud olan ümumi və şəxsi sənədlər; + + + Açıq layihələr, mərhələlər və tapşırıqlar; + + + Kontaktlar, açıq tapşırıqlar, bağlanmamış fürsətlər və CRM əməliyyatları; + + + Söhbət + + + Silinməni ləğv edin + + + Məlumatların silinməsi ləğv edildi + + + Şəxsi məlumatları silin + + + Server xətasına görə məlumatların silinməsi dayandırıldı + + + Silinəcək: + + + Bütün şəxsi sənədlər + + + CRM hesabat faylları; + + + Bütün e-poçtlar və əlavə edilmiş fayllar; + + + Talk fayllarından əlavə edilib; + + + {0} istifadəçisi üçün şəxsi məlumatların silinməsi prosesi başlayıb, bu, uzun müddət çəkə bilər. + + + Şəxsi məlumatların silinməsi haqqında ətraflı məlumat + + + Yenidən silməyə başlayın + + + Kəsildi, bəzi məlumatlar silinə bilər + + + Bütün məlumatlar silinib + + + Məlumatlar silinmədi + + + İstifadəçilər portalda dəvəti təsdiq etdikdən sonra onların statusu "Aktiv" olur + + + Dəvəti bir daha göndərin + + + Portal üçün dəvət "Gözləmə" statusu olan və deaktiv olmayan seçilmiş istifadəçilərə geri göndəriləcək. + + + Hamısını seçin + Ayarlar + + Səhifədə göstər + + + Seçilmiş istifadəçi siyahısına baxın + İstifadəçi uğurla silindi + + Başlıq + + + Ümumi say + Təsdiq üçün gözlənilir - \ No newline at end of file + + E-poçt göndər + + diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.be.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.be.resx index b817d13fc..83ee1ff5f 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.be.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.be.resx @@ -217,9 +217,6 @@ Google - - Yahoo - Імпарт карыстальнікаў diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.bg.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.bg.resx index c0220bec1..fdc872abd 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.bg.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.bg.resx @@ -73,6 +73,9 @@ Добавяне на членове + + Рождени дни + Хора с увреждания @@ -217,9 +220,6 @@ Google - - Yahoo - Импортиране на хора diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.cs.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.cs.resx index 22794fdd4..7193ceb07 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.cs.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.cs.resx @@ -73,6 +73,9 @@ Přidat členy + + Narozeniny + Zakázáno diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.de.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.de.resx index 01a943de5..5c3e3b642 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.de.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.de.resx @@ -73,6 +73,9 @@ Mitglieder hinzufügen + + Geburtstage + Deaktiviert @@ -220,9 +223,6 @@ Google - - Yahoo - Personen importieren diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.el.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.el.resx index e95e9534c..f5334b9c1 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.el.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.el.resx @@ -67,6 +67,9 @@ Προσθήκη μελών + + Γενέθλια + Απενεργοποιημένο @@ -157,9 +160,6 @@ Google - - Yahoo - Εισαγωγή χρηστών diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.es.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.es.resx index 559271b7a..5e9cb1152 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.es.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.es.resx @@ -73,6 +73,9 @@ Añadir miembros + + Cumpleaños + Desactivado @@ -220,9 +223,6 @@ Google - - Yahoo - Importar personas diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.fa.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.fa.resx index 5b5812937..c2ede5793 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.fa.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.fa.resx @@ -61,6 +61,9 @@ افزودن + + تولدها + ارسال مجدد لینک فعال‌سازی diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.fi.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.fi.resx index 8da7b1f98..03a246d3c 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.fi.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.fi.resx @@ -73,6 +73,9 @@ Lisää jäseniä + + Syntymäpäivät + Ei käytössä diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.fr.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.fr.resx index ae4a6eb69..a4d9de62f 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.fr.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.fr.resx @@ -73,6 +73,9 @@ Ajouter membres + + Anniversaires + Désactivé @@ -220,9 +223,6 @@ Google - - Yahoo - Importer personnes diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.hi.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.hi.resx index 98ec08756..004090929 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.hi.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.hi.resx @@ -73,6 +73,9 @@ सदस्यों को जोड़ें + + जन्मदिन + निष्क्रिय करें diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.hu.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.hu.resx index ad7c2e001..850e9c6de 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.hu.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.hu.resx @@ -70,6 +70,9 @@ Tagok hozzáadása + + Születésnapok + Mégse diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.id.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.id.resx index d0a7c4f78..8cc97c671 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.id.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.id.resx @@ -74,6 +74,9 @@ Tambahkan anggota + + Ulang tahun + Nonaktif diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.it.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.it.resx index 542da7c68..1b678bb7f 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.it.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.it.resx @@ -73,6 +73,9 @@ Aggiungi membri + + Compleanni + Disattivato @@ -220,9 +223,6 @@ Google - - Yahoo - Importa persone diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.ja.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.ja.resx index 99cbe46a0..7c5b41b5d 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.ja.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.ja.resx @@ -73,6 +73,9 @@ メンバーを追加する + + 誕生日 + 使用禁止 @@ -175,9 +178,6 @@ Google - - Yahoo - ユーザーを輸入する diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.ko.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.ko.resx index 46e2a4307..b4c4ee08b 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.ko.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.ko.resx @@ -73,6 +73,9 @@ 멤버 추가 + + 생일 + 사용 안 함 @@ -145,9 +148,6 @@ 구글 - - 야후 - 구성원 가져오기 diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.lt.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.lt.resx index 711af5bfd..d1e2556bb 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.lt.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.lt.resx @@ -73,6 +73,9 @@ Pridėti dalyvių + + Gimtadieniai + Išjungta diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.lv.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.lv.resx index 38b091340..9e2ecb594 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.lv.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.lv.resx @@ -73,6 +73,9 @@ Pievienot lietotājus + + Dzimšanas dienas + Atspējots diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.nb-NO.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.nb-NO.resx index 1eb9a7ed2..1db2deb30 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.nb-NO.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.nb-NO.resx @@ -64,6 +64,9 @@ Legg til + + Bursdager + Avbryt diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.nl.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.nl.resx index cd95a0dd7..88117a971 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.nl.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.nl.resx @@ -73,6 +73,9 @@ Voeg leden toe + + Verjaardagen + Gedeactiveerd diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.pl.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.pl.resx index bc7db12b2..498c1988a 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.pl.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.pl.resx @@ -73,6 +73,9 @@ Dodaj członków + + Urodziny + Wyłączony diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.pt-BR.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.pt-BR.resx index f6acca733..f06b1b844 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.pt-BR.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.pt-BR.resx @@ -73,6 +73,9 @@ Adicionar membros + + Aniversários + Desabilitado @@ -220,9 +223,6 @@ Google - - Yahoo - Importar pessoas diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.resx index 6801d092e..eee83c03a 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.resx @@ -1,5 +1,64 @@  + @@ -53,10 +112,10 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Portal Access Rights @@ -73,12 +132,31 @@ Add members + + Birthdays + Disabled Cancel + + Copy + + + CardDav Settings + + + Enable CardDAV to sync contacts from the People module. Copy the server address and enter it in a third-party application or device, use your valid portal login +and password. + + + CardDav + + + Try again + Change status @@ -157,6 +235,9 @@ Email + + Enable synchronizing contacts via CardDAV + Enable @@ -220,9 +301,6 @@ Google - - Yahoo - Import users @@ -262,6 +340,9 @@ Active + + Active connections + Cancel diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.ro.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.ro.resx index dfc64a3f9..48023e6c0 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.ro.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.ro.resx @@ -163,9 +163,6 @@ Google - - Yahoo - Importați Utilizatori diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.ru.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.ru.resx index 5ed6cbaa1..df1bc43a7 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.ru.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.ru.resx @@ -73,6 +73,9 @@ Добавить участников + + Дни Рождения + Заблокирован @@ -220,9 +223,6 @@ Google - - Yahoo - Импортировать людей diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.sk.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.sk.resx index cd0df0bac..ee27d9624 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.sk.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.sk.resx @@ -73,6 +73,9 @@ Pridať člena + + Narodeniny + Zakázaný @@ -199,9 +202,6 @@ Google - - Yahoo - Importovať ľudí diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.sl.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.sl.resx index bd2408a12..2adc15e98 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.sl.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.sl.resx @@ -73,6 +73,9 @@ Dodaj člane + + Rojstni dnevi + Onemogočeno diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.sv.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.sv.resx index 433ae83e4..75f8ffc4b 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.sv.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.sv.resx @@ -73,6 +73,9 @@ Lägg till medlemar + + Födelsedagar + Inaktiverad @@ -217,9 +220,6 @@ Google - - Yahoo - Importera Användare diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.tr.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.tr.resx index ca68a2d46..72de14354 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.tr.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.tr.resx @@ -73,6 +73,9 @@ Üye ekle + + Doğumgünleri + Devre Dışı @@ -184,9 +187,6 @@ Google - - Yahoo - Kişileri Aktar diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.uk.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.uk.resx index f5218cf72..090e6bbab 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.uk.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.uk.resx @@ -73,6 +73,9 @@ Додати учасників + + Дні Народження + Заблокован diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.vi.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.vi.resx index 950890f3a..e3424d665 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.vi.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.vi.resx @@ -73,6 +73,9 @@ Thêm thành viên + + Sinh nhật + Vô hiệu hóa diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.zh-CN.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.zh-CN.resx index 2a10239c1..5b20180a5 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.zh-CN.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.zh-CN.resx @@ -73,6 +73,9 @@ 添加成员 + + 生日 + 已阻止 @@ -217,9 +220,6 @@ Google - - Yahoo - 导入成员 diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.zh-TW.resx b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.zh-TW.resx index 3f4559f59..d8631343d 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.zh-TW.resx +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/PeopleResource.zh-TW.resx @@ -73,6 +73,9 @@ 新增成員 + + 生日 + 已停用 @@ -217,9 +220,6 @@ Google - - Yahoo - 導入用戶 diff --git a/web/studio/ASC.Web.Studio/Products/People/Resources/patterns.xml b/web/studio/ASC.Web.Studio/Products/People/Resources/patterns.xml new file mode 100644 index 000000000..b60f786ee --- /dev/null +++ b/web/studio/ASC.Web.Studio/Products/People/Resources/patterns.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + +${__VirtualRootPath}/Products/People/Birthdays.aspx + + + + + +[${__VirtualRootPath}/Products/People/Birthdays.aspx](${__VirtualRootPath}/Products/People/Birthdays.aspx) + + + + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/People/Templates/PeopleTemplates.html b/web/studio/ASC.Web.Studio/Products/People/Templates/PeopleTemplates.html index 1c171683f..899be2951 100644 --- a/web/studio/ASC.Web.Studio/Products/People/Templates/PeopleTemplates.html +++ b/web/studio/ASC.Web.Studio/Products/People/Templates/PeopleTemplates.html @@ -75,11 +75,11 @@ {{if user.bithdayDaysLeft != null}}
    - {{if user.bithdayDaysLeft == 0}}${ASC.Resources.Master.TemplateResource.DrnToday} - {{else user.bithdayDaysLeft==1}}${ASC.Resources.Master.TemplateResource.DrnTomorrow} - {{else user.bithdayDaysLeft==2}}${ASC.Resources.Master.TemplateResource.In} ${ASC.Resources.Master.TemplateResource.Yet2} - {{else user.bithdayDaysLeft==3}}${ASC.Resources.Master.TemplateResource.In} ${ASC.Resources.Master.TemplateResource.Yet3} - {{/if}} + {{if user.bithdayDaysLeft == 0}}${ASC.Resources.Master.TemplateResource.DrnToday} + {{else user.bithdayDaysLeft==1}}${ASC.Resources.Master.TemplateResource.DrnTomorrow} + {{else user.bithdayDaysLeft==2}}${ASC.Resources.Master.TemplateResource.In} ${ASC.Resources.Master.TemplateResource.Yet2} + {{else user.bithdayDaysLeft==3}}${ASC.Resources.Master.TemplateResource.In} ${ASC.Resources.Master.TemplateResource.Yet3} + {{/if}}
    {{/if}}
    diff --git a/web/studio/ASC.Web.Studio/Products/People/UserControls/ImportUsers/ImportUsers.ascx b/web/studio/ASC.Web.Studio/Products/People/UserControls/ImportUsers/ImportUsers.ascx index b727d519e..6c0145a76 100644 --- a/web/studio/ASC.Web.Studio/Products/People/UserControls/ImportUsers/ImportUsers.ascx +++ b/web/studio/ASC.Web.Studio/Products/People/UserControls/ImportUsers/ImportUsers.ascx @@ -116,7 +116,7 @@ \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/People/UserControls/ImportUsers/ImportUsers.ascx.cs b/web/studio/ASC.Web.Studio/Products/People/UserControls/ImportUsers/ImportUsers.ascx.cs index 1cf4d50b8..510db9db1 100644 --- a/web/studio/ASC.Web.Studio/Products/People/UserControls/ImportUsers/ImportUsers.ascx.cs +++ b/web/studio/ASC.Web.Studio/Products/People/UserControls/ImportUsers/ImportUsers.ascx.cs @@ -67,8 +67,7 @@ namespace ASC.Web.People.UserControls EnableGuests = Constants.CoefficientOfVisitors * TenantExtra.GetTenantQuota().ActiveUsers - TenantStatisticsProvider.GetVisitorsCount(); EnableGuests = EnableGuests >= 0 ? EnableGuests : 0; IsStandalone = CoreContext.Configuration.Standalone; - var tariff = (Studio.UserControls.Management.TariffLimitExceed)LoadControl(Studio.UserControls.Management.TariffLimitExceed.Location); - Tariff.Controls.Add(tariff); + var quota = TenantExtra.GetTenantQuota(); PeopleLimit = Math.Min(quota.ActiveUsers - TenantStatisticsProvider.GetUsersCount(), 0); diff --git a/web/studio/ASC.Web.Studio/Products/People/UserControls/ImportUsers/ImportUsers.ascx.designer.cs b/web/studio/ASC.Web.Studio/Products/People/UserControls/ImportUsers/ImportUsers.ascx.designer.cs index 925c32835..a93246d23 100644 --- a/web/studio/ASC.Web.Studio/Products/People/UserControls/ImportUsers/ImportUsers.ascx.designer.cs +++ b/web/studio/ASC.Web.Studio/Products/People/UserControls/ImportUsers/ImportUsers.ascx.designer.cs @@ -7,11 +7,13 @@ // //------------------------------------------------------------------------------ -namespace ASC.Web.People.UserControls { - - - public partial class ImportUsers { - +namespace ASC.Web.People.UserControls +{ + + + public partial class ImportUsers + { + /// /// limitPanel control. /// @@ -20,7 +22,7 @@ namespace ASC.Web.People.UserControls { /// To modify move field declaration from designer file to code-behind file. /// protected global::ASC.Web.Studio.Controls.Common.Container limitPanel; - + /// /// icon control. /// @@ -29,14 +31,5 @@ namespace ASC.Web.People.UserControls { /// To modify move field declaration from designer file to code-behind file. /// protected global::ASC.Web.Studio.Controls.Common.Container icon; - - /// - /// Tariff control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder Tariff; } } diff --git a/web/studio/ASC.Web.Studio/Products/People/UserControls/ImportUsers/js/importusers.js b/web/studio/ASC.Web.Studio/Products/People/UserControls/ImportUsers/js/importusers.js index 80a31c1a2..932933c87 100644 --- a/web/studio/ASC.Web.Studio/Products/People/UserControls/ImportUsers/js/importusers.js +++ b/web/studio/ASC.Web.Studio/Products/People/UserControls/ImportUsers/js/importusers.js @@ -28,7 +28,7 @@ window.master = { peopleImport = ASC.People.Import; if (typeof evt.data == "string") { try { - obj = jQuery.parseJSON(evt.data); + obj = JSON.parse(evt.data); } catch (err) { return; } @@ -272,7 +272,7 @@ ASC.People.Import = (function () { var dataPath = flatUploader._settings.data; $importFrom.advancedSelector({ - height: 26 * itemsImportFrom.length, + height: 30 * itemsImportFrom.length, onechosen: true, showSearch: false, itemsChoose: itemsImportFrom, @@ -292,14 +292,10 @@ ASC.People.Import = (function () { var button = frame.getElementsByClassName('google'); jq(button).trigger("click"); } - if (item.id === 2) { - var button = frame.getElementsByClassName('yahoo'); - jq(button).trigger("click"); - } }); $delimiter.advancedSelector({ - height: 26 * itemsDelimiter.length, + height: 30 * itemsDelimiter.length, onechosen: true, showSearch: false, itemsChoose: itemsDelimiter, @@ -316,7 +312,7 @@ ASC.People.Import = (function () { $delimiter.advancedSelector("selectBeforeShow", itemsDelimiter[0]); $separator.advancedSelector({ - height: 26 * itemsSeparator.length, + height: 30 * itemsSeparator.length, onechosen: true, showSearch: false, itemsChoose: itemsSeparator, @@ -333,7 +329,7 @@ ASC.People.Import = (function () { $separator.advancedSelector("selectBeforeShow", itemsSeparator[0]); $encoding.advancedSelector({ - height: 26 * itemsEncoding.length, + height: 30 * itemsEncoding.length, onechosen: true, showSearch: false, itemsChoose: itemsEncoding, @@ -372,7 +368,7 @@ ASC.People.Import = (function () { $addAsGuestButton.val(statuses[1].title); $notAddButton.val(statuses[2].title); - jq(document).click(function (event) { + jq(document).on("click", function (event) { jq.dropdownToggle({ rightPos: true }).registerAutoHide(event, '.file', '.fileSelector'); jq('#upload img').attr('src', StudioManager.GetImage('loader_16.gif')); }); @@ -387,7 +383,7 @@ ASC.People.Import = (function () { jq('#donor tr').clone().appendTo('#userList'); $impBtn.add($cncBtn).addClass("disable"); - $checkAll.attr("disabled", "disabled"); + $checkAll.prop("disabled", true); PopupKeyUpActionProvider.ClearActions(); PopupKeyUpActionProvider.EnterActionCallback = callback; PopupKeyUpActionProvider.EnterAction = 'ASC.People.Import.checkAndAdd();'; @@ -447,9 +443,9 @@ ASC.People.Import = (function () { }; function checkAndAdd() { - var firstName = jQuery.trim($firstName.val()), - lastName = jQuery.trim($lastName.val()), - address = jQuery.trim($email.val()); + var firstName = $firstName.val().trim(), + lastName = $lastName.val().trim(), + address = $email.val().trim(); checkInputValues(firstName, firstName == fName, $firstName, $fnError); checkInputValues(lastName, lastName == lName, $lastName, $lnError); @@ -464,7 +460,7 @@ ASC.People.Import = (function () { $inputControl.addClass(classIncorrectBox); makeVisible($errorControl); } else { - $inputControl.focus(); + $inputControl.trigger("focus"); } return; } else { @@ -476,19 +472,19 @@ ASC.People.Import = (function () { function addUser() { var items = $userList.find('.userItem'); - var firstName = jQuery.trim($firstName.val()), - lastName = jQuery.trim($lastName.val()), - address = jQuery.trim($email.val()); + var firstName = $firstName.val().trim(), + lastName = $lastName.val().trim(), + address = $email.val().trim(); if (!isContainErrors(items, firstName, lastName, address)) { $userList.find('tr').not('.userItem').remove(); $email.add($firstName).add($lastName).removeClass(classIncorrectBox); makeHidden($fnError.add($lnError).add($eaError)); appendUser({ FirstName: $firstName.val(), LastName: $lastName.val(), Email: $email.val() }); - $firstName.val('').blur(); - $lastName.val('').blur(); - $email.val('').blur(); - $firstName.focus(); + $firstName.val('').trigger("blur"); + $lastName.val('').trigger("blur"); + $email.val('').trigger("blur"); + $firstName.trigger("focus"); } else { checkValuesBeforeAdd(address, !isValidEmail(address), $email, $eaError); checkValuesBeforeAdd(lastName, lastName == lName || !userNameRegExp.test(lastName), $lastName, $lnError); @@ -500,7 +496,7 @@ ASC.People.Import = (function () { if (currentVal == '' || hasError) { $inputControl.addClass(classIncorrectBox); makeVisible($errorControl); - $inputControl.focus(); + $inputControl.trigger("focus"); } else { $inputControl.removeClass(classIncorrectBox); makeHidden($errorControl); @@ -534,7 +530,7 @@ ASC.People.Import = (function () { function isExists(items, email, select) { for (var index = 0, n = items.length; index < n; index++) { - if (jQuery.trim(jq(items[index]).find('.email input').val()) == email) { + if (jq(items[index]).find('.email input').val().trim() == email) { if (select) { jq(items[index]).find('.email').addClass('incorrectValue'); } @@ -623,10 +619,10 @@ ASC.People.Import = (function () { for (var i = 0, n = items.length; i < n; i++) { if (jq(items[i]).find('.statusValue').text() != statuses[2].title) { arr.push({ - "FirstName": checkValue(jQuery.trim(jq(items[i]).find('.name .firstname .studioEditableInput').val()), emptyFName), - "LastName": checkValue(jQuery.trim(jq(items[i]).find('.name .lastname .studioEditableInput').val()), emptyLName), - "Email": jQuery.trim(jq(items[i]).find('.email input').val()), - "Status": jq(items[i]).find('.statusValue').text() == statuses[0].title ? 1 : 2 + "FirstName": checkValue(jq(items[i]).find('.name .firstname .studioEditableInput').val().trim(), emptyFName), + "LastName": checkValue(jq(items[i]).find('.name .lastname .studioEditableInput').val().trim(), emptyLName), + "Email": jq(items[i]).find('.email input').val().trim(), + "Status": jq(items[i]).find('.statusValue').text() == statuses[0].title ? 1 : 2 }); } } @@ -695,7 +691,7 @@ ASC.People.Import = (function () { } jq(".status").eq(i).advancedSelector( { - height: 26 * 3, + height: 30 * 3, itemsChoose: statuses, showSearch: false, onechosen: true, @@ -754,13 +750,13 @@ ASC.People.Import = (function () { var usersCount = $userList.find('.userItem').length; if (usersCount == 0) { - $impBtn.attr('disabled', 'disabled').addClass('disable'); + $impBtn.prop("disabled", true).addClass('disable'); jq('#last-step').removeClass('disable'); - $cncBtn.attr('disabled', 'disabled').addClass('disable'); + $cncBtn.prop("disabled", true).addClass('disable'); } else { - $impBtn.removeAttr('disabled').removeClass('disable'); - $cncBtn.removeAttr('disabled').removeClass('disable'); + $impBtn.prop("disabled", false).removeClass('disable'); + $cncBtn.prop("disabled", false).removeClass('disable'); } }; @@ -934,7 +930,7 @@ ASC.People.Import = (function () { function showProgressPanel() { $wizardUsers.addClass("disable"); - $wizardUsersInput.attr("disabled", true); + $wizardUsersInput.prop("disabled", true); $importUsersButton.addClass("disable"); $importUserLimitPanelButton.addClass("disable"); $wizardAddToPortal.addClass("active"); @@ -942,7 +938,7 @@ ASC.People.Import = (function () { function hideProgressPanel() { $wizardUsers.removeClass("disable"); - $wizardUsersInput.attr("disabled", false); + $wizardUsersInput.prop("disabled", false); $importUsersButton.removeClass("disable"); $importUserLimitPanelButton.removeClass("disable"); $wizardAddToPortal.removeClass("active"); @@ -988,7 +984,7 @@ ASC.People.Import = (function () { jq(this).attr('class', attrs.replace(/error\d/gi, '')); jq(this).find('.remove').removeClass('removeError'); - var valueMail = jQuery.trim(jq(this).find('.email input').val()); + var valueMail = jq(this).find('.email input').val().trim(); for (var i = 0, n = result.Data.length; i < n; i++) { if (result.Data[i].Email == valueMail) { if (result.Data[i].Result != '') { @@ -1059,7 +1055,7 @@ ASC.People.Import = (function () { if ($userList.find(".userItem").length > 0) { $impBtn.add($cncBtn).removeClass("disable"); - $checkAll.removeAttr("disabled"); + $checkAll.prop("disabled", false); } checkCountUsersForAdd(); checkGroupButtonActive(); @@ -1211,7 +1207,7 @@ ASC.People.Import = (function () { }; function setControlHintSettings(controlName, defaultText) { - jq(controlName).focus(function () { + jq(controlName).on("focus", function () { jq(controlName).removeClass('textEditDefault'); jq(controlName).addClass('textEditMain'); if (jq(controlName).val() == defaultText) { @@ -1219,7 +1215,7 @@ ASC.People.Import = (function () { } }); - jq(controlName).blur(function () { + jq(controlName).on("blur", function () { if (jq(controlName).val() == '') { jq(controlName).removeClass('textEditMain'); jq(controlName).addClass('textEditDefault'); @@ -1397,7 +1393,7 @@ ASC.People.Import = (function () { .hide(); $importBtn.show().removeClass('disable'); - $checkAll.removeAttr('disabled'); + $checkAll.prop("disabled", false); manualImport = true; wizard.next(); diff --git a/web/studio/ASC.Web.Studio/Products/People/UserControls/SideNavigationPanel.ascx b/web/studio/ASC.Web.Studio/Products/People/UserControls/SideNavigationPanel.ascx index f9c2d5359..167181db4 100644 --- a/web/studio/ASC.Web.Studio/Products/People/UserControls/SideNavigationPanel.ascx +++ b/web/studio/ASC.Web.Studio/Products/People/UserControls/SideNavigationPanel.ascx @@ -11,11 +11,13 @@ + <% if (IsBirthdaysAvailable) + {%> +
    + +
    + <%} %> <% if (CurrentUserAdmin && ASC.Web.Studio.ThirdParty.ImportContacts.Import.Enable && (EnableAddVisitors || EnableAddUsers)) @@ -76,5 +103,4 @@
    <% } %> - - \ No newline at end of file + diff --git a/web/studio/ASC.Web.Studio/Products/People/UserControls/SideNavigationPanel.ascx.cs b/web/studio/ASC.Web.Studio/Products/People/UserControls/SideNavigationPanel.ascx.cs index 752b90757..3a32ea492 100644 --- a/web/studio/ASC.Web.Studio/Products/People/UserControls/SideNavigationPanel.ascx.cs +++ b/web/studio/ASC.Web.Studio/Products/People/UserControls/SideNavigationPanel.ascx.cs @@ -17,6 +17,7 @@ using System; using System.Linq; +using System.Web; using System.Web.UI; using ASC.Core; @@ -40,6 +41,8 @@ namespace ASC.Web.People.UserControls protected bool CurrentUserFullAdmin; protected bool CurrentUserAdmin; protected bool EnableAddVisitors; + protected string CurrentPage { get; set; } + protected bool IsBirthdaysAvailable { get; set; } public static string Location { @@ -49,6 +52,8 @@ namespace ASC.Web.People.UserControls protected void Page_Load(object sender, EventArgs e) { InitData(); + InitPermission(); + InitCurrentPage(); var help = (HelpCenter)LoadControl(HelpCenter.Location); help.IsSideBar = true; @@ -66,5 +71,35 @@ namespace ASC.Web.People.UserControls CurrentUserAdmin = CurrentUserFullAdmin || WebItemSecurity.IsProductAdministrator(WebItemManager.PeopleProductID, SecurityContext.CurrentAccount.ID); EnableAddVisitors = CoreContext.Configuration.Standalone || TenantStatisticsProvider.GetVisitorsCount() < TenantExtra.GetTenantQuota().ActiveUsers * Constants.CoefficientOfVisitors; } + + private void InitPermission() + { + IsBirthdaysAvailable = WebItemManager.Instance.GetSubItems(PeopleProduct.ID).Any(item => item.ID == WebItemManager.BirthdaysProductID); + } + + private void InitCurrentPage() + { + var currentPath = HttpContext.Current.Request.Path; + if (currentPath.IndexOf("Default.aspx", StringComparison.OrdinalIgnoreCase) > 0) + { + CurrentPage = "people"; + } + else if (currentPath.IndexOf("Birthdays.aspx", StringComparison.OrdinalIgnoreCase) > 0) + { + CurrentPage = "birthdays"; + } + else if (currentPath.IndexOf("Help.aspx", StringComparison.OrdinalIgnoreCase) > 0) + { + CurrentPage = "help"; + } + else if (currentPath.IndexOf("CardDavSettings.aspx", StringComparison.OrdinalIgnoreCase) > 0) + { + CurrentPage = "carddav"; + } + else + { + CurrentPage = "people"; + } + } } } \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/People/js/birthdays.js b/web/studio/ASC.Web.Studio/Products/People/js/birthdays.js new file mode 100644 index 000000000..55c3938ee --- /dev/null +++ b/web/studio/ASC.Web.Studio/Products/People/js/birthdays.js @@ -0,0 +1,64 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +if (typeof ASC === "undefined") + ASC = {}; + +if (typeof ASC.People === "undefined") + ASC.People = (function () { return {} })(); + +ASC.People.Birthdays = (function() { + + var openContact = function(obj) { + var name = jq(obj).attr("username"), + tcExist = false; + try { + tcExist = !!ASC.Controls.JabberClient; + } catch (err) { + tcExist = false; + } + if (tcExist === true) { + try { + ASC.Controls.JabberClient.open(name); + } catch (err) { + } + } + }; + + var remind = function(obj, onRemind) { + var userCard = jq(obj).parents(".small-user-card"), + userId = jq(userCard).find("input[type=hidden]").val(); + + Teamlab.subscribePeopleBirthday( + { userCard: userCard }, + { userId: userId, onRemind: onRemind }, + { + error: function(params, errors) { + toastr.error(errors[0]); + return false; + }, + success: function(params, data){ + jq(params.userCard).toggleClass("active", data); + } + }); + }; + + return { + openContact: openContact, + remind: remind + }; +})(jQuery); \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/People/js/carddav.js b/web/studio/ASC.Web.Studio/Products/People/js/carddav.js new file mode 100644 index 000000000..efb5caeef --- /dev/null +++ b/web/studio/ASC.Web.Studio/Products/People/js/carddav.js @@ -0,0 +1,85 @@ +var CardDavSettings = new function () { + + var $cardDavSettingsBtn = jq("#cardDavSettingsCheckbox"), + $cardDavUrlInput = jq("#cardDavUrl"), + $cardDavCopyBtn = jq(".copy"), + $cardDavTryAgainBtn = jq(".try-again"); + + + var init = function () { + if ($cardDavSettingsBtn.hasClass("off")) { + $cardDavUrlInput.prop("disabled", true).addClass("display-none"); + } else { + $cardDavUrlInput.prop("disabled", false).removeClass("display-none"); + $cardDavCopyBtn.removeClass("display-none"); + } + + $cardDavSettingsBtn.on("click", getCardDavLink); + + $cardDavCopyBtn.on("click", copyCardDavLink); + + } + function copyCardDavLink() { + $cardDavUrlInput.trigger("select"); + try { + document.execCommand('copy'); + toastr.success("Link has been copied to the clipboard"); + } catch (err) { } + } + + function showError(params, errors) { + + if (errors && errors.length > 0) { + toastr.error(errors[0]); + console.error(errors) + } + window.LoadingBanner.hideLoading(); + } + + + function getCardDavLink() { + var $this = jq(this); + var on = $this.hasClass("off"); + window.LoadingBanner.displayLoading(); + if (on) { + Teamlab.getCardDavLink({}, { + success: function (params, response) { + if (response.completed) { + $cardDavUrlInput.val(response.data); + $cardDavSettingsBtn.removeClass("off").addClass("on"); + $cardDavCopyBtn.removeClass("display-none"); + $cardDavUrlInput.prop("disabled", false).removeClass("display-none"); + } else { + toastr.error(response.error); + console.error([response.statusCode, response.error].join(' ')); + } + window.LoadingBanner.hideLoading(); + }, + error: showError + }); + } else { + Teamlab.deleteCardDavLink({}, { + success: function (params, response) { + if (response.completed) { + $cardDavUrlInput.val(""); + $cardDavSettingsBtn.removeClass("on").addClass("off"); + $cardDavCopyBtn.addClass("display-none"); + $cardDavUrlInput.prop("disabled", true).addClass("display-none"); + } else { + console.error([response.statusCode, response.error].join(' ')); + } + window.LoadingBanner.hideLoading(); + }, + error: showError + }); + } + } + + return { + init: init + }; +} + +jq(function () { + CardDavSettings.init(); +}); \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/People/js/departmentmanagement.js b/web/studio/ASC.Web.Studio/Products/People/js/departmentmanagement.js index abe0ed02c..e4317a468 100644 --- a/web/studio/ASC.Web.Studio/Products/People/js/departmentmanagement.js +++ b/web/studio/ASC.Web.Studio/Products/People/js/departmentmanagement.js @@ -175,7 +175,7 @@ var DepartmentManagement = new function () { return; var departmentName = jq("#studio_newDepName"); - if (jq.trim(jq(departmentName).val()) === "") { + if (jq(departmentName).val().trim() === "") { ShowRequiredError(departmentName); return; } diff --git a/web/studio/ASC.Web.Studio/Products/People/js/filterhandler.js b/web/studio/ASC.Web.Studio/Products/People/js/filterhandler.js index 44d37078b..0d98d99cf 100644 --- a/web/studio/ASC.Web.Studio/Products/People/js/filterhandler.js +++ b/web/studio/ASC.Web.Studio/Products/People/js/filterhandler.js @@ -67,5 +67,5 @@ } } - jq(window).bind('people-render-profiles', onRenderProfiles); + jq(window).on('people-render-profiles', onRenderProfiles); })(); diff --git a/web/studio/ASC.Web.Studio/Products/People/js/navigatorhandler.js b/web/studio/ASC.Web.Studio/Products/People/js/navigatorhandler.js index 20eea3518..d0c1f80a8 100644 --- a/web/studio/ASC.Web.Studio/Products/People/js/navigatorhandler.js +++ b/web/studio/ASC.Web.Studio/Products/People/js/navigatorhandler.js @@ -50,7 +50,7 @@ ASC.People.PageNavigator = (function () { $totalUsers.text(usersCount); } - jq(window).bind('people-render-profiles', onRenderProfiles); + jq(window).on('people-render-profiles', onRenderProfiles); }; return { diff --git a/web/studio/ASC.Web.Studio/Products/People/js/peopleactions.js b/web/studio/ASC.Web.Studio/Products/People/js/peopleactions.js index 798838be6..5d91b8bd2 100644 --- a/web/studio/ASC.Web.Studio/Products/People/js/peopleactions.js +++ b/web/studio/ASC.Web.Studio/Products/People/js/peopleactions.js @@ -72,7 +72,7 @@ window.peopleActions = (function() { delete_group: function(evt, $btn) { jq("#confirmationDeleteDepartmentPanel .confirmationAction").html(jq.format(ASC.Resources.Master.ConfirmRemoveDepartment, "" + jq(".profile-title:first>.header").html() + "")); - jq("#confirmationDeleteDepartmentPanel .middle-button-container>.button.blue.middle").unbind("click").bind("click", function() { + jq("#confirmationDeleteDepartmentPanel .middle-button-container>.button.blue.middle").off("click").on("click", function() { ASC.People.PeopleController.deleteGroup(); }); StudioBlockUIManager.blockUI("#confirmationDeleteDepartmentPanel", 500); diff --git a/web/studio/ASC.Web.Studio/Products/People/js/peoplecontroller.js b/web/studio/ASC.Web.Studio/Products/People/js/peoplecontroller.js index de81a2f81..6ecb1e4b3 100644 --- a/web/studio/ASC.Web.Studio/Products/People/js/peoplecontroller.js +++ b/web/studio/ASC.Web.Studio/Products/People/js/peoplecontroller.js @@ -79,24 +79,6 @@ ASC.People.PeopleController = (function() { function getBithdayDaysLeft(birthdayApiString) { - function stringToDate(dateString) { - var offset = 0; - - if (dateString.indexOf('Z') === -1) { - offset = dateString.substring(dateString.length - 5).split(':'); - offset = (+offset[0] * 60 + +offset[1]) * (dateString.charAt(dateString.length - 6, 1) === '+' ? 1 : -1); - } - - var parts = dateString.split('.')[0].split('T'); - parts[0] = parts[0].split('-'); - parts[1] = parts[1].split(':'); - - var date = new Date(parts[0][0], parts[0][1] - 1, parts[0][2], parts[1][0], parts[1][1], parts[1][2], 0); - date = new Date(date.getTime() - (offset * 60 * 1000)); - - return date; - } - function isLeapYear(year) { if (year < 1 || year > 9999) { throw new Error("ArgumentOutOfRange_Year"); @@ -134,7 +116,7 @@ ASC.People.PeopleController = (function() { if (!birthdayApiString) return -1; - var birthdayDate = stringToDate(birthdayApiString); + var birthdayDate = window.ServiceFactory.serializeUtcDate(birthdayApiString); return getDaysLeft(birthdayDate); }; @@ -236,7 +218,7 @@ ASC.People.PeopleController = (function() { var $this = jq(this), id = jq(this).attr("data-id"), $buttons = $this.find("td.info:first [id^='peopleEmailSwitcher'] .dropdown-item"); - $buttons.bind("click", onButtonClick); + $buttons.on("click", onButtonClick); var emailToggleMenu = $this.find(".btn.email:first"); if (emailToggleMenu.length == 1) { @@ -342,7 +324,7 @@ ASC.People.PeopleController = (function() { jq("#emptyContentForPeopleFilter").removeClass("display-none"); } //scroll on top - jq("#peopleHeaderMenu .on-top-link").click(); + jq("#peopleHeaderMenu .on-top-link").trigger("click"); }; function onDeleteGroup(params, data) { @@ -446,6 +428,8 @@ ASC.People.PeopleController = (function() { if (status == 1 && _tenantQuota.availableUsersCount == 0 && isVisitor == "false") { if (jq("#tariffLimitExceedUsersPanel").length) { TariffLimitExceed.showLimitExceedUsers(); + } else { + toastr.error(ASC.Resources.Master.ResourceJS.UserSelectorErrorLimitUsers); } return; } @@ -926,11 +910,11 @@ ASC.People.PeopleController = (function() { var showGroupActionMenu = function() { jq("#peopleHeaderMenu").show(); //call ScrolledGroupMenu.stickMenuToTheTop - jq(window).scroll(); + jq(window).trigger("scroll"); }; var lockMainActions = function() { - jq("#peopleHeaderMenu").find(".menuChangeType, .menuChangeStatus, .menuSendInvite, .menuRemoveUsers, .menuWriteLetter").removeClass("unlockAction").unbind("click"); + jq("#peopleHeaderMenu").find(".menuChangeType, .menuChangeStatus, .menuSendInvite, .menuRemoveUsers, .menuWriteLetter").removeClass("unlockAction").off("click"); jq("#changeTypePanel, #changeStatusPanel").hide(); }; @@ -1177,7 +1161,7 @@ ASC.People.PeopleController = (function() { initChangeTypeDialog(type); StudioBlockUIManager.blockUI("#changeTypeDialog", 500); PopupKeyUpActionProvider.ClearActions(); - PopupKeyUpActionProvider.EnterAction = "jq(\".changeTypeDialogOk\").click();"; + PopupKeyUpActionProvider.EnterAction = "jq(\".changeTypeDialogOk\").trigger('click');"; }; var initChangeTypeDialog = function(type) { @@ -1275,13 +1259,13 @@ ASC.People.PeopleController = (function() { $containerBox.find("input[type=checkbox]").each(function (i, item) { var $checkbox = jq(item); if ($checkbox.attr("locked")) { - $checkbox.prop("checked", false).attr("disabled", true); + $checkbox.prop("checked", false).prop("disabled", true); } else { if (quota > 0 && count < quota) { - $checkbox.prop("checked", true).attr("disabled", false); + $checkbox.prop("checked", true).prop("disabled", false); count++; } else { - $checkbox.prop("checked", false).attr("disabled", true); + $checkbox.prop("checked", false).prop("disabled", true); } } }); @@ -1308,16 +1292,16 @@ ASC.People.PeopleController = (function() { $inputs.each(function (i, item) { var $item = jq(item); if (!$item.is(":checked")) - $item.attr("disabled", true); + $item.prop("disabled", true); }); } } else { $inputs.each(function (i, item) { var $item = jq(item); if ($item.attr("locked")) { - $item.prop("checked", false).attr("disabled", true); + $item.prop("checked", false).prop("disabled", true); } else { - $item.attr("disabled", false); + $item.prop("disabled", false); } }); } @@ -1368,7 +1352,7 @@ ASC.People.PeopleController = (function() { initChangeStatusDialog(status); StudioBlockUIManager.blockUI("#changeStatusDialog", 650); PopupKeyUpActionProvider.ClearActions(); - PopupKeyUpActionProvider.EnterAction = "jq(\"#changeStatusOkBtn\").click();"; + PopupKeyUpActionProvider.EnterAction = "jq(\"#changeStatusOkBtn\").trigger('click');"; }; var initChangeStatusDialog = function(status) { @@ -1441,15 +1425,15 @@ ASC.People.PeopleController = (function() { $containerBox.find("input[type=checkbox]").each(function (i, item) { var $checkbox = jq(item); if ($checkbox.attr("locked")) { - $checkbox.prop("checked", false).attr("disabled", true); + $checkbox.prop("checked", false).prop("disabled", true); } else { if ($checkbox.attr("user-isvisitor") == "true") { - $checkbox.prop("checked", true).attr("disabled", false); + $checkbox.prop("checked", true).prop("disabled", false); } else if (quota > 0 && count < quota) { - $checkbox.prop("checked", true).attr("disabled", false); + $checkbox.prop("checked", true).prop("disabled", false); count++; } else { - $checkbox.prop("checked", false).attr("disabled", true); + $checkbox.prop("checked", false).prop("disabled", true); } } }); @@ -1484,16 +1468,16 @@ ASC.People.PeopleController = (function() { $inputs.each(function (i, item) { var $item = jq(item); if (!$item.is(":checked") && $item.attr("user-isvisitor") != "true") - $item.attr("disabled", true); + $item.prop("disabled", true); }); } } else { $inputs.each(function (i, item) { var $item = jq(item); if ($item.attr("locked")) { - $item.prop("checked", false).attr("disabled", true); + $item.prop("checked", false).prop("disabled", true); } else { - $item.attr("disabled", false); + $item.prop("disabled", false); } }); } @@ -1542,7 +1526,7 @@ ASC.People.PeopleController = (function() { initResendInviteDialog(); StudioBlockUIManager.blockUI("#resendInviteDialog", 500); PopupKeyUpActionProvider.ClearActions(); - PopupKeyUpActionProvider.EnterAction = "jq(\"#resendInviteDialog .button.blue\").click();"; + PopupKeyUpActionProvider.EnterAction = "jq(\"#resendInviteDialog .button.blue\").trigger('click');"; }; var initResendInviteDialog = function() { @@ -1603,7 +1587,7 @@ ASC.People.PeopleController = (function() { initRemoveUsersDialog(); StudioBlockUIManager.blockUI("#deleteUsersDialog", 500); PopupKeyUpActionProvider.ClearActions(); - PopupKeyUpActionProvider.EnterAction = "jq(\"#deleteUsersDialog .button.blue\").click();"; + PopupKeyUpActionProvider.EnterAction = "jq(\"#deleteUsersDialog .button.blue\").trigger('click');"; }; var initRemoveUsersDialog = function () { @@ -1713,10 +1697,10 @@ ASC.People.PeopleController = (function() { $checkbox.attr("user-id", item.id); $checkbox.attr("user-isVisitor", item.isVisitor); if (item.locked) { - $checkbox.attr("locked", item.id).prop("checked", false).attr("disabled", true); + $checkbox.attr("locked", item.id).prop("checked", false).prop("disabled", true); $label.addClass("gray-text"); } else { - $checkbox.prop("checked", true).attr("disabled", false); + $checkbox.prop("checked", true).prop("disabled", false); } $label.html(item.displayName); $checkbox.prependTo($label); @@ -1753,14 +1737,14 @@ ASC.People.PeopleController = (function() { var lockDialog = function(obj) { LoadingBanner.showLoaderBtn(obj); - jq(obj).find("input[type=checkbox]").attr("disabled", true); + jq(obj).find("input[type=checkbox]").prop("disabled", true); }; var unlockDialog = function(obj) { LoadingBanner.hideLoaderBtn(obj); jq(obj).find("input[type=checkbox]").each(function() { if (!jq(this).attr("locked")) { - jq(this).attr("disabled", false); + jq(this).prop("disabled", false); } }); }; @@ -1898,9 +1882,9 @@ ASC.People.PeopleController = (function() { ], filters: filters }) - .bind("setfilter", ASC.People.PeopleController.setFilter) - .bind("resetfilter", ASC.People.PeopleController.resetFilter) - .bind("resetallfilters", ASC.People.PeopleController.resetAllFilters); + .on("setfilter", ASC.People.PeopleController.setFilter) + .on("resetfilter", ASC.People.PeopleController.resetFilter) + .on("resetallfilters", ASC.People.PeopleController.resetAllFilters); PeopleManager.SelectedCount = ASC.People.Resources.PeopleJSResource.SelectedCount; PeopleManager.UserLimit = ASC.People.Resources.PeopleJSResource.TariffActiveUserLimit; @@ -2049,7 +2033,7 @@ ASC.People.PeopleController = (function() { }); // right mouse button click - jq("body").unbind("contextmenu").bind("contextmenu", function (event) { + jq("body").off("contextmenu").on("contextmenu", function (event) { var e = jq.fixEvent(event); if (typeof e == "undefined" || !e) { diff --git a/web/studio/ASC.Web.Studio/Products/People/js/sidenavigationpanel.js b/web/studio/ASC.Web.Studio/Products/People/js/sidenavigationpanel.js index 536a42b34..ed4b6ab34 100644 --- a/web/studio/ASC.Web.Studio/Products/People/js/sidenavigationpanel.js +++ b/web/studio/ASC.Web.Studio/Products/People/js/sidenavigationpanel.js @@ -75,9 +75,9 @@ } function initToolbar() { - jq("#studio_sidePanel").find("a.dropdown-item:not(.invite-link)").bind("click", onButtonClick); + jq("#studio_sidePanel").find("a.dropdown-item:not(.invite-link)").on("click", onButtonClick); - jq("#actionGroupMenu").find("a.dropdown-item").bind("click", onButtonClick); + jq("#actionGroupMenu").find("a.dropdown-item").on("click", onButtonClick); bindClipboardEvent(); } @@ -108,7 +108,7 @@ }; function initGroupList() { - var groupList = jq.tmpl("groupListTemplate", { groups: window.GroupManager.getAllGroups() }); + var groupList = jq.tmpl("groupListTemplate", { groups: window.GroupManager.getGroupsArray() }); jq("#groupList").empty().append(groupList); @@ -118,7 +118,7 @@ }; function initMenuList() { - jq(window).bind("change-group", onChangeGroup); + jq(window).on("change-group", onChangeGroup); } function changeAnchor (group) { diff --git a/web/studio/ASC.Web.Studio/Products/People/web.config b/web/studio/ASC.Web.Studio/Products/People/web.config index a9f5c7087..32a4bddb3 100644 --- a/web/studio/ASC.Web.Studio/Products/People/web.config +++ b/web/studio/ASC.Web.Studio/Products/People/web.config @@ -1,111 +1,107 @@ - + - - + + - - - - + + - - - - - - - - - + - - - + - - - + + + + + + + + + - - - + - - - + - - - - + + + + + + + + + + - - - - - + - - - - - - - - - - - - - - - - + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - \ No newline at end of file + diff --git a/web/studio/ASC.Web.Studio/Products/Projects/ASC.Web.Projects.csproj b/web/studio/ASC.Web.Studio/Products/Projects/ASC.Web.Projects.csproj index 9a58db867..1e8350bc1 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/ASC.Web.Projects.csproj +++ b/web/studio/ASC.Web.Studio/Products/Projects/ASC.Web.Projects.csproj @@ -6,7 +6,8 @@ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - + + Release @@ -21,8 +22,10 @@ ASC.Web.Projects v4.8 OnBuildSuccess - - + + + + 4.0 false @@ -237,6 +240,7 @@ + @@ -691,6 +695,9 @@ True PatternResource.resx + + EngineResource.resx + EngineResource.resx @@ -2941,19 +2948,19 @@ 4.3.0 - 5.1.2 + 6.2.0 - 4.11.0 + 4.17.0 - 9.0.0 + 10.1.1 - 12.0.3 + 13.0.1 - 3.12.0 + 3.13.2 @@ -2968,10 +2975,12 @@ True 54559 / - + + False False - + + False diff --git a/web/studio/ASC.Web.Studio/Products/Projects/App_Themes/default/css/alltasks.less b/web/studio/ASC.Web.Studio/Products/Projects/App_Themes/default/css/alltasks.less index c144b53e5..3e74fe43e 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/App_Themes/default/css/alltasks.less +++ b/web/studio/ASC.Web.Studio/Products/Projects/App_Themes/default/css/alltasks.less @@ -206,6 +206,14 @@ } } + &.can-move { + background: #fcf6e0; + + &.move-dest { + background: #F9EEC2; + } + } + .user { white-space: nowrap; float: right; @@ -487,6 +495,7 @@ body.media-width-0-1100 #CommonListContainer .taskList .task .taskName { float: left; width: 13px; height: 29px; + margin: 0 8px; input { margin-top: 5px; @@ -545,8 +554,8 @@ body.media-width-0-1100 #CommonListContainer .taskList .task .taskName { } .quickAddSubTaskLink { - margin: 8px 0 6px 0; - border: #fff 1px solid; + margin: 8px 0 6px 8px; + border: #fff 1px solid; } .subtask-loader { @@ -577,7 +586,8 @@ body.media-width-0-1100 #CommonListContainer .taskList .task .taskName { .subtask-name { border-right: none; - padding-left: 24px; + padding-left: 36px; + padding-right: 8px; } input.subtask-name-input, @@ -622,12 +632,12 @@ body.media-width-0-1200 .subtask-responsible-selector } .subtasks { - margin:8px 0; + padding:8px 0; } .taskList .subtasks { /*---for list tasks---*/ display: none; - margin-left: @actionListWidth; + padding-left: @actionListWidth; } *:first-child+html .subtask .check input { margin-top:5px; @@ -827,4 +837,3 @@ body.media-width-0-1200 .subtask .taskName { clear: both; } .tabContent { display: none;} - diff --git a/web/studio/ASC.Web.Studio/Products/Projects/App_Themes/default/css/common.less b/web/studio/ASC.Web.Studio/Products/Projects/App_Themes/default/css/common.less index cdcb50cab..3f00640a0 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/App_Themes/default/css/common.less +++ b/web/studio/ASC.Web.Studio/Products/Projects/App_Themes/default/css/common.less @@ -877,7 +877,7 @@ table.table-list { } .descriptionContainer{ - div{ + >div{ padding-bottom: 10px; } span{ diff --git a/web/studio/ASC.Web.Studio/Products/Projects/App_Themes/default/css/discussions.less b/web/studio/ASC.Web.Studio/Products/Projects/App_Themes/default/css/discussions.less index 790dbb340..f09e526e8 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/App_Themes/default/css/discussions.less +++ b/web/studio/ASC.Web.Studio/Products/Projects/App_Themes/default/css/discussions.less @@ -115,11 +115,6 @@ margin-bottom: 20px; } -#discussionButtonsContainer -{ - margin-bottom: 20px; -} - #discussionTabsContent { clear: left; diff --git a/web/studio/ASC.Web.Studio/Products/Projects/App_Themes/default/css/ganttchart.css b/web/studio/ASC.Web.Studio/Products/Projects/App_Themes/default/css/ganttchart.css index e5424e3d0..920891dc4 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/App_Themes/default/css/ganttchart.css +++ b/web/studio/ASC.Web.Studio/Products/Projects/App_Themes/default/css/ganttchart.css @@ -1108,4 +1108,8 @@ main { margin: 0 !important; padding: 0 !important; overflow: auto !important; +} + +.layout-bottom-spacer { + padding: 0; } \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Projects/App_Themes/default/css/reports.css b/web/studio/ASC.Web.Studio/Products/Projects/App_Themes/default/css/reports.css index cf34c8f11..62aff66f5 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/App_Themes/default/css/reports.css +++ b/web/studio/ASC.Web.Studio/Products/Projects/App_Themes/default/css/reports.css @@ -155,6 +155,9 @@ margin-right: 24px; margin-top: 17px; } +.filter-item-container.with-combo { + margin-right: 8px; +} .filter-option-container { margin-top:26px; } diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Classes/NotifyHelper.cs b/web/studio/ASC.Web.Studio/Products/Projects/Classes/NotifyHelper.cs index bcf7dd2f9..3fbddeab2 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Classes/NotifyHelper.cs +++ b/web/studio/ASC.Web.Studio/Products/Projects/Classes/NotifyHelper.cs @@ -85,7 +85,7 @@ namespace ASC.Web.Projects.Classes try { CoreContext.TenantManager.SetCurrentTenant(tenant); - SecurityContext.AuthenticateMe(ASC.Core.Configuration.Constants.CoreSystem); + SecurityContext.CurrentAccount = ASC.Core.Configuration.Constants.CoreSystem; using (var scope = DIHelper.Resolve()) { var engineFactory = scope.Resolve(); @@ -97,7 +97,7 @@ namespace ASC.Web.Projects.Classes var user = CoreContext.UserManager.GetUsers(t.CreateBy); if (!Constants.LostUser.Equals(user) && user.Status == EmployeeStatus.Active) { - SecurityContext.AuthenticateMe(user.ID); + SecurityContext.CurrentUser = user.ID; Thread.CurrentThread.CurrentCulture = user.GetCulture(); Thread.CurrentThread.CurrentUICulture = user.GetCulture(); @@ -194,7 +194,7 @@ namespace ASC.Web.Projects.Classes try { CoreContext.TenantManager.SetCurrentTenant(tenant); - SecurityContext.AuthenticateMe(ASC.Core.Configuration.Constants.CoreSystem); + SecurityContext.CurrentAccount = ASC.Core.Configuration.Constants.CoreSystem; using (var scope = DIHelper.Resolve()) { var m = scope.Resolve().MilestoneDao.GetById((int)r[1]); @@ -204,7 +204,7 @@ namespace ASC.Web.Projects.Classes var user = CoreContext.UserManager.GetUsers(sender); if (!Constants.LostUser.Equals(user) && user.Status == EmployeeStatus.Active) { - SecurityContext.AuthenticateMe(user.ID); + SecurityContext.CurrentUser = user.ID; Thread.CurrentThread.CurrentCulture = user.GetCulture(); Thread.CurrentThread.CurrentUICulture = user.GetCulture(); @@ -260,7 +260,7 @@ namespace ASC.Web.Projects.Classes var user = CoreContext.UserManager.GetUsers(t.CreateBy); if (user.ID == Constants.LostUser.ID || user.Status != EmployeeStatus.Active) return; - SecurityContext.AuthenticateMe(user.ID); + SecurityContext.CurrentUser = user.ID; Thread.CurrentThread.CurrentCulture = user.GetCulture(); Thread.CurrentThread.CurrentUICulture = user.GetCulture(); diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Classes/ReportHelper.cs b/web/studio/ASC.Web.Studio/Products/Projects/Classes/ReportHelper.cs index 1ce7bc1ca..f2e1f2e2b 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Classes/ReportHelper.cs +++ b/web/studio/ASC.Web.Studio/Products/Projects/Classes/ReportHelper.cs @@ -159,13 +159,15 @@ namespace ASC.Web.Projects.Classes var filterJson = JsonConvert.SerializeObject(template.Filter); var userCulture = CoreContext.UserManager.GetUsers(template.CreateBy).GetCulture(); + var timeInterval = GetIntervarl(template.Filter); var reportInfoJson = JsonConvert.SerializeObject(new Dictionary { { "Title", report.ReportInfo.Title }, { "CreatedText", ReportResource.ReportCreated }, { "CreatedAt", TenantUtil.DateTimeNow().ToString("M/d/yyyy", CultureInfo.InvariantCulture) }, { "CreatedBy", ProjectsFilterResource.By + " " + CoreContext.UserManager.GetUsers(template.CreateBy).DisplayUserName(false) }, - { "DateFormat", userCulture.DateTimeFormat.ShortDatePattern } + { "DateFormat", userCulture.DateTimeFormat.ShortDatePattern }, + { "TimeInterval", timeInterval } }); var tmpFileName = DocbuilderReportsUtility.TmpFileName; @@ -198,6 +200,36 @@ namespace ASC.Web.Projects.Classes return ExtendedReportType.BuildDocbuilderReport(Filter).ToList(); } + private static string GetIntervarl(TaskFilter taskFilter) + { + var type = taskFilter.TimeInterval; + switch (type) + { + case ReportTimeInterval.Absolute: + return null; + case ReportTimeInterval.Today: + return ReportResource.Today; + case ReportTimeInterval.Yesterday: + return ReportResource.Yesterday; + case ReportTimeInterval.CurrWeek: + return ReportResource.ThisWeek; + case ReportTimeInterval.PrevWeek: + return ReportResource.LastWeek; + case ReportTimeInterval.CurrMonth: + return ReportResource.ThisMonth; + case ReportTimeInterval.PrevMonth: + return ReportResource.LastMonth; + case ReportTimeInterval.CurrYear: + return ReportResource.ThisYear; + case ReportTimeInterval.PrevYear: + return ReportResource.LastYear; + case ReportTimeInterval.Relative: + return string.Format(ReportResource.CustomInterval, taskFilter.FromDate.ToString("M/d/yyyy", CultureInfo.InvariantCulture), taskFilter.ToDate.ToString("M/d/yyyy", CultureInfo.InvariantCulture)); + } + return null; + } + + private void PrepareFilter(int templateID) { if (templateID != 0 && !Filter.FromDate.Equals(DateTime.MinValue)) @@ -467,8 +499,8 @@ namespace ASC.Web.Projects.Classes using (var scope = DIHelper.Resolve()) { - var result = scope.Resolve().ProjectEngine - .GetByFilter(filter) + var projects = scope.Resolve().ProjectEngine + .GetByFilterForReport(filter) .Select(r => new object[] { r.Title, CoreContext.UserManager.GetUsers(r.Responsible).DisplayUserName(false), @@ -476,7 +508,14 @@ namespace ASC.Web.Projects.Classes r.MilestoneCount, r.TaskCount, r.ParticipantCount }); - result = result.OrderBy(r => (string)r[1]); + var result = projects.OrderBy(r => (string)r[1]).ToList(); + + if (result.Count() == 0) + { + return result; + } + + result.Insert(0, scope.Resolve().ProjectEngine.GetByFilterCountForReport(filter)); return result; } @@ -494,7 +533,10 @@ namespace ASC.Web.Projects.Classes GrammaticalResource.MilestoneGenitivePlural, GrammaticalResource.TaskGenitivePlural, ReportResource.Participiants, - LocalizedEnumConverter.ConvertToString(ProjectStatus.Open) + LocalizedEnumConverter.ConvertToString(ProjectStatus.Open), + ReportResource.ProjectsCreated, + ReportResource.ProjectsPaused, + ReportResource.ProjectsClosed }; } } @@ -526,7 +568,7 @@ namespace ASC.Web.Projects.Classes public override IEnumerable BuildDocbuilderReport(TaskFilter filter) { - return base.BuildDocbuilderReport(filter).Where(r => (int)r[3] == 0); + return base.BuildDocbuilderReport(filter).Where(r => r.Count() != 2 && (int)r[3] == 0); } public override ReportInfo ReportInfo @@ -553,7 +595,7 @@ namespace ASC.Web.Projects.Classes public override IEnumerable BuildDocbuilderReport(TaskFilter filter) { - return base.BuildDocbuilderReport(filter).Where(r => (int)r[4] == 0); + return base.BuildDocbuilderReport(filter).Where(r => r.Count() != 2 && (int)r[4] == 0); } public override ReportInfo ReportInfo @@ -1202,7 +1244,9 @@ namespace ASC.Web.Projects.Classes TaskResource.Tasks, MilestoneResource.Milestones, MessageResource.Messages, - ProjectsCommonResource.Total + ProjectsCommonResource.Total, + ProjectsCommonResource.AverageProject, + TaskResource.AverageTask }; } } diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Controls/Messages/DiscussionAction.ascx b/web/studio/ASC.Web.Studio/Products/Projects/Controls/Messages/DiscussionAction.ascx index 594aa57a0..93c41fbd8 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Controls/Messages/DiscussionAction.ascx +++ b/web/studio/ASC.Web.Studio/Products/Projects/Controls/Messages/DiscussionAction.ascx @@ -44,7 +44,7 @@
    <%= MessageResource.MessageContent %>
    - + <% if (!MobileDetector.IsMobile) diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Controls/Reports/ReportFilters.ascx b/web/studio/ASC.Web.Studio/Products/Projects/Controls/Reports/ReportFilters.ascx index eb5be14ae..84a42dc3f 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Controls/Reports/ReportFilters.ascx +++ b/web/studio/ASC.Web.Studio/Products/Projects/Controls/Reports/ReportFilters.ascx @@ -8,7 +8,7 @@
    -<% if (Report.ReportType == ReportType.UsersActivity || Report.ReportType == ReportType.TimeSpend) %> +<% if (Report.ReportType == ReportType.UsersActivity || Report.ReportType == ReportType.TimeSpend || Report.ReportType == ReportType.ProjectsList) %> <% { %>
    style="display:none;" <%} %> > diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Controls/Reports/ReportFilters.ascx.cs b/web/studio/ASC.Web.Studio/Products/Projects/Controls/Reports/ReportFilters.ascx.cs index 816a752c2..41452fa1c 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Controls/Reports/ReportFilters.ascx.cs +++ b/web/studio/ASC.Web.Studio/Products/Projects/Controls/Reports/ReportFilters.ascx.cs @@ -30,7 +30,7 @@ namespace ASC.Web.Projects.Controls.Reports protected void Page_Load(object sender, EventArgs e) { - if (Report.ReportType == ReportType.UsersActivity || Report.ReportType == ReportType.TimeSpend) + if (Report.ReportType == ReportType.UsersActivity || Report.ReportType == ReportType.TimeSpend || Report.ReportType == ReportType.ProjectsList) { DateTime fromDateTime; DateTime toDateTime; diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Core/Dao/MilestoneDao.cs b/web/studio/ASC.Web.Studio/Products/Projects/Core/Dao/MilestoneDao.cs index 9829a04ca..8db5ed0a4 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Core/Dao/MilestoneDao.cs +++ b/web/studio/ASC.Web.Studio/Products/Projects/Core/Dao/MilestoneDao.cs @@ -263,7 +263,7 @@ namespace ASC.Projects.Data.DAO .InColumnValue("is_notify", milestone.IsNotify) .InColumnValue("is_key", milestone.IsKey) .InColumnValue("description", milestone.Description) - .InColumnValue("status_changed", milestone.StatusChangedOn) + .InColumnValue("status_changed", TenantUtil.DateTimeToUtc(milestone.StatusChangedOn)) .InColumnValue("responsible_id", milestone.Responsible.ToString()) .Identity(1, 0, true); milestone.ID = Db.ExecuteScalar(insert); diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Core/Dao/ProjectDao.cs b/web/studio/ASC.Web.Studio/Products/Projects/Core/Dao/ProjectDao.cs index b29d44a2f..740716586 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Core/Dao/ProjectDao.cs +++ b/web/studio/ASC.Web.Studio/Products/Projects/Core/Dao/ProjectDao.cs @@ -173,8 +173,7 @@ namespace ASC.Projects.Data.DAO return Db.ExecuteList(query).ConvertAll(converter); } - - public List GetByFilter(TaskFilter filter, bool isAdmin, bool checkAccess) + private SqlQuery CreateQueryForFilter(TaskFilter filter, bool isAdmin, bool checkAccess) { var teamQuery = new SqlQuery(ParticipantTable + " pp") .SelectCount() @@ -216,6 +215,37 @@ namespace ASC.Projects.Data.DAO query = CreateQueryFilter(query, filter, isAdmin, checkAccess); + query.GroupBy("p.id"); + return query; + } + + public List GetByFilter(TaskFilter filter, bool isAdmin, bool checkAccess) + { + return Db.ExecuteList(CreateQueryForFilter(filter, isAdmin, checkAccess)).ConvertAll(ToProjectFull); + } + + public List GetByFilterForReport(TaskFilter filter, bool isAdmin, bool checkAccess) + { + filter = (TaskFilter)filter.Clone(); + Exp exp = null; + if (filter.ProjectStatuses.Contains(ProjectStatus.Closed)) + { + filter.ProjectStatuses.Remove(ProjectStatus.Closed); + exp = Exp.Between("p.status_changed", filter.GetFromDate(), filter.GetToDate()) & Exp.Eq("p.status", 1) | Exp.In("p.status", filter.ProjectStatuses); + filter.ProjectStatuses.Clear(); + }; + + var query = CreateQueryForFilter(filter, isAdmin, checkAccess); + + query.Where(Exp.Between("p.create_on", filter.GetFromDate(), filter.GetToDate())); + + query.Where(Exp.Eq("p.status", 0) | (Exp.Between("p.status_changed", filter.GetFromDate(), filter.GetToDate()))); + + if(exp != null) + { + query.Where(exp); + } + return Db.ExecuteList(query).ConvertAll(ToProjectFull); } @@ -227,11 +257,61 @@ namespace ASC.Projects.Data.DAO query = CreateQueryFilter(query, filter, isAdmin, checkAccess); + query.GroupBy("p.id"); + var queryCount = new SqlQuery().SelectCount().From(query, "t1"); return Db.ExecuteScalar(queryCount); } + public int GetByFilterCountForReport(TaskFilter filter, bool isAdmin, bool checkAccess) + { + filter = (TaskFilter)filter.Clone(); + var query = new SqlQuery(ProjectsTable + " p") + .Select("p.id") + .Where("p.tenant_id", Tenant); + + if (filter.ProjectStatuses.Contains(ProjectStatus.Closed) || filter.ProjectStatuses.Contains(ProjectStatus.Paused)) + { + query.Where(Exp.Between("p.status_changed", filter.GetFromDate(), filter.GetToDate()) & Exp.Eq("p.status", filter.ProjectStatuses[0])); + filter.ProjectStatuses.Clear(); + } + + if (filter.GetFromDate() != DateTime.MinValue && filter.GetToDate() != DateTime.MaxValue) + { + query.Where(Exp.Between("p.create_on", filter.GetFromDate(), filter.GetToDate())); + } + + query = CreateQueryFilter(query, filter, isAdmin, checkAccess); + + query.GroupBy("p.id"); + + var queryCount = new SqlQuery().SelectCount().From(query, "t1"); + + return Db.ExecuteScalar(queryCount); + } + + public List> GetByFilterAverageTime(TaskFilter filter, bool isAdmin, bool checkAccess) + { + var query = new SqlQuery(ProjectsTable + " p") + .Select("ROUND(AVG(TIME_TO_SEC(TIMEDIFF(p.status_changed, p.create_on))/60/60))", "pp.participant_id") + .InnerJoin(ParticipantTable + " pp", Exp.EqColumns("p.tenant_id", "pp.tenant") & Exp.EqColumns("p.id", "pp.project_id")) + .Where("p.tenant_id", Tenant) + .Where(Exp.Eq("p.status", 1)); + + if (filter.GetFromDate() != DateTime.MinValue && filter.GetToDate() != DateTime.MaxValue) + { + query.Where(Exp.Between("p.create_on", filter.GetFromDate(), filter.GetToDate())); + query.Where(Exp.Between("p.status_changed", filter.GetFromDate(), filter.GetToDate())); + } + + query.GroupBy("pp.participant_id"); + + query = CreateQueryFilter(query, filter, isAdmin, checkAccess); + + return Db.ExecuteList(query).ConvertAll(a => new Tuple(Guid.Parse((string)a[1]), Convert.ToInt32(a[0]))); + } + private SqlQuery CreateQueryFilter(SqlQuery query, TaskFilter filter, bool isAdmin, bool checkAccess) { if (filter.TagId != 0) @@ -295,8 +375,6 @@ namespace ASC.Projects.Data.DAO } } - query.GroupBy("p.id"); - if (checkAccess) { query.Where(Exp.Eq("p.private", false)); @@ -512,7 +590,7 @@ namespace ASC.Projects.Data.DAO .Set("title", project.Title) .Set("description", project.Description) .Set("status", project.Status) - .Set("status_changed", project.StatusChangedOn) + .Set("status_changed", TenantUtil.DateTimeToUtc(project.StatusChangedOn)) .Set("responsible_id", project.Responsible.ToString()) .Set("private", project.Private) .Set("last_modified_by", project.LastModifiedBy.ToString()) diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Core/Dao/SubtaskDao.cs b/web/studio/ASC.Web.Studio/Products/Projects/Core/Dao/SubtaskDao.cs index e83ba84e6..dceef8a49 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Core/Dao/SubtaskDao.cs +++ b/web/studio/ASC.Web.Studio/Products/Projects/Core/Dao/SubtaskDao.cs @@ -119,7 +119,7 @@ namespace ASC.Projects.Data.DAO .ConvertAll(x => { var st = ToSubTask(x); - st.StatusChangedOn = Convert.ToDateTime(x.Last()); + st.StatusChangedOn = TenantUtil.DateTimeFromUtc(Convert.ToDateTime(x.Last())); return st; }).ToList(); } diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Core/Dao/TaskDao.cs b/web/studio/ASC.Web.Studio/Products/Projects/Core/Dao/TaskDao.cs index 4f645b88d..e65309b42 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Core/Dao/TaskDao.cs +++ b/web/studio/ASC.Web.Studio/Products/Projects/Core/Dao/TaskDao.cs @@ -240,14 +240,19 @@ namespace ASC.Projects.Data.DAO filter = (TaskFilter)filter.Clone(); var query = new SqlQuery(TasksTable + " t") - .Select("t.create_by", "t.project_id") + .InnerJoin(TasksResponsibleTable + " tr", Exp.EqColumns("t.tenant_id", "tr.tenant_id") & Exp.EqColumns("t.id", "tr.task_id")) + .Select("tr.responsible_id", "t.project_id") .InnerJoin(ProjectsTable + " p", Exp.EqColumns("t.project_id", "p.id") & Exp.EqColumns("t.tenant_id", "p.tenant_id")) - .Where("t.tenant_id", Tenant) - .Where(Exp.Between("t.create_on", filter.GetFromDate(), filter.GetToDate())); + .Where("t.tenant_id", Tenant); + + if (filter.GetFromDate() != DateTime.MinValue && filter.GetToDate() != DateTime.MaxValue) + { + query.Where((Exp.Between("t.create_on", filter.GetFromDate(), filter.GetToDate()) & !Exp.Eq("t.start_date", DateTime.MinValue)) | Exp.Between("t.start_date", filter.GetFromDate(), filter.GetToDate())); + } if (filter.HasUserId) { - query.Where(Exp.In("t.create_by", filter.GetUserIds())); + query.Where(Exp.In("tr.responsible_id", filter.GetUserIds())); filter.UserId = Guid.Empty; filter.DepartmentId = Guid.Empty; } @@ -256,13 +261,44 @@ namespace ASC.Projects.Data.DAO var queryCount = new SqlQuery() .SelectCount() - .Select("t1.create_by", "t1.project_id") + .Select("t1.responsible_id", "t1.project_id") .From(query, "t1") - .GroupBy("create_by", "project_id"); + .GroupBy("responsible_id", "project_id"); + return Db.ExecuteList(queryCount).ConvertAll(b => new Tuple(Guid.Parse((string)b[1]), Convert.ToInt32(b[2]), Convert.ToInt32(b[0]))); } + public List> GetByFilterAverageTime(TaskFilter filter, bool isAdmin, bool checkAccess) + { + filter = (TaskFilter)filter.Clone(); + + var query = new SqlQuery(TasksTable + " t") + .Select("ROUND(AVG(TIME_TO_SEC(TIMEDIFF(t.status_changed, t.create_on))/60/60))", "tr.responsible_id, t.project_id") + .InnerJoin(TasksResponsibleTable + " tr", Exp.EqColumns("t.tenant_id", "tr.tenant_id") & Exp.EqColumns("t.id", "tr.task_id")) + .InnerJoin(ProjectsTable + " p", Exp.EqColumns("t.project_id", "p.id") & Exp.EqColumns("t.tenant_id", "p.tenant_id")) + .Where("t.tenant_id", Tenant) + .Where(Exp.Eq("t.status", 2)) + .Where(Exp.Between("t.status_changed", filter.GetFromDate(), filter.GetToDate())); + + if (filter.GetFromDate() != DateTime.MinValue && filter.GetToDate() != DateTime.MaxValue) + { + query.Where((Exp.Between("t.create_on", filter.GetFromDate(), filter.GetToDate()) & !Exp.Eq("t.start_date", DateTime.MinValue)) | Exp.Between("t.start_date", filter.GetFromDate(), filter.GetToDate())); + } + + if (filter.HasUserId) + { + query.Where(Exp.In("tr.responsible_id", filter.GetUserIds())); + filter.UserId = Guid.Empty; + filter.DepartmentId = Guid.Empty; + } + + query = CreateQueryFilterCount(query, filter, isAdmin, checkAccess); + + query.GroupBy("tr.responsible_id", "project_id"); + return Db.ExecuteList(query).ConvertAll(a => new Tuple(Guid.Parse((string)a[1]), Convert.ToInt32(a[2]), Convert.ToInt32(a[0]))); + } + public List GetByResponsible(Guid responsibleId, IEnumerable statuses) { var q = CreateQuery() diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Core/Dao/TimeSpendDao.cs b/web/studio/ASC.Web.Studio/Products/Projects/Core/Dao/TimeSpendDao.cs index 7fd691ecb..9060322e3 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Core/Dao/TimeSpendDao.cs +++ b/web/studio/ASC.Web.Studio/Products/Projects/Core/Dao/TimeSpendDao.cs @@ -237,7 +237,6 @@ namespace ASC.Projects.Data.DAO public TimeSpend Save(TimeSpend timeSpend) { timeSpend.Date = TenantUtil.DateTimeToUtc(timeSpend.Date); - timeSpend.StatusChangedOn = TenantUtil.DateTimeToUtc(timeSpend.StatusChangedOn); var insert = Insert(TimeTrackingTable) .InColumnValue("id", timeSpend.ID) diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Core/Engine/EngineResource.az-Latn-AZ.resx b/web/studio/ASC.Web.Studio/Products/Projects/Core/Engine/EngineResource.az-Latn-AZ.resx new file mode 100644 index 000000000..d63e6e67f --- /dev/null +++ b/web/studio/ASC.Web.Studio/Products/Projects/Core/Engine/EngineResource.az-Latn-AZ.resx @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Layihə bağlandı + + diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Core/Engine/ProjectEngine.cs b/web/studio/ASC.Web.Studio/Products/Projects/Core/Engine/ProjectEngine.cs index 7d4731b77..24a349ec5 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Core/Engine/ProjectEngine.cs +++ b/web/studio/ASC.Web.Studio/Products/Projects/Core/Engine/ProjectEngine.cs @@ -98,11 +98,47 @@ namespace ASC.Projects.Engine return DaoFactory.ProjectDao.GetByFilter(filter, ProjectSecurity.CurrentUserAdministrator, ProjectSecurity.IsPrivateDisabled); } + public IEnumerable GetByFilterForReport(TaskFilter filter) + { + return DaoFactory.ProjectDao.GetByFilterForReport(filter, ProjectSecurity.CurrentUserAdministrator, ProjectSecurity.IsPrivateDisabled); + } + + public object[] GetByFilterCountForReport(TaskFilter filter) + { + var cloneFilter = (TaskFilter)filter.Clone(); + var open = 0; + var paused = 0; + var closed = 0; + + if (!filter.ProjectStatuses.Any() || filter.ProjectStatuses.Contains(ProjectStatus.Open)) + { + cloneFilter.ProjectStatuses = new List { ProjectStatus.Open }; + open = DaoFactory.ProjectDao.GetByFilterCountForReport(cloneFilter, ProjectSecurity.CurrentUserAdministrator, ProjectSecurity.IsPrivateDisabled); + } + if (!filter.ProjectStatuses.Any() || filter.ProjectStatuses.Contains(ProjectStatus.Paused)) + { + cloneFilter.ProjectStatuses = new List { ProjectStatus.Paused }; + paused = DaoFactory.ProjectDao.GetByFilterCountForReport(cloneFilter, ProjectSecurity.CurrentUserAdministrator, ProjectSecurity.IsPrivateDisabled); + } + if (!filter.ProjectStatuses.Any() || filter.ProjectStatuses.Contains(ProjectStatus.Closed)) + { + cloneFilter.ProjectStatuses = new List { ProjectStatus.Closed }; + closed = DaoFactory.ProjectDao.GetByFilterCountForReport(cloneFilter, ProjectSecurity.CurrentUserAdministrator, ProjectSecurity.IsPrivateDisabled); + } + + return new object[3] { open, paused, closed }; + } + public int GetByFilterCount(TaskFilter filter) { return DaoFactory.ProjectDao.GetByFilterCount(filter, ProjectSecurity.CurrentUserAdministrator, ProjectSecurity.IsPrivateDisabled); } + public List> GetByFilterAverageTime(TaskFilter filter) + { + return DaoFactory.ProjectDao.GetByFilterAverageTime(filter, ProjectSecurity.CurrentUserAdministrator, ProjectSecurity.IsPrivateDisabled); + } + public IEnumerable GetFollowing(Guid participant) { return DaoFactory.ProjectDao.GetFollowing(participant).Where(CanReadDelegate).ToList(); @@ -292,7 +328,7 @@ namespace ASC.Projects.Engine project.LastModifiedBy = SecurityContext.CurrentAccount.ID; project.LastModifiedOn = TenantUtil.DateTimeNow(); - project.StatusChangedOn = DateTime.Now; + project.StatusChangedOn = TenantUtil.DateTimeNow(); project.Status = status; DaoFactory.ProjectDao.Update(project); diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Core/Engine/ProjectSecurity.cs b/web/studio/ASC.Web.Studio/Products/Projects/Core/Engine/ProjectSecurity.cs index 9a519859e..e61c74968 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Core/Engine/ProjectSecurity.cs +++ b/web/studio/ASC.Web.Studio/Products/Projects/Core/Engine/ProjectSecurity.cs @@ -772,6 +772,11 @@ namespace ASC.Projects.Engine if (!CanEdit(task, subtask)) throw CreateSecurityException(); } + public void DemandCreateSubtask(Task task) + { + if (!CanCreateSubtask(task)) throw CreateSecurityException(); + } + public void DemandDelete(T entity) where T : DomainObject { if (!CanDelete(entity)) throw CreateSecurityException(); diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Core/Engine/ReportEngine.cs b/web/studio/ASC.Web.Studio/Products/Projects/Core/Engine/ReportEngine.cs index 4791d6475..fda00fca8 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Core/Engine/ReportEngine.cs +++ b/web/studio/ASC.Web.Studio/Products/Projects/Core/Engine/ReportEngine.cs @@ -198,6 +198,20 @@ namespace ASC.Projects.Engine var milestones = MilestoneEngine.GetByFilterCountForReport(filter); var messages = MessageEngine.GetByFilterCountForReport(filter); + List> averageTasks = null; + List> averageProjects = null; + if (filter.IsShowAverageTime) + { + if (filter.TypeOfShowAverageTime == AverageTime.All || filter.TypeOfShowAverageTime == AverageTime.ClosingProjects) + { + averageProjects = ProjectEngine.GetByFilterAverageTime(filter); + } + if (filter.TypeOfShowAverageTime == AverageTime.All || filter.TypeOfShowAverageTime == AverageTime.CompletingTasks) + { + averageTasks = TaskEngine.GetByFilterAverageTime(filter); + } + } + if (filter.ViewType == 1) { var projectIds = GetProjects(tasks, milestones, messages); @@ -205,7 +219,7 @@ namespace ASC.Projects.Engine foreach (var p in projects) { - var userIds = GetUsers(p.ID, tasks, milestones, messages); + var userIds = GetUsers(p.ID, tasks, milestones, messages, averageTasks); foreach (var userId in userIds) { @@ -214,10 +228,29 @@ namespace ASC.Projects.Engine var milestonesCount = GetCount(milestones, p.ID, userId); var messagesCount = GetCount(messages, p.ID, userId); + var avgTasks = -1; + if(averageTasks != null) + { + if (averageTasks.Where(r => r.Item2 == p.ID && r.Item1 == userId).Any()) + { + avgTasks = averageTasks.FirstOrDefault(r => r.Item2 == p.ID && r.Item1 == userId).Item3; + } + else + { + avgTasks = -2; + } + } + result.Add(new object[] { - p.ID, p.Title, userName, tasksCount, milestonesCount, messagesCount, - tasksCount + milestonesCount + messagesCount + p.ID, + p.Title, + userName, + tasksCount, + milestonesCount, + messagesCount, + tasksCount + milestonesCount + messagesCount, + avgTasks }); } } @@ -225,7 +258,11 @@ namespace ASC.Projects.Engine else { var userIds = GetUsers(-1, tasks, milestones, messages); - + if (averageProjects != null) + { + userIds.AddRange(averageProjects.Select(r => r.Item1)); + userIds = userIds.Distinct().ToList(); + } foreach (var userId in userIds) { var group = CoreContext.UserManager.GetUserGroups(userId).FirstOrDefault(); @@ -235,14 +272,43 @@ namespace ASC.Projects.Engine var milestonesCount = GetCount(milestones, userId); var messagesCount = GetCount(messages, userId); + var avgTasks = -1; + var avgProject = -1; + if (averageTasks != null) + { + if (averageTasks.Where(r => r.Item1 == userId).Any()) + { + avgTasks = averageTasks.Where(r => r.Item1 == userId).Sum(r => r.Item3) / averageTasks.Where(r => r.Item1 == userId).Count(); + } + else + { + avgTasks = -2; + } + } + if (averageProjects != null) + { + if (averageProjects.Where(r => r.Item1 == userId).Any()) + { + avgProject = averageProjects.FirstOrDefault(r => r.Item1 == userId).Item2; + } + else + { + avgProject = -2; + } + } + + result.Add(new object[] { group != null ? group.ID : Guid.Empty, - group != null ? group.Name : "", userName, + group != null ? group.Name : "", + userName, tasksCount, milestonesCount, messagesCount, - tasksCount + milestonesCount + messagesCount + tasksCount + milestonesCount + messagesCount, + avgProject, + avgTasks }); } } @@ -268,7 +334,8 @@ namespace ASC.Projects.Engine foreach (var item in data) { - result.AddRange(item.Where(r => pId == -1 || r.Item2 == pId).Select(r => r.Item1)); + if(item != null) + result.AddRange(item.Where(r => pId == -1 || r.Item2 == pId).Select(r => r.Item1)); } return result.Distinct().ToList(); @@ -278,6 +345,7 @@ namespace ASC.Projects.Engine { return data.Where(r => r.Item1 == userId).Sum(r => r.Item3); } + private static int GetCount(IEnumerable> data, int pId, Guid userId) { var count = 0; diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Core/Engine/SecurityAdapter.cs b/web/studio/ASC.Web.Studio/Products/Projects/Core/Engine/SecurityAdapter.cs index 0e16abb8e..d85e0846a 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Core/Engine/SecurityAdapter.cs +++ b/web/studio/ASC.Web.Studio/Products/Projects/Core/Engine/SecurityAdapter.cs @@ -135,6 +135,11 @@ namespace ASC.Web.Projects.Classes } } + public bool CanDownload(FileEntry entry, Guid userId) + { + return CanRead(entry, userId); + } + private enum SecurityAction { Read, diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Core/Engine/SubtaskEngine.cs b/web/studio/ASC.Web.Studio/Products/Projects/Core/Engine/SubtaskEngine.cs index 817574fa8..379451a43 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Core/Engine/SubtaskEngine.cs +++ b/web/studio/ASC.Web.Studio/Products/Projects/Core/Engine/SubtaskEngine.cs @@ -187,6 +187,38 @@ namespace ASC.Projects.Engine return SaveOrUpdate(subtask, task); } + public Subtask Move(Subtask subtask, Task fromTask, Task toTask, IEnumerable toTaskTeam) + { + if (subtask == null) throw new Exception("subtask.Task"); + if (subtask.Status == TaskStatus.Closed) throw new Exception("subtask can't be closed"); + if (fromTask == null) throw new ArgumentNullException("task"); + if (fromTask.Status == TaskStatus.Closed) throw new Exception("task can't be closed"); + if (toTask == null) throw new ArgumentNullException("task"); + if (toTask.Status == TaskStatus.Closed) throw new Exception("task can't be closed"); + + ProjectSecurity.DemandEdit(fromTask, subtask); + ProjectSecurity.DemandCreateSubtask(toTask); + + var oldResponsible = subtask.Responsible; + + if (oldResponsible != Guid.Empty && !toTaskTeam.Any(r => r.ID == oldResponsible)) + { + subtask.Responsible = Guid.Empty; + } + + subtask.Task = toTask.ID; + + subtask = DaoFactory.SubtaskDao.Save(subtask); + + var senders = GetSubscribers(toTask); + + if (!DisableNotifications && senders.Count != 0) { + NotifyClient.Instance.SendAboutSubTaskMoved(senders, toTask, subtask); + } + + return subtask; + } + private void NotifySubtask(Task task, Subtask subtask, bool isNew, Guid oldResponsible) { //Don't send anything if notifications are disabled diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Core/Engine/TaskEngine.cs b/web/studio/ASC.Web.Studio/Products/Projects/Core/Engine/TaskEngine.cs index 6be812802..54738e68e 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Core/Engine/TaskEngine.cs +++ b/web/studio/ASC.Web.Studio/Products/Projects/Core/Engine/TaskEngine.cs @@ -140,6 +140,11 @@ namespace ASC.Projects.Engine return DaoFactory.TaskDao.GetByFilterCountForReport(filter, ProjectSecurity.CurrentUserAdministrator, ProjectSecurity.IsPrivateDisabled); } + public List> GetByFilterAverageTime(TaskFilter filter) + { + return DaoFactory.TaskDao.GetByFilterAverageTime(filter, ProjectSecurity.CurrentUserAdministrator, ProjectSecurity.IsPrivateDisabled); + } + public IEnumerable GetByFilterCountForStatistic(TaskFilter filter) { return DaoFactory.TaskDao.GetByFilterCountForStatistic(filter, ProjectSecurity.CurrentUserAdministrator, ProjectSecurity.IsPrivateDisabled); diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/DataInterfaces/IProjectDao.cs b/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/DataInterfaces/IProjectDao.cs index b994b6bc6..3e6792ef1 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/DataInterfaces/IProjectDao.cs +++ b/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/DataInterfaces/IProjectDao.cs @@ -39,6 +39,7 @@ namespace ASC.Projects.Core.DataInterfaces List GetOpenProjectsWithTasks(Guid participantId); + List> GetByFilterAverageTime(TaskFilter filter, bool isAdmin, bool checkAccess); DateTime GetMaxLastModified(); @@ -89,8 +90,11 @@ namespace ASC.Projects.Core.DataInterfaces List GetByFilter(TaskFilter filter, bool isAdmin, bool checkAccess); + List GetByFilterForReport(TaskFilter filter, bool isAdmin, bool checkAccess); + int GetByFilterCount(TaskFilter filter, bool isAdmin, bool checkAccess); + int GetByFilterCountForReport(TaskFilter filter, bool isAdmin, bool checkAccess); List GetByContactID(int contactID); diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/DataInterfaces/ITaskDao.cs b/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/DataInterfaces/ITaskDao.cs index 0b087c4c9..ee3fb57c0 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/DataInterfaces/ITaskDao.cs +++ b/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/DataInterfaces/ITaskDao.cs @@ -43,6 +43,8 @@ namespace ASC.Projects.Core.DataInterfaces List> GetByFilterCountForReport(TaskFilter filter, bool isAdmin, bool checkAccess); + List> GetByFilterAverageTime(TaskFilter filter, bool isAdmin, bool checkAccess); + IEnumerable GetByFilterCountForStatistic(TaskFilter filter, bool isAdmin, bool checkAccess); List GetById(ICollection ids); diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Domain/Entities/Enums/AverageTimeEnum.cs b/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Domain/Entities/Enums/AverageTimeEnum.cs new file mode 100644 index 000000000..3937d6291 --- /dev/null +++ b/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Domain/Entities/Enums/AverageTimeEnum.cs @@ -0,0 +1,29 @@ +/* + * + * (c) Copyright Ascensio System Limited 2010-2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + + +using System.ComponentModel; + +namespace ASC.Projects.Core.Domain +{ + [TypeConverter(typeof(LocalizedEnumConverter))] + public enum AverageTime + { + All, + ClosingProjects, + CompletingTasks + } +} diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Domain/Reports/ReportFilter.cs b/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Domain/Reports/ReportFilter.cs index 1a03e5fab..7dbb6d9aa 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Domain/Reports/ReportFilter.cs +++ b/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Domain/Reports/ReportFilter.cs @@ -58,8 +58,14 @@ namespace ASC.Projects.Core.Domain.Reports public List PaymentStatuses { get; set; } + public List ViewAverageTime { get; set; } + public int ViewType { get; set; } + public bool IsShowAverageTime { get; set; } + + public AverageTime TypeOfShowAverageTime { get; set; } + public bool NoResponsible { get; set; } @@ -139,13 +145,8 @@ namespace ASC.Projects.Core.Domain.Reports switch (TimeInterval) { case ReportTimeInterval.Absolute: - date = FromDate; - break; case ReportTimeInterval.Relative: - if (FromDate != DateTime.MinValue && FromDate != DateTime.MaxValue) - { - date = TenantUtil.DateTimeNow(); - } + date = FromDate; break; //Hack for Russian Standard Time case ReportTimeInterval.CurrYear: @@ -170,13 +171,8 @@ namespace ASC.Projects.Core.Domain.Reports switch (TimeInterval) { case ReportTimeInterval.Absolute: - date = ToDate; - break; case ReportTimeInterval.Relative: - if (FromDate != DateTime.MinValue && FromDate != DateTime.MaxValue && ToDate != DateTime.MinValue && ToDate != DateTime.MaxValue) - { - date = TenantUtil.DateTimeNow().Add(ToDate - FromDate); - } + date = ToDate; break; default: date = GetDate(false); diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Domain/Reports/ReportFilterSerializer.cs b/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Domain/Reports/ReportFilterSerializer.cs index ef08ea2c9..3d5127967 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Domain/Reports/ReportFilterSerializer.cs +++ b/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Domain/Reports/ReportFilterSerializer.cs @@ -116,6 +116,19 @@ namespace ASC.Projects.Core.Domain.Reports } } + if (filter.ViewType == 1 && filter.TypeOfShowAverageTime == AverageTime.CompletingTasks && filter.IsShowAverageTime) + { + root.Add(new XElement("showAvgForTask", true)); + } + else if (filter.IsShowAverageTime) + { + root.Add(new XElement("isShowAverageTime", filter.IsShowAverageTime)); + if (filter.TypeOfShowAverageTime != AverageTime.All) + { + root.Add(new XElement("typeOfShowAverageTime", filter.TypeOfShowAverageTime)); + } + } + doc.AddFirst(root); return doc.ToString(SaveOptions.DisableFormatting); } @@ -214,6 +227,25 @@ namespace ASC.Projects.Core.Domain.Reports } } + var isShowAverageTime = root.Element("isShowAverageTime"); + if (isShowAverageTime != null) + { + filter.IsShowAverageTime = bool.Parse(isShowAverageTime.Value); + } + + var typeOfShowAverageTime = root.Element("typeOfShowAverageTime"); + if (typeOfShowAverageTime != null) + { + filter.TypeOfShowAverageTime = (AverageTime)Enum.Parse(typeof(AverageTime), typeOfShowAverageTime.Value); + } + + var showAvgForTask = root.Element("showAvgForTask"); + if (showAvgForTask != null) + { + filter.IsShowAverageTime = true; + filter.TypeOfShowAverageTime = AverageTime.CompletingTasks; + } + return filter; } @@ -270,6 +302,18 @@ namespace ASC.Projects.Core.Domain.Reports { uri.AppendFormat("&fpays={0}", string.Join(LIST_SEP, filter.PaymentStatuses.Select(id => ((int)id).ToString(CultureInfo.InvariantCulture)).ToArray())); } + if (filter.ViewType == 1 && filter.TypeOfShowAverageTime == AverageTime.CompletingTasks && filter.IsShowAverageTime) + { + uri.AppendFormat("&ctasks={0}", true); + } + else if (filter.IsShowAverageTime) + { + uri.AppendFormat("&atchecked={0}", filter.IsShowAverageTime); + if (filter.TypeOfShowAverageTime != AverageTime.All) + { + uri.AppendFormat("&atstat={0}", filter.TypeOfShowAverageTime); + } + } return uri.ToString().Trim('&').ToLower(); } @@ -339,6 +383,22 @@ namespace ASC.Projects.Core.Domain.Reports p = GetParameterFromUri(uri, "fpays"); if (!string.IsNullOrEmpty(p)) filter.PaymentStatuses = ToEnumsArray(p); + p = GetParameterFromUri(uri, "atstat"); + if (!string.IsNullOrEmpty(p)) filter.ViewAverageTime = ToEnumsArray(p); + + p = GetParameterFromUri(uri, "atchecked"); + if (!string.IsNullOrEmpty(p)) filter.IsShowAverageTime = bool.Parse(p); + + p = GetParameterFromUri(uri, "atstat"); + if (!string.IsNullOrEmpty(p)) filter.TypeOfShowAverageTime = (AverageTime)Enum.Parse(typeof(AverageTime), p); + + p = GetParameterFromUri(uri, "ctasks"); + if (!string.IsNullOrEmpty(p) && bool.Parse(p)) + { + filter.TypeOfShowAverageTime = AverageTime.CompletingTasks; + filter.IsShowAverageTime = true; + } + return filter; } diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Resources/ProjectsEnumResource.az-Latn-AZ.resx b/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Resources/ProjectsEnumResource.az-Latn-AZ.resx index c522dfa9d..a035a5cdf 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Resources/ProjectsEnumResource.az-Latn-AZ.resx +++ b/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Resources/ProjectsEnumResource.az-Latn-AZ.resx @@ -91,6 +91,15 @@ Aktiv + + Ödənildi + + + Ödənilməyib + + + Ödənişsiz + Qapalı @@ -104,7 +113,7 @@ Yüksək - Alçaq + Aşağı Normal @@ -115,4 +124,4 @@ Açıq - \ No newline at end of file + diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Services/NotifyService/NotifyClient.cs b/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Services/NotifyService/NotifyClient.cs index abf7dbf3c..d7f4183f8 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Services/NotifyService/NotifyClient.cs +++ b/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Services/NotifyService/NotifyClient.cs @@ -21,25 +21,21 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Web; - using ASC.Common.Logging; using ASC.Core; using ASC.Core.Common.Notify; using ASC.Core.Common.Notify.Push; - using ASC.Notify; using ASC.Notify.Engine; using ASC.Notify.Model; using ASC.Notify.Patterns; using ASC.Notify.Recipients; - using ASC.Projects.Core.Domain; using ASC.Projects.Engine; using ASC.Web.Projects.Classes; using ASC.Web.Projects.Core; using ASC.Web.Projects.Core.Model.Services.NotifyService; using ASC.Web.Projects.Resources; - using Autofac; namespace ASC.Projects.Core.Services.NotifyService @@ -188,7 +184,6 @@ namespace ASC.Projects.Core.Services.NotifyService true, new TagValue(NotifyConstants.Tag_ProjectID, project.ID), new TagValue(NotifyConstants.Tag_ProjectTitle, project.Title), - ReplyToTagProvider.Message(project.ID), new AdditionalSenderTag("push.sender"), new TagValue(PushConstants.PushItemTagName, new PushItem(PushItemType.Project, project.ID.ToString(CultureInfo.InvariantCulture), project.Title)), new TagValue(PushConstants.PushModuleTagName, PushModule.Projects), @@ -207,8 +202,7 @@ namespace ASC.Projects.Core.Services.NotifyService new[] { recipient }, true, new TagValue(NotifyConstants.Tag_ProjectID, project.ID), - new TagValue(NotifyConstants.Tag_ProjectTitle, project.Title), - ReplyToTagProvider.Message(project.ID)); + new TagValue(NotifyConstants.Tag_ProjectTitle, project.Title)); } } @@ -226,8 +220,7 @@ namespace ASC.Projects.Core.Services.NotifyService new TagValue(NotifyConstants.Tag_ProjectTitle, milestone.Project.Title), new TagValue(NotifyConstants.Tag_EntityTitle, milestone.Title), new TagValue(NotifyConstants.Tag_EntityID, milestone.ID), - new TagValue(NotifyConstants.Tag_Priority, 1), - ReplyToTagProvider.Comment("project.milestone", milestone.ID.ToString(CultureInfo.InvariantCulture))); + new TagValue(NotifyConstants.Tag_Priority, 1)); } } @@ -246,7 +239,6 @@ namespace ASC.Projects.Core.Services.NotifyService new TagValue(NotifyConstants.Tag_ProjectID, project.ID), new TagValue(NotifyConstants.Tag_ProjectTitle, project.Title), new TagValue(NotifyConstants.Tag_AdditionalData, description), - ReplyToTagProvider.Message(project.ID), new AdditionalSenderTag("push.sender"), new TagValue(PushConstants.PushItemTagName, new PushItem(PushItemType.Project, project.ID.ToString(CultureInfo.InvariantCulture), project.Title)), new TagValue(PushConstants.PushModuleTagName, PushModule.Projects), @@ -270,7 +262,6 @@ namespace ASC.Projects.Core.Services.NotifyService new TagValue(NotifyConstants.Tag_EntityTitle, milestone.Title), new TagValue(NotifyConstants.Tag_EntityID, milestone.ID), new TagValue(NotifyConstants.Tag_AdditionalData, new Hashtable { { "MilestoneDescription", description } }), - ReplyToTagProvider.Comment("project.milestone", milestone.ID.ToString(CultureInfo.InvariantCulture)), new AdditionalSenderTag("push.sender"), new TagValue(PushConstants.PushItemTagName, new PushItem(PushItemType.Milestone, milestone.ID.ToString(CultureInfo.InvariantCulture), milestone.Title)), new TagValue(PushConstants.PushParentItemTagName, new PushItem(PushItemType.Project, milestone.Project.ID.ToString(CultureInfo.InvariantCulture), milestone.Project.Title)), @@ -296,7 +287,6 @@ namespace ASC.Projects.Core.Services.NotifyService new TagValue(NotifyConstants.Tag_EntityTitle, task.Title), new TagValue(NotifyConstants.Tag_EntityID, task.ID), new TagValue(NotifyConstants.Tag_AdditionalData, new Hashtable { { "TaskDescription", description } }), - ReplyToTagProvider.Comment("project.task", task.ID.ToString(CultureInfo.InvariantCulture)), new AdditionalSenderTag("push.sender"), new TagValue(PushConstants.PushItemTagName, new PushItem(PushItemType.Task, task.ID.ToString(CultureInfo.InvariantCulture), task.Title)), new TagValue(PushConstants.PushParentItemTagName, new PushItem(PushItemType.Project, task.Project.ID.ToString(CultureInfo.InvariantCulture), task.Project.Title)), @@ -325,8 +315,7 @@ namespace ASC.Projects.Core.Services.NotifyService new TagValue(NotifyConstants.Tag_ProjectTitle, task.Project.Title), new TagValue(NotifyConstants.Tag_EntityTitle, task.Title), new TagValue(NotifyConstants.Tag_EntityID, task.ID), - new TagValue(NotifyConstants.Tag_AdditionalData, new Hashtable { { "TaskDescription", description } }), - ReplyToTagProvider.Comment("project.task", task.ID.ToString(CultureInfo.InvariantCulture))); + new TagValue(NotifyConstants.Tag_AdditionalData, new Hashtable { { "TaskDescription", description } })); } finally { @@ -355,7 +344,6 @@ namespace ASC.Projects.Core.Services.NotifyService new TagValue(NotifyConstants.Tag_SubEntityTitle, subtask.Title), new TagValue(NotifyConstants.Tag_EntityID, task.ID), new TagValue(NotifyConstants.Tag_AdditionalData, new Hashtable { { "TaskDescription", description } }), - ReplyToTagProvider.Comment("project.task", task.ID.ToString(CultureInfo.InvariantCulture)), new AdditionalSenderTag("push.sender"), new TagValue(PushConstants.PushItemTagName, new PushItem(PushItemType.Subtask, subtask.ID.ToString(CultureInfo.InvariantCulture), subtask.Title)), new TagValue(PushConstants.PushParentItemTagName, new PushItem(PushItemType.Task, task.ID.ToString(CultureInfo.InvariantCulture), task.Title)), @@ -383,8 +371,7 @@ namespace ASC.Projects.Core.Services.NotifyService new TagValue(NotifyConstants.Tag_EntityTitle, task.Title), new TagValue(NotifyConstants.Tag_EntityID, task.ID), new TagValue(NotifyConstants.Tag_AdditionalData, new Hashtable { { "TaskDescription", description } }), - new TagValue(NotifyConstants.Tag_Priority, 1), - ReplyToTagProvider.Comment("project.task", task.ID.ToString(CultureInfo.InvariantCulture))); + new TagValue(NotifyConstants.Tag_Priority, 1)); } public void SendReminderAboutTaskDeadline(IEnumerable recipients, Task task) @@ -405,18 +392,40 @@ namespace ASC.Projects.Core.Services.NotifyService {"TaskDescription", description}, {"TaskDeadline", task.Deadline.ToString(CultureInfo.InvariantCulture)} }), - new TagValue(NotifyConstants.Tag_Priority, 1), - ReplyToTagProvider.Comment("project.task", task.ID.ToString(CultureInfo.InvariantCulture))); + new TagValue(NotifyConstants.Tag_Priority, 1)); } public void SendNewComment(List recipients, ProjectEntity entity, Comment comment, bool isNew) { INotifyAction action; - if (entity.GetType() == typeof(Message)) action = isNew ? NotifyConstants.Event_NewCommentForMessage : NotifyConstants.Event_EditedCommentForMessage; - else if (entity.GetType() == typeof(Task)) action = isNew ? NotifyConstants.Event_NewCommentForTask : NotifyConstants.Event_EditedCommentForTask; + INotifyAction mentionAction; + if (entity.GetType() == typeof(Message)) + { + action = isNew ? NotifyConstants.Event_NewCommentForMessage : NotifyConstants.Event_EditedCommentForMessage; + mentionAction = NotifyConstants.Event_MentionForMessageComment; + } + else if (entity.GetType() == typeof(Task)) + { + action = isNew ? NotifyConstants.Event_NewCommentForTask : NotifyConstants.Event_EditedCommentForTask; + mentionAction = NotifyConstants.Event_MentionForTaskComment; + } else return; + var mentionedUsers = MentionProvider.GetMentionedUsers(comment.Content); + var mentionedUserIds = mentionedUsers.Select(u => u.ID.ToString()); + + var notMentionedRecipients = recipients.Where(r => !mentionedUserIds.Contains(r.ID)).ToArray(); + + var tags = new ITagValue[]{ + new TagValue(NotifyConstants.Tag_ProjectID, entity.Project.ID), + new TagValue(NotifyConstants.Tag_ProjectTitle, entity.Project.Title), + new TagValue(NotifyConstants.Tag_EntityTitle, entity.Title), + new TagValue(NotifyConstants.Tag_EntityID, entity.ID), + new TagValue(NotifyConstants.Tag_AdditionalData, comment.Content), + new TagValue(NotifyConstants.Tag_CommentID, comment.OldGuidId) + }; + var interceptor = new InitiatorInterceptor(new DirectRecipient(SecurityContext.CurrentAccount.ID.ToString(), "")); try { @@ -424,15 +433,19 @@ namespace ASC.Projects.Core.Services.NotifyService client.SendNoticeToAsync( action, entity.NotifyId, - recipients.ToArray(), + notMentionedRecipients, true, - new TagValue(NotifyConstants.Tag_ProjectID, entity.Project.ID), - new TagValue(NotifyConstants.Tag_ProjectTitle, entity.Project.Title), - new TagValue(NotifyConstants.Tag_EntityTitle, entity.Title), - new TagValue(NotifyConstants.Tag_EntityID, entity.ID), - new TagValue(NotifyConstants.Tag_AdditionalData, comment.Content), - new TagValue(NotifyConstants.Tag_CommentID, comment.OldGuidId), - GetReplyToEntityTag(entity, comment)); + tags); + + if (mentionedUsers.Length > 0) + { + client.SendNoticeToAsync( + mentionAction, + entity.NotifyId, + mentionedUsers, + true, + tags); + } } finally { @@ -519,7 +532,6 @@ namespace ASC.Projects.Core.Services.NotifyService new TagValue(NotifyConstants.Tag_EntityTitle, task.Title), new TagValue(NotifyConstants.Tag_EntityID, task.ID), new TagValue(NotifyConstants.Tag_AdditionalData, description), - ReplyToTagProvider.Comment("project.task", task.ID.ToString(CultureInfo.InvariantCulture)), new AdditionalSenderTag("push.sender"), new TagValue(PushConstants.PushItemTagName, new PushItem(PushItemType.Task, task.ID.ToString(CultureInfo.InvariantCulture), task.Title)), new TagValue(PushConstants.PushParentItemTagName, new PushItem(PushItemType.Project, task.Project.ID.ToString(CultureInfo.InvariantCulture), task.Project.Title)), @@ -549,7 +561,6 @@ namespace ASC.Projects.Core.Services.NotifyService new TagValue(NotifyConstants.Tag_EntityTitle, task.Title), new TagValue(NotifyConstants.Tag_SubEntityTitle, subtask.Title), new TagValue(NotifyConstants.Tag_EntityID, task.ID), - ReplyToTagProvider.Comment("project.task", task.ID.ToString(CultureInfo.InvariantCulture)), new AdditionalSenderTag("push.sender"), new TagValue(PushConstants.PushItemTagName, new PushItem(PushItemType.Subtask, subtask.ID.ToString(CultureInfo.InvariantCulture), subtask.Title)), new TagValue(PushConstants.PushParentItemTagName, new PushItem(PushItemType.Task, task.ID.ToString(CultureInfo.InvariantCulture), task.Title)), @@ -580,7 +591,6 @@ namespace ASC.Projects.Core.Services.NotifyService new TagValue(NotifyConstants.Tag_EntityTitle, task.Title), new TagValue(NotifyConstants.Tag_EntityID, task.ID), new TagValue(NotifyConstants.Tag_AdditionalData, description), - ReplyToTagProvider.Comment("project.task", task.ID.ToString(CultureInfo.InvariantCulture)), new AdditionalSenderTag("push.sender"), new TagValue(PushConstants.PushItemTagName, new PushItem(PushItemType.Task, task.ID.ToString(CultureInfo.InvariantCulture), task.Title)), new TagValue(PushConstants.PushParentItemTagName, new PushItem(PushItemType.Project, task.Project.ID.ToString(CultureInfo.InvariantCulture), task.Project.Title)), @@ -611,7 +621,6 @@ namespace ASC.Projects.Core.Services.NotifyService new TagValue(NotifyConstants.Tag_SubEntityTitle, subtask.Title), new TagValue(NotifyConstants.Tag_EntityID, task.ID), new TagValue(NotifyConstants.Tag_AdditionalData, new Hashtable { { "TaskDescription", description } }), - ReplyToTagProvider.Comment("project.task", task.ID.ToString(CultureInfo.InvariantCulture)), new AdditionalSenderTag("push.sender"), new TagValue(PushConstants.PushItemTagName, new PushItem(PushItemType.Subtask, subtask.ID.ToString(CultureInfo.InvariantCulture), subtask.Title)), new TagValue(PushConstants.PushParentItemTagName, new PushItem(PushItemType.Task, task.ID.ToString(CultureInfo.InvariantCulture), task.Title)), @@ -763,7 +772,6 @@ namespace ASC.Projects.Core.Services.NotifyService new TagValue(NotifyConstants.Tag_EntityID, task.ID), new TagValue(NotifyConstants.Tag_Responsible, resp), new TagValue(NotifyConstants.Tag_AdditionalData, new Hashtable { { "TaskDescription", description } }), - ReplyToTagProvider.Comment("project.task", task.ID.ToString(CultureInfo.InvariantCulture)), new AdditionalSenderTag("push.sender"), new TagValue(PushConstants.PushItemTagName, new PushItem(PushItemType.Task, task.ID.ToString(CultureInfo.InvariantCulture), task.Title)), new TagValue(PushConstants.PushParentItemTagName, new PushItem(PushItemType.Project, task.Project.ID.ToString(CultureInfo.InvariantCulture), task.Project.Title)), @@ -795,7 +803,6 @@ namespace ASC.Projects.Core.Services.NotifyService new TagValue(NotifyConstants.Tag_EntityID, task.ID), new TagValue(NotifyConstants.Tag_Responsible, !subtask.Responsible.Equals(Guid.Empty) ? ToRecipient(subtask.Responsible).Name : PatternResource.subtaskWithoutResponsible), - ReplyToTagProvider.Comment("project.task", task.ID.ToString(CultureInfo.InvariantCulture)), new AdditionalSenderTag("push.sender"), new TagValue(PushConstants.PushItemTagName, new PushItem(PushItemType.Subtask, subtask.ID.ToString(CultureInfo.InvariantCulture), subtask.Title)), new TagValue(PushConstants.PushParentItemTagName, new PushItem(PushItemType.Task, task.ID.ToString(CultureInfo.InvariantCulture), task.Title)), @@ -841,8 +848,7 @@ namespace ASC.Projects.Core.Services.NotifyService new TagValue(NotifyConstants.Tag_ProjectID, task.Project.ID), new TagValue(NotifyConstants.Tag_ProjectTitle, task.Project.Title), new TagValue(NotifyConstants.Tag_EntityTitle, task.Title), - new TagValue(NotifyConstants.Tag_EntityID, task.ID), - ReplyToTagProvider.Comment("project.task", task.ID.ToString(CultureInfo.InvariantCulture))); + new TagValue(NotifyConstants.Tag_EntityID, task.ID)); } finally { @@ -870,8 +876,7 @@ namespace ASC.Projects.Core.Services.NotifyService new TagValue(NotifyConstants.Tag_Responsible, !subtask.Responsible.Equals(Guid.Empty) ? ToRecipient(subtask.Responsible).Name - : PatternResource.subtaskWithoutResponsible), - ReplyToTagProvider.Comment("project.task", task.ID.ToString(CultureInfo.InvariantCulture))); + : PatternResource.subtaskWithoutResponsible)); } finally { @@ -879,6 +884,33 @@ namespace ASC.Projects.Core.Services.NotifyService } } + public void SendAboutSubTaskMoved(List recipients, Task task, Subtask subtask) + { + var interceptor = new InitiatorInterceptor(new DirectRecipient(SecurityContext.CurrentAccount.ID.ToString(), "")); + client.AddInterceptor(interceptor); + + try + { + client.SendNoticeToAsync( + NotifyConstants.Event_SubTaskMoved, + task.NotifyId, + recipients.ToArray(), + true, + new TagValue(NotifyConstants.Tag_ProjectID, task.Project.ID), + new TagValue(NotifyConstants.Tag_ProjectTitle, task.Project.Title), + new TagValue(NotifyConstants.Tag_EntityTitle, task.Title), + new TagValue(NotifyConstants.Tag_SubEntityTitle, subtask.Title), + new TagValue(NotifyConstants.Tag_EntityID, task.ID), + new TagValue(NotifyConstants.Tag_Responsible, + !subtask.Responsible.Equals(Guid.Empty) + ? ToRecipient(subtask.Responsible).Name + : PatternResource.subtaskWithoutResponsible)); + } + finally + { + client.RemoveInterceptor(interceptor.Name); + } + } public void SendAboutMilestoneResumed(IEnumerable recipients, Milestone milestone) { @@ -927,7 +959,6 @@ namespace ASC.Projects.Core.Services.NotifyService new TagValue(NotifyConstants.Tag_EntityTitle, task.Title), new TagValue(NotifyConstants.Tag_EntityID, task.ID), new TagValue(NotifyConstants.Tag_AdditionalData, description), - ReplyToTagProvider.Comment("project.task", task.ID.ToString(CultureInfo.InvariantCulture)), new AdditionalSenderTag("push.sender"), new TagValue(PushConstants.PushItemTagName, new PushItem(PushItemType.Task, task.ID.ToString(CultureInfo.InvariantCulture), task.Title)), new TagValue(PushConstants.PushParentItemTagName, new PushItem(PushItemType.Project, task.Project.ID.ToString(CultureInfo.InvariantCulture), task.Project.Title)), @@ -957,7 +988,6 @@ namespace ASC.Projects.Core.Services.NotifyService new TagValue(NotifyConstants.Tag_EntityTitle, task.Title), new TagValue(NotifyConstants.Tag_SubEntityTitle, subtask.Title), new TagValue(NotifyConstants.Tag_EntityID, task.ID), - ReplyToTagProvider.Comment("project.task", task.ID.ToString(CultureInfo.InvariantCulture)), new AdditionalSenderTag("push.sender"), new TagValue(PushConstants.PushItemTagName, new PushItem(PushItemType.Subtask, subtask.ID.ToString(CultureInfo.InvariantCulture), subtask.Title)), new TagValue(PushConstants.PushParentItemTagName, new PushItem(PushItemType.Task, task.ID.ToString(CultureInfo.InvariantCulture), task.Title)), @@ -986,8 +1016,7 @@ namespace ASC.Projects.Core.Services.NotifyService new TagValue(NotifyConstants.Tag_EntityTitle, task.Title), new TagValue(NotifyConstants.Tag_EntityID, task.ID), new TagValue(NotifyConstants.Tag_SubEntityTitle, milestone != null ? milestone.Title : TaskResource.None), - new TagValue(NotifyConstants.Tag_AdditionalData, description), - ReplyToTagProvider.Comment("project.task", task.ID.ToString(CultureInfo.InvariantCulture))); + new TagValue(NotifyConstants.Tag_AdditionalData, description)); } finally { @@ -995,29 +1024,6 @@ namespace ASC.Projects.Core.Services.NotifyService } } - - private static TagValue GetReplyToEntityTag(ProjectEntity entity, Comment comment) - { - string type = string.Empty; - if (entity is Task) - { - type = "project.task"; - } - if (entity is Message) - { - type = "project.message"; - } - if (entity is Milestone) - { - type = "project.milestone"; - } - if (!string.IsNullOrEmpty(type)) - { - return ReplyToTagProvider.Comment(type, entity.ID.ToString(CultureInfo.InvariantCulture), comment != null ? comment.ID.ToString() : null); - } - return null; - } - public void SendAboutMessageAction(List recipients, Message message, bool isNew, List> fileListInfoHashtable) { var tags = new List @@ -1027,8 +1033,7 @@ namespace ASC.Projects.Core.Services.NotifyService new TagValue(NotifyConstants.Tag_EntityTitle, message.Title), new TagValue(NotifyConstants.Tag_EntityID, message.ID), new TagValue(NotifyConstants.Tag_EventType, isNew ? NotifyConstants.Event_MessageCreated.ID : NotifyConstants.Event_MessageEdited.ID), - new TagValue(NotifyConstants.Tag_AdditionalData, new Hashtable {{"MessagePreview", message.Description}, {"Files", fileListInfoHashtable}}), - ReplyToTagProvider.Comment("project.message", message.ID.ToString(CultureInfo.InvariantCulture)) + new TagValue(NotifyConstants.Tag_AdditionalData, new Hashtable {{"MessagePreview", message.Description}, {"Files", fileListInfoHashtable}}) }; if (isNew) //don't send push about edited message! diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Services/NotifyService/NotifyConstants.cs b/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Services/NotifyService/NotifyConstants.cs index d4dd497ae..f07b7075b 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Services/NotifyService/NotifyConstants.cs +++ b/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Services/NotifyService/NotifyConstants.cs @@ -34,6 +34,9 @@ namespace ASC.Projects.Core.Services.NotifyService public static INotifyAction Event_NewFileForDiscussion = new NotifyAction("NewFileForDiscussion", "new file for discussion"); public static INotifyAction Event_NewFileForTask = new NotifyAction("NewFileForTask", "new file for task"); + public static INotifyAction Event_MentionForTaskComment = new NotifyAction("MentionForTaskComment", "mention for task comment"); + public static INotifyAction Event_MentionForMessageComment = new NotifyAction("MentionForMessageComment", "mention for message comment"); + public static INotifyAction Event_TaskEdited = new NotifyAction("TaskEdited", "task edited"); public static INotifyAction Event_TaskClosed = new NotifyAction("TaskClosed", "task closed"); public static INotifyAction Event_TaskCreated = new NotifyAction("TaskCreated", "task created"); @@ -66,6 +69,7 @@ namespace ASC.Projects.Core.Services.NotifyService public static INotifyAction Event_ResponsibleForSubTask = new NotifyAction("ResponsibleForSubTask", "responsible for subtask"); public static INotifyAction Event_SubTaskCreated = new NotifyAction("SubTaskCreated", "subtask created"); public static INotifyAction Event_SubTaskEdited = new NotifyAction("SubTaskEdited", "subtask edited"); + public static INotifyAction Event_SubTaskMoved = new NotifyAction("SubTaskMoved", "subtask moved"); public static INotifyAction Event_SubTaskResumed = new NotifyAction("SubTaskResumed", "subtask resumed"); public static INotifyAction Event_SubTaskClosed = new NotifyAction("SubTaskClosed", "subtask closed"); public static INotifyAction Event_SubTaskDeleted = new NotifyAction("SubTaskDeleted", "subtask deleted"); diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Services/NotifyService/NotifySource.cs b/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Services/NotifyService/NotifySource.cs index ab8c92733..f4dbf50ad 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Services/NotifyService/NotifySource.cs +++ b/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Services/NotifyService/NotifySource.cs @@ -81,6 +81,7 @@ namespace ASC.Projects.Core.Services.NotifyService NotifyConstants.Event_SubTaskDeleted, NotifyConstants.Event_SubTaskCreated, NotifyConstants.Event_SubTaskEdited, + NotifyConstants.Event_SubTaskMoved, NotifyConstants.Event_SubTaskClosed, NotifyConstants.Event_SubTaskResumed, NotifyConstants.Event_ProjectDeleted, diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Services/NotifyService/PatternResource.Designer.cs b/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Services/NotifyService/PatternResource.Designer.cs index cfcb08772..3ebdb7b86 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Services/NotifyService/PatternResource.Designer.cs +++ b/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Services/NotifyService/PatternResource.Designer.cs @@ -156,6 +156,40 @@ namespace ASC.Web.Projects.Core.Model.Services.NotifyService { } } + /// + /// Looks up a localized string similar to h1.You were mentioned in comment to discussion: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Messages.aspx?prjID=$ProjectID&ID=$EntityID" + /// + ///$__DateTime "$__AuthorName":"$__AuthorUrl" mentioned you in the comment to the discussion: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Messages.aspx?prjID=$ProjectID&ID=$EntityID" + /// + ///$AdditionalData + /// + ///"Read more":"${__VirtualRootPath}/Products/Projects/Messages.aspx?prjID=$ProjectID&ID=$EntityID#comment_$CommentID" + /// + ///^You receive this email because y [rest of string was truncated]";. + /// + public static string pattern_MentionForMessageComment { + get { + return ResourceManager.GetString("pattern_MentionForMessageComment", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to h1.You were mentioned in comment to task: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" + /// + ///$__DateTime "$__AuthorName":"$__AuthorUrl" mentioned you in the comment to the "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" task: + /// + ///$AdditionalData + /// + ///"Read more":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID#comment_$CommentID" + /// + ///^You receive this email because you are a registered [rest of string was truncated]";. + /// + public static string pattern_MentionForTaskComment { + get { + return ResourceManager.GetString("pattern_MentionForTaskComment", resourceCulture); + } + } + /// /// Looks up a localized string similar to h1.Discussion deleted: "$EntityTitle" /// @@ -274,11 +308,11 @@ namespace ASC.Web.Projects.Core.Model.Services.NotifyService { /// ///$__DateTime "$__AuthorName":"$__AuthorUrl" has added a new comment to the "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" task: /// - ///$AdditionalData - /// + ///$AdditionalData + /// ///"Read more":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID#comment_$CommentID" /// - ///^You receive this email because you are a registered user of the "${__VirtualR [rest of string was truncated]";. + ///^You receive this email because you are a registered user of the "${__Virtua [rest of string was truncated]";. /// public static string pattern_NewCommentForTask { get { @@ -698,6 +732,21 @@ namespace ASC.Web.Projects.Core.Model.Services.NotifyService { } } + /// + /// Looks up a localized string similar to h1.Subtask moved: "$SubEntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" + /// + ///$__DateTime "$__AuthorName":"$__AuthorUrl" has moved the "$SubEntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" subtask to the "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" task. + /// + ///The subtask is assigned to "$Responsible". + /// + ///^You receive this email because you are a registered user of the "${__ [rest of string was truncated]";. + /// + public static string pattern_SubTaskMoved { + get { + return ResourceManager.GetString("pattern_SubTaskMoved", resourceCulture); + } + } + /// /// Looks up a localized string similar to h1.Subtask resumed: "$SubEntityTitle" /// @@ -902,6 +951,42 @@ namespace ASC.Web.Projects.Core.Model.Services.NotifyService { } } + /// + /// Looks up a localized string similar to Projects [$ProjectTitle]. Mention in comment to discussion: $EntityTitle. + /// + public static string subject_MentionForMessageComment { + get { + return ResourceManager.GetString("subject_MentionForMessageComment", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Projects [$ProjectTitle](${__VirtualRootPath}/Products/Projects/Projects.aspx?prjID=$ProjectID). Mention in comment to discussion: [$EntityTitle](${__VirtualRootPath}/Products/Projects/Messages.aspx?prjID=$ProjectID&ID=$EntityID). + /// + public static string subject_MentionForMessageComment_tg { + get { + return ResourceManager.GetString("subject_MentionForMessageComment_tg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Projects [$ProjectTitle]. Mention in comment to task: $EntityTitle. + /// + public static string subject_MentionForTaskComment { + get { + return ResourceManager.GetString("subject_MentionForTaskComment", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Projects [$ProjectTitle](${__VirtualRootPath}/Products/Projects/Projects.aspx?prjID=$ProjectID). Mention in comment to task: [$EntityTitle](${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID). + /// + public static string subject_MentionForTaskComment_tg { + get { + return ResourceManager.GetString("subject_MentionForTaskComment_tg", resourceCulture); + } + } + /// /// Looks up a localized string similar to Projects [$ProjectTitle]. Discussion deleted: $EntityTitle. /// @@ -1514,6 +1599,24 @@ namespace ASC.Web.Projects.Core.Model.Services.NotifyService { } } + /// + /// Looks up a localized string similar to Projects [$ProjectTitle]. Subtask moved: $EntityTitle. + /// + public static string subject_SubTaskMoved { + get { + return ResourceManager.GetString("subject_SubTaskMoved", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Projects [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Subtask moved: [$SubEntityTitle](${__VirtualRootPath}/products/projects/tasks.aspx?prjID=$ProjectID&ID=$EntityID). + /// + public static string subject_SubTaskMoved_tg { + get { + return ResourceManager.GetString("subject_SubTaskMoved_tg", resourceCulture); + } + } + /// /// Looks up a localized string similar to Projects [$ProjectTitle]. Subtask resumed: $SubEntityTitle. /// diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Services/NotifyService/PatternResource.az-Latn-AZ.resx b/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Services/NotifyService/PatternResource.az-Latn-AZ.resx index 19178b466..46d2eb8d0 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Services/NotifyService/PatternResource.az-Latn-AZ.resx +++ b/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Services/NotifyService/PatternResource.az-Latn-AZ.resx @@ -58,6 +58,20 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.6.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Müzakirə üçün şərhə düzəliş edilib + + + h1.Bu tapşırıq üçün şərh redaktə edildi: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" + +$__DateTime "$__AuthorName":"$__AuthorUrl", "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" tapşırığınaedilən şərhi redaktə etdi: + +$AdditionalData + +"Ətraflı məlumat":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID#comment_$CommentID" + +^ Siz "${__VirtualRootPath}":"${__VirtualRootPath}" portalının qeydiyyatdan keçmiş istifadəçisi olduğunuz üçün bu e-məktubu alırsınız: Bu tapşırıqla bağlı bildirişlər almaq istəmirsinizsə "abunəlikdən çıxın": "${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID".^ + h1.Müzakirə redaktə olundu: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Messages.aspx?prjID=$ProjectID&ID=$EntityID" @@ -84,9 +98,9 @@ Sizin portal ünvanınız: "$__VirtualRootPath":"$__VirtualRootPath" "Edit subscription settings":"$RecipientSubscriptionConfigURL" - h1.Layihələr. Verilənlərin bazadan importu + h1.Layihələr. Verilənlərin Basecamp-dan idxalı -Yeni verilənlər uğurla import olundular +Yeni verilənlər uğurla idxal olundular Sizin portal ünvanınız: "$__VirtualRootPath":"$__VirtualRootPath" @@ -102,7 +116,7 @@ Sizin portal ünvanınız: "$__VirtualRootPath":"$__VirtualRootPath" "Edit subscription settings":"$RecipientSubscriptionConfigURL" - h1.Yubanmış dönüş nöqtəsi bildirişi: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID&ID=$EntityID" + h1.Gecikmiş mərhələ: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID&ID=$EntityID" Layihənin dönüş nöqtəsi ["$ProjectTitle":"${__VirtualRootPath}/Products/Projects/Projects.aspx?prjID=$ProjectID"] yubanıb: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID&ID=$EntityID" . @@ -111,25 +125,14 @@ Sizin portal ünvanınız: "$__VirtualRootPath":"$__VirtualRootPath" "Edit subscription settings":"$RecipientSubscriptionConfigURL" - h1.Müzakirə silindi: "$EntityTitle" - -$__DateTime istifadəçi "$__AuthorName":"$__AuthorUrl" müzakirəni sildi "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Messages.aspx?prjID=$ProjectID" bu layihədən "$ProjectTitle":"${__VirtualRootPath}/Products/Projects/Projects.aspx?prjID=$ProjectID". - - -^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını" dəyişdirin:"$RecipientSubscriptionConfigURL".^ - -Əgər hər hansı sualınız varsa, bizimlə rahat şəkildə bu ünvandan əlaqə saxlaya bilərsiniz: "support@onlyoffice.com":"mailto:support@onlyoffice.com" - -Uğurlar, -ONLYOFFICE™ Komandası -"www.onlyoffice.com":"http://onlyoffice.com/" + h1.Müzakirə silinib - h1.Dönüş nöqtəsi qapatıldı: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID#status=closed" + h1.Mərhələ bağlandı "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID#status=closed" $__DateTime istifadəçi "$__AuthorName":"$__AuthorUrl" bu dönüş nöqtəsini qapatdı "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID#status=closed" bu layihədə "$ProjectTitle":"${__VirtualRootPath}/Products/Projects/Projects.aspx?prjID=$ProjectID". -^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını" dəyişdirin:"$RecipientSubscriptionConfigURL".^ +^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını":"$RecipientSubscriptionConfigURL" dəyişdirin.^ Əgər hər hansı sualınız varsa, bizimlə rahat şəkildə bu ünvandan əlaqə saxlaya bilərsiniz: "support@onlyoffice.com":"mailto:support@onlyoffice.com" @@ -138,11 +141,11 @@ ONLYOFFICE™ Komandası "www.onlyoffice.com":"http://onlyoffice.com/" - h1.Dönüş nöqtəsi yaradldı: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID" + h1.Mərhələ yaradldı: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID" $__DateTime istifadəçi "$__AuthorName":"$__AuthorUrl" dönüş nöqtəsi əlavə etdi: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID#sortBy=create_on&sortOrder=descending" bu layihəyə "$ProjectTitle":"${__VirtualRootPath}/Products/Projects/Projects.aspx?prjID=$ProjectID". -^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını" dəyişdirin:"$RecipientSubscriptionConfigURL".^ +^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını":"$RecipientSubscriptionConfigURL" dəyişdirin.^ Əgər hər hansı sualınız varsa, bizimlə rahat şəkildə bu ünvandan əlaqə saxlaya bilərsiniz: "support@onlyoffice.com":"mailto:support@onlyoffice.com" @@ -151,7 +154,7 @@ ONLYOFFICE™ Komandası "www.onlyoffice.com":"http://onlyoffice.com/" - h1.Son tarix üzrə dönüş nöqtəsi: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID#my=true" + h1.Son tarix üzrə mərhələ: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID#my=true" Layihənin dönüş nöqtəsinin son vaxtına 48 saatdan az qalır ["$ProjectTitle":"${__VirtualRootPath}/Products/Projects/Projects.aspx?prjID=$ProjectID"]. @@ -160,13 +163,13 @@ Sizin portal ünvanınız: "$__VirtualRootPath":"$__VirtualRootPath" "Edit subscription settings":"$RecipientSubscriptionConfigURL" - h1.Dönüş nöqtəsi silindi: "$EntityTitle" + h1.Mərhələ silindi: "$EntityTitle" $__DateTime istifadəçi "$__AuthorName":"$__AuthorUrl" dönüş nöqtəsini sildi "$EntityTitle" bu layihədən "$ProjectTitle":"${__VirtualRootPath}/Products/Projects/Projects.aspx?prjID=$ProjectID": $AdditionalData -^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını" dəyişdirin:"$RecipientSubscriptionConfigURL".^ +^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını":"$RecipientSubscriptionConfigURL" dəyişdirin.^ Əgər hər hansı sualınız varsa, bizimlə rahat şəkildə bu ünvandan əlaqə saxlaya bilərsiniz: "support@onlyoffice.com":"mailto:support@onlyoffice.com" @@ -175,11 +178,11 @@ ONLYOFFICE™ Komandası "www.onlyoffice.com":"http://onlyoffice.com/" - h1.Dönüş nöqtəsi redaktə olundu: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID#my=true" + h1.Mərhələ redaktə olundu: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID#my=true" $__DateTime istifadəçi "$__AuthorName":"$__AuthorUrl" dönüş nöqtəsini redaktə etdi "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID#my=true" bu layihədə "$ProjectTitle":"${__VirtualRootPath}/Products/Projects/Projects.aspx?prjID=$ProjectID". -^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını" dəyişdirin:"$RecipientSubscriptionConfigURL".^ +^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını":"$RecipientSubscriptionConfigURL" dəyişdirin.^ Əgər hər hansı sualınız varsa, bizimlə rahat şəkildə bu ünvandan əlaqə saxlaya bilərsiniz: "support@onlyoffice.com":"mailto:support@onlyoffice.com" @@ -188,13 +191,13 @@ ONLYOFFICE™ Komandası "www.onlyoffice.com":"http://onlyoffice.com/" - h1.Dönüş nöqtəsinin davam etdirilməsi: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID" + h1.Mərhələnin davam etdirilməsi: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID" $__DateTime istifadəçi "$__AuthorName":"$__AuthorUrl" dönüş nöqtəsini davam etdirdi "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID" bu layihədə "$ProjectTitle":"${__VirtualRootPath}/Products/Projects/Projects.aspx?prjID=$ProjectID": $AdditionalData -^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını" dəyişdirin:"$RecipientSubscriptionConfigURL".^ +^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını":"$RecipientSubscriptionConfigURL" dəyişdirin.^ Əgər hər hansı sualınız varsa, bizimlə rahat şəkildə bu ünvandan əlaqə saxlaya bilərsiniz: "support@onlyoffice.com":"mailto:support@onlyoffice.com" @@ -229,13 +232,13 @@ Sizin portal ünvanınız: "$__VirtualRootPath":"$__VirtualRootPath" "Edit subscription settings":"$RecipientSubscriptionConfigURL" - h1.Müzakirədə yeni fayl: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Messages.aspx?prjID=$ProjectID&ID=$EntityID" + h1.Müzakirə üün yeni fayl: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Messages.aspx?prjID=$ProjectID&ID=$EntityID" $__DateTime istifadəçi "$__AuthorName":"$__AuthorUrl" müzakirəyə yeni fayl əlavə etdi "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Messages.aspx?prjID=$ProjectID&ID=$EntityID": $AdditionalData -^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını" dəyişdirin:"$RecipientSubscriptionConfigURL".^ +^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını":"$RecipientSubscriptionConfigURL" dəyişdirin.^ Əgər hər hansı sualınız varsa, bizimlə rahat şəkildə bu ünvandan əlaqə saxlaya bilərsiniz: "support@onlyoffice.com":"mailto:support@onlyoffice.com" @@ -244,13 +247,13 @@ ONLYOFFICE™ Komandası "www.onlyoffice.com":"http://onlyoffice.com/" - h1.Tapşırıqda yeni fayl: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" + h1.Tapşırıq üçün yeni fayl: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" $__DateTime istifadəçi "$__AuthorName":"$__AuthorUrl" tapşırığa yeni fayl əlavə etdi "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID": $AdditionalData -^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını" dəyişdirin:"$RecipientSubscriptionConfigURL".^ +^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını":"$RecipientSubscriptionConfigURL" dəyişdirin.^ Əgər hər hansı sualınız varsa, bizimlə rahat şəkildə bu ünvandan əlaqə saxlaya bilərsiniz: "support@onlyoffice.com":"mailto:support@onlyoffice.com" @@ -259,7 +262,7 @@ ONLYOFFICE™ Komandası "www.onlyoffice.com":"http://onlyoffice.com/" - h1.Yeni müzakirə: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Messages.aspx?prjID=$ProjectID&ID=$EntityID" + h1.Yeni müzakirə yaradıldı: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Messages.aspx?prjID=$ProjectID&ID=$EntityID" $__DateTime "$__AuthorName":"$__AuthorUrl" yeni müzakirəni başladı: @@ -288,7 +291,7 @@ Sizin portal ünvanınız: "$__VirtualRootPath":"$__VirtualRootPath" $__DateTime istifadəçi "$__AuthorName":"$__AuthorUrl" layihəni sildi "$EntityTitle". -^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını" dəyişdirin:"$RecipientSubscriptionConfigURL".^ +^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını":"$RecipientSubscriptionConfigURL" dəyişdirin.^ Əgər hər hansı sualınız varsa, bizimlə rahat şəkildə bu ünvandan əlaqə saxlaya bilərsiniz: "support@onlyoffice.com":"mailto:support@onlyoffice.com" @@ -296,8 +299,68 @@ Uğurlar, ONLYOFFICE™ Komandası "www.onlyoffice.com":"http://onlyoffice.com/" + + Layihələr [$ProjectTitle]. Layihə komandasına təyin edilmisiniz + + + Layihələr [$ProjectTitle]. Müzakirə silinib: $EntityTitle + + + Layihələr [$ProjectTitle]. Mərhələ bağlandı: $EntityTitle + + + Layihələr [$ProjectTitle]. Mərhələ yaradıldı: $EntityTitle + + + Layihələr [$ProjectTitle]. Mərhələ silindi: $EntityTitle + + + Layihələr [$ProjectTitle]. Mərhələ davam etdirildi: $EntityTitle + + + Layihələr [$ProjectTitle]. Yeni müzakirə: $EntityTitle + + + Layihə. Layihə silindi: $EntityTitle + + + Layihələr [$ProjectTitle]. Sizə mərhələ təyin edildi: $EntityTitle + + + Layihələr [$ProjectTitle]. Siz layihə meneceri olaraq təyin edildiniz + + + Layihələr [$ProjectTitle]. Sizə alt-tapşırıq təyin edildi: $SubEntityTitle + + + Layihələr [$ProjectTitle]. Sizə tapşırıq təyin edildi: $EntityTitle + + + Layihələr [$ProjectTitle]. Alt-tapşırıq bağlandı: $SubEntityTitle + + + Layihələr [$ProjectTitle]. Alt-tapşırıq yaradıldı: $SubEntityTitle + + + Layihələr [$ProjectTitle]. Alt-tapşırıq silindi: $SubEntityTitle + + + Layihələr [$ProjectTitle]. Alt-tapşırıq davam etdirildi: $SubEntityTitle + + + Layihələr [$ProjectTitle]. Tapşırıq bağlandı: $EntityTitle + + + Layihələr [$ProjectTitle]. Tapşırıq yaradıldı: $EntityTitle + + + Layihələr [$ProjectTitle]. Tapşırıq silindi: $EntityTitle + + + Layihələr [$ProjectTitle]. Tapşırıq davam etdirildi: $EntityTitle + - h1.Tapşırığı xatırlat: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" + h1.Tapşırıq haqqında xatırlatma: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" "$__AuthorName":"$__AuthorUrl" Sizə tapşırığı xatırladır: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" . @@ -322,7 +385,7 @@ Sizin portal ünvanınız: "$__VirtualRootPath":"$__VirtualRootPath" "Edit subscription settings":"$RecipientSubscriptionConfigURL" - h1.Tapşırıq xatırladan: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" + h1.Tapşırıq haqqında xatırlatma: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" Bu gün tapşırığın son möhlət günüdür: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" . @@ -348,7 +411,7 @@ $__DateTime Siz artıq bu tapşırığa cavabdeh deyilsiniz: "$EntityTitle":"${_ $AdditionalData.get_item("TaskDescription") -^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını" dəyişdirin:"$RecipientSubscriptionConfigURL".^ +^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını":"$RecipientSubscriptionConfigURL" dəyişdirin.^ Əgər hər hansı sualınız varsa, bizimlə rahat şəkildə bu ünvandan əlaqə saxlaya bilərsiniz: "support@onlyoffice.com":"mailto:support@onlyoffice.com" @@ -357,11 +420,11 @@ ONLYOFFICE™ Komandası "www.onlyoffice.com":"http://onlyoffice.com/" - h1.Sizi dönüş nöqtəsinə cavabdeh təyin etdilər: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID#my=true" + h1.Sizə təyin olunan mərhələ: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID#my=true" $__DateTime istifadəçi "$__AuthorName":"$__AuthorUrl" Sizi dönüş nöqtəsinə cavabdeh təyin etdi: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID#my=true" в проекте "$ProjectTitle":"${__VirtualRootPath}/Products/Projects/Projects.aspx?prjID=$ProjectID". -^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını" dəyişdirin:"$RecipientSubscriptionConfigURL".^ +^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını":"$RecipientSubscriptionConfigURL" dəyişdirin.^ Əgər hər hansı sualınız varsa, bizimlə rahat şəkildə bu ünvandan əlaqə saxlaya bilərsiniz: "support@onlyoffice.com":"mailto:support@onlyoffice.com" @@ -381,7 +444,7 @@ Sizin portal ünvanınız: "$__VirtualRootPath":"$__VirtualRootPath" "Edit subscription settings":"$RecipientSubscriptionConfigURL" - h1.Sizə alt tapşırıq ayrıldı: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" + h1.Sizə alt tapşırıq təyin edildi: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" "$__AuthorName":"$__AuthorUrl" Sizi alt tapşırığa "$SubEntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" cavabdeh olaraq təyin etdi: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" . @@ -390,7 +453,7 @@ Sizin portal ünvanınız: "$__VirtualRootPath":"$__VirtualRootPath" "Edit subscription settings":"$RecipientSubscriptionConfigURL" - h1.Sizə tapşırıq ayrıldı: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" + h1.Sizə tapşırıq təyin edildi: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" "$__AuthorName":"$__AuthorUrl" Sizi tapşırığa cavabdeh olaraq təyin etdi: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" . @@ -413,7 +476,7 @@ Sizin portal ünvanınız: "$__VirtualRootPath":"$__VirtualRootPath" "Edit subscription settings":"$RecipientSubscriptionConfigURL" - h1.Alt tapşırıq qapadıldı: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" + h1.Alt tapşırıq bağlandı "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" $__DateTime "$__AuthorName":"$__AuthorUrl" tapşırıq üçün olan alt tapşırığı qapadı["$ProjectTitle":"${__VirtualRootPath}/Products/Projects/Projects.aspx?prjID=$ProjectID&ID=$EntityID"]. @@ -437,20 +500,38 @@ Sizin portal ünvanınız: "$__VirtualRootPath":"$__VirtualRootPath" $__DateTime istifadəçi "$__AuthorName":"$__AuthorUrl" alt tapşırığı sildi: "$SubEntityTitle" bu tapşırıqdan "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID". -^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını" dəyişdirin:"$RecipientSubscriptionConfigURL".^ +^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını":"$RecipientSubscriptionConfigURL" dəyişdirin.^ Əgər hər hansı sualınız varsa, bizimlə rahat şəkildə bu ünvandan əlaqə saxlaya bilərsiniz: "support@onlyoffice.com":"mailto:support@onlyoffice.com" Uğurlar, ONLYOFFICE™ Komandası "www.onlyoffice.com":"http://onlyoffice.com/" + + + h1.Alt-tapşırıq redaktə edildi: "$SubEntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" + +$__DateTime "$__AuthorName":"$__AuthorUrl", "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" tapşırığının "$SubEntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" alt-tapşırığını redaktə edib. + +Alt-tapşırıq təyin edilib: "$Responsible". + +^"${__VirtualRootPath}":"${__VirtualRootPath}" portalının qeydiyyatdan keçmiş istifadəçisi olduğunuz üçün bu e-məktubu alırsınız. Bu tapşırıqla bağlı bildirişlər almaq istəmirsinizsə,"abunəlikdən çıxın":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID".^ + + + h1.Alt tapşırıq köçürüldü: "$SubEntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" + +$__DateTime "$__AuthorName":"$__AuthorUrl", "$SubEntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" alt-tapşırığını "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" tapşırığına köçürdü. + +Alt-tapşırıq təyin edildi: "$Responsible". + +^"${__VirtualRootPath}":"${__VirtualRootPath}" portalının qeydiyyatdan keçmiş istifadəçisi olduğunuz üçün bu e-məktubu alırsınız. Bu tapşırıqla bağlı bildirişlər almaq istəmirsinizsə, "abunəlikdən çıxın":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID".^ h1.Alt tapşırıq davam etdirildi: "$SubEntityTitle" $__DateTime istifadəçi "$__AuthorName":"$__AuthorUrl" alt tapşırığı davam etdirdi: "$SubEntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" bu tapşırıqğın "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID". -^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını" dəyişdirin:"$RecipientSubscriptionConfigURL".^ +^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını":"$RecipientSubscriptionConfigURL" dəyişdirin.^ Əgər hər hansı sualınız varsa, bizimlə rahat şəkildə bu ünvandan əlaqə saxlaya bilərsiniz: "support@onlyoffice.com":"mailto:support@onlyoffice.com" @@ -459,7 +540,7 @@ ONLYOFFICE™ Komandası "www.onlyoffice.com":"http://onlyoffice.com/" - h1.Tapşırıq qapadıldı: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" + h1.Tapşırıq bağlandı: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" $__DateTime "$__AuthorName":"$__AuthorUrl" tapşırıq üçün olan alt tapşırığı qapadı ["$ProjectTitle":"${__VirtualRootPath}/Products/Projects/Projects.aspx?prjID=$ProjectID"]. @@ -501,7 +582,7 @@ $__DateTime istifadəçi "$__AuthorName":"$__AuthorUrl" tapşırığı sildi "$E $AdditionalData -^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını" dəyişdirin:"$RecipientSubscriptionConfigURL".^ +^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını":"$RecipientSubscriptionConfigURL" dəyişdirin.^ Əgər hər hansı sualınız varsa, bizimlə rahat şəkildə bu ünvandan əlaqə saxlaya bilərsiniz: "support@onlyoffice.com":"mailto:support@onlyoffice.com" @@ -514,13 +595,29 @@ ONLYOFFICE™ Komandası $__DateTime istifadəçi "$__AuthorName":"$__AuthorUrl" tapşırığı redaktə etdi "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" bu layihədə "$ProjectTitle":"${__VirtualRootPath}/Products/Projects/Projects.aspx?prjID=$ProjectID". -^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını" dəyişdirin:"$RecipientSubscriptionConfigURL".^ +^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını":"$RecipientSubscriptionConfigURL" dəyişdirin.^ Əgər hər hansı sualınız varsa, bizimlə rahat şəkildə bu ünvandan əlaqə saxlaya bilərsiniz: "support@onlyoffice.com":"mailto:support@onlyoffice.com" Uğurlar, ONLYOFFICE™ Komandası "www.onlyoffice.com":"http://onlyoffice.com/" + + + h1.Tapşırıq köçürüldü: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" + +$__DateTime "$__AuthorName":"$__AuthorUrl","$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" tapşırığını "$ProjectTitle":"${__VirtualRootPath}/Products/Projects/Projects.aspx?prjID=$ProjectID" layihəsindəki"$SubEntityTitle":"${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID#my=true" mərhələsindən sildi. + +"$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" tapşırığı indi heç bir mərhələnin hissəsi deyil. + +^"${__VirtualRootPath}":"${__VirtualRootPath}" portalının qeydiyyatdan keçmiş istifadəçisi olduğunuz üçün bu e-məktubu alırsınız. Bildiriş növünü dəyişmək üçün, "abunəlik parametrlərinizi":"$RecipientSubscriptionConfigURL" idarə edin.^ + + + h1.Tapşırıq köçürüldü: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" + +$__DateTime "$__AuthorName":"$__AuthorUrl", "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" tapşırığını "$ProjectTitle":"${__VirtualRootPath}/Products/Projects/Projects.aspx?prjID=$ProjectID" layihəsindəki "$SubEntityTitle":"${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID#my=true" mərhələsinə köçürüb . + +^"${__VirtualRootPath}":"${__VirtualRootPath}" portalının qeydiyyatdan keçmiş istifadəçisi olduğunuz üçün bu e-məktubu alırsınız. Bildiriş növünü dəyişmək üçün "abunəlik ayarlarınızı":"$RecipientSubscriptionConfigURL" idarə edin.^ h1.Tapşırıq davam etdirildi: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" @@ -529,7 +626,7 @@ $__DateTime istifadəçi "$__AuthorName":"$__AuthorUrl" tapşırığı davam etd $AdditionalData - ^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını" dəyişdirin:"$RecipientSubscriptionConfigURL".^ +^Portalda qeydiyyatdan keçdiyiniz üçün bu məktubu alırsınız "${__VirtualRootPath}":"${__VirtualRootPath}". Bu və ya digər hadisələr haqqında bildirişlər almaq istəyirsinizsə, və ya ümumiyyətlə bildiriş almaq istəmirsinizsə, "abunəlik ayarlarını":"$RecipientSubscriptionConfigURL" dəyişdirin.^ Əgər hər hansı sualınız varsa, bizimlə rahat şəkildə bu ünvandan əlaqə saxlaya bilərsiniz: "support@onlyoffice.com":"mailto:support@onlyoffice.com" @@ -537,106 +634,295 @@ Uğurlar, ONLYOFFICE™ Komandası "www.onlyoffice.com":"http://onlyoffice.com/" + + Layihələr [$ProjectTitle]. Şərh müzakirə üçün redaktə edilib: $EntityTitle + + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Müzakirənin şərhi redaktə edildi: [$EntityTitle](${__VirtualRootPath}/products/projects/messages.aspx?prjID=$ProjectID&ID=$EntityID) + + + Layihələr [$ProjectTitle]. Tapşırıq üçün şərh redaktə edildi: $EntityTitle + + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Tapşırığın şərhi redaktə edildi: [$EntityTitle](${__VirtualRootPath}/products/projects/tasks.aspx?prjID=$ProjectID&ID=$EntityID) + Layihələr [$ProjectTitle]. Müzakirə redakte edildi: $EntityTitle + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Müzakirə redaktə edildi: [$EntityTitle](${__VirtualRootPath}/products/projects/messages.aspx?prjID=$ProjectID&ID=$EntityID) + - Layihələr. Verilənlərin Basecamp'dan importu + Layihələr. Verilənlərin Basecamp'dan idxalı - Layihələr [$ProjectTitle]. Layihə komandasına dəyişikliklər + Layihələr [$ProjectTitle]. Layihə komandasına təyin edilmisiniz + + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Layihə komandasına əlavə edilmisiniz - Layihələr [$ProjectTitle]. Yubanmış dönüş nöqtəsi bildirişi: $EntityTitle + Layihələr [$ProjectTitle]. Gecikmiş mərhələ bildirişi: $EntityTitle Layihələr [$ProjectTitle]. Müzakirə silindi: $EntityTitle + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Müzakirə silindi: [$EntityTitle](${__VirtualRootPath}/products/projects/messages.aspx?prjID=$ProjectID) + - Layihələr [$ProjectTitle]. Layihələr qapatıldılar: $EntityTitle + Layihələr [$ProjectTitle]. Layihələr bağlandı: $EntityTitle + + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Mərhələ bağlandı: [$EntityTitle](${__VirtualRootPath}/products/projects/milestones.aspx?prjID=$ProjectID#status=closed) - Layihələr [$ProjectTitle]. Dönüş nöqtəsi yaradıldı: $EntityTitle + Layihələr [$ProjectTitle]. Mərhələ yaradıldı: $EntityTitle + + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Mərhələ yaradıldı: [$EntityTitle](${__VirtualRootPath}/products/projects/milestones.aspx?prjID=$ProjectID) - Layihələr [$ProjectTitle]. Bildiriş tarixi üzrə dönüş nöqtəsi: $EntityTitle + Layihələr [$ProjectTitle]. Bildiriş tarixi üzrə mərhələ: $EntityTitle - Layihələr [$ProjectTitle]. Layihə silindi: $EntityTitle + Layihələr [$ProjectTitle]. Mərhələ silindi: $EntityTitle + + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Mərhələ silindi: [$EntityTitle](${__VirtualRootPath}/products/projects/milestones.aspx?prjID=$ProjectID) - Layihələr [$ProjectTitle]. Layihə redaktə olundu: $EntityTitle + Layihələr [$ProjectTitle]. Mərhələ redaktə olundu: $EntityTitle + + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Mərhələ redaktə edildi: [$EntityTitle](${__VirtualRootPath}/products/projects/milestones.aspx?prjID=$ProjectID) - Layihələr [$ProjectTitle]. Dönüş nöqtəi davam etdirildi: $EntityTitle + Layihələr [$ProjectTitle]. Mərhələ davam etdirildi: $EntityTitle + + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Mərhələ davam etdirildi: [$EntityTitle](${__VirtualRootPath}/products/projects/milestones.aspx?prjID=$ProjectID) Layihələr [$ProjectTitle]. Müzakirəyə yeni şərh: $EntityTitle + + LAyihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Müzakirəyə yeni şərh: [$EntityTitle](${__VirtualRootPath}/products/projects/messages.aspx?prjID=$ProjectID&ID=$EntityID) + Layihələr [$ProjectTitle]. Tapşırığa yeni şərh: $EntityTitle + + LAyihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Tapşırığa yeni şərh: [$EntityTitle](${__VirtualRootPath}/products/projects/tasks.aspx?prjID=$ProjectID&ID=$EntityID) + Layihələr [$ProjectTitle]. Müzakirədə yeni fayl: $EntityTitle + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Müzakirə üçün yeni fayl: [$EntityTitle](${__VirtualRootPath}/products/projects/messages.aspx?prjID=$ProjectID&ID=$EntityID) + Layihələr [$ProjectTitle]. Tapşırıqda yeni fayl: $EntityTitle + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Tapşırıq üçün yeni fayl: [$EntityTitle](${__VirtualRootPath}/products/projects/tasks.aspx?prjID=$ProjectID&ID=$EntityID) + Layihələr [$ProjectTitle]. Yeni müzakirə: $EntityTitle + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Yeni müzakirə: [$EntityTitle](${__VirtualRootPath}/products/projects/messages.aspx?prjID=$ProjectID&ID=$EntityID) + Layihə. Layihə silindi: $EntityTitle + + Layihə komandasına əlavə edilmisiniz + + + Müzakirə silinib + + + Mərhələ bağlanıb + + + Yeni mərhələ + + + Mərhələ silinib + + + Mərhələ davam etdirilib + + + Yeni müzakirə + + + Layihələr. Layihə silindi + + + Sizə mərhələ təyin edlib + + + Layihə meneceri təyin edildiniz + + + Sizə alt-tapşırıq təyin edildi. + + + Sizə tapşırıq təyin edilib + + + Alt-tapşırıq bağlandı + + + Yeni alt-tapşırıq yaradıldı + + + Alt-tapşırıq silinib + + + Alt-tapşırıq davam etdirilib + + + Tapşırıq bağlanıb + + + Yeni tapşırıq yaradıldı + + + Tapşırıq silinib + + + Tapşırıq davam etdirilib + - Layihələr [$ProjectTitle]. Tapşırığı xatırlat: $EntityTitle + Layihələr [$ProjectTitle]. Tapşırıq haqqında xatırlatma: $EntityTitle + + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Tapşırığı xatırlat: [$EntityTitle](${__VirtualRootPath}/products/projects/tasks.aspx?prjID=$ProjectID) - Layihələr [$ProjectTitle]. Tapşırığı xatırlat: $EntityTitle + Layihələr [$ProjectTitle]. Tapşırıq haqqında xatırlatma: $EntityTitle + + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Tapşırığı xatırlat: [$EntityTitle](${__VirtualRootPath}/products/projects/tasks.aspx?prjID=$ProjectID) - Layihələr [$ProjectTitle]. Layihə komandasına dəyişikliklər + Layihələr [$ProjectTitle]. Layihə komandasından kənarlaşdırıldınız + + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Layihə komandasından kənarlaşdırılmısınız - Layihələr [$ProjectTitle]. Siz bu tapşırığa görə daha cavabdeh deyilsiniz: $EntityTitle + Layihələr [$ProjectTitle]. Bu tapşırıq artıq sizə təyin edilməyib: $EntityTitle + + + LAyihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Artıq sizə tapşırıq təyin edilməyib: [$EntityTitle](${__VirtualRootPath}/products/projects/tasks.aspx?prjID=$ProjectID&ID=$EntityID) - Layihələr [$ProjectTitle]. Sizi bu dönüş nöqtəsinə cavabdeh təyin etdilər: $EntityTitle + Layihələr [$ProjectTitle]. Sizə mərhələ təyin edildi: $EntityTitle + + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Sizə mərhələ təyin edildi: [$EntityTitle](${__VirtualRootPath}/products/projects/milestones.aspx?prjID=$ProjectID) Layihələr [$ProjectTitle]. Siz layihə meneceri olaraq təyin edildiniz + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Layihə meneceri kimi təyin edildiniz + - Layihələr [$ProjectTitle]. Sizə alt tapşırıq ayrıldı: $EntityTitle + Layihələr [$ProjectTitle]. Sizə alt tapşırıq təyin edildi: $EntityTitle + + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Sizə alt-tapşırıq təyin edildi: [$SubEntityTitle](${__VirtualRootPath}/products/projects/tasks.aspx?prjID=$ProjectID) - Layihələr [$ProjectTitle]. Sizə tapşırıq ayrıldı: $EntityTitle + Layihələr [$ProjectTitle]. Sizə tapşırıq təyin edildi: $EntityTitle + + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Sizə tapşırıq təyin edildi: [$EntityTitle](${__VirtualRootPath}/products/projects/tasks.aspx?prjID=$ProjectID&ID=$EntityID) - Layihələr [$ProjectTitle]. Alt tapşırıq qapadıldı: $EntityTitle + Layihələr [$ProjectTitle]. Alt tapşırıq bağlandı: $EntityTitle + + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Alt-tapşırıq bağlandı: [$SubEntityTitle](${__VirtualRootPath}/products/projects/tasks.aspx?prjID=$ProjectID&ID=$EntityID) Layihələr [$ProjectTitle]. Alt tapşırıq yaradıldı: $EntityTitle + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Alt-tapşırıq yaradıldı: [$SubEntityTitle](${__VirtualRootPath}/products/projects/tasks.aspx?prjID=$ProjectID&ID=$EntityID) + Layihələr [$ProjectTitle]. Alt tapşırıq silindi: $SubEntityTitle + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Alt-tapşırıq silindi: [$SubEntityTitle](${__VirtualRootPath}/products/projects/tasks.aspx?prjID=$ProjectID) + + + Layihələr [$ProjectTitle]. Alt-tapşırıq redaktə edildi: $EntityTitle + + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Alt-tapşırıq redaktə edildi: [$SubEntityTitle](${__VirtualRootPath}/products/projects/tasks.aspx?prjID=$ProjectID&ID=$EntityID) + + + Layihələr [$ProjectTitle]. Alt-tapşırıq köçürüldü: $EntityTitle + + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Alt-tapşırıq köçürüldü: [$SubEntityTitle](${__VirtualRootPath}/products/projects/tasks.aspx?prjID=$ProjectID&ID=$EntityID) + Layihələr [$ProjectTitle]. Alt tapşırıq davam etdirildi: $SubEntityTitle + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Alt-tapşırıq davam etdirildi: [$SubEntityTitle](${__VirtualRootPath}/products/projects/tasks.aspx?prjID=$ProjectID) + - Layihələr [$ProjectTitle]. Tapşırıq qapadıldı: $EntityTitle + Layihələr [$ProjectTitle]. Tapşırıq bağlandı: $EntityTitle + + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Tapşırıq bağlandı: [$EntityTitle](${__VirtualRootPath}/products/projects/tasks.aspx?prjID=$ProjectID&ID=$EntityID) Layihələr [$ProjectTitle]. Tapşırıq yaradıldı: $EntityTitle + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Tapşırıq yaradıldı: [$EntityTitle](${__VirtualRootPath}/products/projects/tasks.aspx?prjID=$ProjectID&ID=$EntityID) + Layihələr [$ProjectTitle]. Tapşırıq silindi: $EntityTitle + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Tapşırıq silindi: [$EntityTitle](${__VirtualRootPath}/products/projects/tasks.aspx?prjID=$ProjectID) + Layihələr [$ProjectTitle]. Tapşırıq redaktə edildi: $EntityTitle + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Tapşırıq redaktə edildi: [$EntityTitle](${__VirtualRootPath}/products/projects/tasks.aspx?prjID=$ProjectID&ID=$EntityID) + + + Layihələr [$ProjectTitle]. Tapşırıq silindi: $EntityTitle + + + Layihələr[$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Tapşırıq silindi: [$EntityTitle](${__VirtualRootPath}/products/projects/tasks.aspx?prjID=$ProjectID&ID=$EntityID) + + + Layihələr [$ProjectTitle]. Tapşırıq köçürüldü: $EntityTitle + + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Tapşırıq köçürüldü: [$EntityTitle](${__VirtualRootPath}/products/projects/tasks.aspx?prjID=$ProjectID&ID=$EntityID) + Layihələr [$ProjectTitle]. Tapşırıq davam etdirildi: $EntityTitle - \ No newline at end of file + + Layihələr [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Tapşırıq davam etdirildi: [$EntityTitle](${__VirtualRootPath}/products/projects/tasks.aspx?prjID=$ProjectID) + + + Heç kim + + diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Services/NotifyService/PatternResource.resx b/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Services/NotifyService/PatternResource.resx index eff11d062..59d0285ea 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Services/NotifyService/PatternResource.resx +++ b/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Services/NotifyService/PatternResource.resx @@ -194,8 +194,8 @@ $AdditionalData $__DateTime "$__AuthorName":"$__AuthorUrl" has added a new comment to the "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" task: -$AdditionalData - +$AdditionalData + "Read more":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID#comment_$CommentID" ^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. If you don't want to receive the notifications about this task, please "unsubscribe":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID".^ @@ -755,4 +755,54 @@ $AdditionalData Nobody + + h1.Subtask moved: "$SubEntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" + +$__DateTime "$__AuthorName":"$__AuthorUrl" has moved the "$SubEntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" subtask to the "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" task. + +The subtask is assigned to "$Responsible". + +^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. If you don't want to receive the notifications about this task, please "unsubscribe":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID".^ + + + Projects [$ProjectTitle]. Subtask moved: $EntityTitle + + + Projects [$ProjectTitle](${__VirtualRootPath}/products/projects/projects.aspx?prjID=$ProjectID). Subtask moved: [$SubEntityTitle](${__VirtualRootPath}/products/projects/tasks.aspx?prjID=$ProjectID&ID=$EntityID) + + + h1.You were mentioned in comment to task: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" + +$__DateTime "$__AuthorName":"$__AuthorUrl" mentioned you in the comment to the "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID" task: + +$AdditionalData + +"Read more":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID#comment_$CommentID" + +^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. If you don't want to receive the notifications about this task, please "unsubscribe":"${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID".^ + + + Projects [$ProjectTitle]. Mention in comment to task: $EntityTitle + + + Projects [$ProjectTitle](${__VirtualRootPath}/Products/Projects/Projects.aspx?prjID=$ProjectID). Mention in comment to task: [$EntityTitle](${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID) + + + h1.You were mentioned in comment to discussion: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Messages.aspx?prjID=$ProjectID&ID=$EntityID" + +$__DateTime "$__AuthorName":"$__AuthorUrl" mentioned you in the comment to the discussion: "$EntityTitle":"${__VirtualRootPath}/Products/Projects/Messages.aspx?prjID=$ProjectID&ID=$EntityID" + +$AdditionalData + +"Read more":"${__VirtualRootPath}/Products/Projects/Messages.aspx?prjID=$ProjectID&ID=$EntityID#comment_$CommentID" + +^You receive this email because you are a registered user of the "${__VirtualRootPath}":"${__VirtualRootPath}" portal. To change the notification type, please manage your "subscription settings":"$RecipientSubscriptionConfigURL".^ + + + + Projects [$ProjectTitle]. Mention in comment to discussion: $EntityTitle + + + Projects [$ProjectTitle](${__VirtualRootPath}/Products/Projects/Projects.aspx?prjID=$ProjectID). Mention in comment to discussion: [$EntityTitle](${__VirtualRootPath}/Products/Projects/Messages.aspx?prjID=$ProjectID&ID=$EntityID) + \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Services/NotifyService/patterns.xml b/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Services/NotifyService/patterns.xml index fba938749..c0fbdf732 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Services/NotifyService/patterns.xml +++ b/web/studio/ASC.Web.Studio/Products/Projects/Core/Model/Services/NotifyService/patterns.xml @@ -17,9 +17,9 @@ - $__AuthorName + $__AuthorName - $AdditionalData.get_item("MessagePreview") +$AdditionalData.get_item("MessagePreview") @@ -43,7 +43,7 @@ - $__AuthorName + $__AuthorName $AdditionalData.get_item("MessagePreview") @@ -65,11 +65,53 @@ $AdditionalData.get_item("MessagePreview") - $__AuthorName + $__AuthorName $AdditionalData + + + + + + + + +$__AuthorName + +$AdditionalData + +${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID + + + + + $__AuthorName +$AdditionalData + + + + + + + + + + +$__AuthorName + +$AdditionalData + +${__VirtualRootPath}/Products/Projects/Messages.aspx?prjID=$ProjectID&ID=$EntityID + + + + + $__AuthorName +$AdditionalData + + @@ -87,7 +129,7 @@ $AdditionalData - $__AuthorName + $__AuthorName $AdditionalData @@ -109,7 +151,7 @@ $AdditionalData - $__AuthorName + $__AuthorName $AdditionalData @@ -131,7 +173,7 @@ $AdditionalData - $__AuthorName + $__AuthorName $AdditionalData @@ -153,7 +195,7 @@ $AdditionalData - $__AuthorName + $__AuthorName $AdditionalData @@ -175,7 +217,7 @@ $AdditionalData - $__AuthorName + $__AuthorName $AdditionalData @@ -197,7 +239,7 @@ $AdditionalData - $__AuthorName + $__AuthorName $AdditionalData @@ -227,7 +269,7 @@ $AdditionalData - $__AuthorName + $__AuthorName $AdditionalData @@ -251,7 +293,7 @@ $AdditionalData - $__AuthorName + $__AuthorName @@ -275,7 +317,7 @@ $AdditionalData - $__AuthorName + $__AuthorName $AdditionalData.get_item("MilestoneDescription") @@ -301,7 +343,7 @@ $AdditionalData.get_item("MilestoneDescription") - $__AuthorName + $__AuthorName $AdditionalData.get_item("TaskDescription") @@ -321,7 +363,7 @@ $AdditionalData.get_item("TaskDescription") - $__AuthorName + $__AuthorName @@ -341,7 +383,7 @@ $AdditionalData.get_item("TaskDescription") - $__AuthorName + $__AuthorName $AdditionalData @@ -365,7 +407,7 @@ $AdditionalData - $__AuthorName + $__AuthorName @@ -385,9 +427,7 @@ $AdditionalData - - $__AuthorName - + $__AuthorName @@ -408,7 +448,7 @@ $AdditionalData - $__AuthorName + $__AuthorName @@ -428,7 +468,7 @@ $AdditionalData - $__AuthorName + $__AuthorName $AdditionalData @@ -452,8 +492,7 @@ $AdditionalData - - $__AuthorName + $__AuthorName @@ -475,7 +514,7 @@ $AdditionalData - $__AuthorName + $__AuthorName @@ -497,7 +536,7 @@ $AdditionalData - $__AuthorName + $__AuthorName @@ -519,7 +558,25 @@ $AdditionalData - $__AuthorName + $__AuthorName + + + + + + + + + + + $__AuthorName + + ${__VirtualRootPath}/Products/Projects/Tasks.aspx?prjID=$ProjectID&ID=$EntityID + + + + + $__AuthorName @@ -537,7 +594,7 @@ $AdditionalData - $__AuthorName + $__AuthorName @@ -559,7 +616,7 @@ $AdditionalData - $__AuthorName + $__AuthorName @@ -583,7 +640,7 @@ $AdditionalData - $__AuthorName + $__AuthorName $AdditionalData @@ -605,7 +662,7 @@ $AdditionalData - $__AuthorName + $__AuthorName $AdditionalData @@ -623,8 +680,8 @@ $AdditionalData - - [${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID&ID=$EntityID](${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID&ID=$EntityID) + +[${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID&ID=$EntityID](${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID&ID=$EntityID) @@ -640,8 +697,8 @@ $AdditionalData - - [${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID&ID=$EntityID](${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID&ID=$EntityID) + +[${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID&ID=$EntityID](${__VirtualRootPath}/Products/Projects/Milestones.aspx?prjID=$ProjectID&ID=$EntityID) @@ -661,7 +718,7 @@ $AdditionalData - $__AuthorName + $__AuthorName $AdditionalData @@ -687,7 +744,7 @@ $AdditionalData - $__AuthorName + $__AuthorName $AdditionalData.get_item("TaskDescription") @@ -713,7 +770,7 @@ $AdditionalData.get_item("TaskDescription") - $__AuthorName + $__AuthorName $AdditionalData.get_item("MilestoneDescription") @@ -737,7 +794,7 @@ $AdditionalData.get_item("MilestoneDescription") - $__AuthorName + $__AuthorName @@ -761,7 +818,7 @@ $AdditionalData.get_item("MilestoneDescription") - $__AuthorName + $__AuthorName $AdditionalData.get_item("TaskDescription") @@ -781,7 +838,7 @@ $AdditionalData.get_item("TaskDescription") - $AdditionalData.get_item("TaskDescription") + $AdditionalData.get_item("TaskDescription") @@ -799,7 +856,7 @@ $AdditionalData.get_item("TaskDescription") - $__AuthorName + $__AuthorName @@ -819,7 +876,7 @@ $AdditionalData.get_item("TaskDescription") - + @@ -839,7 +896,7 @@ $AdditionalData.get_item("TaskDescription") - $__AuthorName + $__AuthorName $AdditionalData.get_item("TaskDescription") diff --git a/web/studio/ASC.Web.Studio/Products/Projects/DocbuilderTemplates/MilestonesNearest.docbuilder b/web/studio/ASC.Web.Studio/Products/Projects/DocbuilderTemplates/MilestonesNearest.docbuilder index ac9db3bb5..dbb49114b 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/DocbuilderTemplates/MilestonesNearest.docbuilder +++ b/web/studio/ASC.Web.Studio/Products/Projects/DocbuilderTemplates/MilestonesNearest.docbuilder @@ -1,4 +1,6 @@ -oWs.SetColumnWidth(2, 20); +writeInterval(); + +oWs.SetColumnWidth(2, 20); var projects = []; diff --git a/web/studio/ASC.Web.Studio/Products/Projects/DocbuilderTemplates/ProjectsList.docbuilder b/web/studio/ASC.Web.Studio/Products/Projects/DocbuilderTemplates/ProjectsList.docbuilder index 928364e4e..b72680639 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/DocbuilderTemplates/ProjectsList.docbuilder +++ b/web/studio/ASC.Web.Studio/Products/Projects/DocbuilderTemplates/ProjectsList.docbuilder @@ -1,4 +1,35 @@ -oWs.SetColumnWidth(2, 20); +writeInterval(); + +oWs.SetColumnWidth(2, 20); +oWs.SetColumnWidth(4, 20); +oWs.SetColumnWidth(5, 20); +oWs.SetColumnWidth(6, 20); + +var currentRow = 4; +var currentColumn = "E"; + +writeRow([7, 8, 9], reportColumns, [1, 2, 3], { + color: darkGrayBorderColor +}, { + size: smallFontSize, + color: grayFontColor +}); + +var currentRow = 5; +var currentColumn = "E"; + +var data = reportData.shift(); + +writeRow([0, 1, 2], data, [1, 2, 3], { + color: darkGrayBorderColor +}, { + size: smallFontSize, + color: blackFontColor, +}); + + +var currentRow = 7; +var currentColumn = "B"; writeRow([0, 1, 2, 3, 4, 5], reportColumns, [3, 4, 5], { color: darkGrayBorderColor diff --git a/web/studio/ASC.Web.Studio/Products/Projects/DocbuilderTemplates/TimeSpend_0.docbuilder b/web/studio/ASC.Web.Studio/Products/Projects/DocbuilderTemplates/TimeSpend_0.docbuilder index 5391baa5a..f4c017afa 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/DocbuilderTemplates/TimeSpend_0.docbuilder +++ b/web/studio/ASC.Web.Studio/Products/Projects/DocbuilderTemplates/TimeSpend_0.docbuilder @@ -1,4 +1,6 @@ -oWs.SetColumnWidth(2, 16); +writeInterval(); + +oWs.SetColumnWidth(2, 16); oWs.SetColumnWidth(3, 16); var timeFormat = "[h]\\" + reportColumns[6] + " mm\\" + reportColumns[7]; diff --git a/web/studio/ASC.Web.Studio/Products/Projects/DocbuilderTemplates/TimeSpend_1.docbuilder b/web/studio/ASC.Web.Studio/Products/Projects/DocbuilderTemplates/TimeSpend_1.docbuilder index 6e5f94e7c..cfc65ad01 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/DocbuilderTemplates/TimeSpend_1.docbuilder +++ b/web/studio/ASC.Web.Studio/Products/Projects/DocbuilderTemplates/TimeSpend_1.docbuilder @@ -1,4 +1,6 @@ -oWs.SetColumnWidth(2, 16); +writeInterval(); + +oWs.SetColumnWidth(2, 16); oWs.SetColumnWidth(3, 16); var timeFormat = "[h]\\" + reportColumns[6] + " mm\\" + reportColumns[7]; diff --git a/web/studio/ASC.Web.Studio/Products/Projects/DocbuilderTemplates/TimeSpend_2.docbuilder b/web/studio/ASC.Web.Studio/Products/Projects/DocbuilderTemplates/TimeSpend_2.docbuilder index b93b43e9e..ce4161875 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/DocbuilderTemplates/TimeSpend_2.docbuilder +++ b/web/studio/ASC.Web.Studio/Products/Projects/DocbuilderTemplates/TimeSpend_2.docbuilder @@ -1,4 +1,6 @@ -oWs.SetColumnWidth(2, 16); +writeInterval(); + +oWs.SetColumnWidth(2, 16); oWs.SetColumnWidth(3, 16); var timeFormat = "[h]\\" + reportColumns[6] + " mm\\" + reportColumns[7]; diff --git a/web/studio/ASC.Web.Studio/Products/Projects/DocbuilderTemplates/UsersActivity.docbuilder b/web/studio/ASC.Web.Studio/Products/Projects/DocbuilderTemplates/UsersActivity.docbuilder index 45ca8d02d..0444e41a3 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/DocbuilderTemplates/UsersActivity.docbuilder +++ b/web/studio/ASC.Web.Studio/Products/Projects/DocbuilderTemplates/UsersActivity.docbuilder @@ -1,4 +1,6 @@ -var notGrouped = []; +writeInterval(); + +var notGrouped = []; var grouped1 = []; for (var i = 0; i < reportData.length; i++) { @@ -37,18 +39,53 @@ function showGroups(grouped) { }); currentRow++; - - printHeader(); - + var first = grouped[0]; + var indexes = [1, 2, 3, 4, 5]; + var alignRight = [1, 2, 3, 4]; + if(first.length == 9) + { + var avgP = first[first.length-2]; + var avgT = first[first.length-1]; + if(avgP != -1) + { + indexes.push(6); + alignRight.push(alignRight.length + 1); + } + if(avgT != -1) + { + indexes.push(7); + alignRight.push(alignRight.length + 1); + } + printHeader(avgP, avgT); + } + else + { + var avg = first[first.length-1]; + if(avg != -1) + { + indexes.push(6); + alignRight.push(alignRight.length + 1); + } + printHeaderForProjects(avg); + } + var sum = [0, 0, 0, 0]; for (var j = 0; j < grouped.length; j++) { var groupedItem = grouped[j]; if (groupItem.id === groupedItem[0]) { groupedItem = groupedItem.slice(1); - writeRow([1, 2, 3, 4, 5], + if(groupedItem[groupedItem.length-1] == -2) + { + groupedItem[groupedItem.length-1] = null; + } + if(groupedItem[groupedItem.length-2] == -2) + { + groupedItem[groupedItem.length-2] = null; + } + writeRow(indexes, groupedItem, - [1, 2, 3, 4], + alignRight, { color: lightGrayBorderColor }, @@ -65,8 +102,42 @@ function showGroups(grouped) { } } -function printHeader() { - writeRow([0, 1, 2, 3, 4], reportColumns, [1, 2, 3, 4], { +function printHeader(avgP, avgT) { + var indexes = [0, 1, 2, 3, 4]; + var alignRight = [1, 2, 3, 4]; + if(avgP != -1) + { + indexes.push(5); + var cell = alignRight.length + 1; + oWs.SetColumnWidth(cell + 1, 30); + alignRight.push(cell); + } + if(avgT != -1) + { + indexes.push(6); + var cell = alignRight.length + 1; + oWs.SetColumnWidth(cell + 1, 30); + alignRight.push(cell); + } + writeRow(indexes, reportColumns, alignRight, { + color: darkGrayBorderColor + }, { + size: smallFontSize, + color: grayFontColor + }); +} + +function printHeaderForProjects(avg) { + var indexes = [0, 1, 2, 3, 4]; + var alignRight = [1, 2, 3, 4]; + if(avg != -1) + { + indexes.push(6); + var cell = alignRight.length + 1; + oWs.SetColumnWidth(cell + 1, 30); + alignRight.push(cell); + } + writeRow(indexes, reportColumns, alignRight, { color: darkGrayBorderColor }, { size: smallFontSize, diff --git a/web/studio/ASC.Web.Studio/Products/Projects/DocbuilderTemplates/master.docbuilder b/web/studio/ASC.Web.Studio/Products/Projects/DocbuilderTemplates/master.docbuilder index 0273adfd3..696535ea1 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/DocbuilderTemplates/master.docbuilder +++ b/web/studio/ASC.Web.Studio/Products/Projects/DocbuilderTemplates/master.docbuilder @@ -84,6 +84,18 @@ function writeMeta() { currentRow += 3; }; +function writeInterval() +{ + if(reportInfo.TimeInterval) + { + range = oWs.GetRange("B3"); + range.SetFontColor(grayFontColor); + range.SetBold(true); + range.SetAlignHorizontal("left"); + range.SetValue(reportInfo.TimeInterval); + } +} + function writeText(value, textStyle = defaultText, newLine = true) { let range = oWs.GetRange(currentColumn + currentRow); diff --git a/web/studio/ASC.Web.Studio/Products/Projects/Projects.aspx.cs b/web/studio/ASC.Web.Studio/Products/Projects/Projects.aspx.cs index f1200d927..656bf2075 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/Projects.aspx.cs +++ b/web/studio/ASC.Web.Studio/Products/Projects/Projects.aspx.cs @@ -17,6 +17,8 @@ using ASC.Projects.Core.Domain; using ASC.Web.Projects.Classes; +using ASC.Web.Projects.Resources; +using ASC.Web.Studio.Utility; namespace ASC.Web.Projects { @@ -57,6 +59,8 @@ namespace ASC.Web.Projects if (action.HasValue && action.Value == UrlAction.Edit) { Master.AddControl(LoadControl(PathProvider.GetFileStaticRelativePath("Projects/ProjectAction.ascx"))); + + Title = HeaderStringHelper.GetPageTitle(ProjectResource.EditProject); } } else @@ -64,6 +68,8 @@ namespace ASC.Web.Projects if (action.HasValue && action.Value == UrlAction.Add) { Master.AddControl(LoadControl(PathProvider.GetFileStaticRelativePath("Projects/ProjectAction.ascx"))); + + Title = HeaderStringHelper.GetPageTitle(ProjectResource.CreateNewProject); } } } diff --git a/web/studio/ASC.Web.Studio/Products/Projects/ProjectsTemplates/ReportTemplates.html b/web/studio/ASC.Web.Studio/Products/Projects/ProjectsTemplates/ReportTemplates.html index f98f2c69a..b75b9ae1c 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/ProjectsTemplates/ReportTemplates.html +++ b/web/studio/ASC.Web.Studio/Products/Projects/ProjectsTemplates/ReportTemplates.html @@ -12,12 +12,17 @@ {{each check}} {{tmpl($item.data.check[$index]) 'projects_reportFilterCheck'}} {{/each}} + {{each checkcombo}} + {{tmpl($item.data.checkcombo[$index]) 'projects_reportFilterCombo'}} + {{/each}} @@ -29,8 +34,8 @@ \ No newline at end of file + diff --git a/web/studio/ASC.Web.Studio/Products/Projects/ProjectsTemplates/SubtaskTemplates.html b/web/studio/ASC.Web.Studio/Products/Projects/ProjectsTemplates/SubtaskTemplates.html index 01220b175..b79fce357 100644 --- a/web/studio/ASC.Web.Studio/Products/Projects/ProjectsTemplates/SubtaskTemplates.html +++ b/web/studio/ASC.Web.Studio/Products/Projects/ProjectsTemplates/SubtaskTemplates.html @@ -5,7 +5,7 @@ diff --git a/web/studio/ASC.Web.Studio/Templates/CommonTemplates.html b/web/studio/ASC.Web.Studio/Templates/CommonTemplates.html index 0ae6b2d9a..805044314 100644 --- a/web/studio/ASC.Web.Studio/Templates/CommonTemplates.html +++ b/web/studio/ASC.Web.Studio/Templates/CommonTemplates.html @@ -260,7 +260,7 @@ ${ASC.Resources.Master.TemplateResource.MailIcsCalendarDescriptionLabel}: - ${description} + {{html description}} {{/if}} @@ -287,6 +287,39 @@
    + + - -<% } %> diff --git a/web/studio/ASC.Web.Studio/UserControls/Common/Support/SupportChat.ascx.cs b/web/studio/ASC.Web.Studio/UserControls/Common/Support/SupportChat.ascx.cs index 4c88e55cc..7ab016ace 100644 --- a/web/studio/ASC.Web.Studio/UserControls/Common/Support/SupportChat.ascx.cs +++ b/web/studio/ASC.Web.Studio/UserControls/Common/Support/SupportChat.ascx.cs @@ -17,6 +17,7 @@ using System; using System.Configuration; +using System.Web; using System.Web.UI; namespace ASC.Web.Studio.UserControls.Common.Support @@ -38,6 +39,8 @@ namespace ASC.Web.Studio.UserControls.Common.Support protected void Page_Load(object sender, EventArgs e) { + Page.RegisterBodyScripts(VirtualPathUtility.ToAbsolute("~/UserControls/Common/Support/livechat.js")); + Page.RegisterInlineScript(string.Format("ASC.ZopimLiveChat.init('{0}');", SupportKey)); } } } \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/UserControls/Common/Support/livechat.js b/web/studio/ASC.Web.Studio/UserControls/Common/Support/livechat.js index dac8b6094..a108739aa 100644 --- a/web/studio/ASC.Web.Studio/UserControls/Common/Support/livechat.js +++ b/web/studio/ASC.Web.Studio/UserControls/Common/Support/livechat.js @@ -14,38 +14,98 @@ * */ -jq(function () { - var time = 60000; - var waitForZopim = setInterval(function () { - if (time < 0) { - clearInterval(waitForZopim); - }; +if (typeof (ASC) === 'undefined') { + ASC = {}; +} - if (window.$zopim === undefined || window.$zopim.livechat === undefined) { - time -= 100; - return ; - }; +if (typeof (ASC.ZopimLiveChat) === 'undefined') { + ASC.ZopimLiveChat = {}; +} - var $switcher = jq('#liveChatSwitch .switch-btn'); +ASC.ZopimLiveChat = (function () { - jq('#liveChatSwitch').click(function () { - $switcher.toggleClass('switch-on'); - if ($switcher.hasClass('switch-on')) { - $zopim.livechat.window.hide(); - localStorage.setItem('livechat', 'on'); - } else { - $zopim.livechat.hideAll(); - localStorage.setItem('livechat', 'off'); + var keyChat; + var onloadTimeout = null; + var counter = 0; + var counterMax = 100; + var $switcher = jq('#liveChatSwitch .switch-btn'); + + var init = function (key) { + + if (!$switcher.length) return; + + keyChat = key; + + jq('#liveChatSwitch').on("click", function () { + if (jq(this).hasClass('disabled')) return; + + if (window.$zopim === undefined || window.$zopim.livechat === undefined) { + jq(this).addClass('disabled'); + loadLiveChat(); + return; } + toggleSwitcher(localStorage.getItem('livechat') === 'off'); }); - if (localStorage.getItem("livechat") !== 'on') { - $zopim.livechat.hideAll(); - } else { - $switcher.click(); - }; + if (localStorage.getItem('livechat') === 'on') { + jq('#liveChatSwitch').addClass('disabled'); + loadLiveChat(); + } + }; - clearInterval(waitForZopim); - }, 100); -}); + var loadLiveChat = function () { + window.$zopim || (function (d, s) { + var z = $zopim = function (c) { z._.push(c) }, + $ = z.s = d.createElement(s), + e = d.getElementsByTagName(s)[0]; + z.set = function (o) { + z.set._.push(o) + }; + z._ = []; + z.set._ = []; + $.async = !0; + $.setAttribute("charset", "utf-8"); + $.src = "https://v2.zopim.com/?" + keyChat; + z.t = +new Date; + $.type = "text/javascript"; + $.onload = onloadLiveChat; + e.parentNode.insertBefore($, e) + })(document, "script"); + }; + + var onloadLiveChat = function () { + clearTimeout(onloadTimeout); + + if (counter < counterMax) { + + if (window.$zopim.livechat === undefined) { + console.log('livechat undefined'); + onloadTimeout = setTimeout(onloadLiveChat, 200); + counter += 1; + return; + } + + toggleSwitcher(true); + } + }; + + var toggleSwitcher = function (toggle) { + jq('#liveChatSwitch').removeClass('disabled'); + if (toggle) { + $switcher.addClass('switch-on'); + $zopim.livechat.setDisableSound(false); + $zopim.livechat.window.hide(); + localStorage.setItem('livechat', 'on'); + } else { + $switcher.removeClass('switch-on'); + $zopim.livechat.setDisableSound(true); + $zopim.livechat.hideAll(); + localStorage.setItem('livechat', 'off'); + } + } + + return { + init: init + } +})(); \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/ThirdPartyBanner.ascx b/web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/ThirdPartyBanner.ascx deleted file mode 100644 index 880de4c6d..000000000 --- a/web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/ThirdPartyBanner.ascx +++ /dev/null @@ -1,11 +0,0 @@ -<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="ThirdPartyBanner.ascx.cs" Inherits="ASC.Web.Studio.UserControls.Common.ThirdPartyBanner.ThirdPartyBanner" %> -<%@ Import Namespace="ASC.Web.Studio.Utility" %> - - diff --git a/web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/ThirdPartyBanner.ascx.cs b/web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/ThirdPartyBanner.ascx.cs deleted file mode 100644 index 15d266be3..000000000 --- a/web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/ThirdPartyBanner.ascx.cs +++ /dev/null @@ -1,120 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using System.Web.UI; - -using AjaxPro; - -using ASC.Core; -using ASC.FederatedLogin.LoginProviders; -using ASC.VoipService.Dao; -using ASC.Web.Core; -using ASC.Web.Studio.Core; -using ASC.Web.Studio.PublicResources; -using ASC.Web.Studio.Utility; - -namespace ASC.Web.Studio.UserControls.Common.ThirdPartyBanner -{ - [AjaxNamespace("ThirdPartyBanner")] - public partial class ThirdPartyBanner : UserControl - { - public static string Location - { - get { return "~/UserControls/Common/ThirdPartyBanner/ThirdPartyBanner.ascx"; } - } - - public static bool Display - { - get { return SetupInfo.ThirdPartyBannerEnabled && SecurityContext.CheckPermissions(SecutiryConstants.EditPortalSettings) && GetBanner.Any(); } - } - - protected Tuple CurrentBanner; - - protected void Page_Load(object sender, EventArgs e) - { - AjaxPro.Utility.RegisterTypeForAjax(GetType()); - - Page.RegisterStyle("~/UserControls/Common/ThirdPartyBanner/css/thirdpartybanner.css"); - - var tmp = GetBanner; - var i = new Random().Next(0, tmp.Count()); - CurrentBanner = GetBanner.ToArray()[i]; - } - - private static IEnumerable> GetBanner - { - get - { - if (CoreContext.Configuration.Personal - || CoreContext.Configuration.CustomMode - || HttpContext.Current.Request.DesktopApp()) - { - yield break; - } - - var currentProductId = CommonLinkUtility.GetProductID(); - - if ((currentProductId == WebItemManager.DocumentsProductID - || currentProductId == WebItemManager.PeopleProductID) - && !ThirdPartyBannerSettings.CheckClosed("bitly") - && !BitlyLoginProvider.Enabled) - { - yield return new Tuple("bitly", UserControlsCommonResource.BannerBitly); - } - - if ((currentProductId == WebItemManager.CRMProductID - || currentProductId == WebItemManager.PeopleProductID) - && !ThirdPartyBannerSettings.CheckClosed("social") - && - (!TwitterLoginProvider.Instance.IsEnabled || - !FacebookLoginProvider.Instance.IsEnabled || - !LinkedInLoginProvider.Instance.IsEnabled || - !GoogleLoginProvider.Instance.IsEnabled)) - { - yield return new Tuple("social", UserControlsCommonResource.BannerSocial); - } - - if (currentProductId == WebItemManager.DocumentsProductID - && !ThirdPartyBannerSettings.CheckClosed("storage") - && (!BoxLoginProvider.Instance.IsEnabled - || !DropboxLoginProvider.Instance.IsEnabled - || !OneDriveLoginProvider.Instance.IsEnabled)) - { - yield return new Tuple("storage", UserControlsCommonResource.BannerStorage); - } - - if (currentProductId == WebItemManager.CRMProductID - && !VoipDao.ConfigSettingsExist - && VoipDao.Consumer.CanSet) - { - if (!ThirdPartyBannerSettings.CheckClosed("twilio2")) - yield return new Tuple("twilio2", UserControlsCommonResource.BannerTwilio2); - } - } - } - - [AjaxMethod] - public void CloseBanner(string banner) - { - ThirdPartyBannerSettings.Close(banner); - } - } -} \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/ThirdPartyBanner.ascx.designer.cs b/web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/ThirdPartyBanner.ascx.designer.cs deleted file mode 100644 index 0cd7093fe..000000000 --- a/web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/ThirdPartyBanner.ascx.designer.cs +++ /dev/null @@ -1,15 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace ASC.Web.Studio.UserControls.Common.ThirdPartyBanner { - - - public partial class ThirdPartyBanner { - } -} diff --git a/web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/ThirdPartyBannerSettings.cs b/web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/ThirdPartyBannerSettings.cs deleted file mode 100644 index 4a872a391..000000000 --- a/web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/ThirdPartyBannerSettings.cs +++ /dev/null @@ -1,60 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -using System; -using System.Linq; -using System.Runtime.Serialization; - -using ASC.Core.Common.Settings; - -namespace ASC.Web.Studio.UserControls.Common.ThirdPartyBanner -{ - [Serializable] - [DataContract] - public class ThirdPartyBannerSettings : BaseSettings - { - [DataMember(Name = "ClosedSetting")] - public string ClosedSetting { get; set; } - - public override ISettings GetDefault() - { - return new ThirdPartyBannerSettings - { - ClosedSetting = null, - }; - } - - public override Guid ID - { - get { return new Guid("{B6E9B080-4B14-4C54-95E7-C2E9E87965EB}"); } - } - - public static bool CheckClosed(string banner) - { - return (LoadForCurrentUser().ClosedSetting ?? "").Split('|').Contains(banner); - } - - public static void Close(string banner) - { - var closed = (LoadForCurrentUser().ClosedSetting ?? "").Split('|').ToList(); - if (closed.Contains(banner)) return; - closed.Add(banner); - - new ThirdPartyBannerSettings { ClosedSetting = string.Join("|", closed.ToArray()) }.SaveForCurrentUser(); - } - } -} \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/css/img/bitly.png b/web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/css/img/bitly.png deleted file mode 100644 index 2da0926d5e5d678fb384afb5d2ad2bf7213af8d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2496 zcmV;x2|xCUP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D02y>eSaefwW^{L9 za%BK;VQFr3E^cLXAT%y8E-^DS16z6k00}TjL_t(YOTAWUY*g13J_12WNhK0g1QDt6 zynEl9@px>r2ZCwUrmY~GXh5J~GxOdo9`DAxP3!{3(4df1AVsQ>@*`2&q>*T8Q=nB^ zmLz}(VF!;F>=`d(k3Hkr$2Jtdes@5XQuPPF_;EB(Z|*ziyWjcFcP|~N-ax@}J}0)_ zur1(ZORlspr85Ut)0c0rqRac1QAcYIo!`HbzW&X_IQ{Nwy4{#ViS3!xRb`~EoeEvr zvz)H(%cGP#gD#ciQiJ<7YAe}D?^nM>@7Da1KCQ^7$-*W1{nc5E&mTmdw^!0R)VR6F zLU&p$ba$7Ld`&WW5xz!FSKD%Fpi-vca)r8fXVcxc%;}&D>e59Ms&pdeN}U9}d%8dKbAXzeeN*LkXqbg^v(eY<}trHV7? zd}$uJO14mY@kWYON|dZIP^5zYJYi>JVT~R4RA$fZsLG~}3Jdkr8j(Ne@xe}OOWsSk zWId#@={NwVY=%E1!CJ7GQ(v`0H}>YytvWNs3NtBDV4!$`VOCO=PEWY7IR_sebFk;H zwl1Yx&AHDcHN$~nr&4{(nmwb#l}!WXMhX`Kuqqu*AosY+^jLwxj4o1YOD2uFCAzXN z_wm6->}{i^t^(j?4Gf-m02YJ+2tI*BexpZ}xg@&pWN)=>M)GB>1p-LBG zPFcUN8OH7xE?FGf!6{^8MF~6C?>m@2rDd#4?lRD^=5_Riwvu#v#*qYaqedcXXITi; zl_*fk(!d@ctPLMpCBm*RrJPI$3xDmXF++n((_pWI+3U?{Sz{LYsWT3(YNh+)YD=lZ?jB&2yYWlzw591KVbVR73xI3bf_{?aLyd~)3 zt>Z~pO3$SAv8}T`_p6dk({a`SvvNeGcWrMF?p6P3lsUP8Ml*wPFtd3gQ z%cwKMv(G}^04xIZbL@&!(8bQgQ7r6W*CSwg0?$)uT?$O4ROwt#qj}MpL#xSOqRasM zpQWHdbmde?<%IPw#jTPa#Ae43;(4;rpigSjgQ#YBHsWA~7%)BP;686l?gC$(k%ruo zF{ZN6BV#%+rtFylD1~j3l63L#BQh#R1>34bo;nLri~AL7f{FJ!%zx3H1|5lyM{JmG zC!?-v1TrVaatuMr;b=aDIvS`h#e^S>{ z++`qNttm5UV_{Kau#Ru*YckS+Cex)hfW2LBrdthW8g=orLhLAbxT{8{&bBPVqzV@I zilorojgW)qV_5nDOc(kbEs;OD*`7mv4ayv_*D2&Osj?A&mGOsNyN$5CZ1PoRt;1<| zPvk%+?)21}U{xlHxY)6z$YYoE5xerykSZ@6wJ{3__$*xy1i{;17ZMw^^JO=8<;CrVe(ffkn2MF0j%uozw#a>?JMutNb3LXV)e9<={d z)X8g-YUYBQs)dfbUY!4LZOKX4+dZri2f^P4-Lm8_mN$dhI~cM_;TB;h??zkSMA2CQ za0Cp+#2o>EufWna`rrz^@X>&kZ$|4sz|fup*d73VAT}+e7A!Nh_z#%qaK+E)zhJRz z>cy?9cc=NOgw>FTkJh5dGC0G+zEVYKnx@oDx}xRM?~B&s0+cZaYhFZ=+yQ929}_-U zEEBXgAMbGjNh|!7%2PwQM&dc`p&}m$SrxWQv&WpA0Cx7cTRwu5EF9IiKH=cku-V@Q zHFhnI6A!n+QzMnI#rLx5Xz5SB3%0MyoDP5$)159LY|IO=g84~+y{6^RAB*zoftHb8 z1F$p*ume~#^blMk==c!zXMbO!SK zFyxdeSgHpkg-0?A+zID?bXj7|GI|D;VJnar{0CiT(q!2?unkLg%9Z0000< KMNUMnLSTY`1;)An diff --git a/web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/css/img/social.png b/web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/css/img/social.png deleted file mode 100644 index 8d3f2073bdb759bf1d6db49ad4107a632cbe69a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2897 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 UQ4?qS)PSlEPgg&ebxsLQ0A+lV#{d8T diff --git a/web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/css/img/twilio2.png b/web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/css/img/twilio2.png deleted file mode 100644 index ca46432dd2b936088153108fd4e21f729c09f4f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5711 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 diff --git a/web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/css/thirdpartybanner.css b/web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/css/thirdpartybanner.css deleted file mode 100644 index d6c794b63..000000000 --- a/web/studio/ASC.Web.Studio/UserControls/Common/ThirdPartyBanner/css/thirdpartybanner.css +++ /dev/null @@ -1,39 +0,0 @@ -/* - * - * (c) Copyright Ascensio System Limited 2010-2021 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * -*/ - - -.banner-bitly { - background-image: url("img/bitly.png"); - background-position: 15px 11px; -} -.banner-social { - background-image: url("img/social.png"); - background-position: 15px 11px; -} -.banner-storage { - background-image: url("img/storage.png"); - background-position: 15px 11px; -} -.banner-link.banner-large.banner-twilio2 { - background-image: url("img/twilio2.png"); - background-position: 15px 11px; - max-height: none; - padding: 12px 15px 11px 85px; - width: 138px; -} -.banner-link.banner-large.banner-twilio2 .cancelButton { - margin-top: -8px; -} \ No newline at end of file diff --git a/web/studio/ASC.Web.Studio/UserControls/Common/TopStudioPanel/TopStudioPanel.ascx b/web/studio/ASC.Web.Studio/UserControls/Common/TopStudioPanel/TopStudioPanel.ascx index d28ee52ba..ed8b957be 100644 --- a/web/studio/ASC.Web.Studio/UserControls/Common/TopStudioPanel/TopStudioPanel.ascx +++ b/web/studio/ASC.Web.Studio/UserControls/Common/TopStudioPanel/TopStudioPanel.ascx @@ -223,7 +223,7 @@
    <% foreach (var service in AuthServiceList) { %> - " alt="<%= service.Title %>" /> + " alt="<%= service.Title %>" /> <% } %>
    @@ -306,32 +306,32 @@ <% } %> <% if (!String.IsNullOrEmpty(Settings.CompanyName)) { %> -
    <%= Settings.CompanyName %>
    +
    <%: Settings.CompanyName %>
    <% } %>

    &N0cHjmZ@H8xGAdK%}l+11;k@{>Bezdp@{Yq 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 +b!7Hs7yN5?69ejiI%stGJ>&w%p>i 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Ξ@avkiTAV(UXs4sdCk$Q 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�SC$f>)Qf%Y@j{aABb<} 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

il;a^3%j!A-~(f{VG=opUcCpDgUiJ)$r(4Cm5D0 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 z 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%0DclwIT_qt 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

2|M9#60;uTwgvXQ1fFdZtPHY0fiI#YceNn)Bf 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 IC=4^4vzjHc1&T8vg|S-u6AG-J<)84VRrB< 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