From bf1c9bebebfac2d0a64176f08dcb8f5f8a87a281 Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 19 Jul 2017 20:26:28 +0300 Subject: [PATCH] no message --- LegacyComponents.xcodeproj/project.pbxproj | 834 ++++++++- LegacyComponents/LegacyComponents.h | 85 +- LegacyComponents/LegacyComponentsGlobals.h | 18 + LegacyComponents/LegacyComponentsGlobals.m | 19 + LegacyComponents/LegacyComponentsInternal.h | 18 + LegacyComponents/LegacyComponentsInternal.m | 36 + LegacyComponents/NSInputStream+TL.h | 28 + LegacyComponents/NSInputStream+TL.m | 390 ++++ LegacyComponents/NSObject+TGLock.h | 23 + LegacyComponents/NSObject+TGLock.m | 73 + LegacyComponents/PSCoding.h | 10 + LegacyComponents/PSData.h | 14 + LegacyComponents/PSKeyValueCoder.h | 54 + LegacyComponents/PSKeyValueCoder.m | 180 ++ LegacyComponents/PSKeyValueDecoder.h | 12 + LegacyComponents/PSKeyValueDecoder.m | 698 ++++++++ LegacyComponents/PSKeyValueEncoder.h | 8 + LegacyComponents/PSKeyValueEncoder.m | 373 ++++ LegacyComponents/PSKeyValueReader.h | 24 + LegacyComponents/PSKeyValueStore.h | 11 + LegacyComponents/PSKeyValueWriter.h | 11 + LegacyComponents/RMPhoneFormat.h | 58 + LegacyComponents/RMPhoneFormat.m | 754 ++++++++ LegacyComponents/TGActionMediaAttachment.h | 52 + LegacyComponents/TGActionMediaAttachment.m | 388 ++++ LegacyComponents/TGAudioMediaAttachment.h | 33 + LegacyComponents/TGAudioMediaAttachment.m | 71 + LegacyComponents/TGAudioWaveform.h | 15 + LegacyComponents/TGAudioWaveform.m | 85 + .../TGAuthorSignatureMediaAttachment.h | 15 + .../TGAuthorSignatureMediaAttachment.m | 46 + LegacyComponents/TGBotComandInfo.h | 12 + LegacyComponents/TGBotComandInfo.m | 29 + .../TGBotContextResultAttachment.h | 16 + .../TGBotContextResultAttachment.m | 44 + LegacyComponents/TGBotInfo.h | 15 + LegacyComponents/TGBotInfo.m | 33 + LegacyComponents/TGBotReplyMarkup.h | 24 + LegacyComponents/TGBotReplyMarkup.m | 87 + LegacyComponents/TGBotReplyMarkupButton.h | 61 + LegacyComponents/TGBotReplyMarkupButton.m | 262 +++ LegacyComponents/TGBotReplyMarkupRow.h | 13 + LegacyComponents/TGBotReplyMarkupRow.m | 40 + LegacyComponents/TGChannelAdminRights.h | 27 + LegacyComponents/TGChannelAdminRights.m | 95 + LegacyComponents/TGChannelBannedRights.h | 25 + LegacyComponents/TGChannelBannedRights.m | 90 + LegacyComponents/TGContactMediaAttachment.h | 20 + LegacyComponents/TGContactMediaAttachment.m | 98 ++ LegacyComponents/TGConversation.h | 296 ++++ LegacyComponents/TGConversation.m | 1266 +++++++++++++ LegacyComponents/TGDatabaseMessageDraft.h | 20 + LegacyComponents/TGDatabaseMessageDraft.m | 46 + .../TGDocumentAttributeAnimated.h | 5 + .../TGDocumentAttributeAnimated.m | 28 + LegacyComponents/TGDocumentAttributeAudio.h | 17 + LegacyComponents/TGDocumentAttributeAudio.m | 56 + .../TGDocumentAttributeFilename.h | 9 + .../TGDocumentAttributeFilename.m | 45 + .../TGDocumentAttributeImageSize.h | 11 + .../TGDocumentAttributeImageSize.m | 43 + LegacyComponents/TGDocumentAttributeSticker.h | 25 + LegacyComponents/TGDocumentAttributeSticker.m | 105 ++ LegacyComponents/TGDocumentAttributeVideo.h | 13 + LegacyComponents/TGDocumentAttributeVideo.m | 42 + LegacyComponents/TGDocumentMediaAttachment.h | 55 + LegacyComponents/TGDocumentMediaAttachment.m | 404 +++++ .../TGForwardedMessageMediaAttachment.h | 27 + .../TGForwardedMessageMediaAttachment.m | 133 ++ LegacyComponents/TGGameMediaAttachment.h | 23 + LegacyComponents/TGGameMediaAttachment.m | 72 + LegacyComponents/TGImageInfo.h | 34 + LegacyComponents/TGImageInfo.mm | 358 ++++ LegacyComponents/TGImageMediaAttachment.h | 35 + LegacyComponents/TGImageMediaAttachment.m | 267 +++ LegacyComponents/TGInstantPage.h | 298 ++++ LegacyComponents/TGInstantPage.m | 909 ++++++++++ LegacyComponents/TGInvoiceMediaAttachment.h | 25 + LegacyComponents/TGInvoiceMediaAttachment.m | 121 ++ .../TGLocalMessageMetaMediaAttachment.h | 19 + .../TGLocalMessageMetaMediaAttachment.m | 101 ++ LegacyComponents/TGLocalization.h | 20 + LegacyComponents/TGLocalization.m | 124 ++ LegacyComponents/TGLocationMediaAttachment.h | 31 + LegacyComponents/TGLocationMediaAttachment.m | 117 ++ LegacyComponents/TGMediaAttachment.h | 26 + LegacyComponents/TGMediaAttachment.m | 15 + LegacyComponents/TGMessage.h | 285 +++ LegacyComponents/TGMessage.mm | 1179 +++++++++++++ .../TGMessageEntitiesAttachment.h | 21 + .../TGMessageEntitiesAttachment.m | 62 + LegacyComponents/TGMessageEntity.h | 11 + LegacyComponents/TGMessageEntity.m | 42 + LegacyComponents/TGMessageEntityBold.h | 5 + LegacyComponents/TGMessageEntityBold.m | 10 + LegacyComponents/TGMessageEntityBotCommand.h | 5 + LegacyComponents/TGMessageEntityBotCommand.m | 10 + LegacyComponents/TGMessageEntityCode.h | 5 + LegacyComponents/TGMessageEntityCode.m | 10 + LegacyComponents/TGMessageEntityEmail.h | 5 + LegacyComponents/TGMessageEntityEmail.m | 10 + LegacyComponents/TGMessageEntityHashtag.h | 5 + LegacyComponents/TGMessageEntityHashtag.m | 10 + LegacyComponents/TGMessageEntityItalic.h | 5 + LegacyComponents/TGMessageEntityItalic.m | 10 + LegacyComponents/TGMessageEntityMention.h | 5 + LegacyComponents/TGMessageEntityMention.m | 10 + LegacyComponents/TGMessageEntityMentionName.h | 9 + LegacyComponents/TGMessageEntityMentionName.m | 41 + LegacyComponents/TGMessageEntityPre.h | 9 + LegacyComponents/TGMessageEntityPre.m | 41 + LegacyComponents/TGMessageEntityTextUrl.h | 9 + LegacyComponents/TGMessageEntityTextUrl.m | 46 + LegacyComponents/TGMessageEntityUrl.h | 7 + LegacyComponents/TGMessageEntityUrl.m | 10 + LegacyComponents/TGMessageGroup.h | 13 + LegacyComponents/TGMessageGroup.m | 32 + LegacyComponents/TGMessageHole.h | 16 + LegacyComponents/TGMessageHole.m | 51 + .../TGMessageViewCountContentProperty.h | 10 + .../TGMessageViewCountContentProperty.m | 23 + LegacyComponents/TGPeerIdAdapter.h | 52 + LegacyComponents/TGPhoneUtils.h | 21 + LegacyComponents/TGPhoneUtils.m | 101 ++ LegacyComponents/TGPluralization.h | 20 + LegacyComponents/TGPluralization.m | 346 ++++ LegacyComponents/TGReplyMarkupAttachment.h | 13 + LegacyComponents/TGReplyMarkupAttachment.m | 72 + .../TGReplyMessageMediaAttachment.h | 12 + .../TGReplyMessageMediaAttachment.m | 66 + LegacyComponents/TGStickerPackReference.h | 29 + LegacyComponents/TGStickerPackReference.m | 145 ++ LegacyComponents/TGStringUtils.h | 83 + LegacyComponents/TGStringUtils.mm | 1561 +++++++++++++++++ LegacyComponents/TGTextCheckingResult.h | 26 + LegacyComponents/TGTextCheckingResult.m | 22 + .../TGUnsupportedMediaAttachment.h | 17 + .../TGUnsupportedMediaAttachment.m | 44 + LegacyComponents/TGUser.h | 127 ++ LegacyComponents/TGUser.m | 415 +++++ LegacyComponents/TGViaUserAttachment.h | 12 + LegacyComponents/TGViaUserAttachment.m | 44 + LegacyComponents/TGVideoInfo.h | 19 + LegacyComponents/TGVideoInfo.mm | 176 ++ LegacyComponents/TGVideoMediaAttachment.h | 38 + LegacyComponents/TGVideoMediaAttachment.m | 285 +++ LegacyComponents/TGWebDocument.h | 34 + LegacyComponents/TGWebDocument.m | 112 ++ LegacyComponents/TGWebPageMediaAttachment.h | 34 + LegacyComponents/TGWebPageMediaAttachment.m | 169 ++ 150 files changed, 16748 insertions(+), 37 deletions(-) create mode 100644 LegacyComponents/LegacyComponentsGlobals.h create mode 100644 LegacyComponents/LegacyComponentsGlobals.m create mode 100644 LegacyComponents/LegacyComponentsInternal.h create mode 100644 LegacyComponents/LegacyComponentsInternal.m create mode 100644 LegacyComponents/NSInputStream+TL.h create mode 100644 LegacyComponents/NSInputStream+TL.m create mode 100644 LegacyComponents/NSObject+TGLock.h create mode 100644 LegacyComponents/NSObject+TGLock.m create mode 100644 LegacyComponents/PSCoding.h create mode 100644 LegacyComponents/PSData.h create mode 100644 LegacyComponents/PSKeyValueCoder.h create mode 100644 LegacyComponents/PSKeyValueCoder.m create mode 100644 LegacyComponents/PSKeyValueDecoder.h create mode 100644 LegacyComponents/PSKeyValueDecoder.m create mode 100644 LegacyComponents/PSKeyValueEncoder.h create mode 100644 LegacyComponents/PSKeyValueEncoder.m create mode 100644 LegacyComponents/PSKeyValueReader.h create mode 100644 LegacyComponents/PSKeyValueStore.h create mode 100644 LegacyComponents/PSKeyValueWriter.h create mode 100644 LegacyComponents/RMPhoneFormat.h create mode 100644 LegacyComponents/RMPhoneFormat.m create mode 100644 LegacyComponents/TGActionMediaAttachment.h create mode 100644 LegacyComponents/TGActionMediaAttachment.m create mode 100644 LegacyComponents/TGAudioMediaAttachment.h create mode 100644 LegacyComponents/TGAudioMediaAttachment.m create mode 100644 LegacyComponents/TGAudioWaveform.h create mode 100644 LegacyComponents/TGAudioWaveform.m create mode 100644 LegacyComponents/TGAuthorSignatureMediaAttachment.h create mode 100644 LegacyComponents/TGAuthorSignatureMediaAttachment.m create mode 100644 LegacyComponents/TGBotComandInfo.h create mode 100644 LegacyComponents/TGBotComandInfo.m create mode 100644 LegacyComponents/TGBotContextResultAttachment.h create mode 100644 LegacyComponents/TGBotContextResultAttachment.m create mode 100644 LegacyComponents/TGBotInfo.h create mode 100644 LegacyComponents/TGBotInfo.m create mode 100644 LegacyComponents/TGBotReplyMarkup.h create mode 100644 LegacyComponents/TGBotReplyMarkup.m create mode 100644 LegacyComponents/TGBotReplyMarkupButton.h create mode 100644 LegacyComponents/TGBotReplyMarkupButton.m create mode 100644 LegacyComponents/TGBotReplyMarkupRow.h create mode 100644 LegacyComponents/TGBotReplyMarkupRow.m create mode 100644 LegacyComponents/TGChannelAdminRights.h create mode 100644 LegacyComponents/TGChannelAdminRights.m create mode 100644 LegacyComponents/TGChannelBannedRights.h create mode 100644 LegacyComponents/TGChannelBannedRights.m create mode 100644 LegacyComponents/TGContactMediaAttachment.h create mode 100644 LegacyComponents/TGContactMediaAttachment.m create mode 100644 LegacyComponents/TGConversation.h create mode 100644 LegacyComponents/TGConversation.m create mode 100644 LegacyComponents/TGDatabaseMessageDraft.h create mode 100644 LegacyComponents/TGDatabaseMessageDraft.m create mode 100644 LegacyComponents/TGDocumentAttributeAnimated.h create mode 100644 LegacyComponents/TGDocumentAttributeAnimated.m create mode 100644 LegacyComponents/TGDocumentAttributeAudio.h create mode 100644 LegacyComponents/TGDocumentAttributeAudio.m create mode 100644 LegacyComponents/TGDocumentAttributeFilename.h create mode 100644 LegacyComponents/TGDocumentAttributeFilename.m create mode 100644 LegacyComponents/TGDocumentAttributeImageSize.h create mode 100644 LegacyComponents/TGDocumentAttributeImageSize.m create mode 100644 LegacyComponents/TGDocumentAttributeSticker.h create mode 100644 LegacyComponents/TGDocumentAttributeSticker.m create mode 100644 LegacyComponents/TGDocumentAttributeVideo.h create mode 100644 LegacyComponents/TGDocumentAttributeVideo.m create mode 100644 LegacyComponents/TGDocumentMediaAttachment.h create mode 100644 LegacyComponents/TGDocumentMediaAttachment.m create mode 100644 LegacyComponents/TGForwardedMessageMediaAttachment.h create mode 100644 LegacyComponents/TGForwardedMessageMediaAttachment.m create mode 100644 LegacyComponents/TGGameMediaAttachment.h create mode 100644 LegacyComponents/TGGameMediaAttachment.m create mode 100644 LegacyComponents/TGImageInfo.h create mode 100644 LegacyComponents/TGImageInfo.mm create mode 100644 LegacyComponents/TGImageMediaAttachment.h create mode 100644 LegacyComponents/TGImageMediaAttachment.m create mode 100644 LegacyComponents/TGInstantPage.h create mode 100644 LegacyComponents/TGInstantPage.m create mode 100644 LegacyComponents/TGInvoiceMediaAttachment.h create mode 100644 LegacyComponents/TGInvoiceMediaAttachment.m create mode 100644 LegacyComponents/TGLocalMessageMetaMediaAttachment.h create mode 100644 LegacyComponents/TGLocalMessageMetaMediaAttachment.m create mode 100644 LegacyComponents/TGLocalization.h create mode 100644 LegacyComponents/TGLocalization.m create mode 100644 LegacyComponents/TGLocationMediaAttachment.h create mode 100644 LegacyComponents/TGLocationMediaAttachment.m create mode 100644 LegacyComponents/TGMediaAttachment.h create mode 100644 LegacyComponents/TGMediaAttachment.m create mode 100644 LegacyComponents/TGMessage.h create mode 100644 LegacyComponents/TGMessage.mm create mode 100644 LegacyComponents/TGMessageEntitiesAttachment.h create mode 100644 LegacyComponents/TGMessageEntitiesAttachment.m create mode 100644 LegacyComponents/TGMessageEntity.h create mode 100644 LegacyComponents/TGMessageEntity.m create mode 100644 LegacyComponents/TGMessageEntityBold.h create mode 100644 LegacyComponents/TGMessageEntityBold.m create mode 100644 LegacyComponents/TGMessageEntityBotCommand.h create mode 100644 LegacyComponents/TGMessageEntityBotCommand.m create mode 100644 LegacyComponents/TGMessageEntityCode.h create mode 100644 LegacyComponents/TGMessageEntityCode.m create mode 100644 LegacyComponents/TGMessageEntityEmail.h create mode 100644 LegacyComponents/TGMessageEntityEmail.m create mode 100644 LegacyComponents/TGMessageEntityHashtag.h create mode 100644 LegacyComponents/TGMessageEntityHashtag.m create mode 100644 LegacyComponents/TGMessageEntityItalic.h create mode 100644 LegacyComponents/TGMessageEntityItalic.m create mode 100644 LegacyComponents/TGMessageEntityMention.h create mode 100644 LegacyComponents/TGMessageEntityMention.m create mode 100644 LegacyComponents/TGMessageEntityMentionName.h create mode 100644 LegacyComponents/TGMessageEntityMentionName.m create mode 100644 LegacyComponents/TGMessageEntityPre.h create mode 100644 LegacyComponents/TGMessageEntityPre.m create mode 100644 LegacyComponents/TGMessageEntityTextUrl.h create mode 100644 LegacyComponents/TGMessageEntityTextUrl.m create mode 100644 LegacyComponents/TGMessageEntityUrl.h create mode 100644 LegacyComponents/TGMessageEntityUrl.m create mode 100644 LegacyComponents/TGMessageGroup.h create mode 100644 LegacyComponents/TGMessageGroup.m create mode 100644 LegacyComponents/TGMessageHole.h create mode 100644 LegacyComponents/TGMessageHole.m create mode 100644 LegacyComponents/TGMessageViewCountContentProperty.h create mode 100644 LegacyComponents/TGMessageViewCountContentProperty.m create mode 100644 LegacyComponents/TGPeerIdAdapter.h create mode 100644 LegacyComponents/TGPhoneUtils.h create mode 100644 LegacyComponents/TGPhoneUtils.m create mode 100644 LegacyComponents/TGPluralization.h create mode 100644 LegacyComponents/TGPluralization.m create mode 100644 LegacyComponents/TGReplyMarkupAttachment.h create mode 100644 LegacyComponents/TGReplyMarkupAttachment.m create mode 100644 LegacyComponents/TGReplyMessageMediaAttachment.h create mode 100644 LegacyComponents/TGReplyMessageMediaAttachment.m create mode 100644 LegacyComponents/TGStickerPackReference.h create mode 100644 LegacyComponents/TGStickerPackReference.m create mode 100644 LegacyComponents/TGStringUtils.h create mode 100644 LegacyComponents/TGStringUtils.mm create mode 100644 LegacyComponents/TGTextCheckingResult.h create mode 100644 LegacyComponents/TGTextCheckingResult.m create mode 100644 LegacyComponents/TGUnsupportedMediaAttachment.h create mode 100644 LegacyComponents/TGUnsupportedMediaAttachment.m create mode 100644 LegacyComponents/TGUser.h create mode 100644 LegacyComponents/TGUser.m create mode 100644 LegacyComponents/TGViaUserAttachment.h create mode 100644 LegacyComponents/TGViaUserAttachment.m create mode 100644 LegacyComponents/TGVideoInfo.h create mode 100644 LegacyComponents/TGVideoInfo.mm create mode 100644 LegacyComponents/TGVideoMediaAttachment.h create mode 100644 LegacyComponents/TGVideoMediaAttachment.m create mode 100644 LegacyComponents/TGWebDocument.h create mode 100644 LegacyComponents/TGWebDocument.m create mode 100644 LegacyComponents/TGWebPageMediaAttachment.h create mode 100644 LegacyComponents/TGWebPageMediaAttachment.m diff --git a/LegacyComponents.xcodeproj/project.pbxproj b/LegacyComponents.xcodeproj/project.pbxproj index 305463fb1c..b1c15797b7 100644 --- a/LegacyComponents.xcodeproj/project.pbxproj +++ b/LegacyComponents.xcodeproj/project.pbxproj @@ -8,12 +8,308 @@ /* Begin PBXBuildFile section */ D017772C1F1F8F100044446D /* LegacyComponents.h in Headers */ = {isa = PBXBuildFile; fileRef = D017772A1F1F8F100044446D /* LegacyComponents.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01777511F1F8FE60044446D /* LegacyComponentsGlobals.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777381F1F8FE60044446D /* LegacyComponentsGlobals.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01777531F1F8FE60044446D /* PSCoding.h in Headers */ = {isa = PBXBuildFile; fileRef = D017773A1F1F8FE60044446D /* PSCoding.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01777541F1F8FE60044446D /* PSData.h in Headers */ = {isa = PBXBuildFile; fileRef = D017773B1F1F8FE60044446D /* PSData.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01777551F1F8FE60044446D /* PSKeyValueCoder.h in Headers */ = {isa = PBXBuildFile; fileRef = D017773C1F1F8FE60044446D /* PSKeyValueCoder.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01777561F1F8FE60044446D /* PSKeyValueCoder.m in Sources */ = {isa = PBXBuildFile; fileRef = D017773D1F1F8FE60044446D /* PSKeyValueCoder.m */; }; + D01777571F1F8FE60044446D /* PSKeyValueDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = D017773E1F1F8FE60044446D /* PSKeyValueDecoder.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01777581F1F8FE60044446D /* PSKeyValueDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = D017773F1F1F8FE60044446D /* PSKeyValueDecoder.m */; }; + D01777591F1F8FE60044446D /* PSKeyValueEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777401F1F8FE60044446D /* PSKeyValueEncoder.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D017775A1F1F8FE60044446D /* PSKeyValueEncoder.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777411F1F8FE60044446D /* PSKeyValueEncoder.m */; }; + D017775B1F1F8FE60044446D /* PSKeyValueReader.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777421F1F8FE60044446D /* PSKeyValueReader.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D017775C1F1F8FE60044446D /* PSKeyValueStore.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777431F1F8FE60044446D /* PSKeyValueStore.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D017775D1F1F8FE60044446D /* PSKeyValueWriter.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777441F1F8FE60044446D /* PSKeyValueWriter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D017775E1F1F8FE60044446D /* TGBotComandInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777451F1F8FE60044446D /* TGBotComandInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D017775F1F1F8FE60044446D /* TGBotComandInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777461F1F8FE60044446D /* TGBotComandInfo.m */; }; + D01777601F1F8FE60044446D /* TGBotInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777471F1F8FE60044446D /* TGBotInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01777611F1F8FE60044446D /* TGBotInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777481F1F8FE60044446D /* TGBotInfo.m */; }; + D01777621F1F8FE60044446D /* TGLocalization.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777491F1F8FE60044446D /* TGLocalization.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01777631F1F8FE60044446D /* TGLocalization.m in Sources */ = {isa = PBXBuildFile; fileRef = D017774A1F1F8FE60044446D /* TGLocalization.m */; }; + D01777641F1F8FE60044446D /* TGPluralization.h in Headers */ = {isa = PBXBuildFile; fileRef = D017774B1F1F8FE60044446D /* TGPluralization.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01777651F1F8FE60044446D /* TGPluralization.m in Sources */ = {isa = PBXBuildFile; fileRef = D017774C1F1F8FE60044446D /* TGPluralization.m */; }; + D01777661F1F8FE60044446D /* TGStringUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = D017774D1F1F8FE60044446D /* TGStringUtils.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01777671F1F8FE60044446D /* TGStringUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = D017774E1F1F8FE60044446D /* TGStringUtils.mm */; }; + D01777681F1F8FE60044446D /* TGUser.h in Headers */ = {isa = PBXBuildFile; fileRef = D017774F1F1F8FE60044446D /* TGUser.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01777691F1F8FE60044446D /* TGUser.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777501F1F8FE60044446D /* TGUser.m */; }; + D017776B1F1F909E0044446D /* LegacyComponentsGlobals.m in Sources */ = {isa = PBXBuildFile; fileRef = D017776A1F1F909E0044446D /* LegacyComponentsGlobals.m */; }; + D01777721F1F92420044446D /* TGPhoneUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777701F1F92420044446D /* TGPhoneUtils.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01777731F1F92420044446D /* TGPhoneUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777711F1F92420044446D /* TGPhoneUtils.m */; }; + D01777761F1F92570044446D /* RMPhoneFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777741F1F92570044446D /* RMPhoneFormat.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01777771F1F92570044446D /* RMPhoneFormat.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777751F1F92570044446D /* RMPhoneFormat.m */; }; + D017777A1F1F927A0044446D /* NSObject+TGLock.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777781F1F927A0044446D /* NSObject+TGLock.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D017777B1F1F927A0044446D /* NSObject+TGLock.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777791F1F927A0044446D /* NSObject+TGLock.m */; }; + D017777E1F1F930B0044446D /* TGConversation.h in Headers */ = {isa = PBXBuildFile; fileRef = D017777C1F1F930B0044446D /* TGConversation.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D017777F1F1F930B0044446D /* TGConversation.m in Sources */ = {isa = PBXBuildFile; fileRef = D017777D1F1F930B0044446D /* TGConversation.m */; }; + D01777821F1F93250044446D /* TGImageInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777801F1F93250044446D /* TGImageInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01777831F1F93250044446D /* TGImageInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = D01777811F1F93250044446D /* TGImageInfo.mm */; }; + D01777871F1F93550044446D /* TGMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777851F1F93550044446D /* TGMessage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01777881F1F93550044446D /* TGMessage.mm in Sources */ = {isa = PBXBuildFile; fileRef = D01777861F1F93550044446D /* TGMessage.mm */; }; + D01777F41F1F961D0044446D /* TGTextCheckingResult.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777891F1F961C0044446D /* TGTextCheckingResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01777F51F1F961D0044446D /* TGTextCheckingResult.m in Sources */ = {isa = PBXBuildFile; fileRef = D017778A1F1F961C0044446D /* TGTextCheckingResult.m */; }; + D01777F61F1F961D0044446D /* TGPeerIdAdapter.h in Headers */ = {isa = PBXBuildFile; fileRef = D017778B1F1F961C0044446D /* TGPeerIdAdapter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01777F71F1F961D0044446D /* TGChannelBannedRights.h in Headers */ = {isa = PBXBuildFile; fileRef = D017778C1F1F961C0044446D /* TGChannelBannedRights.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01777F81F1F961D0044446D /* TGChannelBannedRights.m in Sources */ = {isa = PBXBuildFile; fileRef = D017778D1F1F961D0044446D /* TGChannelBannedRights.m */; }; + D01777F91F1F961D0044446D /* TGChannelAdminRights.h in Headers */ = {isa = PBXBuildFile; fileRef = D017778E1F1F961D0044446D /* TGChannelAdminRights.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01777FA1F1F961D0044446D /* TGChannelAdminRights.m in Sources */ = {isa = PBXBuildFile; fileRef = D017778F1F1F961D0044446D /* TGChannelAdminRights.m */; }; + D01777FB1F1F961D0044446D /* TGDatabaseMessageDraft.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777901F1F961D0044446D /* TGDatabaseMessageDraft.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01777FC1F1F961D0044446D /* TGDatabaseMessageDraft.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777911F1F961D0044446D /* TGDatabaseMessageDraft.m */; }; + D01777FD1F1F961D0044446D /* TGMessageGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777921F1F961D0044446D /* TGMessageGroup.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01777FE1F1F961D0044446D /* TGMessageGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777931F1F961D0044446D /* TGMessageGroup.m */; }; + D01777FF1F1F961D0044446D /* TGMessageHole.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777941F1F961D0044446D /* TGMessageHole.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778001F1F961D0044446D /* TGMessageHole.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777951F1F961D0044446D /* TGMessageHole.m */; }; + D01778011F1F961D0044446D /* TGMessageViewCountContentProperty.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777961F1F961D0044446D /* TGMessageViewCountContentProperty.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778021F1F961D0044446D /* TGMessageViewCountContentProperty.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777971F1F961D0044446D /* TGMessageViewCountContentProperty.m */; }; + D01778031F1F961D0044446D /* TGAuthorSignatureMediaAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777981F1F961D0044446D /* TGAuthorSignatureMediaAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778041F1F961D0044446D /* TGAuthorSignatureMediaAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777991F1F961D0044446D /* TGAuthorSignatureMediaAttachment.m */; }; + D01778051F1F961D0044446D /* TGWebDocument.h in Headers */ = {isa = PBXBuildFile; fileRef = D017779A1F1F961D0044446D /* TGWebDocument.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778061F1F961D0044446D /* TGWebDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = D017779B1F1F961D0044446D /* TGWebDocument.m */; }; + D01778071F1F961D0044446D /* TGInvoiceMediaAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = D017779C1F1F961D0044446D /* TGInvoiceMediaAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778081F1F961D0044446D /* TGInvoiceMediaAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = D017779D1F1F961D0044446D /* TGInvoiceMediaAttachment.m */; }; + D01778091F1F961D0044446D /* TGGameMediaAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = D017779E1F1F961D0044446D /* TGGameMediaAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D017780A1F1F961D0044446D /* TGGameMediaAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = D017779F1F1F961D0044446D /* TGGameMediaAttachment.m */; }; + D017780B1F1F961D0044446D /* TGViaUserAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777A01F1F961D0044446D /* TGViaUserAttachment.m */; }; + D017780C1F1F961D0044446D /* TGViaUserAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777A11F1F961D0044446D /* TGViaUserAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D017780D1F1F961D0044446D /* TGBotContextResultAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777A21F1F961D0044446D /* TGBotContextResultAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D017780E1F1F961D0044446D /* TGBotContextResultAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777A31F1F961D0044446D /* TGBotContextResultAttachment.m */; }; + D017780F1F1F961D0044446D /* TGMessageEntity.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777A41F1F961D0044446D /* TGMessageEntity.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778101F1F961D0044446D /* TGMessageEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777A51F1F961D0044446D /* TGMessageEntity.m */; }; + D01778111F1F961D0044446D /* TGMessageEntityBold.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777A61F1F961D0044446D /* TGMessageEntityBold.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778121F1F961D0044446D /* TGMessageEntityBold.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777A71F1F961D0044446D /* TGMessageEntityBold.m */; }; + D01778131F1F961D0044446D /* TGMessageEntityBotCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777A81F1F961D0044446D /* TGMessageEntityBotCommand.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778141F1F961D0044446D /* TGMessageEntityBotCommand.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777A91F1F961D0044446D /* TGMessageEntityBotCommand.m */; }; + D01778151F1F961D0044446D /* TGMessageEntityCode.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777AA1F1F961D0044446D /* TGMessageEntityCode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778161F1F961D0044446D /* TGMessageEntityCode.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777AB1F1F961D0044446D /* TGMessageEntityCode.m */; }; + D01778171F1F961D0044446D /* TGMessageEntityEmail.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777AC1F1F961D0044446D /* TGMessageEntityEmail.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778181F1F961D0044446D /* TGMessageEntityEmail.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777AD1F1F961D0044446D /* TGMessageEntityEmail.m */; }; + D01778191F1F961D0044446D /* TGMessageEntityHashtag.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777AE1F1F961D0044446D /* TGMessageEntityHashtag.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D017781A1F1F961D0044446D /* TGMessageEntityHashtag.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777AF1F1F961D0044446D /* TGMessageEntityHashtag.m */; }; + D017781B1F1F961D0044446D /* TGMessageEntityItalic.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777B01F1F961D0044446D /* TGMessageEntityItalic.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D017781C1F1F961D0044446D /* TGMessageEntityItalic.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777B11F1F961D0044446D /* TGMessageEntityItalic.m */; }; + D017781D1F1F961D0044446D /* TGMessageEntityMention.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777B21F1F961D0044446D /* TGMessageEntityMention.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D017781E1F1F961D0044446D /* TGMessageEntityMention.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777B31F1F961D0044446D /* TGMessageEntityMention.m */; }; + D017781F1F1F961D0044446D /* TGMessageEntityMentionName.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777B41F1F961D0044446D /* TGMessageEntityMentionName.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778201F1F961D0044446D /* TGMessageEntityMentionName.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777B51F1F961D0044446D /* TGMessageEntityMentionName.m */; }; + D01778211F1F961D0044446D /* TGMessageEntityPre.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777B61F1F961D0044446D /* TGMessageEntityPre.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778221F1F961D0044446D /* TGMessageEntityPre.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777B71F1F961D0044446D /* TGMessageEntityPre.m */; }; + D01778231F1F961D0044446D /* TGMessageEntityTextUrl.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777B81F1F961D0044446D /* TGMessageEntityTextUrl.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778241F1F961D0044446D /* TGMessageEntityTextUrl.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777B91F1F961D0044446D /* TGMessageEntityTextUrl.m */; }; + D01778251F1F961D0044446D /* TGMessageEntityUrl.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777BA1F1F961D0044446D /* TGMessageEntityUrl.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778261F1F961D0044446D /* TGMessageEntityUrl.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777BB1F1F961D0044446D /* TGMessageEntityUrl.m */; }; + D01778271F1F961D0044446D /* TGMessageEntitiesAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777BC1F1F961D0044446D /* TGMessageEntitiesAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778281F1F961D0044446D /* TGMessageEntitiesAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777BD1F1F961D0044446D /* TGMessageEntitiesAttachment.m */; }; + D01778291F1F961D0044446D /* TGBotReplyMarkup.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777BE1F1F961D0044446D /* TGBotReplyMarkup.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D017782A1F1F961D0044446D /* TGBotReplyMarkup.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777BF1F1F961D0044446D /* TGBotReplyMarkup.m */; }; + D017782B1F1F961D0044446D /* TGBotReplyMarkupButton.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777C01F1F961D0044446D /* TGBotReplyMarkupButton.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D017782C1F1F961D0044446D /* TGBotReplyMarkupButton.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777C11F1F961D0044446D /* TGBotReplyMarkupButton.m */; }; + D017782D1F1F961D0044446D /* TGBotReplyMarkupRow.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777C21F1F961D0044446D /* TGBotReplyMarkupRow.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D017782E1F1F961D0044446D /* TGBotReplyMarkupRow.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777C31F1F961D0044446D /* TGBotReplyMarkupRow.m */; }; + D017782F1F1F961D0044446D /* TGReplyMarkupAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777C41F1F961D0044446D /* TGReplyMarkupAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778301F1F961D0044446D /* TGReplyMarkupAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777C51F1F961D0044446D /* TGReplyMarkupAttachment.m */; }; + D01778311F1F961D0044446D /* TGInstantPage.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777C61F1F961D0044446D /* TGInstantPage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778321F1F961D0044446D /* TGInstantPage.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777C71F1F961D0044446D /* TGInstantPage.m */; }; + D01778331F1F961D0044446D /* TGWebPageMediaAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777C81F1F961D0044446D /* TGWebPageMediaAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778341F1F961D0044446D /* TGWebPageMediaAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777C91F1F961D0044446D /* TGWebPageMediaAttachment.m */; }; + D01778351F1F961D0044446D /* TGReplyMessageMediaAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777CA1F1F961D0044446D /* TGReplyMessageMediaAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778361F1F961D0044446D /* TGReplyMessageMediaAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777CB1F1F961D0044446D /* TGReplyMessageMediaAttachment.m */; }; + D01778371F1F961D0044446D /* TGAudioMediaAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777CC1F1F961D0044446D /* TGAudioMediaAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778381F1F961D0044446D /* TGAudioMediaAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777CD1F1F961D0044446D /* TGAudioMediaAttachment.m */; }; + D01778391F1F961D0044446D /* TGAudioWaveform.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777CE1F1F961D0044446D /* TGAudioWaveform.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D017783A1F1F961D0044446D /* TGAudioWaveform.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777CF1F1F961D0044446D /* TGAudioWaveform.m */; }; + D017783B1F1F961D0044446D /* TGStickerPackReference.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777D01F1F961D0044446D /* TGStickerPackReference.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D017783C1F1F961D0044446D /* TGStickerPackReference.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777D11F1F961D0044446D /* TGStickerPackReference.m */; }; + D017783D1F1F961D0044446D /* TGDocumentAttributeFilename.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777D21F1F961D0044446D /* TGDocumentAttributeFilename.m */; }; + D017783E1F1F961D0044446D /* TGDocumentAttributeImageSize.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777D31F1F961D0044446D /* TGDocumentAttributeImageSize.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D017783F1F1F961D0044446D /* TGDocumentAttributeImageSize.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777D41F1F961D0044446D /* TGDocumentAttributeImageSize.m */; }; + D01778401F1F961D0044446D /* TGDocumentAttributeSticker.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777D51F1F961D0044446D /* TGDocumentAttributeSticker.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778411F1F961D0044446D /* TGDocumentAttributeSticker.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777D61F1F961D0044446D /* TGDocumentAttributeSticker.m */; }; + D01778421F1F961D0044446D /* TGDocumentAttributeVideo.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777D71F1F961D0044446D /* TGDocumentAttributeVideo.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778431F1F961D0044446D /* TGDocumentAttributeVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777D81F1F961D0044446D /* TGDocumentAttributeVideo.m */; }; + D01778441F1F961D0044446D /* TGDocumentAttributeAnimated.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777D91F1F961D0044446D /* TGDocumentAttributeAnimated.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778451F1F961D0044446D /* TGDocumentAttributeAnimated.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777DA1F1F961D0044446D /* TGDocumentAttributeAnimated.m */; }; + D01778461F1F961D0044446D /* TGDocumentAttributeAudio.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777DB1F1F961D0044446D /* TGDocumentAttributeAudio.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778471F1F961D0044446D /* TGDocumentAttributeAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777DC1F1F961D0044446D /* TGDocumentAttributeAudio.m */; }; + D01778481F1F961D0044446D /* TGDocumentAttributeFilename.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777DD1F1F961D0044446D /* TGDocumentAttributeFilename.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778491F1F961D0044446D /* TGDocumentMediaAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777DE1F1F961D0044446D /* TGDocumentMediaAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D017784A1F1F961D0044446D /* TGDocumentMediaAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777DF1F1F961D0044446D /* TGDocumentMediaAttachment.m */; }; + D017784B1F1F961D0044446D /* TGUnsupportedMediaAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777E01F1F961D0044446D /* TGUnsupportedMediaAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D017784C1F1F961D0044446D /* TGUnsupportedMediaAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777E11F1F961D0044446D /* TGUnsupportedMediaAttachment.m */; }; + D017784D1F1F961D0044446D /* TGForwardedMessageMediaAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777E21F1F961D0044446D /* TGForwardedMessageMediaAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D017784E1F1F961D0044446D /* TGForwardedMessageMediaAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777E31F1F961D0044446D /* TGForwardedMessageMediaAttachment.m */; }; + D017784F1F1F961D0044446D /* TGContactMediaAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777E41F1F961D0044446D /* TGContactMediaAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778501F1F961D0044446D /* TGContactMediaAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777E51F1F961D0044446D /* TGContactMediaAttachment.m */; }; + D01778511F1F961D0044446D /* TGVideoInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777E61F1F961D0044446D /* TGVideoInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778521F1F961D0044446D /* TGVideoInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = D01777E71F1F961D0044446D /* TGVideoInfo.mm */; }; + D01778531F1F961D0044446D /* TGVideoMediaAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777E81F1F961D0044446D /* TGVideoMediaAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778541F1F961D0044446D /* TGVideoMediaAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777E91F1F961D0044446D /* TGVideoMediaAttachment.m */; }; + D01778551F1F961D0044446D /* TGLocalMessageMetaMediaAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777EA1F1F961D0044446D /* TGLocalMessageMetaMediaAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778561F1F961D0044446D /* TGLocalMessageMetaMediaAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777EB1F1F961D0044446D /* TGLocalMessageMetaMediaAttachment.m */; }; + D01778571F1F961D0044446D /* TGLocationMediaAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777EC1F1F961D0044446D /* TGLocationMediaAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D01778581F1F961D0044446D /* TGLocationMediaAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777ED1F1F961D0044446D /* TGLocationMediaAttachment.m */; }; + D01778591F1F961D0044446D /* TGImageMediaAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777EE1F1F961D0044446D /* TGImageMediaAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D017785A1F1F961D0044446D /* TGImageMediaAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777EF1F1F961D0044446D /* TGImageMediaAttachment.m */; }; + D017785B1F1F961D0044446D /* TGMediaAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777F01F1F961D0044446D /* TGMediaAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D017785C1F1F961D0044446D /* TGMediaAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777F11F1F961D0044446D /* TGMediaAttachment.m */; }; + D017785D1F1F961D0044446D /* TGActionMediaAttachment.h in Headers */ = {isa = PBXBuildFile; fileRef = D01777F21F1F961D0044446D /* TGActionMediaAttachment.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D017785E1F1F961D0044446D /* TGActionMediaAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = D01777F31F1F961D0044446D /* TGActionMediaAttachment.m */; }; + D01778691F1F99180044446D /* NSInputStream+TL.h in Headers */ = {isa = PBXBuildFile; fileRef = D01778671F1F99180044446D /* NSInputStream+TL.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D017786A1F1F99180044446D /* NSInputStream+TL.m in Sources */ = {isa = PBXBuildFile; fileRef = D01778681F1F99180044446D /* NSInputStream+TL.m */; }; + D017789D1F1FC99A0044446D /* LegacyComponentsInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = D017789B1F1FC99A0044446D /* LegacyComponentsInternal.h */; }; + D017789E1F1FC99A0044446D /* LegacyComponentsInternal.m in Sources */ = {isa = PBXBuildFile; fileRef = D017789C1F1FC99A0044446D /* LegacyComponentsInternal.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ D01777271F1F8F100044446D /* LegacyComponents.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LegacyComponents.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D017772A1F1F8F100044446D /* LegacyComponents.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LegacyComponents.h; sourceTree = ""; }; D017772B1F1F8F100044446D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D01777381F1F8FE60044446D /* LegacyComponentsGlobals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LegacyComponentsGlobals.h; sourceTree = ""; }; + D017773A1F1F8FE60044446D /* PSCoding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSCoding.h; sourceTree = ""; }; + D017773B1F1F8FE60044446D /* PSData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSData.h; sourceTree = ""; }; + D017773C1F1F8FE60044446D /* PSKeyValueCoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSKeyValueCoder.h; sourceTree = ""; }; + D017773D1F1F8FE60044446D /* PSKeyValueCoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSKeyValueCoder.m; sourceTree = ""; }; + D017773E1F1F8FE60044446D /* PSKeyValueDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSKeyValueDecoder.h; sourceTree = ""; }; + D017773F1F1F8FE60044446D /* PSKeyValueDecoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSKeyValueDecoder.m; sourceTree = ""; }; + D01777401F1F8FE60044446D /* PSKeyValueEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSKeyValueEncoder.h; sourceTree = ""; }; + D01777411F1F8FE60044446D /* PSKeyValueEncoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PSKeyValueEncoder.m; sourceTree = ""; }; + D01777421F1F8FE60044446D /* PSKeyValueReader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSKeyValueReader.h; sourceTree = ""; }; + D01777431F1F8FE60044446D /* PSKeyValueStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSKeyValueStore.h; sourceTree = ""; }; + D01777441F1F8FE60044446D /* PSKeyValueWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PSKeyValueWriter.h; sourceTree = ""; }; + D01777451F1F8FE60044446D /* TGBotComandInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGBotComandInfo.h; sourceTree = ""; }; + D01777461F1F8FE60044446D /* TGBotComandInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGBotComandInfo.m; sourceTree = ""; }; + D01777471F1F8FE60044446D /* TGBotInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGBotInfo.h; sourceTree = ""; }; + D01777481F1F8FE60044446D /* TGBotInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGBotInfo.m; sourceTree = ""; }; + D01777491F1F8FE60044446D /* TGLocalization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLocalization.h; sourceTree = ""; }; + D017774A1F1F8FE60044446D /* TGLocalization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLocalization.m; sourceTree = ""; }; + D017774B1F1F8FE60044446D /* TGPluralization.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGPluralization.h; sourceTree = ""; }; + D017774C1F1F8FE60044446D /* TGPluralization.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGPluralization.m; sourceTree = ""; }; + D017774D1F1F8FE60044446D /* TGStringUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGStringUtils.h; sourceTree = ""; }; + D017774E1F1F8FE60044446D /* TGStringUtils.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TGStringUtils.mm; sourceTree = ""; }; + D017774F1F1F8FE60044446D /* TGUser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGUser.h; sourceTree = ""; }; + D01777501F1F8FE60044446D /* TGUser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGUser.m; sourceTree = ""; }; + D017776A1F1F909E0044446D /* LegacyComponentsGlobals.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LegacyComponentsGlobals.m; sourceTree = ""; }; + D01777701F1F92420044446D /* TGPhoneUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGPhoneUtils.h; sourceTree = ""; }; + D01777711F1F92420044446D /* TGPhoneUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGPhoneUtils.m; sourceTree = ""; }; + D01777741F1F92570044446D /* RMPhoneFormat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMPhoneFormat.h; sourceTree = ""; }; + D01777751F1F92570044446D /* RMPhoneFormat.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMPhoneFormat.m; sourceTree = ""; }; + D01777781F1F927A0044446D /* NSObject+TGLock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+TGLock.h"; sourceTree = ""; }; + D01777791F1F927A0044446D /* NSObject+TGLock.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+TGLock.m"; sourceTree = ""; }; + D017777C1F1F930B0044446D /* TGConversation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGConversation.h; sourceTree = ""; }; + D017777D1F1F930B0044446D /* TGConversation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGConversation.m; sourceTree = ""; }; + D01777801F1F93250044446D /* TGImageInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGImageInfo.h; sourceTree = ""; }; + D01777811F1F93250044446D /* TGImageInfo.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TGImageInfo.mm; sourceTree = ""; }; + D01777851F1F93550044446D /* TGMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGMessage.h; sourceTree = ""; }; + D01777861F1F93550044446D /* TGMessage.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TGMessage.mm; sourceTree = ""; }; + D01777891F1F961C0044446D /* TGTextCheckingResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGTextCheckingResult.h; sourceTree = ""; }; + D017778A1F1F961C0044446D /* TGTextCheckingResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGTextCheckingResult.m; sourceTree = ""; }; + D017778B1F1F961C0044446D /* TGPeerIdAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGPeerIdAdapter.h; sourceTree = ""; }; + D017778C1F1F961C0044446D /* TGChannelBannedRights.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGChannelBannedRights.h; sourceTree = ""; }; + D017778D1F1F961D0044446D /* TGChannelBannedRights.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGChannelBannedRights.m; sourceTree = ""; }; + D017778E1F1F961D0044446D /* TGChannelAdminRights.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGChannelAdminRights.h; sourceTree = ""; }; + D017778F1F1F961D0044446D /* TGChannelAdminRights.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGChannelAdminRights.m; sourceTree = ""; }; + D01777901F1F961D0044446D /* TGDatabaseMessageDraft.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGDatabaseMessageDraft.h; sourceTree = ""; }; + D01777911F1F961D0044446D /* TGDatabaseMessageDraft.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGDatabaseMessageDraft.m; sourceTree = ""; }; + D01777921F1F961D0044446D /* TGMessageGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGMessageGroup.h; sourceTree = ""; }; + D01777931F1F961D0044446D /* TGMessageGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGMessageGroup.m; sourceTree = ""; }; + D01777941F1F961D0044446D /* TGMessageHole.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGMessageHole.h; sourceTree = ""; }; + D01777951F1F961D0044446D /* TGMessageHole.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGMessageHole.m; sourceTree = ""; }; + D01777961F1F961D0044446D /* TGMessageViewCountContentProperty.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGMessageViewCountContentProperty.h; sourceTree = ""; }; + D01777971F1F961D0044446D /* TGMessageViewCountContentProperty.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGMessageViewCountContentProperty.m; sourceTree = ""; }; + D01777981F1F961D0044446D /* TGAuthorSignatureMediaAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGAuthorSignatureMediaAttachment.h; sourceTree = ""; }; + D01777991F1F961D0044446D /* TGAuthorSignatureMediaAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGAuthorSignatureMediaAttachment.m; sourceTree = ""; }; + D017779A1F1F961D0044446D /* TGWebDocument.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGWebDocument.h; sourceTree = ""; }; + D017779B1F1F961D0044446D /* TGWebDocument.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGWebDocument.m; sourceTree = ""; }; + D017779C1F1F961D0044446D /* TGInvoiceMediaAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGInvoiceMediaAttachment.h; sourceTree = ""; }; + D017779D1F1F961D0044446D /* TGInvoiceMediaAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGInvoiceMediaAttachment.m; sourceTree = ""; }; + D017779E1F1F961D0044446D /* TGGameMediaAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGGameMediaAttachment.h; sourceTree = ""; }; + D017779F1F1F961D0044446D /* TGGameMediaAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGGameMediaAttachment.m; sourceTree = ""; }; + D01777A01F1F961D0044446D /* TGViaUserAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGViaUserAttachment.m; sourceTree = ""; }; + D01777A11F1F961D0044446D /* TGViaUserAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGViaUserAttachment.h; sourceTree = ""; }; + D01777A21F1F961D0044446D /* TGBotContextResultAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGBotContextResultAttachment.h; sourceTree = ""; }; + D01777A31F1F961D0044446D /* TGBotContextResultAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGBotContextResultAttachment.m; sourceTree = ""; }; + D01777A41F1F961D0044446D /* TGMessageEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGMessageEntity.h; sourceTree = ""; }; + D01777A51F1F961D0044446D /* TGMessageEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGMessageEntity.m; sourceTree = ""; }; + D01777A61F1F961D0044446D /* TGMessageEntityBold.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGMessageEntityBold.h; sourceTree = ""; }; + D01777A71F1F961D0044446D /* TGMessageEntityBold.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGMessageEntityBold.m; sourceTree = ""; }; + D01777A81F1F961D0044446D /* TGMessageEntityBotCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGMessageEntityBotCommand.h; sourceTree = ""; }; + D01777A91F1F961D0044446D /* TGMessageEntityBotCommand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGMessageEntityBotCommand.m; sourceTree = ""; }; + D01777AA1F1F961D0044446D /* TGMessageEntityCode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGMessageEntityCode.h; sourceTree = ""; }; + D01777AB1F1F961D0044446D /* TGMessageEntityCode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGMessageEntityCode.m; sourceTree = ""; }; + D01777AC1F1F961D0044446D /* TGMessageEntityEmail.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGMessageEntityEmail.h; sourceTree = ""; }; + D01777AD1F1F961D0044446D /* TGMessageEntityEmail.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGMessageEntityEmail.m; sourceTree = ""; }; + D01777AE1F1F961D0044446D /* TGMessageEntityHashtag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGMessageEntityHashtag.h; sourceTree = ""; }; + D01777AF1F1F961D0044446D /* TGMessageEntityHashtag.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGMessageEntityHashtag.m; sourceTree = ""; }; + D01777B01F1F961D0044446D /* TGMessageEntityItalic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGMessageEntityItalic.h; sourceTree = ""; }; + D01777B11F1F961D0044446D /* TGMessageEntityItalic.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGMessageEntityItalic.m; sourceTree = ""; }; + D01777B21F1F961D0044446D /* TGMessageEntityMention.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGMessageEntityMention.h; sourceTree = ""; }; + D01777B31F1F961D0044446D /* TGMessageEntityMention.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGMessageEntityMention.m; sourceTree = ""; }; + D01777B41F1F961D0044446D /* TGMessageEntityMentionName.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGMessageEntityMentionName.h; sourceTree = ""; }; + D01777B51F1F961D0044446D /* TGMessageEntityMentionName.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGMessageEntityMentionName.m; sourceTree = ""; }; + D01777B61F1F961D0044446D /* TGMessageEntityPre.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGMessageEntityPre.h; sourceTree = ""; }; + D01777B71F1F961D0044446D /* TGMessageEntityPre.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGMessageEntityPre.m; sourceTree = ""; }; + D01777B81F1F961D0044446D /* TGMessageEntityTextUrl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGMessageEntityTextUrl.h; sourceTree = ""; }; + D01777B91F1F961D0044446D /* TGMessageEntityTextUrl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGMessageEntityTextUrl.m; sourceTree = ""; }; + D01777BA1F1F961D0044446D /* TGMessageEntityUrl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGMessageEntityUrl.h; sourceTree = ""; }; + D01777BB1F1F961D0044446D /* TGMessageEntityUrl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGMessageEntityUrl.m; sourceTree = ""; }; + D01777BC1F1F961D0044446D /* TGMessageEntitiesAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGMessageEntitiesAttachment.h; sourceTree = ""; }; + D01777BD1F1F961D0044446D /* TGMessageEntitiesAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGMessageEntitiesAttachment.m; sourceTree = ""; }; + D01777BE1F1F961D0044446D /* TGBotReplyMarkup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGBotReplyMarkup.h; sourceTree = ""; }; + D01777BF1F1F961D0044446D /* TGBotReplyMarkup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGBotReplyMarkup.m; sourceTree = ""; }; + D01777C01F1F961D0044446D /* TGBotReplyMarkupButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGBotReplyMarkupButton.h; sourceTree = ""; }; + D01777C11F1F961D0044446D /* TGBotReplyMarkupButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGBotReplyMarkupButton.m; sourceTree = ""; }; + D01777C21F1F961D0044446D /* TGBotReplyMarkupRow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGBotReplyMarkupRow.h; sourceTree = ""; }; + D01777C31F1F961D0044446D /* TGBotReplyMarkupRow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGBotReplyMarkupRow.m; sourceTree = ""; }; + D01777C41F1F961D0044446D /* TGReplyMarkupAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGReplyMarkupAttachment.h; sourceTree = ""; }; + D01777C51F1F961D0044446D /* TGReplyMarkupAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGReplyMarkupAttachment.m; sourceTree = ""; }; + D01777C61F1F961D0044446D /* TGInstantPage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGInstantPage.h; sourceTree = ""; }; + D01777C71F1F961D0044446D /* TGInstantPage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGInstantPage.m; sourceTree = ""; }; + D01777C81F1F961D0044446D /* TGWebPageMediaAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGWebPageMediaAttachment.h; sourceTree = ""; }; + D01777C91F1F961D0044446D /* TGWebPageMediaAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGWebPageMediaAttachment.m; sourceTree = ""; }; + D01777CA1F1F961D0044446D /* TGReplyMessageMediaAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGReplyMessageMediaAttachment.h; sourceTree = ""; }; + D01777CB1F1F961D0044446D /* TGReplyMessageMediaAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGReplyMessageMediaAttachment.m; sourceTree = ""; }; + D01777CC1F1F961D0044446D /* TGAudioMediaAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGAudioMediaAttachment.h; sourceTree = ""; }; + D01777CD1F1F961D0044446D /* TGAudioMediaAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGAudioMediaAttachment.m; sourceTree = ""; }; + D01777CE1F1F961D0044446D /* TGAudioWaveform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGAudioWaveform.h; sourceTree = ""; }; + D01777CF1F1F961D0044446D /* TGAudioWaveform.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGAudioWaveform.m; sourceTree = ""; }; + D01777D01F1F961D0044446D /* TGStickerPackReference.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGStickerPackReference.h; sourceTree = ""; }; + D01777D11F1F961D0044446D /* TGStickerPackReference.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGStickerPackReference.m; sourceTree = ""; }; + D01777D21F1F961D0044446D /* TGDocumentAttributeFilename.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGDocumentAttributeFilename.m; sourceTree = ""; }; + D01777D31F1F961D0044446D /* TGDocumentAttributeImageSize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGDocumentAttributeImageSize.h; sourceTree = ""; }; + D01777D41F1F961D0044446D /* TGDocumentAttributeImageSize.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGDocumentAttributeImageSize.m; sourceTree = ""; }; + D01777D51F1F961D0044446D /* TGDocumentAttributeSticker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGDocumentAttributeSticker.h; sourceTree = ""; }; + D01777D61F1F961D0044446D /* TGDocumentAttributeSticker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGDocumentAttributeSticker.m; sourceTree = ""; }; + D01777D71F1F961D0044446D /* TGDocumentAttributeVideo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGDocumentAttributeVideo.h; sourceTree = ""; }; + D01777D81F1F961D0044446D /* TGDocumentAttributeVideo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGDocumentAttributeVideo.m; sourceTree = ""; }; + D01777D91F1F961D0044446D /* TGDocumentAttributeAnimated.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGDocumentAttributeAnimated.h; sourceTree = ""; }; + D01777DA1F1F961D0044446D /* TGDocumentAttributeAnimated.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGDocumentAttributeAnimated.m; sourceTree = ""; }; + D01777DB1F1F961D0044446D /* TGDocumentAttributeAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGDocumentAttributeAudio.h; sourceTree = ""; }; + D01777DC1F1F961D0044446D /* TGDocumentAttributeAudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGDocumentAttributeAudio.m; sourceTree = ""; }; + D01777DD1F1F961D0044446D /* TGDocumentAttributeFilename.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGDocumentAttributeFilename.h; sourceTree = ""; }; + D01777DE1F1F961D0044446D /* TGDocumentMediaAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGDocumentMediaAttachment.h; sourceTree = ""; }; + D01777DF1F1F961D0044446D /* TGDocumentMediaAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGDocumentMediaAttachment.m; sourceTree = ""; }; + D01777E01F1F961D0044446D /* TGUnsupportedMediaAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGUnsupportedMediaAttachment.h; sourceTree = ""; }; + D01777E11F1F961D0044446D /* TGUnsupportedMediaAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGUnsupportedMediaAttachment.m; sourceTree = ""; }; + D01777E21F1F961D0044446D /* TGForwardedMessageMediaAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGForwardedMessageMediaAttachment.h; sourceTree = ""; }; + D01777E31F1F961D0044446D /* TGForwardedMessageMediaAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGForwardedMessageMediaAttachment.m; sourceTree = ""; }; + D01777E41F1F961D0044446D /* TGContactMediaAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGContactMediaAttachment.h; sourceTree = ""; }; + D01777E51F1F961D0044446D /* TGContactMediaAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGContactMediaAttachment.m; sourceTree = ""; }; + D01777E61F1F961D0044446D /* TGVideoInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGVideoInfo.h; sourceTree = ""; }; + D01777E71F1F961D0044446D /* TGVideoInfo.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TGVideoInfo.mm; sourceTree = ""; }; + D01777E81F1F961D0044446D /* TGVideoMediaAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGVideoMediaAttachment.h; sourceTree = ""; }; + D01777E91F1F961D0044446D /* TGVideoMediaAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGVideoMediaAttachment.m; sourceTree = ""; }; + D01777EA1F1F961D0044446D /* TGLocalMessageMetaMediaAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLocalMessageMetaMediaAttachment.h; sourceTree = ""; }; + D01777EB1F1F961D0044446D /* TGLocalMessageMetaMediaAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLocalMessageMetaMediaAttachment.m; sourceTree = ""; }; + D01777EC1F1F961D0044446D /* TGLocationMediaAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLocationMediaAttachment.h; sourceTree = ""; }; + D01777ED1F1F961D0044446D /* TGLocationMediaAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLocationMediaAttachment.m; sourceTree = ""; }; + D01777EE1F1F961D0044446D /* TGImageMediaAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGImageMediaAttachment.h; sourceTree = ""; }; + D01777EF1F1F961D0044446D /* TGImageMediaAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGImageMediaAttachment.m; sourceTree = ""; }; + D01777F01F1F961D0044446D /* TGMediaAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGMediaAttachment.h; sourceTree = ""; }; + D01777F11F1F961D0044446D /* TGMediaAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGMediaAttachment.m; sourceTree = ""; }; + D01777F21F1F961D0044446D /* TGActionMediaAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGActionMediaAttachment.h; sourceTree = ""; }; + D01777F31F1F961D0044446D /* TGActionMediaAttachment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGActionMediaAttachment.m; sourceTree = ""; }; + D01778671F1F99180044446D /* NSInputStream+TL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSInputStream+TL.h"; sourceTree = ""; }; + D01778681F1F99180044446D /* NSInputStream+TL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSInputStream+TL.m"; sourceTree = ""; }; + D017789B1F1FC99A0044446D /* LegacyComponentsInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LegacyComponentsInternal.h; sourceTree = ""; }; + D017789C1F1FC99A0044446D /* LegacyComponentsInternal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LegacyComponentsInternal.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -46,12 +342,192 @@ D01777291F1F8F100044446D /* LegacyComponents */ = { isa = PBXGroup; children = ( + D017776F1F1F91B00044446D /* Utils */, + D017776D1F1F91850044446D /* PS Coding */, + D017776C1F1F91780044446D /* Peers */, + D01777841F1F93430044446D /* Message */, D017772A1F1F8F100044446D /* LegacyComponents.h */, D017772B1F1F8F100044446D /* Info.plist */, ); path = LegacyComponents; sourceTree = ""; }; + D017776C1F1F91780044446D /* Peers */ = { + isa = PBXGroup; + children = ( + D017778B1F1F961C0044446D /* TGPeerIdAdapter.h */, + D01777471F1F8FE60044446D /* TGBotInfo.h */, + D01777481F1F8FE60044446D /* TGBotInfo.m */, + D017774F1F1F8FE60044446D /* TGUser.h */, + D01777501F1F8FE60044446D /* TGUser.m */, + D017777C1F1F930B0044446D /* TGConversation.h */, + D017777D1F1F930B0044446D /* TGConversation.m */, + ); + name = Peers; + sourceTree = ""; + }; + D017776D1F1F91850044446D /* PS Coding */ = { + isa = PBXGroup; + children = ( + D017773A1F1F8FE60044446D /* PSCoding.h */, + D017773B1F1F8FE60044446D /* PSData.h */, + D017773C1F1F8FE60044446D /* PSKeyValueCoder.h */, + D017773D1F1F8FE60044446D /* PSKeyValueCoder.m */, + D017773E1F1F8FE60044446D /* PSKeyValueDecoder.h */, + D017773F1F1F8FE60044446D /* PSKeyValueDecoder.m */, + D01777401F1F8FE60044446D /* PSKeyValueEncoder.h */, + D01777411F1F8FE60044446D /* PSKeyValueEncoder.m */, + D01777421F1F8FE60044446D /* PSKeyValueReader.h */, + D01777431F1F8FE60044446D /* PSKeyValueStore.h */, + D01777441F1F8FE60044446D /* PSKeyValueWriter.h */, + ); + name = "PS Coding"; + sourceTree = ""; + }; + D017776F1F1F91B00044446D /* Utils */ = { + isa = PBXGroup; + children = ( + D01777381F1F8FE60044446D /* LegacyComponentsGlobals.h */, + D017776A1F1F909E0044446D /* LegacyComponentsGlobals.m */, + D017789B1F1FC99A0044446D /* LegacyComponentsInternal.h */, + D017789C1F1FC99A0044446D /* LegacyComponentsInternal.m */, + D01777491F1F8FE60044446D /* TGLocalization.h */, + D017774A1F1F8FE60044446D /* TGLocalization.m */, + D017774B1F1F8FE60044446D /* TGPluralization.h */, + D017774C1F1F8FE60044446D /* TGPluralization.m */, + D017774D1F1F8FE60044446D /* TGStringUtils.h */, + D017774E1F1F8FE60044446D /* TGStringUtils.mm */, + D01777701F1F92420044446D /* TGPhoneUtils.h */, + D01777711F1F92420044446D /* TGPhoneUtils.m */, + D01777781F1F927A0044446D /* NSObject+TGLock.h */, + D01777791F1F927A0044446D /* NSObject+TGLock.m */, + D01777741F1F92570044446D /* RMPhoneFormat.h */, + D01777751F1F92570044446D /* RMPhoneFormat.m */, + D01778671F1F99180044446D /* NSInputStream+TL.h */, + D01778681F1F99180044446D /* NSInputStream+TL.m */, + ); + name = Utils; + sourceTree = ""; + }; + D01777841F1F93430044446D /* Message */ = { + isa = PBXGroup; + children = ( + D01777891F1F961C0044446D /* TGTextCheckingResult.h */, + D017778A1F1F961C0044446D /* TGTextCheckingResult.m */, + D017778C1F1F961C0044446D /* TGChannelBannedRights.h */, + D017778D1F1F961D0044446D /* TGChannelBannedRights.m */, + D017778E1F1F961D0044446D /* TGChannelAdminRights.h */, + D017778F1F1F961D0044446D /* TGChannelAdminRights.m */, + D01777901F1F961D0044446D /* TGDatabaseMessageDraft.h */, + D01777911F1F961D0044446D /* TGDatabaseMessageDraft.m */, + D01777451F1F8FE60044446D /* TGBotComandInfo.h */, + D01777461F1F8FE60044446D /* TGBotComandInfo.m */, + D01777921F1F961D0044446D /* TGMessageGroup.h */, + D01777931F1F961D0044446D /* TGMessageGroup.m */, + D01777941F1F961D0044446D /* TGMessageHole.h */, + D01777951F1F961D0044446D /* TGMessageHole.m */, + D01777961F1F961D0044446D /* TGMessageViewCountContentProperty.h */, + D01777971F1F961D0044446D /* TGMessageViewCountContentProperty.m */, + D01777981F1F961D0044446D /* TGAuthorSignatureMediaAttachment.h */, + D01777991F1F961D0044446D /* TGAuthorSignatureMediaAttachment.m */, + D017779A1F1F961D0044446D /* TGWebDocument.h */, + D017779B1F1F961D0044446D /* TGWebDocument.m */, + D017779C1F1F961D0044446D /* TGInvoiceMediaAttachment.h */, + D017779D1F1F961D0044446D /* TGInvoiceMediaAttachment.m */, + D017779E1F1F961D0044446D /* TGGameMediaAttachment.h */, + D017779F1F1F961D0044446D /* TGGameMediaAttachment.m */, + D01777A11F1F961D0044446D /* TGViaUserAttachment.h */, + D01777A01F1F961D0044446D /* TGViaUserAttachment.m */, + D01777A21F1F961D0044446D /* TGBotContextResultAttachment.h */, + D01777A31F1F961D0044446D /* TGBotContextResultAttachment.m */, + D01777A41F1F961D0044446D /* TGMessageEntity.h */, + D01777A51F1F961D0044446D /* TGMessageEntity.m */, + D01777A61F1F961D0044446D /* TGMessageEntityBold.h */, + D01777A71F1F961D0044446D /* TGMessageEntityBold.m */, + D01777A81F1F961D0044446D /* TGMessageEntityBotCommand.h */, + D01777A91F1F961D0044446D /* TGMessageEntityBotCommand.m */, + D01777AA1F1F961D0044446D /* TGMessageEntityCode.h */, + D01777AB1F1F961D0044446D /* TGMessageEntityCode.m */, + D01777AC1F1F961D0044446D /* TGMessageEntityEmail.h */, + D01777AD1F1F961D0044446D /* TGMessageEntityEmail.m */, + D01777AE1F1F961D0044446D /* TGMessageEntityHashtag.h */, + D01777AF1F1F961D0044446D /* TGMessageEntityHashtag.m */, + D01777B01F1F961D0044446D /* TGMessageEntityItalic.h */, + D01777B11F1F961D0044446D /* TGMessageEntityItalic.m */, + D01777B21F1F961D0044446D /* TGMessageEntityMention.h */, + D01777B31F1F961D0044446D /* TGMessageEntityMention.m */, + D01777B41F1F961D0044446D /* TGMessageEntityMentionName.h */, + D01777B51F1F961D0044446D /* TGMessageEntityMentionName.m */, + D01777B61F1F961D0044446D /* TGMessageEntityPre.h */, + D01777B71F1F961D0044446D /* TGMessageEntityPre.m */, + D01777B81F1F961D0044446D /* TGMessageEntityTextUrl.h */, + D01777B91F1F961D0044446D /* TGMessageEntityTextUrl.m */, + D01777BA1F1F961D0044446D /* TGMessageEntityUrl.h */, + D01777BB1F1F961D0044446D /* TGMessageEntityUrl.m */, + D01777BC1F1F961D0044446D /* TGMessageEntitiesAttachment.h */, + D01777BD1F1F961D0044446D /* TGMessageEntitiesAttachment.m */, + D01777BE1F1F961D0044446D /* TGBotReplyMarkup.h */, + D01777BF1F1F961D0044446D /* TGBotReplyMarkup.m */, + D01777C01F1F961D0044446D /* TGBotReplyMarkupButton.h */, + D01777C11F1F961D0044446D /* TGBotReplyMarkupButton.m */, + D01777C21F1F961D0044446D /* TGBotReplyMarkupRow.h */, + D01777C31F1F961D0044446D /* TGBotReplyMarkupRow.m */, + D01777C41F1F961D0044446D /* TGReplyMarkupAttachment.h */, + D01777C51F1F961D0044446D /* TGReplyMarkupAttachment.m */, + D01777C61F1F961D0044446D /* TGInstantPage.h */, + D01777C71F1F961D0044446D /* TGInstantPage.m */, + D01777C81F1F961D0044446D /* TGWebPageMediaAttachment.h */, + D01777C91F1F961D0044446D /* TGWebPageMediaAttachment.m */, + D01777CA1F1F961D0044446D /* TGReplyMessageMediaAttachment.h */, + D01777CB1F1F961D0044446D /* TGReplyMessageMediaAttachment.m */, + D01777CC1F1F961D0044446D /* TGAudioMediaAttachment.h */, + D01777CD1F1F961D0044446D /* TGAudioMediaAttachment.m */, + D01777CE1F1F961D0044446D /* TGAudioWaveform.h */, + D01777CF1F1F961D0044446D /* TGAudioWaveform.m */, + D01777D01F1F961D0044446D /* TGStickerPackReference.h */, + D01777D11F1F961D0044446D /* TGStickerPackReference.m */, + D01777DD1F1F961D0044446D /* TGDocumentAttributeFilename.h */, + D01777D21F1F961D0044446D /* TGDocumentAttributeFilename.m */, + D01777D31F1F961D0044446D /* TGDocumentAttributeImageSize.h */, + D01777D41F1F961D0044446D /* TGDocumentAttributeImageSize.m */, + D01777D51F1F961D0044446D /* TGDocumentAttributeSticker.h */, + D01777D61F1F961D0044446D /* TGDocumentAttributeSticker.m */, + D01777D71F1F961D0044446D /* TGDocumentAttributeVideo.h */, + D01777D81F1F961D0044446D /* TGDocumentAttributeVideo.m */, + D01777D91F1F961D0044446D /* TGDocumentAttributeAnimated.h */, + D01777DA1F1F961D0044446D /* TGDocumentAttributeAnimated.m */, + D01777DB1F1F961D0044446D /* TGDocumentAttributeAudio.h */, + D01777DC1F1F961D0044446D /* TGDocumentAttributeAudio.m */, + D01777DE1F1F961D0044446D /* TGDocumentMediaAttachment.h */, + D01777DF1F1F961D0044446D /* TGDocumentMediaAttachment.m */, + D01777E01F1F961D0044446D /* TGUnsupportedMediaAttachment.h */, + D01777E11F1F961D0044446D /* TGUnsupportedMediaAttachment.m */, + D01777E21F1F961D0044446D /* TGForwardedMessageMediaAttachment.h */, + D01777E31F1F961D0044446D /* TGForwardedMessageMediaAttachment.m */, + D01777E41F1F961D0044446D /* TGContactMediaAttachment.h */, + D01777E51F1F961D0044446D /* TGContactMediaAttachment.m */, + D01777E61F1F961D0044446D /* TGVideoInfo.h */, + D01777E71F1F961D0044446D /* TGVideoInfo.mm */, + D01777E81F1F961D0044446D /* TGVideoMediaAttachment.h */, + D01777E91F1F961D0044446D /* TGVideoMediaAttachment.m */, + D01777EA1F1F961D0044446D /* TGLocalMessageMetaMediaAttachment.h */, + D01777EB1F1F961D0044446D /* TGLocalMessageMetaMediaAttachment.m */, + D01777EC1F1F961D0044446D /* TGLocationMediaAttachment.h */, + D01777ED1F1F961D0044446D /* TGLocationMediaAttachment.m */, + D01777EE1F1F961D0044446D /* TGImageMediaAttachment.h */, + D01777EF1F1F961D0044446D /* TGImageMediaAttachment.m */, + D01777F01F1F961D0044446D /* TGMediaAttachment.h */, + D01777F11F1F961D0044446D /* TGMediaAttachment.m */, + D01777F21F1F961D0044446D /* TGActionMediaAttachment.h */, + D01777F31F1F961D0044446D /* TGActionMediaAttachment.m */, + D01777801F1F93250044446D /* TGImageInfo.h */, + D01777811F1F93250044446D /* TGImageInfo.mm */, + D01777851F1F93550044446D /* TGMessage.h */, + D01777861F1F93550044446D /* TGMessage.mm */, + ); + name = Message; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -59,7 +535,84 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + D01778691F1F99180044446D /* NSInputStream+TL.h in Headers */, + D01777531F1F8FE60044446D /* PSCoding.h in Headers */, + D017781D1F1F961D0044446D /* TGMessageEntityMention.h in Headers */, + D01778031F1F961D0044446D /* TGAuthorSignatureMediaAttachment.h in Headers */, + D01778511F1F961D0044446D /* TGVideoInfo.h in Headers */, + D01777601F1F8FE60044446D /* TGBotInfo.h in Headers */, + D01778191F1F961D0044446D /* TGMessageEntityHashtag.h in Headers */, + D01777721F1F92420044446D /* TGPhoneUtils.h in Headers */, + D01777761F1F92570044446D /* RMPhoneFormat.h in Headers */, + D01777F61F1F961D0044446D /* TGPeerIdAdapter.h in Headers */, + D017782F1F1F961D0044446D /* TGReplyMarkupAttachment.h in Headers */, + D01778371F1F961D0044446D /* TGAudioMediaAttachment.h in Headers */, + D017775C1F1F8FE60044446D /* PSKeyValueStore.h in Headers */, + D01777511F1F8FE60044446D /* LegacyComponentsGlobals.h in Headers */, + D01778211F1F961D0044446D /* TGMessageEntityPre.h in Headers */, D017772C1F1F8F100044446D /* LegacyComponents.h in Headers */, + D01778111F1F961D0044446D /* TGMessageEntityBold.h in Headers */, + D017789D1F1FC99A0044446D /* LegacyComponentsInternal.h in Headers */, + D01777621F1F8FE60044446D /* TGLocalization.h in Headers */, + D01778481F1F961D0044446D /* TGDocumentAttributeFilename.h in Headers */, + D01778171F1F961D0044446D /* TGMessageEntityEmail.h in Headers */, + D01777F41F1F961D0044446D /* TGTextCheckingResult.h in Headers */, + D01778231F1F961D0044446D /* TGMessageEntityTextUrl.h in Headers */, + D01778391F1F961D0044446D /* TGAudioWaveform.h in Headers */, + D01777FF1F1F961D0044446D /* TGMessageHole.h in Headers */, + D017777E1F1F930B0044446D /* TGConversation.h in Headers */, + D017781F1F1F961D0044446D /* TGMessageEntityMentionName.h in Headers */, + D01777551F1F8FE60044446D /* PSKeyValueCoder.h in Headers */, + D01778011F1F961D0044446D /* TGMessageViewCountContentProperty.h in Headers */, + D017777A1F1F927A0044446D /* NSObject+TGLock.h in Headers */, + D01778311F1F961D0044446D /* TGInstantPage.h in Headers */, + D01778271F1F961D0044446D /* TGMessageEntitiesAttachment.h in Headers */, + D017775B1F1F8FE60044446D /* PSKeyValueReader.h in Headers */, + D01778131F1F961D0044446D /* TGMessageEntityBotCommand.h in Headers */, + D017782B1F1F961D0044446D /* TGBotReplyMarkupButton.h in Headers */, + D017784D1F1F961D0044446D /* TGForwardedMessageMediaAttachment.h in Headers */, + D01777F71F1F961D0044446D /* TGChannelBannedRights.h in Headers */, + D01778401F1F961D0044446D /* TGDocumentAttributeSticker.h in Headers */, + D017783E1F1F961D0044446D /* TGDocumentAttributeImageSize.h in Headers */, + D017780F1F1F961D0044446D /* TGMessageEntity.h in Headers */, + D01777681F1F8FE60044446D /* TGUser.h in Headers */, + D01778571F1F961D0044446D /* TGLocationMediaAttachment.h in Headers */, + D01778531F1F961D0044446D /* TGVideoMediaAttachment.h in Headers */, + D01777641F1F8FE60044446D /* TGPluralization.h in Headers */, + D017783B1F1F961D0044446D /* TGStickerPackReference.h in Headers */, + D01777571F1F8FE60044446D /* PSKeyValueDecoder.h in Headers */, + D017780D1F1F961D0044446D /* TGBotContextResultAttachment.h in Headers */, + D01778071F1F961D0044446D /* TGInvoiceMediaAttachment.h in Headers */, + D01778421F1F961D0044446D /* TGDocumentAttributeVideo.h in Headers */, + D01778331F1F961D0044446D /* TGWebPageMediaAttachment.h in Headers */, + D01777821F1F93250044446D /* TGImageInfo.h in Headers */, + D017782D1F1F961D0044446D /* TGBotReplyMarkupRow.h in Headers */, + D017775E1F1F8FE60044446D /* TGBotComandInfo.h in Headers */, + D01777871F1F93550044446D /* TGMessage.h in Headers */, + D01777591F1F8FE60044446D /* PSKeyValueEncoder.h in Headers */, + D017784B1F1F961D0044446D /* TGUnsupportedMediaAttachment.h in Headers */, + D01778461F1F961D0044446D /* TGDocumentAttributeAudio.h in Headers */, + D017785B1F1F961D0044446D /* TGMediaAttachment.h in Headers */, + D01778351F1F961D0044446D /* TGReplyMessageMediaAttachment.h in Headers */, + D017780C1F1F961D0044446D /* TGViaUserAttachment.h in Headers */, + D017775D1F1F8FE60044446D /* PSKeyValueWriter.h in Headers */, + D01777FD1F1F961D0044446D /* TGMessageGroup.h in Headers */, + D01777541F1F8FE60044446D /* PSData.h in Headers */, + D01778091F1F961D0044446D /* TGGameMediaAttachment.h in Headers */, + D01778551F1F961D0044446D /* TGLocalMessageMetaMediaAttachment.h in Headers */, + D01778151F1F961D0044446D /* TGMessageEntityCode.h in Headers */, + D01778251F1F961D0044446D /* TGMessageEntityUrl.h in Headers */, + D01777F91F1F961D0044446D /* TGChannelAdminRights.h in Headers */, + D01778051F1F961D0044446D /* TGWebDocument.h in Headers */, + D017781B1F1F961D0044446D /* TGMessageEntityItalic.h in Headers */, + D01777661F1F8FE60044446D /* TGStringUtils.h in Headers */, + D017785D1F1F961D0044446D /* TGActionMediaAttachment.h in Headers */, + D017784F1F1F961D0044446D /* TGContactMediaAttachment.h in Headers */, + D01778491F1F961D0044446D /* TGDocumentMediaAttachment.h in Headers */, + D01778441F1F961D0044446D /* TGDocumentAttributeAnimated.h in Headers */, + D01778591F1F961D0044446D /* TGImageMediaAttachment.h in Headers */, + D01778291F1F961D0044446D /* TGBotReplyMarkup.h in Headers */, + D01777FB1F1F961D0044446D /* TGDatabaseMessageDraft.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -95,8 +648,7 @@ TargetAttributes = { D01777261F1F8F100044446D = { CreatedOnToolsVersion = 8.3.2; - DevelopmentTeam = X834Q8SBVP; - ProvisioningStyle = Automatic; + ProvisioningStyle = Manual; }; }; }; @@ -132,13 +684,226 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + D01778241F1F961D0044446D /* TGMessageEntityTextUrl.m in Sources */, + D01778471F1F961D0044446D /* TGDocumentAttributeAudio.m in Sources */, + D01778181F1F961D0044446D /* TGMessageEntityEmail.m in Sources */, + D017784A1F1F961D0044446D /* TGDocumentMediaAttachment.m in Sources */, + D017782E1F1F961D0044446D /* TGBotReplyMarkupRow.m in Sources */, + D01778561F1F961D0044446D /* TGLocalMessageMetaMediaAttachment.m in Sources */, + D01778081F1F961D0044446D /* TGInvoiceMediaAttachment.m in Sources */, + D017782C1F1F961D0044446D /* TGBotReplyMarkupButton.m in Sources */, + D01777771F1F92570044446D /* RMPhoneFormat.m in Sources */, + D017781E1F1F961D0044446D /* TGMessageEntityMention.m in Sources */, + D01777FC1F1F961D0044446D /* TGDatabaseMessageDraft.m in Sources */, + D017777F1F1F930B0044446D /* TGConversation.m in Sources */, + D01777F51F1F961D0044446D /* TGTextCheckingResult.m in Sources */, + D01778581F1F961D0044446D /* TGLocationMediaAttachment.m in Sources */, + D017785C1F1F961D0044446D /* TGMediaAttachment.m in Sources */, + D01778061F1F961D0044446D /* TGWebDocument.m in Sources */, + D01778001F1F961D0044446D /* TGMessageHole.m in Sources */, + D01777FA1F1F961D0044446D /* TGChannelAdminRights.m in Sources */, + D01778041F1F961D0044446D /* TGAuthorSignatureMediaAttachment.m in Sources */, + D01778411F1F961D0044446D /* TGDocumentAttributeSticker.m in Sources */, + D01778221F1F961D0044446D /* TGMessageEntityPre.m in Sources */, + D01777561F1F8FE60044446D /* PSKeyValueCoder.m in Sources */, + D01777831F1F93250044446D /* TGImageInfo.mm in Sources */, + D017780E1F1F961D0044446D /* TGBotContextResultAttachment.m in Sources */, + D017781A1F1F961D0044446D /* TGMessageEntityHashtag.m in Sources */, + D01777881F1F93550044446D /* TGMessage.mm in Sources */, + D017782A1F1F961D0044446D /* TGBotReplyMarkup.m in Sources */, + D01778321F1F961D0044446D /* TGInstantPage.m in Sources */, + D017776B1F1F909E0044446D /* LegacyComponentsGlobals.m in Sources */, + D01778301F1F961D0044446D /* TGReplyMarkupAttachment.m in Sources */, + D01778501F1F961D0044446D /* TGContactMediaAttachment.m in Sources */, + D017786A1F1F99180044446D /* NSInputStream+TL.m in Sources */, + D017777B1F1F927A0044446D /* NSObject+TGLock.m in Sources */, + D01778281F1F961D0044446D /* TGMessageEntitiesAttachment.m in Sources */, + D01777581F1F8FE60044446D /* PSKeyValueDecoder.m in Sources */, + D017783F1F1F961D0044446D /* TGDocumentAttributeImageSize.m in Sources */, + D017783D1F1F961D0044446D /* TGDocumentAttributeFilename.m in Sources */, + D017780B1F1F961D0044446D /* TGViaUserAttachment.m in Sources */, + D01777671F1F8FE60044446D /* TGStringUtils.mm in Sources */, + D017783C1F1F961D0044446D /* TGStickerPackReference.m in Sources */, + D017775A1F1F8FE60044446D /* PSKeyValueEncoder.m in Sources */, + D01778021F1F961D0044446D /* TGMessageViewCountContentProperty.m in Sources */, + D01778261F1F961D0044446D /* TGMessageEntityUrl.m in Sources */, + D017785A1F1F961D0044446D /* TGImageMediaAttachment.m in Sources */, + D017784C1F1F961D0044446D /* TGUnsupportedMediaAttachment.m in Sources */, + D01777611F1F8FE60044446D /* TGBotInfo.m in Sources */, + D01778521F1F961D0044446D /* TGVideoInfo.mm in Sources */, + D01777651F1F8FE60044446D /* TGPluralization.m in Sources */, + D01777F81F1F961D0044446D /* TGChannelBannedRights.m in Sources */, + D01778451F1F961D0044446D /* TGDocumentAttributeAnimated.m in Sources */, + D01778141F1F961D0044446D /* TGMessageEntityBotCommand.m in Sources */, + D01778541F1F961D0044446D /* TGVideoMediaAttachment.m in Sources */, + D01778361F1F961D0044446D /* TGReplyMessageMediaAttachment.m in Sources */, + D01778341F1F961D0044446D /* TGWebPageMediaAttachment.m in Sources */, + D01778431F1F961D0044446D /* TGDocumentAttributeVideo.m in Sources */, + D01778101F1F961D0044446D /* TGMessageEntity.m in Sources */, + D01778381F1F961D0044446D /* TGAudioMediaAttachment.m in Sources */, + D017775F1F1F8FE60044446D /* TGBotComandInfo.m in Sources */, + D01777FE1F1F961D0044446D /* TGMessageGroup.m in Sources */, + D01778121F1F961D0044446D /* TGMessageEntityBold.m in Sources */, + D01778201F1F961D0044446D /* TGMessageEntityMentionName.m in Sources */, + D017780A1F1F961D0044446D /* TGGameMediaAttachment.m in Sources */, + D017783A1F1F961D0044446D /* TGAudioWaveform.m in Sources */, + D01777631F1F8FE60044446D /* TGLocalization.m in Sources */, + D01777731F1F92420044446D /* TGPhoneUtils.m in Sources */, + D017781C1F1F961D0044446D /* TGMessageEntityItalic.m in Sources */, + D01777691F1F8FE60044446D /* TGUser.m in Sources */, + D017789E1F1FC99A0044446D /* LegacyComponentsInternal.m in Sources */, + D017784E1F1F961D0044446D /* TGForwardedMessageMediaAttachment.m in Sources */, + D01778161F1F961D0044446D /* TGMessageEntityCode.m in Sources */, + D017785E1F1F961D0044446D /* TGActionMediaAttachment.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ - D017772D1F1F8F100044446D /* Debug */ = { + D017772D1F1F8F100044446D /* Debug AppStore */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = "Debug AppStore"; + }; + D017772E1F1F8F100044446D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + D01777301F1F8F100044446D /* Debug AppStore */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_IDENTITY = ""; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = LegacyComponents/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACH_O_TYPE = staticlib; + PRODUCT_BUNDLE_IDENTIFIER = org.Telegram.LegacyComponents; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + }; + name = "Debug AppStore"; + }; + D01777311F1F8F100044446D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_IDENTITY = ""; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = LegacyComponents/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACH_O_TYPE = staticlib; + PRODUCT_BUNDLE_IDENTIFIER = org.Telegram.LegacyComponents; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + }; + name = Release; + }; + D01778931F1FA58C0044446D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; @@ -190,7 +955,29 @@ }; name = Debug; }; - D017772E1F1F8F100044446D /* Release */ = { + D01778941F1FA58C0044446D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_IDENTITY = ""; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = LegacyComponents/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACH_O_TYPE = staticlib; + PRODUCT_BUNDLE_IDENTIFIER = org.Telegram.LegacyComponents; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + D01778951F1FA5960044446D /* Hockeyapp */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; @@ -234,43 +1021,29 @@ VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; - name = Release; + name = Hockeyapp; }; - D01777301F1F8F100044446D /* Debug */ = { + D01778961F1FA5960044446D /* Hockeyapp */ = { isa = XCBuildConfiguration; buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; CODE_SIGN_IDENTITY = ""; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = X834Q8SBVP; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = LegacyComponents/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACH_O_TYPE = staticlib; PRODUCT_BUNDLE_IDENTIFIER = org.Telegram.LegacyComponents; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; }; - name = Debug; - }; - D01777311F1F8F100044446D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_IDENTITY = ""; - DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = X834Q8SBVP; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = LegacyComponents/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = org.Telegram.LegacyComponents; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - }; - name = Release; + name = Hockeyapp; }; /* End XCBuildConfiguration section */ @@ -278,8 +1051,10 @@ D01777211F1F8F100044446D /* Build configuration list for PBXProject "LegacyComponents" */ = { isa = XCConfigurationList; buildConfigurations = ( - D017772D1F1F8F100044446D /* Debug */, + D017772D1F1F8F100044446D /* Debug AppStore */, + D01778931F1FA58C0044446D /* Debug */, D017772E1F1F8F100044446D /* Release */, + D01778951F1FA5960044446D /* Hockeyapp */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -287,10 +1062,13 @@ D017772F1F1F8F100044446D /* Build configuration list for PBXNativeTarget "LegacyComponents" */ = { isa = XCConfigurationList; buildConfigurations = ( - D01777301F1F8F100044446D /* Debug */, + D01777301F1F8F100044446D /* Debug AppStore */, + D01778941F1FA58C0044446D /* Debug */, D01777311F1F8F100044446D /* Release */, + D01778961F1FA5960044446D /* Hockeyapp */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; diff --git a/LegacyComponents/LegacyComponents.h b/LegacyComponents/LegacyComponents.h index 4cd0629a5c..8e1d796dc5 100644 --- a/LegacyComponents/LegacyComponents.h +++ b/LegacyComponents/LegacyComponents.h @@ -1,11 +1,3 @@ -// -// LegacyComponents.h -// LegacyComponents -// -// Created by Peter on 7/19/17. -// Copyright © 2017 Telegram. All rights reserved. -// - #import //! Project version number for LegacyComponents. @@ -14,6 +6,81 @@ FOUNDATION_EXPORT double LegacyComponentsVersionNumber; //! Project version string for LegacyComponents. FOUNDATION_EXPORT const unsigned char LegacyComponentsVersionString[]; -// In this header, you should import all the public headers of your framework using statements like #import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import +#import +#import +#import +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import diff --git a/LegacyComponents/LegacyComponentsGlobals.h b/LegacyComponents/LegacyComponentsGlobals.h new file mode 100644 index 0000000000..fc607ac037 --- /dev/null +++ b/LegacyComponents/LegacyComponentsGlobals.h @@ -0,0 +1,18 @@ +#import + +@class TGLocalization; + +@protocol LegacyComponentsGlobalsProvider + +- (TGLocalization *)effectiveLocalization; +- (void)log:(NSString *)format :(va_list)args; + +@end + +@interface LegacyComponentsGlobals : NSObject + ++ (void)setProvider:(id)provider; ++ (id)provider; + +@end + diff --git a/LegacyComponents/LegacyComponentsGlobals.m b/LegacyComponents/LegacyComponentsGlobals.m new file mode 100644 index 0000000000..e73cd7291c --- /dev/null +++ b/LegacyComponents/LegacyComponentsGlobals.m @@ -0,0 +1,19 @@ +#import + +#import "LegacyComponentsInternal.h" + +#import "TGLocalization.h" + +static id _provider; + +@implementation LegacyComponentsGlobals + ++ (void)setProvider:(id)provider { + _provider = provider; +} + ++ (id)provider { + return _provider; +} + +@end diff --git a/LegacyComponents/LegacyComponentsInternal.h b/LegacyComponents/LegacyComponentsInternal.h new file mode 100644 index 0000000000..1f9f766fcb --- /dev/null +++ b/LegacyComponents/LegacyComponentsInternal.h @@ -0,0 +1,18 @@ +#import "LegacyComponentsGlobals.h" + +@class TGLocalization; + +#ifdef __cplusplus +extern "C" { +#endif + +TGLocalization *effectiveLocalization(); +NSString *TGLocalized(NSString *s); +bool TGObjectCompare(id obj1, id obj2); +bool TGStringCompare(NSString *s1, NSString *s2); +void TGLog(NSString *format, ...); + +#ifdef __cplusplus +} +#endif + diff --git a/LegacyComponents/LegacyComponentsInternal.m b/LegacyComponents/LegacyComponentsInternal.m new file mode 100644 index 0000000000..a73f5471e1 --- /dev/null +++ b/LegacyComponents/LegacyComponentsInternal.m @@ -0,0 +1,36 @@ +#import "LegacyComponentsInternal.h" + +#import "TGLocalization.h" + +TGLocalization *effectiveLocalization() { + return [[LegacyComponentsGlobals provider] effectiveLocalization]; +} + +NSString *TGLocalized(NSString *s) { + return [effectiveLocalization() get:s]; +} + +bool TGObjectCompare(id obj1, id obj2) { + if (obj1 == nil && obj2 == nil) + return true; + + return [obj1 isEqual:obj2]; +} + +bool TGStringCompare(NSString *s1, NSString *s2) { + if (s1.length == 0 && s2.length == 0) + return true; + + if ((s1 == nil) != (s2 == nil)) + return false; + + return s1 == nil || [s1 isEqualToString:s2]; +} + +void TGLog(NSString *format, ...) +{ + va_list L; + va_start(L, format); + [[LegacyComponentsGlobals provider] log:format :L]; + va_end(L); +} diff --git a/LegacyComponents/NSInputStream+TL.h b/LegacyComponents/NSInputStream+TL.h new file mode 100644 index 0000000000..c03540f88d --- /dev/null +++ b/LegacyComponents/NSInputStream+TL.h @@ -0,0 +1,28 @@ +/* + * This is the source code of Telegram for iOS v. 1.1 + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Peter Iakovlev, 2013. + */ + +#import + +@interface NSInputStream (TL) + +- (int32_t)readInt32; +- (int32_t)readInt32:(bool *)failed __attribute__((nonnull(1))); +- (int64_t)readInt64; +- (int64_t)readInt64:(bool *)failed __attribute__((nonnull(1))); +- (double)readDouble; +- (double)readDouble:(bool *)failed __attribute__((nonnull(1))); +- (NSData *)readData:(int)length; +- (NSData *)readData:(int)length failed:(bool *)failed __attribute__((nonnull(2))); +- (NSMutableData *)readMutableData:(int)length; +- (NSMutableData *)readMutableData:(int)length failed:(bool *)failed __attribute__((nonnull(2))); +- (NSString *)readString; +- (NSString *)readString:(bool *)failed __attribute__((nonnull(1))); +- (NSData *)readBytes; +- (NSData *)readBytes:(bool *)failed __attribute__((nonnull(1))); + +@end diff --git a/LegacyComponents/NSInputStream+TL.m b/LegacyComponents/NSInputStream+TL.m new file mode 100644 index 0000000000..199313828b --- /dev/null +++ b/LegacyComponents/NSInputStream+TL.m @@ -0,0 +1,390 @@ +#import "NSInputStream+TL.h" + +#import "LegacyComponentsInternal.h" + +#import + +static inline int roundUpInput(int numToRound, int multiple) +{ + if (multiple == 0) + { + return numToRound; + } + + int remainder = numToRound % multiple; + if (remainder == 0) + { + return numToRound; + } + + return numToRound + multiple - remainder; +} + +@implementation NSInputStream (TL) + +- (int32_t)readInt32 +{ + int32_t value = 0; + + if ([self read:(uint8_t *)&value maxLength:4] != 4) + { + TGLog(@"***** Couldn't read int32"); + + @throw [[NSException alloc] initWithName:@"NSInputStream+TLException" reason:@"readInt32 end of stream" userInfo:@{}]; + } + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#elif __BYTE_ORDER == __BIG_ENDIAN +# error "Big endian is not implemented" +#else +# error "Unknown byte order" +#endif + + return value; +} + +- (int32_t)readInt32:(bool *)failed +{ + int32_t value = 0; + + if ([self read:(uint8_t *)&value maxLength:4] != 4) + { + *failed = true; + return 0; + } + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#elif __BYTE_ORDER == __BIG_ENDIAN +# error "Big endian is not implemented" +#else +# error "Unknown byte order" +#endif + + return value; +} + +- (int64_t)readInt64 +{ + int64_t value = 0; + + if ([self read:(uint8_t *)&value maxLength:8] != 8) + { + TGLog(@"***** Couldn't read int64"); + } + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#elif __BYTE_ORDER == __BIG_ENDIAN +# error "Big endian is not implemented" +#else +# error "Unknown byte order" +#endif + + return value; +} + +- (int64_t)readInt64:(bool *)failed +{ + int64_t value = 0; + + if ([self read:(uint8_t *)&value maxLength:8] != 8) + { + *failed = true; + return 0; + } + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#elif __BYTE_ORDER == __BIG_ENDIAN +# error "Big endian is not implemented" +#else +# error "Unknown byte order" +#endif + + return value; +} + +- (double)readDouble +{ + double value = 0.0; + + if ([self read:(uint8_t *)&value maxLength:8] != 8) + { + TGLog(@"***** Couldn't read double"); + } + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#elif __BYTE_ORDER == __BIG_ENDIAN +# error "Big endian is not implemented" +#else +# error "Unknown byte order" +#endif + + return value; +} + +- (double)readDouble:(bool *)failed +{ + double value = 0.0; + + if ([self read:(uint8_t *)&value maxLength:8] != 8) + { + *failed = true; + return 0.0; + } + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#elif __BYTE_ORDER == __BIG_ENDIAN +# error "Big endian is not implemented" +#else +# error "Unknown byte order" +#endif + + return value; +} + +- (NSData *)readData:(int)length +{ + uint8_t *bytes = (uint8_t *)malloc(length); + int readLen = (int)[self read:bytes maxLength:length]; + if (readLen != length) + { + TGLog(@"***** Couldn't read %d bytes", length); + } + NSData *data = [[NSData alloc] initWithBytesNoCopy:bytes length:length freeWhenDone:true]; + return data; +} + +- (NSData *)readData:(int)length failed:(bool *)failed +{ + uint8_t *bytes = (uint8_t *)malloc(length); + int readLen = (int)[self read:bytes maxLength:length]; + if (readLen != length) + { + free(bytes); + *failed = true; + return nil; + } + NSData *data = [[NSData alloc] initWithBytesNoCopy:bytes length:length freeWhenDone:true]; + return data; +} + +- (NSMutableData *)readMutableData:(int)length +{ + uint8_t *bytes = (uint8_t *)malloc(length); + int readLen = (int)[self read:bytes maxLength:length]; + if (readLen != length) + { + TGLog(@"***** Couldn't read %d bytes", length); + } + NSMutableData *data = [[NSMutableData alloc] initWithBytesNoCopy:bytes length:length freeWhenDone:true]; + return data; +} + +- (NSMutableData *)readMutableData:(int)length failed:(bool *)failed +{ + uint8_t *bytes = (uint8_t *)malloc(length); + int readLen = (int)[self read:bytes maxLength:length]; + if (readLen != length) + { + free(bytes); + *failed = true; + return nil; + } + NSMutableData *data = [[NSMutableData alloc] initWithBytesNoCopy:bytes length:length freeWhenDone:true]; + return data; +} + +- (NSString *)readString +{ + uint8_t tmp = 0; + [self read:&tmp maxLength:1]; + + int paddingBytes = 0; + + int32_t length = tmp; + if (length == 254) + { + length = 0; + [self read:((uint8_t *)&length) + 1 maxLength:3]; + length >>= 8; + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#elif __BYTE_ORDER == __BIG_ENDIAN +# error "Big endian is not implemented" +#else +# error "Unknown byte order" +#endif + + paddingBytes = roundUpInput(length, 4) - length; + } + else + { + paddingBytes = roundUpInput(length + 1, 4) - (length + 1); + } + + NSString *string = nil; + + if (length > 0) + { + uint8_t *bytes = (uint8_t *)malloc(length); + int readLen = (int)[self read:bytes maxLength:length]; + if (readLen != length) + { + TGLog(@"***** Couldn't read %d bytes", length); + } + + string = [[NSString alloc] initWithBytesNoCopy:bytes length:length encoding:NSUTF8StringEncoding freeWhenDone:true]; + } + else + { + string = @""; + } + + for (int i = 0; i < paddingBytes; i++) + [self read:&tmp maxLength:1]; + + return string; +} + +- (NSString *)readString:(bool *)failed +{ + uint8_t tmp = 0; + [self read:&tmp maxLength:1]; + + int paddingBytes = 0; + + int32_t length = tmp; + if (length == 254) + { + length = 0; + [self read:((uint8_t *)&length) + 1 maxLength:3]; + length >>= 8; + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#elif __BYTE_ORDER == __BIG_ENDIAN +# error "Big endian is not implemented" +#else +# error "Unknown byte order" +#endif + + paddingBytes = roundUpInput(length, 4) - length; + } + else + { + paddingBytes = roundUpInput(length + 1, 4) - (length + 1); + } + + NSString *string = nil; + + if (length > 0) + { + uint8_t *bytes = (uint8_t *)malloc(length); + int readLen = (int)[self read:bytes maxLength:length]; + if (readLen != length) + { + free(bytes); + *failed = true; + return nil; + } + + string = [[NSString alloc] initWithBytesNoCopy:bytes length:length encoding:NSUTF8StringEncoding freeWhenDone:true]; + } + else + { + string = @""; + } + + for (int i = 0; i < paddingBytes; i++) + [self read:&tmp maxLength:1]; + + return string; +} + +- (NSData *)readBytes +{ + uint8_t tmp = 0; + [self read:&tmp maxLength:1]; + + int paddingBytes = 0; + + int32_t length = tmp; + if (length == 254) + { + length = 0; + [self read:((uint8_t *)&length) + 1 maxLength:3]; + length >>= 8; + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#elif __BYTE_ORDER == __BIG_ENDIAN +# error "Big endian is not implemented" +#else +# error "Unknown byte order" +#endif + + paddingBytes = roundUpInput(length, 4) - length; + } + else + { + paddingBytes = roundUpInput(length + 1, 4) - (length + 1); + } + + uint8_t *bytes = (uint8_t *)malloc(length); + int readLen = (int)[self read:bytes maxLength:length]; + if (readLen != length) + { + TGLog(@"***** Couldn't read %d bytes", length); + } + + NSData *result = [NSData dataWithBytesNoCopy:bytes length:length freeWhenDone:true]; + + for (int i = 0; i < paddingBytes; i++) + [self read:&tmp maxLength:1]; + + return result; +} + +- (NSData *)readBytes:(bool *)failed +{ + uint8_t tmp = 0; + [self read:&tmp maxLength:1]; + + int paddingBytes = 0; + + int32_t length = tmp; + if (length == 254) + { + length = 0; + [self read:((uint8_t *)&length) + 1 maxLength:3]; + length >>= 8; + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#elif __BYTE_ORDER == __BIG_ENDIAN +# error "Big endian is not implemented" +#else +# error "Unknown byte order" +#endif + + paddingBytes = roundUpInput(length, 4) - length; + } + else + { + paddingBytes = roundUpInput(length + 1, 4) - (length + 1); + } + + uint8_t *bytes = (uint8_t *)malloc(length); + int readLen = (int)[self read:bytes maxLength:length]; + if (readLen != length) + { + free(bytes); + *failed = true; + return nil; + } + + NSData *result = [NSData dataWithBytesNoCopy:bytes length:length freeWhenDone:true]; + + for (int i = 0; i < paddingBytes; i++) + [self read:&tmp maxLength:1]; + + return result; +} + +@end diff --git a/LegacyComponents/NSObject+TGLock.h b/LegacyComponents/NSObject+TGLock.h new file mode 100644 index 0000000000..a620beea66 --- /dev/null +++ b/LegacyComponents/NSObject+TGLock.h @@ -0,0 +1,23 @@ +/* + * This is the source code of Telegram for iOS v. 1.1 + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Peter Iakovlev, 2013. + */ + +#import + +#import + +#define TG_SYNCHRONIZED_DEFINE(lock) pthread_mutex_t _TG_SYNCHRONIZED_##lock +#define TG_SYNCHRONIZED_INIT(lock) pthread_mutex_init(&_TG_SYNCHRONIZED_##lock, NULL) +#define TG_SYNCHRONIZED_BEGIN(lock) pthread_mutex_lock(&_TG_SYNCHRONIZED_##lock); +#define TG_SYNCHRONIZED_END(lock) pthread_mutex_unlock(&_TG_SYNCHRONIZED_##lock); + +@interface NSObject (TGLock) + +- (void)tgLockObject; +- (void)tgUnlockObject; + +@end diff --git a/LegacyComponents/NSObject+TGLock.m b/LegacyComponents/NSObject+TGLock.m new file mode 100644 index 0000000000..fd70fde64a --- /dev/null +++ b/LegacyComponents/NSObject+TGLock.m @@ -0,0 +1,73 @@ +#import "NSObject+TGLock.h" + +#import + +static const char *lockPropertyKey = "TGObjectLock::lock"; + +@interface TGObjectLockImpl : NSObject +{ + TG_SYNCHRONIZED_DEFINE(objectLock); +} + +- (void)tgTakeLock; +- (void)tgFreeLock; + +@end + +@implementation TGObjectLockImpl + +- (id)init +{ + self = [super init]; + if (self != nil) + { + TG_SYNCHRONIZED_INIT(objectLock); + } + return self; +} + +- (void)tgTakeLock +{ + TG_SYNCHRONIZED_BEGIN(objectLock); +} + +- (void)tgFreeLock +{ + TG_SYNCHRONIZED_END(objectLock); +} + +@end + +@implementation NSObject (TGLock) + +- (void)tgLockObject +{ + TGObjectLockImpl *lock = (TGObjectLockImpl *)objc_getAssociatedObject(self, lockPropertyKey); + if (lock == nil) + { + @synchronized(self) + { + lock = [[TGObjectLockImpl alloc] init]; + objc_setAssociatedObject(self, lockPropertyKey, lock, OBJC_ASSOCIATION_RETAIN); + } + } + + [lock tgTakeLock]; +} + +- (void)tgUnlockObject +{ + TGObjectLockImpl *lock = (TGObjectLockImpl *)objc_getAssociatedObject(self, lockPropertyKey); + if (lock == nil) + { + @synchronized(self) + { + lock = [[TGObjectLockImpl alloc] init]; + objc_setAssociatedObject(self, lockPropertyKey, lock, OBJC_ASSOCIATION_RETAIN); + } + } + + [lock tgFreeLock]; +} + +@end diff --git a/LegacyComponents/PSCoding.h b/LegacyComponents/PSCoding.h new file mode 100644 index 0000000000..615783b235 --- /dev/null +++ b/LegacyComponents/PSCoding.h @@ -0,0 +1,10 @@ +#import + +@class PSKeyValueCoder; + +@protocol PSCoding + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder; +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder; + +@end diff --git a/LegacyComponents/PSData.h b/LegacyComponents/PSData.h new file mode 100644 index 0000000000..f0e333c994 --- /dev/null +++ b/LegacyComponents/PSData.h @@ -0,0 +1,14 @@ +#ifndef __PSData_h +#define __PSData_h + +typedef struct +{ + uint8_t *data; + NSUInteger length; +} PSData; + +typedef PSData PSConstData; + +#define PSDataOffset(d, offset) {d.data += offset; d.length -= offset;} + +#endif diff --git a/LegacyComponents/PSKeyValueCoder.h b/LegacyComponents/PSKeyValueCoder.h new file mode 100644 index 0000000000..907bb72c8f --- /dev/null +++ b/LegacyComponents/PSKeyValueCoder.h @@ -0,0 +1,54 @@ +#import + +#import + +typedef enum { + PSKeyValueCoderFieldTypeString = 1, + PSKeyValueCoderFieldTypeInt32 = 2, + PSKeyValueCoderFieldTypeInt64 = 3, + PSKeyValueCoderFieldTypeCustomClass = 4, + PSKeyValueCoderFieldTypeArray = 5, + PSKeyValueCoderFieldTypeData = 6, + PSKeyValueCoderFieldTypeInt32Array = 7, + PSKeyValueCoderFieldTypeInt32Dictionary = 8, + PSKeyValueCoderFieldTypeDouble = 9 +} PSKeyValueCoderFieldType; + +@interface PSKeyValueCoder : NSObject + +- (void)encodeString:(NSString *)string forKey:(NSString *)key; +- (void)encodeString:(NSString *)string forCKey:(const char *)key; +- (void)encodeInt32:(int32_t)number forKey:(NSString *)key; +- (void)encodeInt32:(int32_t)number forCKey:(const char *)key; +- (void)encodeInt64:(int64_t)number forKey:(NSString *)key; +- (void)encodeInt64:(int64_t)number forCKey:(const char *)key; +- (void)encodeObject:(id)object forKey:(NSString *)key; +- (void)encodeObject:(id)object forCKey:(const char *)key; +- (void)encodeArray:(NSArray *)array forKey:(NSString *)key; +- (void)encodeArray:(NSArray *)array forCKey:(const char *)key; +- (void)encodeData:(NSData *)data forCKey:(const char *)key; +- (void)encodeBytes:(uint8_t const *)value length:(NSUInteger)length forCKey:(const char *)key; +- (void)encodeInt32Array:(NSArray *)value forCKey:(const char *)key; +- (void)encodeInt32Dictionary:(NSDictionary *)value forCKey:(const char *)key; +- (void)encodeDouble:(double)value forCKey:(const char *)key; + +- (NSString *)decodeStringForKey:(NSString *)key; +- (NSString *)decodeStringForCKey:(const char *)key; +- (int32_t)decodeInt32ForKey:(NSString *)key; +- (int32_t)decodeInt32ForCKey:(const char *)key; +- (int64_t)decodeInt64ForKey:(NSString *)key; +- (int64_t)decodeInt64ForCKey:(const char *)key; +- (id)decodeObjectForKey:(NSString *)key; +- (id)decodeObjectForCKey:(const char *)key; +- (NSArray *)decodeArrayForKey:(NSString *)key; +- (NSArray *)decodeArrayForCKey:(const char *)key; +- (NSData *)decodeDataCorCKey:(const char *)key; +- (void)decodeBytesForCKey:(const char *)key value:(uint8_t *)value length:(NSUInteger)length; +- (NSDictionary *)decodeObjectsByKeys; +- (NSArray *)decodeInt32ArrayForCKey:(const char *)key; +- (NSDictionary *)decodeInt32DictionaryForCKey:(const char *)key; +- (double)decodeDoubleForCKey:(const char *)key; + ++ (Class)classForName:(NSString *)name; + +@end diff --git a/LegacyComponents/PSKeyValueCoder.m b/LegacyComponents/PSKeyValueCoder.m new file mode 100644 index 0000000000..20bd9382c0 --- /dev/null +++ b/LegacyComponents/PSKeyValueCoder.m @@ -0,0 +1,180 @@ +#import "PSKeyValueCoder.h" + +#import + +pthread_rwlock_t classNameCacheLock = PTHREAD_RWLOCK_INITIALIZER; + +NSMutableDictionary *classNameCache() +{ + static NSMutableDictionary *dict = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^ + { + dict = [[NSMutableDictionary alloc] init]; + }); + + return dict; +} + +@implementation PSKeyValueCoder + ++ (Class)classForName:(NSString *)name +{ + if (name == nil) + return nil; + + Class result = nil; + + pthread_rwlock_rdlock(&classNameCacheLock); + result = [classNameCache() objectForKey:name]; + pthread_rwlock_unlock(&classNameCacheLock); + + if (result == nil) + { + result = NSClassFromString(name); + if (result != nil) + { + pthread_rwlock_wrlock(&classNameCacheLock); + classNameCache()[name] = result; + pthread_rwlock_unlock(&classNameCacheLock); + } + } + + return result; +} + +- (void)encodeString:(NSString *)__unused string forKey:(NSString *)__unused key +{ +} + +- (void)encodeInt32:(int32_t)__unused number forKey:(NSString *)__unused key +{ +} + +- (void)encodeInt64:(int64_t)__unused number forKey:(NSString *)__unused key +{ +} + +- (void)encodeObject:(id)__unused object forKey:(NSString *)__unused key +{ +} + +- (NSString *)decodeStringForKey:(NSString *)__unused key +{ + return nil; +} + +- (int32_t)decodeInt32ForKey:(NSString *)__unused key +{ + return 0; +} + +- (int64_t)decodeInt64ForKey:(NSString *)__unused key +{ + return 0; +} + +- (id)decodeObjectForKey:(NSString *)__unused key +{ + return nil; +} + +- (void)encodeString:(NSString *)__unused string forCKey:(const char *)__unused key +{ +} + +- (void)encodeInt32:(int32_t)__unused number forCKey:(const char *)__unused key +{ +} + +- (void)encodeInt64:(int64_t)__unused number forCKey:(const char *)__unused key +{ +} + +- (void)encodeObject:(id)__unused object forCKey:(const char *)__unused key +{ +} + +- (void)encodeArray:(NSArray *)__unused array forKey:(NSString *)__unused key +{ +} + +- (void)encodeArray:(NSArray *)__unused array forCKey:(const char *)__unused key +{ +} + +- (void)encodeData:(NSData *)__unused data forCKey:(const char *)__unused key +{ +} + +- (void)encodeBytes:(uint8_t const *)__unused value length:(NSUInteger)__unused length forCKey:(const char *)__unused key +{ +} + +- (void)encodeInt32Array:(NSArray *)__unused value forCKey:(const char *)__unused key +{ +} + +- (void)encodeInt32Dictionary:(NSDictionary *)__unused value forCKey:(const char *)__unused key { +} + +- (void)encodeDouble:(double)__unused value forCKey:(const char *)__unused key { +} + +- (NSString *)decodeStringForCKey:(const char *)__unused key +{ + return nil; +} + +- (int32_t)decodeInt32ForCKey:(const char *)__unused key +{ + return 0; +} + +- (int64_t)decodeInt64ForCKey:(const char *)__unused key +{ + return 0; +} + +- (id)decodeObjectForCKey:(const char *)__unused key +{ + return nil; +} + +- (NSArray *)decodeArrayForKey:(NSString *)__unused key +{ + return nil; +} + +- (NSArray *)decodeArrayForCKey:(const char *)__unused key +{ + return nil; +} + +- (NSData *)decodeDataCorCKey:(const char *)__unused key +{ + return nil; +} + +- (void)decodeBytesForCKey:(const char *)__unused key value:(uint8_t *)__unused value length:(NSUInteger)__unused length +{ +} + +- (NSDictionary *)decodeObjectsByKeys +{ + return nil; +} + +- (NSArray *)decodeInt32ArrayForCKey:(const char *)__unused key { + return nil; +} + +- (NSDictionary *)decodeInt32DictionaryForCKey:(const char *)__unused key { + return nil; +} + +- (double)decodeDoubleForCKey:(const char *)__unused key { + return 0.0f; +} + +@end diff --git a/LegacyComponents/PSKeyValueDecoder.h b/LegacyComponents/PSKeyValueDecoder.h new file mode 100644 index 0000000000..1b031f2309 --- /dev/null +++ b/LegacyComponents/PSKeyValueDecoder.h @@ -0,0 +1,12 @@ +#import + +@interface PSKeyValueDecoder : PSKeyValueCoder + +- (instancetype)init; +- (instancetype)initWithData:(NSData *)data; + +- (void)resetData:(NSData *)data; +- (void)resetBytes:(uint8_t const *)bytes length:(NSUInteger)length; +- (void)rewind; + +@end diff --git a/LegacyComponents/PSKeyValueDecoder.m b/LegacyComponents/PSKeyValueDecoder.m new file mode 100644 index 0000000000..b9e695ce5f --- /dev/null +++ b/LegacyComponents/PSKeyValueDecoder.m @@ -0,0 +1,698 @@ +#import "PSKeyValueDecoder.h" + +#import + +@interface PSKeyValueDecoder () +{ + NSData *_data; + +@public + uint8_t const *_currentPtr; + uint8_t const *_begin; + uint8_t const *_end; + + PSKeyValueDecoder *_tempCoder; +} + +@end + +static uint32_t readLength(uint8_t const **currentPtr) +{ + uint32_t result = 0; + + result |= (*(*currentPtr)) & 127; + + if ((*(*currentPtr)) & 128) + { + (*currentPtr)++; + result |= ((*(*currentPtr)) & 127) << (7 * 1); + + if ((*(*currentPtr)) & 128) + { + (*currentPtr)++; + result |= ((*(*currentPtr)) & 127) << (7 * 2); + + if ((*(*currentPtr)) & 128) + { + (*currentPtr)++; + result |= ((*(*currentPtr)) & 127) << (7 * 3); + + if ((*(*currentPtr)) & 128) + { + (*currentPtr)++; + result |= ((*(*currentPtr)) & 127) << (7 * 4); + } + } + } + } + + (*currentPtr)++; + + return result; +} + +static NSString *readString(uint8_t const **currentPtr) +{ + uint32_t stringLength = readLength(currentPtr); + + NSString *string = [[NSString alloc] initWithBytes:*currentPtr length:stringLength encoding:NSUTF8StringEncoding]; + (*currentPtr) += stringLength; + return string; +} + +static void skipString(uint8_t const **currentPtr) +{ + uint32_t stringLength = readLength(currentPtr); + (*currentPtr) += stringLength; +} + +static int32_t readInt32(uint8_t const **currentPtr) +{ + int32_t number = *((int32_t *)(*currentPtr)); + (*currentPtr) += 4; + return number; +} + +static void skipInt32(uint8_t const **currentPtr) +{ + (*currentPtr) += 4; +} + +static int64_t readInt64(uint8_t const **currentPtr) +{ + int64_t number; + memcpy(&number, *currentPtr, 8); + + (*currentPtr) += 8; + return number; +} + +static double readDouble(uint8_t const **currentPtr) +{ + double number; + memcpy(&number, *currentPtr, 8); + + (*currentPtr) += 8; + return number; +} + +static void skipInt64(uint8_t const **currentPtr) +{ + (*currentPtr) += 8; +} + +static id readObject(uint8_t const **currentPtr, PSKeyValueDecoder *tempCoder) +{ + uint32_t objectLength = *((uint32_t *)(*currentPtr)); + (*currentPtr) += 4; + + uint8_t const *objectEnd = (*currentPtr) + objectLength; + + + const char *className = (const char *)(*currentPtr); + NSUInteger classNameLength = strlen(className) + 1; + (*currentPtr) += classNameLength; + + id object = nil; + + Class objectClass = objc_getClass(className); + if (objectClass != nil) + { + tempCoder->_begin = *currentPtr; + tempCoder->_end = objectEnd; + tempCoder->_currentPtr = tempCoder->_begin; + + object = [(id)[(id)objectClass alloc] initWithKeyValueCoder:tempCoder]; + } + + *currentPtr = objectEnd; + + return object; +} + +static void skipObject(uint8_t const **currentPtr) +{ + uint32_t objectLength = *((uint32_t *)(*currentPtr)); + (*currentPtr) += 4 + objectLength; +} + +static NSArray *readArray(uint8_t const **currentPtr, PSKeyValueDecoder *tempCoder) +{ + uint32_t objectLength = *((uint32_t *)(*currentPtr)); + (*currentPtr) += 4; + + uint8_t const *objectEnd = (*currentPtr) + objectLength; + + uint32_t count = readLength(currentPtr); + + NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:count]; + + for (uint32_t i = 0; i < count; i++) + { + id object = readObject(currentPtr, tempCoder); + if (object != nil) + [array addObject:object]; + } + + *currentPtr = objectEnd; + + return array; +} + +static void skipArray(uint8_t const **currentPtr) +{ + uint32_t objectLength = ((uint32_t *)*currentPtr)[0]; + (*currentPtr) += 4 + objectLength; +} + +static NSDictionary *readInt32Dictionary(uint8_t const **currentPtr, PSKeyValueDecoder *tempCoder) +{ + uint32_t objectLength = *((uint32_t *)(*currentPtr)); + (*currentPtr) += 4; + + uint8_t const *objectEnd = (*currentPtr) + objectLength; + + uint32_t count = readLength(currentPtr); + + NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity:count]; + + for (uint32_t i = 0; i < count; i++) + { + int32_t key = *((int32_t *)(*currentPtr)); + (*currentPtr) += 4; + + id object = readObject(currentPtr, tempCoder); + if (object != nil) { + dict[@(key)] = object; + } + } + + *currentPtr = objectEnd; + + return dict; +} + +static void skipInt32Dictionary(uint8_t const **currentPtr) +{ + uint32_t objectLength = ((uint32_t *)*currentPtr)[0]; + (*currentPtr) += 4 + objectLength; +} + +static NSData *readData(uint8_t const **currentPtr) +{ + uint32_t length = readLength(currentPtr); + + NSData *data = [[NSData alloc] initWithBytes:*currentPtr length:length]; + + *currentPtr += length; + + return data; +} + +static void readBytes(uint8_t const **currentPtr, uint8_t *value, NSUInteger maxLength) +{ + uint32_t length = readLength(currentPtr); + + memcpy(value, *currentPtr, MIN((uint32_t)maxLength, length)); + + *currentPtr += length; +} + +static void skipData(uint8_t const **currentPtr) +{ + uint32_t length = readLength(currentPtr); + (*currentPtr) += length; +} + +static void skipField(uint8_t const **currentPtr) +{ + uint8_t fieldType = *(*currentPtr); + (*currentPtr)++; + + switch (fieldType) + { + case PSKeyValueCoderFieldTypeString: + { + skipString(currentPtr); + break; + } + case PSKeyValueCoderFieldTypeInt32: + { + skipInt32(currentPtr); + break; + } + case PSKeyValueCoderFieldTypeInt64: + { + skipInt64(currentPtr); + break; + } + case PSKeyValueCoderFieldTypeCustomClass: + { + skipObject(currentPtr); + break; + } + case PSKeyValueCoderFieldTypeArray: + { + skipArray(currentPtr); + break; + } + case PSKeyValueCoderFieldTypeData: + { + skipData(currentPtr); + break; + } + case PSKeyValueCoderFieldTypeInt32Dictionary: + { + skipInt32Dictionary(currentPtr); + break; + } + case PSKeyValueCoderFieldTypeDouble: + { + skipInt64(currentPtr); + break; + } + default: + break; + } +} + +@implementation PSKeyValueDecoder + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + + } + return self; +} + +- (instancetype)initWithData:(NSData *)data +{ + self = [super init]; + if (self != nil) + { + _data = data; + + _begin = (uint8_t const *)[_data bytes]; + _end = _begin + [_data length]; + _currentPtr = _begin; + } + return self; +} + +- (void)resetData:(NSData *)data +{ + _data = data; + + _begin = (uint8_t const *)[_data bytes]; + _end = _begin + [_data length]; + _currentPtr = _begin; +} + +- (void)resetBytes:(uint8_t const *)bytes length:(NSUInteger)length +{ + _data = nil; + + _begin = bytes; + _end = _begin + length; + _currentPtr = _begin; +} + +- (void)rewind { + _begin = (uint8_t const *)[_data bytes]; + _end = _begin + [_data length]; + _currentPtr = _begin; +} + +static bool skipToValueForRawKey(PSKeyValueDecoder *self, uint8_t const *key, NSUInteger keyLength) +{ + uint8_t const *middlePtr = self->_currentPtr; + + for (int i = 0; i < 2; i++) + { + uint8_t const *scanEnd = self->_end; + + if (i == 1) + { + self->_currentPtr = self->_begin; + scanEnd = middlePtr; + } + + while (self->_currentPtr < scanEnd) + { + uint32_t compareKeyLength = readLength(&self->_currentPtr); + + if (compareKeyLength != keyLength || memcmp(key, self->_currentPtr, keyLength)) + { + if (compareKeyLength > 1000) { + return false; + } + + self->_currentPtr += compareKeyLength; + skipField(&self->_currentPtr); + + continue; + } + + self->_currentPtr += compareKeyLength; + + return true; + } + } + + return false; +} + +static NSString *decodeStringForRawKey(PSKeyValueDecoder *self, uint8_t const *key, NSUInteger keyLength) +{ + if (skipToValueForRawKey(self, key, keyLength)) + { + uint8_t fieldType = *self->_currentPtr; + self->_currentPtr++; + + if (fieldType == PSKeyValueCoderFieldTypeString) + return readString(&self->_currentPtr); + else if (fieldType == PSKeyValueCoderFieldTypeInt32) + return [[NSString alloc] initWithFormat:@"%" PRId32 "", readInt32(&self->_currentPtr)]; + else if (fieldType == PSKeyValueCoderFieldTypeInt64) + return [[NSString alloc] initWithFormat:@"%" PRId64 "", readInt64(&self->_currentPtr)]; + else + { + skipField(&self->_currentPtr); + + return nil; + } + } + + return nil; +} + +- (NSString *)decodeStringForKey:(NSString *)key +{ + NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding]; + return decodeStringForRawKey(self, (uint8_t const *)[keyData bytes], [keyData length]); +} + +- (NSString *)decodeStringForCKey:(const char *)key +{ + return decodeStringForRawKey(self, (uint8_t const *)key, (NSUInteger)strlen(key)); +} + +static int32_t decodeInt32ForRawKey(PSKeyValueDecoder *self, uint8_t const *key, NSUInteger keyLength) +{ + if (skipToValueForRawKey(self, key, keyLength)) + { + uint8_t fieldType = *self->_currentPtr; + self->_currentPtr++; + + if (fieldType == PSKeyValueCoderFieldTypeString) + return (int32_t)[readString(&self->_currentPtr) intValue]; + else if (fieldType == PSKeyValueCoderFieldTypeInt32) + return readInt32(&self->_currentPtr); + else if (fieldType == PSKeyValueCoderFieldTypeInt64) + return (int32_t)readInt64(&self->_currentPtr); + else + { + skipField(&self->_currentPtr); + + return 0; + } + } + + return 0; +} + +- (int32_t)decodeInt32ForKey:(NSString *)key +{ + NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding]; + return decodeInt32ForRawKey(self, (uint8_t const *)[keyData bytes], [keyData length]); +} + +- (int32_t)decodeInt32ForCKey:(const char *)key +{ + return decodeInt32ForRawKey(self, (uint8_t const *)key, strlen(key)); +} + +static int64_t decodeInt64ForRawKey(PSKeyValueDecoder *self, uint8_t const *key, NSUInteger keyLength) +{ + if (skipToValueForRawKey(self, key, keyLength)) + { + uint8_t fieldType = *self->_currentPtr; + self->_currentPtr++; + + if (fieldType == PSKeyValueCoderFieldTypeString) + return (int64_t)[readString(&self->_currentPtr) longLongValue]; + else if (fieldType == PSKeyValueCoderFieldTypeInt32) + return readInt32(&self->_currentPtr); + else if (fieldType == PSKeyValueCoderFieldTypeInt64) + return readInt64(&self->_currentPtr); + else + { + skipField(&self->_currentPtr); + return 0; + } + } + + return 0; +} + +- (int64_t)decodeInt64ForKey:(NSString *)key +{ + NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding]; + return decodeInt64ForRawKey(self, (uint8_t const *)[keyData bytes], [keyData length]); +} + +- (int64_t)decodeInt64ForCKey:(const char *)key +{ + return decodeInt64ForRawKey(self, (uint8_t const *)key, strlen(key)); +} + +static id decodeObjectForRawKey(PSKeyValueDecoder *self, uint8_t const *key, NSUInteger keyLength) +{ + if (skipToValueForRawKey(self, key, keyLength)) + { + uint8_t fieldType = *self->_currentPtr; + self->_currentPtr++; + + if (fieldType == PSKeyValueCoderFieldTypeCustomClass) + { + if (self->_tempCoder == nil) + self->_tempCoder = [[PSKeyValueDecoder alloc] init]; + return readObject(&self->_currentPtr, self->_tempCoder); + } + else + { + skipField(&self->_currentPtr); + + return nil; + } + } + + return nil; +} + +- (id)decodeObjectForKey:(NSString *)key +{ + NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding]; + return decodeObjectForRawKey(self, (uint8_t const *)[keyData bytes], [keyData length]); +} + +- (id)decodeObjectForCKey:(const char *)key +{ + return decodeObjectForRawKey(self, (uint8_t const *)key, strlen(key)); +} + +static NSArray *decodeArrayForRawKey(PSKeyValueDecoder *self, uint8_t const *key, NSUInteger keyLength) +{ + if (skipToValueForRawKey(self, key, keyLength)) + { + uint8_t fieldType = *self->_currentPtr; + self->_currentPtr++; + + if (fieldType == PSKeyValueCoderFieldTypeArray) + { + if (self->_tempCoder == nil) + self->_tempCoder = [[PSKeyValueDecoder alloc] init]; + return readArray(&self->_currentPtr, self->_tempCoder); + } + else + { + skipField(&self->_currentPtr); + + return nil; + } + } + + return nil; +} + +- (NSArray *)decodeArrayForKey:(NSString *)key +{ + NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding]; + return decodeArrayForRawKey(self, (uint8_t const *)[keyData bytes], [keyData length]); +} + +- (NSArray *)decodeArrayForCKey:(const char *)key +{ + return decodeArrayForRawKey(self, (uint8_t const *)key, strlen(key)); +} + +static NSData *decodeDataForRawKey(PSKeyValueDecoder *self, uint8_t const *key, NSUInteger keyLength) +{ + if (skipToValueForRawKey(self, key, keyLength)) + { + uint8_t fieldType = *self->_currentPtr; + self->_currentPtr++; + + if (fieldType == PSKeyValueCoderFieldTypeData) + { + return readData(&self->_currentPtr); + } + else + { + skipField(&self->_currentPtr); + + return nil; + } + } + + return nil; +} + +static void decodeBytesForRawKey(PSKeyValueDecoder *self, uint8_t const *key, NSUInteger keyLength, uint8_t *value, NSUInteger maxLength) +{ + if (skipToValueForRawKey(self, key, keyLength)) + { + uint8_t fieldType = *self->_currentPtr; + self->_currentPtr++; + + if (fieldType == PSKeyValueCoderFieldTypeData) + { + readBytes(&self->_currentPtr, value, maxLength); + } + else + { + skipField(&self->_currentPtr); + } + } +} + +- (NSData *)decodeDataCorCKey:(const char *)key +{ + return decodeDataForRawKey(self, (uint8_t const *)key, strlen(key)); +} + +- (void)decodeBytesForCKey:(const char *)key value:(uint8_t *)value length:(NSUInteger)length +{ + decodeBytesForRawKey(self, (uint8_t const *)key, strlen(key), value, length); +} + +- (NSDictionary *)decodeObjectsByKeys +{ + NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; + + if (self->_tempCoder == nil) + self->_tempCoder = [[PSKeyValueDecoder alloc] init]; + + self->_currentPtr = self->_begin; + while (self->_currentPtr < self->_end) + { + uint32_t keyLength = readLength(&self->_currentPtr); + NSString *key = [[NSString alloc] initWithBytes:self->_currentPtr length:keyLength encoding:NSUTF8StringEncoding]; + self->_currentPtr += keyLength; + + uint8_t fieldType = *self->_currentPtr; + self->_currentPtr++; + + if (fieldType == PSKeyValueCoderFieldTypeCustomClass) + { + id value = readObject(&self->_currentPtr, self->_tempCoder); + if (value == nil) + continue; + + dict[key] = value; + } + else + break; + } + + self->_currentPtr = self->_begin; + + return dict; +} + +- (NSArray *)decodeInt32ArrayForCKey:(const char *)key { + if (skipToValueForRawKey(self, (void *)key, strlen(key))) { + uint8_t fieldType = *self->_currentPtr; + self->_currentPtr++; + + if (fieldType == PSKeyValueCoderFieldTypeInt32Array) { + int32_t count = 0; + memcpy(&count, self->_currentPtr, 4); + self->_currentPtr += 4; + NSMutableArray *array = [[NSMutableArray alloc] init]; + + for (int32_t i = 0; i < count; i++) { + int32_t number = 0; + memcpy(&number, self->_currentPtr, 4); + self->_currentPtr += 4; + [array addObject:@(number)]; + } + + return array; + } else { + self->_currentPtr--; + skipField(&self->_currentPtr); + return nil; + } + } else { + return nil; + } +} + +- (NSDictionary *)decodeInt32DictionaryForCKey:(const char *)key { + if (skipToValueForRawKey(self, (uint8_t const *)key, strlen(key))) { + uint8_t fieldType = *self->_currentPtr; + self->_currentPtr++; + + if (fieldType == PSKeyValueCoderFieldTypeInt32Dictionary) { + if (self->_tempCoder == nil) + self->_tempCoder = [[PSKeyValueDecoder alloc] init]; + return readInt32Dictionary(&self->_currentPtr, self->_tempCoder); + } + else { + skipField(&self->_currentPtr); + } + } + + return nil; +} + +- (double)decodeDoubleForCKey:(const char *)key { + if (skipToValueForRawKey(self, (uint8_t const *)key, strlen(key))) + { + uint8_t fieldType = *self->_currentPtr; + self->_currentPtr++; + + if (fieldType == PSKeyValueCoderFieldTypeString) + return (int32_t)[readString(&self->_currentPtr) doubleValue]; + else if (fieldType == PSKeyValueCoderFieldTypeInt32) + return readInt32(&self->_currentPtr); + else if (fieldType == PSKeyValueCoderFieldTypeInt64) + return (double)readInt64(&self->_currentPtr); + else if (fieldType == PSKeyValueCoderFieldTypeDouble) + return readDouble(&self->_currentPtr); + else + { + skipField(&self->_currentPtr); + + return 0; + } + } + + return 0.0; +} + +@end diff --git a/LegacyComponents/PSKeyValueEncoder.h b/LegacyComponents/PSKeyValueEncoder.h new file mode 100644 index 0000000000..c3d12f1b91 --- /dev/null +++ b/LegacyComponents/PSKeyValueEncoder.h @@ -0,0 +1,8 @@ +#import + +@interface PSKeyValueEncoder : PSKeyValueCoder + +- (void)reset; +- (NSData *)data; + +@end diff --git a/LegacyComponents/PSKeyValueEncoder.m b/LegacyComponents/PSKeyValueEncoder.m new file mode 100644 index 0000000000..23bb690b58 --- /dev/null +++ b/LegacyComponents/PSKeyValueEncoder.m @@ -0,0 +1,373 @@ +#import "PSKeyValueEncoder.h" + +#import + +@interface PSKeyValueEncoder () +{ +@public + NSMutableData *_data; +} + +@end + +@implementation PSKeyValueEncoder + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + _data = [[NSMutableData alloc] init]; + } + return self; +} + +static inline void writeLength(PSKeyValueEncoder *self, uint32_t value) +{ + uint8_t bytes[5]; + int length = 0; + + if (value > 127) + { + bytes[length++] = ((uint8_t)(value & 127)) | 128; + value >>= 7; + } + + if (value > 127) + { + bytes[length++] = ((uint8_t)(value & 127)) | 128; + value >>= 7; + } + + if (value > 127) + { + bytes[length++] = ((uint8_t)(value & 127)) | 128; + value >>= 7; + } + + if (value > 127) + { + bytes[length++] = ((uint8_t)(value & 127)) | 128; + value >>= 7; + } + + bytes[length++] = ((uint8_t)(value & 127)); + + [self->_data appendBytes:bytes length:length]; +} + +- (void)encodeString:(NSString *)string forKey:(NSString *)key +{ + if (key == nil || string == nil) + return; + + NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding]; + writeLength(self, (uint32_t)keyData.length); + [_data appendData:keyData]; + + uint8_t fieldType = PSKeyValueCoderFieldTypeString; + [_data appendBytes:&fieldType length:1]; + + NSData *stringData = [string dataUsingEncoding:NSUTF8StringEncoding]; + writeLength(self, (uint32_t)stringData.length); + [_data appendData:stringData]; +} + +- (void)encodeString:(NSString *)string forCKey:(const char *)key +{ + if (key == nil || string == nil) + return; + + uint32_t keyLength = (uint32_t)strlen(key); + writeLength(self, keyLength); + [_data appendBytes:key length:keyLength]; + + uint8_t fieldType = PSKeyValueCoderFieldTypeString; + [_data appendBytes:&fieldType length:1]; + + NSData *stringData = [string dataUsingEncoding:NSUTF8StringEncoding]; + writeLength(self, (uint32_t)stringData.length); + [_data appendData:stringData]; +} + +- (void)encodeInt32:(int32_t)number forKey:(NSString *)key +{ + if (key == nil) + return; + + NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding]; + writeLength(self, (uint32_t)keyData.length); + [_data appendData:keyData]; + + uint8_t fieldType = PSKeyValueCoderFieldTypeInt32; + [_data appendBytes:&fieldType length:1]; + + [_data appendBytes:&number length:4]; +} + +- (void)encodeInt32:(int32_t)number forCKey:(const char *)key +{ + if (key == nil) + return; + + uint32_t keyLength = (uint32_t)strlen(key); + writeLength(self, keyLength); + [_data appendBytes:key length:keyLength]; + + uint8_t fieldType = PSKeyValueCoderFieldTypeInt32; + [_data appendBytes:&fieldType length:1]; + + [_data appendBytes:&number length:4]; +} + +- (void)encodeInt64:(int64_t)number forKey:(NSString *)key +{ + if (key == nil) + return; + + NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding]; + writeLength(self, (uint32_t)keyData.length); + [_data appendData:keyData]; + + uint8_t fieldType = PSKeyValueCoderFieldTypeInt64; + [_data appendBytes:&fieldType length:1]; + + [_data appendBytes:&number length:8]; +} + +- (void)encodeInt64:(int64_t)number forCKey:(const char *)key +{ + if (key == nil) + return; + + uint32_t keyLength = (uint32_t)strlen(key); + writeLength(self, keyLength); + [_data appendBytes:key length:keyLength]; + + uint8_t fieldType = PSKeyValueCoderFieldTypeInt64; + [_data appendBytes:&fieldType length:1]; + + [_data appendBytes:&number length:8]; +} + +- (void)encodeObject:(id)object forKey:(NSString *)key +{ + if (key == nil || object == nil) + return; + + NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding]; + writeLength(self, (uint32_t)keyData.length); + [_data appendData:keyData]; + + uint8_t fieldType = PSKeyValueCoderFieldTypeCustomClass; + [_data appendBytes:&fieldType length:1]; + + uint32_t objectLength = 0; + NSUInteger objectLengthPosition = [_data length]; + [_data appendBytes:&objectLength length:4]; + + NSString *className = NSStringFromClass([object class]); + NSData *classNameData = [className dataUsingEncoding:NSUTF8StringEncoding]; + [_data appendData:classNameData]; + uint8_t zero = 0; + [_data appendBytes:&zero length:1]; + + [object encodeWithKeyValueCoder:self]; + + objectLength = (int)([_data length] - objectLengthPosition - 4); + [_data replaceBytesInRange:NSMakeRange(objectLengthPosition, 4) withBytes:&objectLength]; +} + +static void encodeObjectValue(PSKeyValueEncoder *self, id object) +{ + uint32_t objectLength = 0; + NSUInteger objectLengthPosition = [self->_data length]; + [self->_data appendBytes:&objectLength length:4]; + + Class objectClass = object_getClass(object); + const char *className = class_getName(objectClass); + [self->_data appendBytes:className length:strlen(className) + 1]; + + [object encodeWithKeyValueCoder:self]; + + objectLength = (int)([self->_data length] - objectLengthPosition - 4); + [self->_data replaceBytesInRange:NSMakeRange(objectLengthPosition, 4) withBytes:&objectLength]; +} + +- (void)encodeObject:(id)object forCKey:(const char *)key +{ + if (key == nil || object == nil) + return; + + uint32_t keyLength = (uint32_t)strlen(key); + writeLength(self, keyLength); + [_data appendBytes:key length:keyLength]; + + uint8_t fieldType = PSKeyValueCoderFieldTypeCustomClass; + [_data appendBytes:&fieldType length:1]; + + encodeObjectValue(self, object); +} + +- (void)encodeArray:(NSArray *)array forKey:(NSString *)key +{ + if (key == nil || array == nil) + return; + + NSData *keyData = [key dataUsingEncoding:NSUTF8StringEncoding]; + writeLength(self, (uint32_t)keyData.length); + [_data appendData:keyData]; + + uint8_t fieldType = PSKeyValueCoderFieldTypeArray; + [_data appendBytes:&fieldType length:1]; + + uint32_t objectLength = 0; + NSUInteger objectLengthPosition = [self->_data length]; + [self->_data appendBytes:&objectLength length:4]; + + uint32_t count = (uint32_t)[array count]; + writeLength(self, count); + + for (uint32_t i = 0; i < count; i++) + { + encodeObjectValue(self, array[i]); + } + + objectLength = (int)([_data length] - objectLengthPosition - 4); + [_data replaceBytesInRange:NSMakeRange(objectLengthPosition, 4) withBytes:&objectLength]; +} + +- (void)encodeArray:(NSArray *)array forCKey:(const char *)key +{ + if (key == nil || array == nil) + return; + + uint32_t keyLength = (uint32_t)strlen(key); + writeLength(self, keyLength); + [_data appendBytes:key length:keyLength]; + + uint8_t fieldType = PSKeyValueCoderFieldTypeArray; + [_data appendBytes:&fieldType length:1]; + + uint32_t objectLength = 0; + NSUInteger objectLengthPosition = [self->_data length]; + [self->_data appendBytes:&objectLength length:4]; + + uint32_t count = (uint32_t)[array count]; + writeLength(self, count); + + for (uint32_t i = 0; i < count; i++) + { + encodeObjectValue(self, array[i]); + } + + objectLength = (int)([_data length] - objectLengthPosition - 4); + [_data replaceBytesInRange:NSMakeRange(objectLengthPosition, 4) withBytes:&objectLength]; +} + +- (void)encodeData:(NSData *)data forCKey:(const char *)key +{ + if (key == nil || data == nil) + return; + + uint32_t keyLength = (uint32_t)strlen(key); + writeLength(self, keyLength); + [_data appendBytes:key length:keyLength]; + + uint8_t fieldType = PSKeyValueCoderFieldTypeData; + [_data appendBytes:&fieldType length:1]; + + writeLength(self, (uint32_t)data.length); + [_data appendData:data]; +} + +- (void)encodeBytes:(uint8_t const *)value length:(NSUInteger)length forCKey:(const char *)key +{ + if (key == nil) + return; + + uint32_t keyLength = (uint32_t)strlen(key); + writeLength(self, keyLength); + [_data appendBytes:key length:keyLength]; + + uint8_t fieldType = PSKeyValueCoderFieldTypeData; + [_data appendBytes:&fieldType length:1]; + + writeLength(self, (uint32_t)length); + [_data appendBytes:value length:length]; +} + +- (void)encodeInt32Array:(NSArray *)value forCKey:(const char *)key { + if (key == NULL) { + return; + } + + uint32_t keyLength = (uint32_t)strlen(key); + writeLength(self, keyLength); + [_data appendBytes:key length:keyLength]; + + uint8_t fieldType = PSKeyValueCoderFieldTypeInt32Array; + [_data appendBytes:&fieldType length:1]; + + int32_t count = (int32_t)value.count; + [_data appendBytes:&count length:4]; + + for (NSNumber *nNumber in value) { + int32_t number = [nNumber intValue]; + [_data appendBytes:&number length:4]; + } +} + +- (void)encodeInt32Dictionary:(NSDictionary *)value forCKey:(const char *)key { + if (key == NULL) { + return; + } + + uint32_t keyLength = (uint32_t)strlen(key); + writeLength(self, keyLength); + [_data appendBytes:key length:keyLength]; + + uint8_t fieldType = PSKeyValueCoderFieldTypeInt32Dictionary; + [_data appendBytes:&fieldType length:1]; + + uint32_t objectLength = 0; + NSUInteger objectLengthPosition = [self->_data length]; + [self->_data appendBytes:&objectLength length:4]; + + NSArray *allKeys = [value allKeys]; + uint32_t count = (uint32_t)[allKeys count]; + writeLength(self, count); + + for (NSNumber *nKey in allKeys) { + id object = value[nKey]; + int32_t objectKey = [nKey intValue]; + [self->_data appendBytes:&objectKey length:4]; + encodeObjectValue(self, object); + } + + objectLength = (int)([_data length] - objectLengthPosition - 4); + [_data replaceBytesInRange:NSMakeRange(objectLengthPosition, 4) withBytes:&objectLength]; +} + +- (void)encodeDouble:(double)value forCKey:(const char *)key { + uint32_t keyLength = (uint32_t)strlen(key); + writeLength(self, keyLength); + [_data appendBytes:key length:keyLength]; + + uint8_t fieldType = PSKeyValueCoderFieldTypeDouble; + [_data appendBytes:&fieldType length:1]; + + [_data appendBytes:&value length:8]; +} + +- (void)reset +{ + [_data setLength:0]; +} + +- (NSData *)data +{ + return _data; +} + +@end diff --git a/LegacyComponents/PSKeyValueReader.h b/LegacyComponents/PSKeyValueReader.h new file mode 100644 index 0000000000..be958a4d1c --- /dev/null +++ b/LegacyComponents/PSKeyValueReader.h @@ -0,0 +1,24 @@ +#import + +#import + +typedef enum { + PSKeyValueReaderSelectLowerKey = 0, + PSKeyValueReaderSelectHigherKey = 1 +} PSKeyValueReaderSelectKey; + +typedef enum { + PSKeyValueReaderEnumerationReverse = 1, + PSKeyValueReaderEnumerationLowerBoundExclusive = 2, + PSKeyValueReaderEnumerationUpperBoundExclusive = 4 +} PSKeyValueReaderEnumerationOptions; + +@protocol PSKeyValueReader + +- (bool)readValueForRawKey:(PSConstData *)key value:(PSConstData *)value; + +- (bool)readValueBetweenLowerBoundKey:(PSConstData *)lowerBoundKey upperBoundKey:(PSConstData *)upperBoundKey selectKey:(PSKeyValueReaderSelectKey)selectKey selectedKey:(PSConstData *)selectedKey selectedValue:(PSConstData *)selectedValue; + +- (void)enumerateKeysAndValuesBetweenLowerBoundKey:(PSConstData *)lowerBoundKey upperBoundKey:(PSConstData *)upperBoundKey options:(NSInteger)options withBlock:(void (^)(PSConstData *key, PSConstData *value, bool *stop))block; + +@end diff --git a/LegacyComponents/PSKeyValueStore.h b/LegacyComponents/PSKeyValueStore.h new file mode 100644 index 0000000000..a9dd0d2289 --- /dev/null +++ b/LegacyComponents/PSKeyValueStore.h @@ -0,0 +1,11 @@ +#import +#import + +@protocol PSKeyValueStore + +- (void)readInTransaction:(void (^)(id))transaction; +- (void)readWriteInTransaction:(void (^)(id))transaction; + +- (void)sync; + +@end diff --git a/LegacyComponents/PSKeyValueWriter.h b/LegacyComponents/PSKeyValueWriter.h new file mode 100644 index 0000000000..6e668f5ba1 --- /dev/null +++ b/LegacyComponents/PSKeyValueWriter.h @@ -0,0 +1,11 @@ +#import + +#import + +@protocol PSKeyValueWriter + +- (void)writeValueForRawKey:(const uint8_t *)key keyLength:(NSUInteger)keyLength value:(const uint8_t *)value valueLength:(NSUInteger)valueLength; +- (bool)deleteValueForRawKey:(PSData *)key; +- (void)deleteAllValues; + +@end diff --git a/LegacyComponents/RMPhoneFormat.h b/LegacyComponents/RMPhoneFormat.h new file mode 100644 index 0000000000..83525db4f7 --- /dev/null +++ b/LegacyComponents/RMPhoneFormat.h @@ -0,0 +1,58 @@ +/* + * This is the source code of Telegram for iOS v. 1.1 + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Peter Iakovlev, 2013. + */ + +// +// RMPhoneFormat.h v1.0 + +// Copyright (c) 2012, Rick Maddy +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// * 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. + +#import + +@interface RMPhoneFormat : NSObject + ++ (RMPhoneFormat *)instance; + +- (id)init; +- (id)initWithDefaultCountry:(NSString *)countryCode; + +- (NSString *)format:(NSString *)str implicitPlus:(bool)implicitPlus; + +// Calling code for the user's default country based on their Region Format setting +- (NSString *)defaultCallingCode; +// countryCode must be 2-letter ISO 3166-1 code. Result does not include a leading + +- (NSString *)callingCodeForCountryCode:(NSString *)countryCode; +// callingCode should be 1 to 3 digit calling code. Result is a set of matching, lowercase, 2-letter ISO 3166-1 country codes +- (NSSet *)countriesForCallingCode:(NSString *)callingCode; + +#ifdef DEBUG +- (void)dump; +#endif + +@end diff --git a/LegacyComponents/RMPhoneFormat.m b/LegacyComponents/RMPhoneFormat.m new file mode 100644 index 0000000000..fd00690ef3 --- /dev/null +++ b/LegacyComponents/RMPhoneFormat.m @@ -0,0 +1,754 @@ +#import "RMPhoneFormat.h" + +@interface PhoneRule : NSObject + +@property (nonatomic, assign) int minVal; +@property (nonatomic, assign) int maxVal; +@property (nonatomic, assign) int byte8; +@property (nonatomic, assign) int maxLen; +@property (nonatomic, assign) int otherFlag; +@property (nonatomic, assign) int prefixLen; +@property (nonatomic, assign) int flag12; +@property (nonatomic, assign) int flag13; +@property (nonatomic) NSString *format; +#ifdef DEBUG +@property (nonatomic) NSSet *countries; +@property (nonatomic) NSString *callingCode; +@property (nonatomic, assign) int matchLen; +#endif + +- (NSString *)format:(NSString *)str intlPrefix:(NSString *)intlPrefix trunkPrefix:(NSString *)trunkPrefix; + +@end + +@implementation PhoneRule + +@synthesize minVal = _minVal; +@synthesize maxVal = _maxVal; +@synthesize byte8 = _byte8; +@synthesize maxLen = _maxLen; +@synthesize otherFlag = _otherFlag; +@synthesize prefixLen = _prefixLen; +@synthesize flag12 = _flag12; +@synthesize flag13 = _flag13; +@synthesize format = _format; + +#ifdef DEBUG +@synthesize countries = _countries; +@synthesize callingCode = _callingCode; +@synthesize matchLen = _matchLen; +#endif + +- (NSString *)format:(NSString *)str intlPrefix:(NSString *)intlPrefix trunkPrefix:(NSString *)trunkPrefix { + BOOL hadC = NO; + BOOL hadN = NO; + BOOL hasOpen = NO; + int spot = 0; + NSMutableString *res = [[NSMutableString alloc] initWithCapacity:20]; + for (int i = 0; i < (int)[self.format length]; i++) { + unichar ch = [self.format characterAtIndex:i]; + switch (ch) { + case 'c': + // Add international prefix if there is one. + hadC = YES; + if (intlPrefix != nil) { + [res appendString:intlPrefix]; + } + break; + case 'n': + // Add trunk prefix if there is one. + hadN = YES; + if (trunkPrefix != nil) { + [res appendString:trunkPrefix]; + } + break; + case '#': + // Add next digit from number. If there aren't enough digits left then do nothing unless we need to + // space-fill a pair of parenthesis. + if (spot < (int)[str length]) { + [res appendString:[str substringWithRange:NSMakeRange(spot, 1)]]; + spot++; + } else if (hasOpen) { + [res appendString:@" "]; + } + break; + case '(': + // Flag we found an open paren so it can be space-filled. But only do so if we aren't beyond the + // end of the number. + if (spot < (int)[str length]) { + hasOpen = YES; + } + // fall through + default: // rest like ) and - + // Don't show space after n if no trunkPrefix or after c if no intlPrefix + if (!(ch == ' ' && i > 0 && (([self.format characterAtIndex:i - 1] == 'n' && trunkPrefix == nil) || ([self.format characterAtIndex:i - 1] == 'c' && intlPrefix == nil)))) { + // Only show punctuation if not beyond the end of the supplied number. + // The only exception is to show a close paren if we had found + if (spot < (int)[str length] || (hasOpen && ch == ')')) { + [res appendString:[self.format substringWithRange:NSMakeRange(i, 1)]]; + if (ch == ')') { + hasOpen = NO; // close it + } + } + } + break; + } + } + + // Not all format strings have a 'c' or 'n' in them. If we have an international prefix or a trunk prefix but the + // format string doesn't explictly say where to put it then simply add it to the beginning. + if (intlPrefix != nil && !hadC) { + [res insertString:[NSString stringWithFormat:@"%@ ", intlPrefix] atIndex:0]; + } else if (trunkPrefix != nil && !hadN) { + [res insertString:trunkPrefix atIndex:0]; + } + + return res; +} + +- (NSString *)description { +#ifdef DEBUG + return [NSString stringWithFormat:@"Rule: { countries: %@, calling code: %@, matchlen: %d, minVal: %d, maxVal: %d, byte8: %d, maxLen: %d, nFlag: %d, prefixLen: %d, flag12: %d, flag13: %d, format: %@ }", self.countries, self.callingCode, self.matchLen, self.minVal, self.maxVal, self.byte8, self.maxLen, self.otherFlag, self.prefixLen, self.flag12, self.flag13, self.format]; +#else + return [NSString stringWithFormat:@"Rule: { minVal: %d, maxVal: %d, byte8: %d, maxLen: %d, nFlag: %d, prefixLen: %d, flag12: %d, flag13: %d, format: %@ }", self.minVal, self.maxVal, self.byte8, self.maxLen, self.otherFlag, self.prefixLen, self.flag12, self.flag13, self.format]; +#endif +} + + +@end + + +@interface RuleSet : NSObject + +@property (nonatomic, assign) int matchLen; +@property (nonatomic) NSMutableArray *rules; + +- (NSString *)format:(NSString *)str intlPrefix:(NSString *)intlPrefix trunkPrefix:(NSString *)trunkPrefix prefixRequired:(BOOL)prefixRequired; + +@end + +@implementation RuleSet + +@synthesize matchLen = _matchLen; +@synthesize rules = _rules; + +- (NSString *)format:(NSString *)str intlPrefix:(NSString *)intlPrefix trunkPrefix:(NSString *)trunkPrefix prefixRequired:(BOOL)prefixRequired { + if ((int)[str length] >= self.matchLen) + { + NSString *begin = [str substringToIndex:self.matchLen]; + int val = [begin intValue]; + for (PhoneRule *rule in self.rules) + { + if (val >= rule.minVal && val <= rule.maxVal && (int)[str length] <= rule.maxLen) + { + if (prefixRequired) + { + if ( + ((rule.flag12 & 0x03) == 0 && + trunkPrefix == nil && + intlPrefix == nil) || + (trunkPrefix != nil && (rule.flag12 & 0x01)) || + (intlPrefix != nil && (rule.flag12 & 0x02)) + ) + { + return [rule format:str intlPrefix:intlPrefix trunkPrefix:trunkPrefix]; + } + } + else + { + if ((trunkPrefix == nil && intlPrefix == nil) || (trunkPrefix != nil && (rule.flag12 & 0x01)) || (intlPrefix != nil && (rule.flag12 & 0x02))) + { + return [rule format:str intlPrefix:intlPrefix trunkPrefix:trunkPrefix]; + } + } + } + } + + if (!prefixRequired) + { + if (intlPrefix != nil) + { + for (PhoneRule *rule in self.rules) + { + if (val >= rule.minVal && val <= rule.maxVal && (int)[str length] <= rule.maxLen) + { + if (trunkPrefix == nil || (rule.flag12 & 0x01)) + { + return [rule format:str intlPrefix:intlPrefix trunkPrefix:trunkPrefix]; + } + } + } + } else if (trunkPrefix != nil) + { + for (PhoneRule *rule in self.rules) + { + if (val >= rule.minVal && val <= rule.maxVal && (int)[str length] <= rule.maxLen) + { + if (intlPrefix == nil || (rule.flag12 & 0x02)) + { + return [rule format:str intlPrefix:intlPrefix trunkPrefix:trunkPrefix]; + } + } + } + } + } + + return nil; // no match found + } else { + return nil; // not long enough to compare + } +} + +- (NSString *)description { + NSMutableString *res = [NSMutableString stringWithCapacity:100]; + [res appendFormat:@"RuleSet: { matchLen: %d, rules: %@ }", self.matchLen, self.rules]; + + return res; +} + + +@end + + +@interface CallingCodeInfo : NSObject + +@property (nonatomic) NSSet *countries; +@property (nonatomic) NSString *callingCode; +@property (nonatomic) NSMutableArray *trunkPrefixes; +@property (nonatomic) NSMutableArray *intlPrefixes; +@property (nonatomic) NSMutableArray *ruleSets; +@property (nonatomic) NSMutableArray *formatStrings; + +- (NSString *)matchingAccessCode:(NSString *)str; +- (NSString *)format:(NSString *)str; + +@end + +@implementation CallingCodeInfo + +@synthesize countries = _countries; +@synthesize callingCode = _callingCode; +@synthesize trunkPrefixes = _trunkPrefixes; +@synthesize intlPrefixes = _intlPrefixes; +@synthesize ruleSets = _ruleSets; +@synthesize formatStrings = _formatStrings; + +- (NSString *)matchingAccessCode:(NSString *)str { + for (NSString *code in self.intlPrefixes) { + if ([str hasPrefix:code]) { + return code; + } + } + + return nil; +} + +- (NSString *)matchingTrunkCode:(NSString *)str { + for (NSString *code in self.trunkPrefixes) { + if ([str hasPrefix:code]) { + return code; + } + } + + return nil; +} + +- (NSString *)format:(NSString *)orig +{ + NSString *str = orig; + NSString *trunkPrefix = nil; + NSString *intlPrefix = nil; + if ([str hasPrefix:self.callingCode]) + { + intlPrefix = self.callingCode; + str = [str substringFromIndex:[intlPrefix length]]; + } + else + { + NSString *trunk = [self matchingTrunkCode:str]; + if (trunk) + { + trunkPrefix = trunk; + str = [str substringFromIndex:[trunkPrefix length]]; + } + } + + for (RuleSet *set in self.ruleSets) + { + NSString *phone = [set format:str intlPrefix:intlPrefix trunkPrefix:trunkPrefix prefixRequired:YES]; + if (phone) + { + return phone; + } + } + + for (RuleSet *set in self.ruleSets) + { + NSString *phone = [set format:str intlPrefix:intlPrefix trunkPrefix:trunkPrefix prefixRequired:NO]; + if (phone) + { + return phone; + } + } + + if (intlPrefix != nil && [str length]) + { + return [NSString stringWithFormat:@"%@ %@", intlPrefix, str]; + } + + return orig; +} + +- (NSString *)description { + NSMutableString *res = [NSMutableString stringWithCapacity:100]; + [res appendFormat:@"CountryInfo { countries: %@, code: %@, trunkPrefixes: %@, intlPrefixes: %@", self.countries, self.callingCode, self.trunkPrefixes, self.intlPrefixes]; + [res appendFormat:@", rule sets: %@ }", self.ruleSets]; + + return res; +} + + +@end + +static NSCharacterSet *phoneChars = nil; +#ifdef DEBUG +static NSMutableDictionary *extra1CallingCodes = nil; +static NSMutableDictionary *extra2CallingCodes = nil; +static NSMutableDictionary *extra3CallingCodes = nil; +static NSMutableDictionary *flagRules = nil; +#endif + +@implementation RMPhoneFormat { + NSData *_data; + NSString *_defaultCountry; + NSString *_defaultCallingCode; + NSMutableDictionary *_callingCodeOffsets; + NSMutableDictionary *_callingCodeCountries; + NSMutableDictionary *_callingCodeData; + NSMutableDictionary *_countryCallingCode; +} + ++ (void)initialize { + phoneChars = [NSCharacterSet characterSetWithCharactersInString:@"0123456789+*#"]; + +#ifdef DEBUG + extra1CallingCodes = [[NSMutableDictionary alloc] init]; + extra2CallingCodes = [[NSMutableDictionary alloc] init]; + extra3CallingCodes = [[NSMutableDictionary alloc] init]; + flagRules = [[NSMutableDictionary alloc] init]; +#endif +} + ++ (NSString *)strip:(NSString *)str { + NSMutableString *res = [NSMutableString stringWithString:str]; + for (int i = (int)[res length] - 1; i >= 0; i--) { + if (![phoneChars characterIsMember:[res characterAtIndex:i]]) { + [res deleteCharactersInRange:NSMakeRange(i, 1)]; + } + } + + return res; +} + ++ (RMPhoneFormat *)instance { + static RMPhoneFormat *instance = nil; + static dispatch_once_t predicate = 0; + + dispatch_once(&predicate, ^{ instance = [self new]; }); + + return instance; +} + +- (id)init { + self = [self initWithDefaultCountry:nil]; + + return self; +} + +- (id)initWithDefaultCountry:(NSString *)countryCode { + if ((self = [super init])) { + _data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"PhoneFormats" ofType:@"dat"]]; + NSAssert(_data, @"The file PhoneFormats.dat is not in the resource bundle. See the README."); + + if (countryCode.length) { + _defaultCountry = countryCode; + } else { + NSLocale *loc = [NSLocale currentLocale]; + _defaultCountry = [[loc objectForKey:NSLocaleCountryCode] lowercaseString]; + } + _callingCodeOffsets = [[NSMutableDictionary alloc] initWithCapacity:255]; + _callingCodeCountries = [[NSMutableDictionary alloc] initWithCapacity:255]; + _callingCodeData = [[NSMutableDictionary alloc] initWithCapacity:10]; + _countryCallingCode = [[NSMutableDictionary alloc] initWithCapacity:255]; + + [self parseDataHeader]; + } + + return self; +} + +- (NSString *)defaultCallingCode { + return [self callingCodeForCountryCode:_defaultCountry]; +} + +- (NSString *)callingCodeForCountryCode:(NSString *)countryCode { + return [_countryCallingCode objectForKey:[countryCode lowercaseString]]; +} + +- (NSSet *)countriesForCallingCode:(NSString *)callingCode { + if ([callingCode hasPrefix:@"+"]) { + callingCode = [callingCode substringFromIndex:1]; + } + + return [_callingCodeCountries objectForKey:callingCode]; +} + +- (CallingCodeInfo *)findCallingCodeInfo:(NSString *)str { + CallingCodeInfo *res = nil; + for (int i = 0; i < 3; i++) { + if (i < (int)[str length]) { + res = [self callingCodeInfo:[str substringToIndex:i + 1]]; + if (res) { + break; + } + } else { + break; + } + } + + return res; +} + +- (NSString *)format:(NSString *)orig implicitPlus:(bool)implicitPlus +{ + NSString *str = [RMPhoneFormat strip:orig]; + + bool hasPlusPrefix = [str hasPrefix:@"+"]; + + if ([str hasPrefix:@"+"] || implicitPlus) + { + NSString *rest = hasPlusPrefix ? [str substringFromIndex:1] : str; + + CallingCodeInfo *info = [self findCallingCodeInfo:rest]; + if (info) + { + NSString *phone = [info format:rest]; + return [@"+" stringByAppendingString:phone]; + } else + { + return orig; + } + } + else + { + CallingCodeInfo *info = [self callingCodeInfo:_defaultCallingCode]; + if (info == nil) + { + return orig; + } + + NSString *accessCode = [info matchingAccessCode:str]; + if (accessCode) + { + NSString *rest = [str substringFromIndex:[accessCode length]]; + NSString *phone = rest; + + CallingCodeInfo *info2 = [self findCallingCodeInfo:rest]; + if (info2) + { + phone = [info2 format:rest]; + } + + if ([phone length] == 0) + { + return accessCode; + } + else + { + return [NSString stringWithFormat:@"%@ %@", accessCode, phone]; + } + } + else + { + NSString *phone = [info format:str]; + + return phone; + } + } + + return orig; +} + +- (uint32_t)value32:(NSUInteger)offset { + if (offset + 4 <= [_data length]) { + return OSReadLittleInt32([_data bytes], offset); + } else { + return 0; + } +} + +- (int)value16:(NSUInteger)offset { + if (offset + 2 <= [_data length]) { + return OSReadLittleInt16([_data bytes], offset); + } else { + return 0; + } +} + +- (int)value16BE:(NSUInteger)offset { + if (offset + 2 <= [_data length]) { + return OSReadBigInt16([_data bytes], offset); + } else { + return 0; + } +} + +- (CallingCodeInfo *)callingCodeInfo:(NSString *)callingCode +{ + CallingCodeInfo *res = [_callingCodeData objectForKey:callingCode]; + if (res == nil) + { + NSNumber *num = [_callingCodeOffsets objectForKey:callingCode]; + if (num) + { + const uint8_t *bytes = [_data bytes]; + uint32_t start = (int)[num longValue]; + uint32_t offset = start; + res = [[CallingCodeInfo alloc] init]; + res.callingCode = callingCode; + res.countries = [_callingCodeCountries objectForKey:callingCode]; + [_callingCodeData setObject:res forKey:callingCode]; + + uint16_t block1Len = (uint16_t)[self value16:offset]; + offset += 2; +#ifdef DEBUG + uint16_t extra1 = (uint16_t)[self value16:offset]; +#endif + offset += 2; + uint16_t block2Len = (uint16_t)[self value16:offset]; + offset += 2; +#ifdef DEBUG + uint16_t extra2 = (uint16_t)[self value16:offset]; +#endif + offset += 2; + uint16_t setCnt = (uint16_t)[self value16:offset]; + offset += 2; +#ifdef DEBUG + uint16_t extra3 = (uint16_t)[self value16:offset]; +#endif + offset += 2; + +#ifdef DEBUG + if (extra1) { + NSMutableArray *vals = [extra1CallingCodes objectForKey:[NSNumber numberWithInt:extra1]]; + if (!vals) { + vals = [[NSMutableArray alloc] init]; + [extra1CallingCodes setObject:vals forKey:[NSNumber numberWithInt:extra1]]; + } + [vals addObject:res]; + } + if (extra2) { + NSMutableArray *vals = [extra2CallingCodes objectForKey:[NSNumber numberWithInt:extra2]]; + if (!vals) { + vals = [[NSMutableArray alloc] init]; + [extra2CallingCodes setObject:vals forKey:[NSNumber numberWithInt:extra2]]; + } + [vals addObject:res]; + } + if (extra3) { + NSMutableArray *vals = [extra3CallingCodes objectForKey:[NSNumber numberWithInt:extra3]]; + if (!vals) { + vals = [[NSMutableArray alloc] init]; + [extra3CallingCodes setObject:vals forKey:[NSNumber numberWithInt:extra3]]; + } + [vals addObject:res]; + } +#endif + + NSMutableArray *strs = [NSMutableArray arrayWithCapacity:5]; + NSString *str; + while ([(str = [NSString stringWithCString:(char *)bytes + offset encoding:NSUTF8StringEncoding]) length]) { + [strs addObject:str]; + offset += [str length] + 1; + } + res.trunkPrefixes = strs; + offset++; // skip NULL + + strs = [NSMutableArray arrayWithCapacity:5]; + while ([(str = [NSString stringWithCString:(char *)bytes + offset encoding:NSUTF8StringEncoding]) length]) { + [strs addObject:str]; + offset += [str length] + 1; + } + res.intlPrefixes = strs; + + NSMutableArray *ruleSets = [NSMutableArray arrayWithCapacity:setCnt]; + offset = start + block1Len; // Start of rule sets + for (int s = 0; s < setCnt; s++) { + RuleSet *ruleSet = [[RuleSet alloc] init]; + int matchCnt = [self value16:offset]; + ruleSet.matchLen = matchCnt; + offset += 2; + int ruleCnt = [self value16:offset]; + offset += 2; + NSMutableArray *rules = [NSMutableArray arrayWithCapacity:ruleCnt]; + for (int r = 0; r < ruleCnt; r++) { + PhoneRule *rule = [[PhoneRule alloc] init]; + rule.minVal = [self value32:offset]; + offset += 4; + rule.maxVal = [self value32:offset]; + offset += 4; + rule.byte8 = (int)bytes[offset++]; + rule.maxLen = (int)bytes[offset++]; + rule.otherFlag = (int)bytes[offset++]; + rule.prefixLen = (int)bytes[offset++]; + rule.flag12 = (int)bytes[offset++]; + rule.flag13 = (int)bytes[offset++]; + uint16_t strOffset = (uint16_t)[self value16:offset]; + offset += 2; + rule.format = [NSString stringWithCString:(char *)bytes + start + block1Len + block2Len + strOffset encoding:NSUTF8StringEncoding]; + // Several formats contain [[9]] or [[8]]. Using the Contacts app as a test, I can find no use + // for these. Do they mean "optional"? They don't seem to have any use. This code strips out + // anything in [[..]] + NSRange openPos = [rule.format rangeOfString:@"[["]; + if (openPos.location != NSNotFound) { + NSRange closePos = [rule.format rangeOfString:@"]]"]; + rule.format = [NSString stringWithFormat:@"%@%@", [rule.format substringToIndex:openPos.location], [rule.format substringFromIndex:closePos.location + closePos.length]]; + } + + [rules addObject:rule]; +#ifdef DEBUG + rule.countries = res.countries; + rule.callingCode = res.callingCode; + rule.matchLen = matchCnt; + if (rule.byte8) { + NSMutableDictionary *data = [flagRules objectForKey:@"byte8"]; + if (!data) { + data = [[NSMutableDictionary alloc] init]; + [flagRules setObject:data forKey:@"byte8"]; + } + NSMutableArray *list = [data objectForKey:[NSNumber numberWithInt:rule.byte8]]; + if (!list) { + list = [[NSMutableArray alloc] init]; + [data setObject:list forKey:[NSNumber numberWithInt:rule.byte8]]; + } + + [list addObject:rule]; + } + if (rule.prefixLen) { + NSMutableDictionary *data = [flagRules objectForKey:@"prefixLen"]; + if (!data) { + data = [[NSMutableDictionary alloc] init]; + [flagRules setObject:data forKey:@"prefixLen"]; + } + NSMutableArray *list = [data objectForKey:[NSNumber numberWithInt:rule.prefixLen]]; + if (!list) { + list = [[NSMutableArray alloc] init]; + [data setObject:list forKey:[NSNumber numberWithInt:rule.prefixLen]]; + } + + [list addObject:rule]; + } + if (rule.otherFlag) { + NSMutableDictionary *data = [flagRules objectForKey:@"otherFlag"]; + if (!data) { + data = [[NSMutableDictionary alloc] init]; + [flagRules setObject:data forKey:@"otherFlag"]; + } + NSMutableArray *list = [data objectForKey:[NSNumber numberWithInt:rule.otherFlag]]; + if (!list) { + list = [[NSMutableArray alloc] init]; + [data setObject:list forKey:[NSNumber numberWithInt:rule.otherFlag]]; + } + + [list addObject:rule]; + } + if (rule.flag12) { + NSMutableDictionary *data = [flagRules objectForKey:@"flag12"]; + if (!data) { + data = [[NSMutableDictionary alloc] init]; + [flagRules setObject:data forKey:@"flag12"]; + } + NSMutableArray *list = [data objectForKey:[NSNumber numberWithInt:rule.flag12]]; + if (!list) { + list = [[NSMutableArray alloc] init]; + [data setObject:list forKey:[NSNumber numberWithInt:rule.flag12]]; + } + + [list addObject:rule]; + } + if (rule.flag13) { + NSMutableDictionary *data = [flagRules objectForKey:@"flag13"]; + if (!data) { + data = [[NSMutableDictionary alloc] init]; + [flagRules setObject:data forKey:@"flag13"]; + } + NSMutableArray *list = [data objectForKey:[NSNumber numberWithInt:rule.flag13]]; + if (!list) { + list = [[NSMutableArray alloc] init]; + [data setObject:list forKey:[NSNumber numberWithInt:rule.flag13]]; + } + + [list addObject:rule]; + } +#endif + } + ruleSet.rules = rules; + [ruleSets addObject:ruleSet]; + } + res.ruleSets = ruleSets; + } + } + + return res; +} + +- (void)parseDataHeader { + int count = [self value32:0]; + uint32_t base = count * 12 + 4; + const void *bytes = [_data bytes]; + NSUInteger spot = 4; + for (int i = 0; i < count; i++) { + NSString *callingCode = [NSString stringWithCString:bytes + spot encoding:NSUTF8StringEncoding]; + spot += 4; + NSString *country = [NSString stringWithCString:bytes + spot encoding:NSUTF8StringEncoding]; + spot += 4; + uint32_t offset = [self value32:spot] + base; + spot += 4; + + if ([country isEqualToString:_defaultCountry]) { + _defaultCallingCode = callingCode; + } + + [_countryCallingCode setObject:callingCode forKey:country]; + + [_callingCodeOffsets setObject:[NSNumber numberWithLong:offset] forKey:callingCode]; + NSMutableSet *countries = [_callingCodeCountries objectForKey:callingCode]; + if (!countries) { + countries = [[NSMutableSet alloc] init]; + [_callingCodeCountries setObject:countries forKey:callingCode]; + } + [countries addObject:country]; + } + + if (_defaultCallingCode) { + [self callingCodeInfo:_defaultCallingCode]; + } +} + +#ifdef DEBUG +- (void)dump { + NSArray *callingCodes = [[_callingCodeOffsets allKeys] sortedArrayUsingSelector:@selector(compare:)]; + for (NSString *callingCode in callingCodes) { + CallingCodeInfo *info = [self callingCodeInfo:callingCode]; + NSLog(@"%@", info); + } + + NSLog(@"flagRules: %@", flagRules); + NSLog(@"extra1 calling codes: %@", extra1CallingCodes); + NSLog(@"extra2 calling codes: %@", extra2CallingCodes); + NSLog(@"extra3 calling codes: %@", extra3CallingCodes); +} +#endif + + +@end diff --git a/LegacyComponents/TGActionMediaAttachment.h b/LegacyComponents/TGActionMediaAttachment.h new file mode 100644 index 0000000000..38d788223f --- /dev/null +++ b/LegacyComponents/TGActionMediaAttachment.h @@ -0,0 +1,52 @@ +/* + * This is the source code of Telegram for iOS v. 1.1 + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Peter Iakovlev, 2013. + */ + +#import + +#define TGActionMediaAttachmentType 0x1167E28B + +typedef enum { + TGMessageActionNone = 0, + TGMessageActionChatEditTitle = 1, + TGMessageActionChatAddMember = 2, + TGMessageActionChatDeleteMember = 3, + TGMessageActionCreateChat = 4, + TGMessageActionChatEditPhoto = 5, + TGMessageActionContactRequest = 6, + TGMessageActionAcceptContactRequest = 7, + TGMessageActionContactRegistered = 8, + TGMessageActionUserChangedPhoto = 9, + TGMessageActionEncryptedChatRequest = 10, + TGMessageActionEncryptedChatAccept = 11, + TGMessageActionEncryptedChatDecline = 12, + TGMessageActionEncryptedChatMessageLifetime = 13, + TGMessageActionEncryptedChatScreenshot = 14, + TGMessageActionEncryptedChatMessageScreenshot = 15, + TGMessageActionCreateBroadcastList = 16, + TGMessageActionJoinedByLink = 17, + TGMessageActionChannelCreated = 18, + TGMessageActionChannelCommentsStatusChanged = 19, + TGMessageActionChannelInviter = 20, + TGMessageActionGroupMigratedTo = 21, + TGMessageActionGroupDeactivated = 22, + TGMessageActionGroupActivated = 23, + TGMessageActionChannelMigratedFrom = 24, + TGMessageActionPinnedMessage = 25, + TGMessageActionClearChat = 26, + TGMessageActionGameScore = 27, + TGMessageActionPhoneCall = 28, + TGMessageActionPaymentSent = 29, + TGMessageActionCustom = 1000 +} TGMessageAction; + +@interface TGActionMediaAttachment : TGMediaAttachment + +@property (nonatomic) TGMessageAction actionType; +@property (nonatomic, strong) NSDictionary *actionData; + +@end diff --git a/LegacyComponents/TGActionMediaAttachment.m b/LegacyComponents/TGActionMediaAttachment.m new file mode 100644 index 0000000000..8e5abe7b38 --- /dev/null +++ b/LegacyComponents/TGActionMediaAttachment.m @@ -0,0 +1,388 @@ +#import "TGActionMediaAttachment.h" + +#import "TGImageMediaAttachment.h" + +@implementation TGActionMediaAttachment + +- (id)init +{ + self = [super init]; + if (self != nil) + { + self.type = TGActionMediaAttachmentType; + } + return self; +} + +- (void)serialize:(NSMutableData *)data +{ + int dataLengthPtr = (int)data.length; + int zero = 0; + [data appendBytes:&zero length:4]; + + int actionType = _actionType; + [data appendBytes:&actionType length:4]; + + if (actionType == TGMessageActionChatAddMember || actionType == TGMessageActionChatDeleteMember) + { + int uid = [[_actionData objectForKey:@"uid"] intValue]; + [data appendBytes:&uid length:4]; + NSArray *uids = _actionData[@"uids"]; + int32_t uidsCount = (int32_t)uids.count; + [data appendBytes:&uidsCount length:4]; + for (NSNumber *nUid in uids) { + int32_t listUid = [nUid intValue]; + [data appendBytes:&listUid length:4]; + } + } + else if (actionType == TGMessageActionJoinedByLink) + { + int uid = [[_actionData objectForKey:@"inviterId"] intValue]; + [data appendBytes:&uid length:4]; + } + else if (actionType == TGMessageActionChatEditTitle) + { + NSString *title = [_actionData objectForKey:@"title"]; + NSData *titleData = [title dataUsingEncoding:NSUTF8StringEncoding]; + int length = (int)titleData.length; + [data appendBytes:&length length:4]; + [data appendData:titleData]; + } + else if (actionType == TGMessageActionCreateChat) + { + NSString *title = [_actionData objectForKey:@"title"]; + NSData *titleData = [title dataUsingEncoding:NSUTF8StringEncoding]; + int length = (int)titleData.length; + [data appendBytes:&length length:4]; + [data appendData:titleData]; + + NSArray *uids = [_actionData objectForKey:@"uids"]; + int count = (int)uids.count; + [data appendBytes:&count length:4]; + for (NSNumber *nUid in uids) + { + int uid = [nUid intValue]; + [data appendBytes:&uid length:4]; + } + } + else if (actionType == TGMessageActionCreateBroadcastList) + { + NSString *title = [_actionData objectForKey:@"title"]; + NSData *titleData = [title dataUsingEncoding:NSUTF8StringEncoding]; + int length = (int)titleData.length; + [data appendBytes:&length length:4]; + [data appendData:titleData]; + + NSArray *uids = [_actionData objectForKey:@"uids"]; + int count = (int)uids.count; + [data appendBytes:&count length:4]; + for (NSNumber *nUid in uids) + { + int uid = [nUid intValue]; + [data appendBytes:&uid length:4]; + } + } + else if (actionType == TGMessageActionChatEditPhoto) + { + TGImageMediaAttachment *photo = [_actionData objectForKey:@"photo"]; + if (photo != nil) + { + [photo serialize:data]; + } + } + else if (actionType == TGMessageActionContactRequest) + { + int hasPhone = [[_actionData objectForKey:@"hasPhone"] boolValue] ? 1 : 0; + [data appendBytes:&hasPhone length:4]; + } + else if (actionType == TGMessageActionAcceptContactRequest) + { + } + else if (actionType == TGMessageActionContactRegistered) + { + } + else if (actionType == TGMessageActionUserChangedPhoto) + { + TGImageMediaAttachment *photo = [_actionData objectForKey:@"photo"]; + if (photo != nil) + { + [photo serialize:data]; + } + } + else if (actionType == TGMessageActionEncryptedChatRequest) + { + } + else if (actionType == TGMessageActionEncryptedChatAccept) + { + + } + else if (actionType == TGMessageActionEncryptedChatDecline) + { + + } + else if (actionType == TGMessageActionEncryptedChatMessageLifetime) + { + int32_t messageLifetime = [_actionData[@"messageLifetime"] intValue]; + [data appendBytes:&messageLifetime length:4]; + } + else if (actionType == TGMessageActionEncryptedChatScreenshot) + { + } + else if (actionType == TGMessageActionChannelCreated) + { + NSString *title = [_actionData objectForKey:@"title"]; + NSData *titleData = [title dataUsingEncoding:NSUTF8StringEncoding]; + int length = (int)titleData.length; + [data appendBytes:&length length:4]; + [data appendData:titleData]; + } + else if (actionType == TGMessageActionChannelCommentsStatusChanged) { + uint8_t enabled = [_actionData[@"enabled"] boolValue]; + [data appendBytes:&enabled length:1]; + } else if (actionType == TGMessageActionChannelInviter) { + int32_t inviter = [_actionData[@"uid"] intValue]; + [data appendBytes:&inviter length:4]; + } else if (actionType == TGMessageActionGroupMigratedTo) { + int32_t channelId = [_actionData[@"channelId"] intValue]; + [data appendBytes:&channelId length:4]; + } else if (actionType == TGMessageActionGroupDeactivated) { + + } else if (actionType == TGMessageActionGroupActivated) { + + } else if (actionType == TGMessageActionChannelMigratedFrom) { + NSString *title = [_actionData objectForKey:@"title"]; + NSData *titleData = [title dataUsingEncoding:NSUTF8StringEncoding]; + int length = (int)titleData.length; + [data appendBytes:&length length:4]; + [data appendData:titleData]; + + int32_t channelId = [_actionData[@"groupId"] intValue]; + [data appendBytes:&channelId length:4]; + } else if (actionType == TGMessageActionPinnedMessage) { + } else if (actionType == TGMessageActionClearChat) { + } else if (actionType == TGMessageActionGameScore) { + int32_t gameId = [_actionData[@"gameId"] intValue]; + [data appendBytes:&gameId length:4]; + int32_t score = [_actionData[@"score"] intValue]; + [data appendBytes:&score length:4]; + } else if (actionType == TGMessageActionPhoneCall) { + int64_t callId = [_actionData[@"callId"] longLongValue]; + [data appendBytes:&callId length:8]; + int32_t reason = [_actionData[@"reason"] intValue]; + [data appendBytes:&reason length:4]; + int32_t duration = [_actionData[@"duration"] intValue]; + [data appendBytes:&duration length:4]; + } else if (actionType == TGMessageActionPaymentSent) { + NSString *curreny = _actionData[@"currency"]; + NSData *currencyBytes = [curreny dataUsingEncoding:NSUTF8StringEncoding]; + int32_t currencyLength = (int32_t)currencyBytes.length; + [data appendBytes:¤cyLength length:4]; + [data appendData:currencyBytes]; + int32_t totalAmount = [_actionData[@"totalAmount"] intValue]; + [data appendBytes:&totalAmount length:4]; + } + + int dataLength = (int)data.length - dataLengthPtr - 4; + [data replaceBytesInRange:NSMakeRange(dataLengthPtr, 4) withBytes:&dataLength]; +} + +- (TGMediaAttachment *)parseMediaAttachment:(NSInputStream *)is +{ + int dataLength = 0; + [is read:(uint8_t *)&dataLength maxLength:4]; + + TGActionMediaAttachment *actionAttachment = [[TGActionMediaAttachment alloc] init]; + + int actionType = 0; + [is read:(uint8_t *)&actionType maxLength:4]; + dataLength -= 4; + actionAttachment.actionType = (TGMessageAction)actionType; + + if (actionType == TGMessageActionChatAddMember || actionType == TGMessageActionChatDeleteMember) + { + int uid = 0; + [is read:(uint8_t *)&uid maxLength:4]; + dataLength -= 4; + + NSMutableArray *uids = [[NSMutableArray alloc] init]; + if (dataLength >= 4) { + int32_t uidsCount = 0; + [is read:(uint8_t *)&uidsCount maxLength:4]; + dataLength -= 4; + + for (int32_t i = 0; dataLength > 0 && i < uidsCount; i++) { + int32_t listUid = 0; + [is read:(uint8_t *)&listUid maxLength:4]; + [uids addObject:@(listUid)]; + dataLength -= 4; + } + } + + if (uids.count != 0) { + actionAttachment.actionData = @{@"uid": @(uid), @"uids": uids}; + } else { + actionAttachment.actionData = @{@"uid": @(uid)}; + } + } + else if (actionType == TGMessageActionJoinedByLink) + { + int uid = 0; + [is read:(uint8_t *)&uid maxLength:4]; + actionAttachment.actionData = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:uid] forKey:@"inviterId"]; + } + else if (actionType == TGMessageActionChatEditTitle) + { + int length = 0; + [is read:(uint8_t *)&length maxLength:4]; + uint8_t *titleBytes = malloc(length); + [is read:titleBytes maxLength:length]; + NSString *title = [[NSString alloc] initWithBytesNoCopy:titleBytes length:length encoding:NSUTF8StringEncoding freeWhenDone:true]; + actionAttachment.actionData = [NSDictionary dictionaryWithObject:(title == nil ? @"" : title) forKey:@"title"]; + } + else if (actionType == TGMessageActionCreateChat) + { + int length = 0; + [is read:(uint8_t *)&length maxLength:4]; + uint8_t *titleBytes = malloc(length); + [is read:titleBytes maxLength:length]; + NSString *title = [[NSString alloc] initWithBytesNoCopy:titleBytes length:length encoding:NSUTF8StringEncoding freeWhenDone:true]; + + int count = 0; + [is read:(uint8_t *)&count maxLength:4]; + NSMutableArray *uids = [[NSMutableArray alloc] initWithCapacity:count]; + for (int i = 0; i < count; i++) + { + int uid = 0; + [is read:(uint8_t *)&uid maxLength:4]; + if (uid != 0) + [uids addObject:[[NSNumber alloc] initWithInt:uid]]; + } + + actionAttachment.actionData = [[NSDictionary alloc] initWithObjectsAndKeys:(title == nil ? @"" : title), @"title", uids, @"uids", nil]; + } + else if (actionType == TGMessageActionCreateBroadcastList) + { + int length = 0; + [is read:(uint8_t *)&length maxLength:4]; + uint8_t *titleBytes = malloc(length); + [is read:titleBytes maxLength:length]; + NSString *title = [[NSString alloc] initWithBytesNoCopy:titleBytes length:length encoding:NSUTF8StringEncoding freeWhenDone:true]; + + int count = 0; + [is read:(uint8_t *)&count maxLength:4]; + NSMutableArray *uids = [[NSMutableArray alloc] initWithCapacity:count]; + for (int i = 0; i < count; i++) + { + int uid = 0; + [is read:(uint8_t *)&uid maxLength:4]; + if (uid != 0) + [uids addObject:[[NSNumber alloc] initWithInt:uid]]; + } + + actionAttachment.actionData = [[NSDictionary alloc] initWithObjectsAndKeys:(title == nil ? @"" : title), @"title", uids, @"uids", nil]; + } + else if (actionType == TGMessageActionChatEditPhoto) + { + TGImageMediaAttachment *photo = (TGImageMediaAttachment *)[[[TGImageMediaAttachment alloc] init] parseMediaAttachment:is]; + if (photo != nil) + actionAttachment.actionData = [[NSDictionary alloc] initWithObjectsAndKeys:photo, @"photo", nil]; + } + else if (actionType == TGMessageActionContactRequest) + { + int hasPhone = 0; + [is read:(uint8_t *)&hasPhone maxLength:4]; + actionAttachment.actionData = [[NSDictionary alloc] initWithObjectsAndKeys:[[NSNumber alloc] initWithBool:hasPhone != 0], @"hasPhone", nil]; + } + else if (actionType == TGMessageActionAcceptContactRequest) + { + } + else if (actionType == TGMessageActionContactRegistered) + { + } + else if (actionType == TGMessageActionUserChangedPhoto) + { + TGImageMediaAttachment *photo = (TGImageMediaAttachment *)[[[TGImageMediaAttachment alloc] init] parseMediaAttachment:is]; + if (photo != nil) + actionAttachment.actionData = [[NSDictionary alloc] initWithObjectsAndKeys:photo, @"photo", nil]; + } + else if (actionType == TGMessageActionEncryptedChatRequest) + { + } + else if (actionType == TGMessageActionEncryptedChatAccept) + { + } + else if (actionType == TGMessageActionEncryptedChatDecline) + { + } + else if (actionType == TGMessageActionEncryptedChatMessageLifetime) + { + int32_t messageLifetime = 0; + [is read:(uint8_t *)&messageLifetime maxLength:4]; + actionAttachment.actionData = [[NSDictionary alloc] initWithObjectsAndKeys:[[NSNumber alloc] initWithInt:messageLifetime], @"messageLifetime", nil]; + } + else if (actionType == TGMessageActionEncryptedChatScreenshot) + { + } + else if (actionType == TGMessageActionChannelCreated) + { + int length = 0; + [is read:(uint8_t *)&length maxLength:4]; + uint8_t *titleBytes = malloc(length); + [is read:titleBytes maxLength:length]; + NSString *title = [[NSString alloc] initWithBytesNoCopy:titleBytes length:length encoding:NSUTF8StringEncoding freeWhenDone:true]; + actionAttachment.actionData = [NSDictionary dictionaryWithObject:(title == nil ? @"" : title) forKey:@"title"]; + } + else if (actionType == TGMessageActionChannelCommentsStatusChanged) { + uint8_t enabled = 0; + [is read:(uint8_t *)&enabled maxLength:1]; + actionAttachment.actionData = @{@"enabled": @(enabled != 0)}; + } else if (actionType == TGMessageActionChannelInviter) { + int32_t uid = 0; + [is read:(uint8_t *)&uid maxLength:4]; + actionAttachment.actionData = @{@"uid": @(uid)}; + } else if (actionType == TGMessageActionGroupMigratedTo) { + int32_t channelId = 0; + [is read:(uint8_t *)&channelId maxLength:4]; + actionAttachment.actionData = @{@"channelId": @(channelId)}; + } else if (actionType == TGMessageActionChannelMigratedFrom) { + int length = 0; + [is read:(uint8_t *)&length maxLength:4]; + uint8_t *titleBytes = malloc(length); + [is read:titleBytes maxLength:length]; + NSString *title = [[NSString alloc] initWithBytesNoCopy:titleBytes length:length encoding:NSUTF8StringEncoding freeWhenDone:true]; + + int32_t groupId = 0; + [is read:(uint8_t *)&groupId maxLength:4]; + + actionAttachment.actionData = @{@"groupId": @(groupId), @"title": title}; + } else if (actionType == TGMessageActionPinnedMessage) { + } else if (actionType == TGMessageActionClearChat) { + } else if (actionType == TGMessageActionGameScore) { + int gameId = 0; + [is read:(uint8_t *)&gameId maxLength:4]; + int score = 0; + [is read:(uint8_t *)&score maxLength:4]; + actionAttachment.actionData = @{@"gameId": @(gameId), @"score": @(score)}; + } else if (actionType == TGMessageActionPhoneCall) { + int64_t callId = 0; + [is read:(uint8_t *)&callId maxLength:8]; + int32_t reason = 0; + [is read:(uint8_t *)&reason maxLength:4]; + int32_t duration = 0; + [is read:(uint8_t *)&duration maxLength:4]; + actionAttachment.actionData = @{@"callId": @(callId), @"reason": @(reason), @"duration": @(duration)}; + } else if (actionType == TGMessageActionPaymentSent) { + int32_t currencyLength = 0; + [is read:(uint8_t *)¤cyLength maxLength:4]; + uint8_t *titleBytes = malloc(currencyLength); + [is read:titleBytes maxLength:currencyLength]; + NSString *title = [[NSString alloc] initWithBytesNoCopy:titleBytes length:currencyLength encoding:NSUTF8StringEncoding freeWhenDone:true]; + int32_t totalAmount = 0; + [is read:(uint8_t *)&totalAmount maxLength:4]; + actionAttachment.actionData = @{@"currency": title, @"totalAmount": @(totalAmount)}; + } + + return actionAttachment; +} + +@end diff --git a/LegacyComponents/TGAudioMediaAttachment.h b/LegacyComponents/TGAudioMediaAttachment.h new file mode 100644 index 0000000000..92ac7bfe56 --- /dev/null +++ b/LegacyComponents/TGAudioMediaAttachment.h @@ -0,0 +1,33 @@ +/* + * This is the source code of Telegram for iOS v. 1.1 + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Peter Iakovlev, 2013. + */ + +#import + +#define TGAudioMediaAttachmentType 0x3A0E7A32 + +@interface TGAudioMediaAttachment : TGMediaAttachment + +@property (nonatomic) int64_t audioId; +@property (nonatomic) int64_t accessHash; +@property (nonatomic) int32_t datacenterId; + +@property (nonatomic) int64_t localAudioId; + +@property (nonatomic) int32_t duration; +@property (nonatomic) int32_t fileSize; + +@property (nonatomic, strong) NSString *audioUri; + +/*- (NSString *)localFilePath; + ++ (NSString *)localAudioFileDirectoryForLocalAudioId:(int64_t)audioId; ++ (NSString *)localAudioFileDirectoryForRemoteAudioId:(int64_t)audioId; ++ (NSString *)localAudioFilePathForLocalAudioId:(int64_t)audioId; ++ (NSString *)localAudioFilePathForRemoteAudioId:(int64_t)audioId;*/ + +@end diff --git a/LegacyComponents/TGAudioMediaAttachment.m b/LegacyComponents/TGAudioMediaAttachment.m new file mode 100644 index 0000000000..ba6bb7dc32 --- /dev/null +++ b/LegacyComponents/TGAudioMediaAttachment.m @@ -0,0 +1,71 @@ +#import "TGAudioMediaAttachment.h" + +@implementation TGAudioMediaAttachment + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + self.type = TGAudioMediaAttachmentType; + } + return self; +} + +- (void)serialize:(NSMutableData *)data +{ + int dataLengthPtr = (int)data.length; + int32_t zero = 0; + [data appendBytes:&zero length:4]; + + [data appendBytes:&_audioId length:8]; + [data appendBytes:&_accessHash length:8]; + [data appendBytes:&_datacenterId length:4]; + + [data appendBytes:&_localAudioId length:8]; + + [data appendBytes:&_duration length:4]; + [data appendBytes:&_fileSize length:4]; + + NSData *audioUriData = [_audioUri dataUsingEncoding:NSUTF8StringEncoding]; + int32_t audioUriLength = (int32_t)audioUriData.length; + [data appendBytes:&audioUriLength length:4]; + if (audioUriLength != 0) + [data appendData:audioUriData]; + + int dataLength = (int)(data.length - dataLengthPtr - 4); + [data replaceBytesInRange:NSMakeRange(dataLengthPtr, 4) withBytes:&dataLength]; +} + +- (TGMediaAttachment *)parseMediaAttachment:(NSInputStream *)is +{ + int dataLength = 0; + [is read:(uint8_t *)&dataLength maxLength:4]; + + TGAudioMediaAttachment *attachment = [[TGAudioMediaAttachment alloc] init]; + + if (attachment != nil) + { + [is read:(uint8_t *)&attachment->_audioId maxLength:8]; + [is read:(uint8_t *)&attachment->_accessHash maxLength:8]; + [is read:(uint8_t *)&attachment->_datacenterId maxLength:4]; + + [is read:(uint8_t *)&attachment->_localAudioId maxLength:8]; + + [is read:(uint8_t *)&attachment->_duration maxLength:4]; + [is read:(uint8_t *)&attachment->_fileSize maxLength:4]; + + int32_t audioUriLength = 0; + [is read:(uint8_t *)&audioUriLength maxLength:4]; + if (audioUriLength != 0) + { + uint8_t *audioUriBytes = malloc(audioUriLength); + [is read:audioUriBytes maxLength:audioUriLength]; + attachment.audioUri = [[NSString alloc] initWithBytesNoCopy:audioUriBytes length:audioUriLength encoding:NSUTF8StringEncoding freeWhenDone:true]; + } + } + + return attachment; +} + +@end diff --git a/LegacyComponents/TGAudioWaveform.h b/LegacyComponents/TGAudioWaveform.h new file mode 100644 index 0000000000..9deed08ce3 --- /dev/null +++ b/LegacyComponents/TGAudioWaveform.h @@ -0,0 +1,15 @@ +#import + +#import + +@interface TGAudioWaveform : NSObject + +@property (nonatomic, strong, readonly) NSData *samples; +@property (nonatomic, readonly) int32_t peak; + +- (instancetype)initWithSamples:(NSData *)samples peak:(int32_t)peak; +- (instancetype)initWithBitstream:(NSData *)bitstream bitsPerSample:(NSUInteger)bitsPerSample; + +- (NSData *)bitstream; + +@end diff --git a/LegacyComponents/TGAudioWaveform.m b/LegacyComponents/TGAudioWaveform.m new file mode 100644 index 0000000000..0802460a06 --- /dev/null +++ b/LegacyComponents/TGAudioWaveform.m @@ -0,0 +1,85 @@ +#import "TGAudioWaveform.h" + +#import "LegacyComponentsInternal.h" + +#import "PSKeyValueCoder.h" + +static int32_t get_bits(uint8_t const *bytes, unsigned int bitOffset, unsigned int numBits) +{ + uint8_t const *data = bytes; + numBits = (unsigned int)pow(2, numBits) - 1; //this will only work up to 32 bits, of course + data += bitOffset / 8; + bitOffset %= 8; + return (*((int*)data) >> bitOffset) & numBits; +} + +static void set_bits(uint8_t *bytes, int32_t bitOffset, int32_t numBits, int32_t value) { + numBits = (unsigned int)pow(2, numBits) - 1; //this will only work up to 32 bits, of course + uint8_t *data = bytes; + data += bitOffset / 8; + bitOffset %= 8; + *((int32_t *)data) |= ((value) << bitOffset); +} + +@implementation TGAudioWaveform + +- (instancetype)initWithSamples:(NSData *)samples peak:(int32_t)peak { + self = [super init]; + if (self != nil) { + _samples = samples; + _peak = peak; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + return [self initWithSamples:[aDecoder decodeObjectForKey:@"samples"] peak:[aDecoder decodeInt32ForKey:@"peak"]]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_samples forKey:@"samples"]; + [aCoder encodeInt32:_peak forKey:@"peak"]; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder { + return [self initWithSamples:[coder decodeDataCorCKey:"samples"] peak:[coder decodeInt32ForCKey:"peak"]]; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder { + [coder encodeData:_samples forCKey:"samples"]; + [coder encodeInt32:_peak forCKey:"peak"]; +} + +- (instancetype)initWithBitstream:(NSData *)bitstream bitsPerSample:(NSUInteger)bitsPerSample { + int numSamples = (int)(bitstream.length * 8 / bitsPerSample); + uint8_t const *bytes = bitstream.bytes; + int32_t maxSample = (1 << bitsPerSample) - 1; + NSMutableData *result = [[NSMutableData alloc] initWithLength:numSamples * 2]; + int16_t *samples = result.mutableBytes; + int32_t norm = (1 << bitsPerSample) - 1; + for (int i = 0; i < numSamples; i++) { + samples[i] = (int16_t)(((int64_t)get_bits(bytes, i * 5, 5) * maxSample) / norm); + } + return [self initWithSamples:result peak:31]; +} + +- (NSData *)bitstream { + int numSamples = (int)(_samples.length / 2); + int bitstreamLength = (numSamples * 5) / 8 + (((numSamples * 5) % 8) == 0 ? 0 : 1); + NSMutableData *result = [[NSMutableData alloc] initWithLength:bitstreamLength]; + int32_t maxSample = _peak; + uint16_t const *samples = _samples.bytes; + uint8_t *bytes = result.mutableBytes; + + for (int i = 0; i < numSamples; i++) { + int32_t value = MIN(31, ABS((int32_t)samples[i]) * 31 / maxSample); + set_bits(bytes, i * 5, 5, value & 31); + } + return result; +} + +- (BOOL)isEqual:(id)object { + return [object isKindOfClass:[TGAudioWaveform class]] && TGObjectCompare(_samples, ((TGAudioWaveform *)object)->_samples) && _peak == ((TGAudioWaveform *)object)->_peak; +} + +@end diff --git a/LegacyComponents/TGAuthorSignatureMediaAttachment.h b/LegacyComponents/TGAuthorSignatureMediaAttachment.h new file mode 100644 index 0000000000..dd55422c1d --- /dev/null +++ b/LegacyComponents/TGAuthorSignatureMediaAttachment.h @@ -0,0 +1,15 @@ +#import + +@class TGImageMediaAttachment; +@class TGDocumentMediaAttachment; +@class TGWebPageMediaAttachment; + +#define TGAuthorSignatureMediaAttachmentType ((int)0x157b8516) + +@interface TGAuthorSignatureMediaAttachment : TGMediaAttachment + +@property (nonatomic, strong, readonly) NSString *signature; + +- (instancetype)initWithSignature:(NSString *)signature; + +@end diff --git a/LegacyComponents/TGAuthorSignatureMediaAttachment.m b/LegacyComponents/TGAuthorSignatureMediaAttachment.m new file mode 100644 index 0000000000..cc1f3a4920 --- /dev/null +++ b/LegacyComponents/TGAuthorSignatureMediaAttachment.m @@ -0,0 +1,46 @@ +#import "TGAuthorSignatureMediaAttachment.h" + +#import "LegacyComponentsInternal.h" + +#import "NSInputStream+TL.h" + +@implementation TGAuthorSignatureMediaAttachment + +- (instancetype)initWithSignature:(NSString *)signature { + self = [super init]; + if (self != nil) { + self.type = TGAuthorSignatureMediaAttachmentType; + + _signature = signature; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + return [self initWithSignature:[aDecoder decodeObjectForKey:@"signature"]]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_signature forKey:@"signature"]; +} + +- (void)serialize:(NSMutableData *)data +{ + NSData *serializedData = [NSKeyedArchiver archivedDataWithRootObject:self]; + int32_t length = (int32_t)serializedData.length; + [data appendBytes:&length length:4]; + [data appendData:serializedData]; +} + +- (TGMediaAttachment *)parseMediaAttachment:(NSInputStream *)is +{ + int32_t length = [is readInt32]; + NSData *data = [is readData:length]; + return [NSKeyedUnarchiver unarchiveObjectWithData:data]; +} + +- (BOOL)isEqual:(id)object { + return [object isKindOfClass:[TGAuthorSignatureMediaAttachment class]] && TGStringCompare(((TGAuthorSignatureMediaAttachment *)object)->_signature, _signature); +} + +@end diff --git a/LegacyComponents/TGBotComandInfo.h b/LegacyComponents/TGBotComandInfo.h new file mode 100644 index 0000000000..23be3747d7 --- /dev/null +++ b/LegacyComponents/TGBotComandInfo.h @@ -0,0 +1,12 @@ +#import + +#import + +@interface TGBotComandInfo : NSObject + +@property (nonatomic, strong, readonly) NSString *command; +@property (nonatomic, strong, readonly) NSString *commandDescription; + +- (instancetype)initWithCommand:(NSString *)command commandDescription:(NSString *)commandDescription; + +@end diff --git a/LegacyComponents/TGBotComandInfo.m b/LegacyComponents/TGBotComandInfo.m new file mode 100644 index 0000000000..9e18f66869 --- /dev/null +++ b/LegacyComponents/TGBotComandInfo.m @@ -0,0 +1,29 @@ +#import "TGBotComandInfo.h" + +#import "PSKeyValueCoder.h" + +@implementation TGBotComandInfo + +- (instancetype)initWithCommand:(NSString *)command commandDescription:(NSString *)commandDescription +{ + self = [super init]; + if (self != nil) + { + _command = command; + _commandDescription = commandDescription; + } + return self; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + return [self initWithCommand:[coder decodeStringForCKey:"command"] commandDescription:[coder decodeStringForCKey:"commandDescription"]]; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + [coder encodeString:_command forCKey:"command"]; + [coder encodeString:_commandDescription forCKey:"commandDescription"]; +} + +@end diff --git a/LegacyComponents/TGBotContextResultAttachment.h b/LegacyComponents/TGBotContextResultAttachment.h new file mode 100644 index 0000000000..a2095ac83b --- /dev/null +++ b/LegacyComponents/TGBotContextResultAttachment.h @@ -0,0 +1,16 @@ +#import + +#import +#import + +#define TGBotContextResultAttachmentType ((int)0x1718023f) + +@interface TGBotContextResultAttachment : TGMediaAttachment + +@property (nonatomic, readonly) int32_t userId; +@property (nonatomic, strong, readonly) NSString *resultId; +@property (nonatomic, readonly) int64_t queryId; + +- (instancetype)initWithUserId:(int32_t)userId resultId:(NSString *)resultId queryId:(int64_t)queryId; + +@end diff --git a/LegacyComponents/TGBotContextResultAttachment.m b/LegacyComponents/TGBotContextResultAttachment.m new file mode 100644 index 0000000000..862c6de745 --- /dev/null +++ b/LegacyComponents/TGBotContextResultAttachment.m @@ -0,0 +1,44 @@ +#import "TGBotContextResultAttachment.h" + +#import "NSInputStream+TL.h" + +@implementation TGBotContextResultAttachment + +- (instancetype)initWithUserId:(int32_t)userId resultId:(NSString *)resultId queryId:(int64_t)queryId { + self = [super init]; + if (self != nil) { + self.type = TGBotContextResultAttachmentType; + _userId = userId; + _resultId = resultId; + _queryId = queryId; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + return [self initWithUserId:[aDecoder decodeInt32ForKey:@"userId"] resultId:[aDecoder decodeObjectForKey:@"resultId"] queryId:[aDecoder decodeInt64ForKey:@"queryId"]]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeInt32:_userId forKey:@"userId"]; + [aCoder encodeObject:_resultId forKey:@"resultId"]; + [aCoder encodeInt64:_queryId forKey:@"queryId"]; +} + + +- (void)serialize:(NSMutableData *)data +{ + NSData *serializedData = [NSKeyedArchiver archivedDataWithRootObject:self]; + int32_t length = (int32_t)serializedData.length; + [data appendBytes:&length length:4]; + [data appendData:serializedData]; +} + +- (TGMediaAttachment *)parseMediaAttachment:(NSInputStream *)is +{ + int32_t length = [is readInt32]; + NSData *data = [is readData:length]; + return [NSKeyedUnarchiver unarchiveObjectWithData:data]; +} + +@end diff --git a/LegacyComponents/TGBotInfo.h b/LegacyComponents/TGBotInfo.h new file mode 100644 index 0000000000..47cb547ebf --- /dev/null +++ b/LegacyComponents/TGBotInfo.h @@ -0,0 +1,15 @@ +#import + +#import +#import + +@interface TGBotInfo : NSObject + +@property (nonatomic, readonly) int32_t version; +@property (nonatomic, strong, readonly) NSString *shortDescription; +@property (nonatomic, strong, readonly) NSString *botDescription; +@property (nonatomic, strong, readonly) NSArray *commandList; + +- (instancetype)initWithVersion:(int32_t)version shortDescription:(NSString *)shortDescription botDescription:(NSString *)botDescription commandList:(NSArray *)commandList; + +@end diff --git a/LegacyComponents/TGBotInfo.m b/LegacyComponents/TGBotInfo.m new file mode 100644 index 0000000000..f575200bad --- /dev/null +++ b/LegacyComponents/TGBotInfo.m @@ -0,0 +1,33 @@ +#import "TGBotInfo.h" + +#import "PSKeyValueCoder.h" + +@implementation TGBotInfo + +- (instancetype)initWithVersion:(int32_t)version shortDescription:(NSString *)shortDescription botDescription:(NSString *)botDescription commandList:(NSArray *)commandList +{ + self = [super init]; + if (self != nil) + { + _version = version; + _botDescription = botDescription; + _shortDescription = shortDescription; + _commandList = commandList; + } + return self; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + return [self initWithVersion:[coder decodeInt32ForCKey:"version"] shortDescription:[coder decodeStringForCKey:"shortDescription"] botDescription:[coder decodeStringForCKey:"botDescription"] commandList:[coder decodeArrayForCKey:"commandList"]]; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + [coder encodeInt32:_version forCKey:"version"]; + [coder encodeString:_shortDescription forCKey:"shortDescription"]; + [coder encodeString:_botDescription forCKey:"botDescription"]; + [coder encodeArray:_commandList forCKey:"commandList"]; +} + +@end diff --git a/LegacyComponents/TGBotReplyMarkup.h b/LegacyComponents/TGBotReplyMarkup.h new file mode 100644 index 0000000000..7dccceda62 --- /dev/null +++ b/LegacyComponents/TGBotReplyMarkup.h @@ -0,0 +1,24 @@ +#import + +#import + +#import + +@interface TGBotReplyMarkup : NSObject + +@property (nonatomic, readonly) int32_t userId; +@property (nonatomic, readonly) int32_t messageId; +@property (nonatomic, strong, readonly) NSArray *rows; +@property (nonatomic) bool matchDefaultHeight; +@property (nonatomic) bool hideKeyboardOnActivation; +@property (nonatomic) bool alreadyActivated; +@property (nonatomic) bool manuallyHidden; +@property (nonatomic) bool isInline; + +- (instancetype)initWithUserId:(int32_t)userId messageId:(int32_t)messageId rows:(NSArray *)rows matchDefaultHeight:(bool)matchDefaultHeight hideKeyboardOnActivation:(bool)hideKeyboardOnActivation alreadyActivated:(bool)alreadyActivated manuallyHidden:(bool)manuallyHidden isInline:(bool)isInline; + +- (TGBotReplyMarkup *)activatedMarkup; +- (TGBotReplyMarkup *)manuallyHide; +- (TGBotReplyMarkup *)manuallyUnhide; + +@end diff --git a/LegacyComponents/TGBotReplyMarkup.m b/LegacyComponents/TGBotReplyMarkup.m new file mode 100644 index 0000000000..9c9bfcdb6a --- /dev/null +++ b/LegacyComponents/TGBotReplyMarkup.m @@ -0,0 +1,87 @@ +#import "TGBotReplyMarkup.h" + +#import "PSKeyValueCoder.h" + +@implementation TGBotReplyMarkup + +- (instancetype)initWithUserId:(int32_t)userId messageId:(int32_t)messageId rows:(NSArray *)rows matchDefaultHeight:(bool)matchDefaultHeight hideKeyboardOnActivation:(bool)hideKeyboardOnActivation alreadyActivated:(bool)alreadyActivated manuallyHidden:(bool)manuallyHidden isInline:(bool)isInline +{ + self = [super init]; + if (self != nil) + { + _userId = userId; + _messageId = messageId; + _rows = rows; + _matchDefaultHeight = matchDefaultHeight; + _hideKeyboardOnActivation = hideKeyboardOnActivation; + _alreadyActivated = alreadyActivated; + _manuallyHidden = manuallyHidden; + _isInline = isInline; + } + return self; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + return [self initWithUserId:[coder decodeInt32ForCKey:"userId"] messageId:[coder decodeInt32ForCKey:"messageId"] rows:[coder decodeArrayForCKey:"rows"] matchDefaultHeight:[coder decodeInt32ForCKey:"matchDefaultHeight"] hideKeyboardOnActivation:[coder decodeInt32ForCKey:"hideKeyboardOnActivation"] alreadyActivated:[coder decodeInt32ForCKey:"alreadyActivated"] manuallyHidden:[coder decodeInt32ForCKey:"manuallyHidden"] isInline:[coder decodeInt32ForCKey:"isInline"]]; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + [coder encodeInt32:_userId forCKey:"userId"]; + [coder encodeInt32:_messageId forCKey:"messageId"]; + [coder encodeArray:_rows forCKey:"rows"]; + [coder encodeInt32:_matchDefaultHeight forCKey:"matchDefaultHeight"]; + [coder encodeInt32:_hideKeyboardOnActivation forCKey:"hideKeyboardOnActivation"]; + [coder encodeInt32:_alreadyActivated forCKey:"alreadyActivated"]; + [coder encodeInt32:_manuallyHidden forCKey:"manuallyHidden"]; + [coder encodeInt32:_isInline forCKey:"isInline"]; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + return [self initWithUserId:[aDecoder decodeInt32ForKey:@"userId"] messageId:[aDecoder decodeInt32ForKey:@"messageId"] rows:[aDecoder decodeObjectForKey:@"rows"] matchDefaultHeight:[aDecoder decodeBoolForKey:@"matchDefaultHeight"] hideKeyboardOnActivation:[aDecoder decodeBoolForKey:@"hideKeyboardOnDeactivation"] alreadyActivated:[aDecoder decodeBoolForKey:@"alreadyActivated"] manuallyHidden:[aDecoder decodeBoolForKey:@"manuallyHidden"] isInline:[aDecoder decodeBoolForKey:@"isInline"]]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeInt32:_userId forKey:@"userId"]; + [aCoder encodeInt32:_messageId forKey:@"messageId"]; + [aCoder encodeObject:_rows forKey:@"rows"]; + [aCoder encodeBool:_matchDefaultHeight forKey:@"matchDefaultHeight"]; + [aCoder encodeBool:_hideKeyboardOnActivation forKey:@"hideKeyboardOnActivation"]; + [aCoder encodeBool:_alreadyActivated forKey:@"alreadyActivated"]; + [aCoder encodeBool:_manuallyHidden forKey:@"manuallyHidden"]; + [aCoder encodeBool:_isInline forKey:@"isInline"]; +} + +- (BOOL)isEqual:(id)object +{ + return [object isKindOfClass:[TGBotReplyMarkup class]] && [((TGBotReplyMarkup *)object)->_rows isEqual:_rows] && ((TGBotReplyMarkup *)object)->_userId == _userId && ((TGBotReplyMarkup *)object)->_messageId == _messageId && ((TGBotReplyMarkup *)object)->_matchDefaultHeight == _matchDefaultHeight && _isInline == ((TGBotReplyMarkup *)object)->_isInline; +} + +- (TGBotReplyMarkup *)activatedMarkup +{ + if (_alreadyActivated) + return self; + + return [[TGBotReplyMarkup alloc] initWithUserId:_userId messageId:_messageId rows:_rows matchDefaultHeight:_matchDefaultHeight hideKeyboardOnActivation:_hideKeyboardOnActivation alreadyActivated:true manuallyHidden:_manuallyHidden isInline:_isInline]; +} + +- (TGBotReplyMarkup *)manuallyHide +{ + if (_manuallyHidden) { + return self; + } + + return [[TGBotReplyMarkup alloc] initWithUserId:_userId messageId:_messageId rows:_rows matchDefaultHeight:_matchDefaultHeight hideKeyboardOnActivation:_hideKeyboardOnActivation alreadyActivated:_alreadyActivated manuallyHidden:true isInline:_isInline]; +} + +- (TGBotReplyMarkup *)manuallyUnhide +{ + if (!_manuallyHidden) { + return self; + } + + return [[TGBotReplyMarkup alloc] initWithUserId:_userId messageId:_messageId rows:_rows matchDefaultHeight:_matchDefaultHeight hideKeyboardOnActivation:_hideKeyboardOnActivation alreadyActivated:_alreadyActivated manuallyHidden:false isInline:_isInline]; +} + +@end diff --git a/LegacyComponents/TGBotReplyMarkupButton.h b/LegacyComponents/TGBotReplyMarkupButton.h new file mode 100644 index 0000000000..75ec8be851 --- /dev/null +++ b/LegacyComponents/TGBotReplyMarkupButton.h @@ -0,0 +1,61 @@ +#import + +#import + +@interface TGBotReplyMarkupButtonActionUrl : NSObject + +@property (nonatomic, strong, readonly) NSString *url; + +- (instancetype)initWithUrl:(NSString *)url; + +@end + +@interface TGBotReplyMarkupButtonActionCallback : NSObject + +@property (nonatomic, strong, readonly) NSData *data; + +- (instancetype)initWithData:(NSData *)data; + +@end + +@interface TGBotReplyMarkupButtonActionRequestPhone : NSObject + +@end + +@interface TGBotReplyMarkupButtonActionRequestLocation : NSObject + +@end + +@interface TGBotReplyMarkupButtonActionSwitchInline : NSObject + +@property (nonatomic, strong, readonly) NSString *query; +@property (nonatomic, readonly) bool samePeer; + +- (instancetype)initWithQuery:(NSString *)query samePeer:(bool)samePeer; + +@end + +@interface TGBotReplyMarkupButtonActionGame : NSObject + +@property (nonatomic, strong, readonly) NSString *text; + +- (instancetype)initWithText:(NSString *)text; + +@end + +@interface TGBotReplyMarkupButtonActionPurchase : NSObject + +@property (nonatomic, strong, readonly) NSString *text; + +- (instancetype)initWithText:(NSString *)text; + +@end + +@interface TGBotReplyMarkupButton : NSObject + +@property (nonatomic, strong, readonly) NSString *text; +@property (nonatomic, strong, readonly) id action; + +- (instancetype)initWithText:(NSString *)text action:(id)action; + +@end diff --git a/LegacyComponents/TGBotReplyMarkupButton.m b/LegacyComponents/TGBotReplyMarkupButton.m new file mode 100644 index 0000000000..f1e0010427 --- /dev/null +++ b/LegacyComponents/TGBotReplyMarkupButton.m @@ -0,0 +1,262 @@ +#import "TGBotReplyMarkupButton.h" + +#import "LegacyComponentsInternal.h" + +#import "PSKeyValueCoder.h" + +@implementation TGBotReplyMarkupButtonActionUrl + +- (instancetype)initWithUrl:(NSString *)url { + self = [super init]; + if (self != nil) { + _url = url; + } + return self; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder { + return [self initWithUrl:[coder decodeStringForCKey:"url"]]; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder { + [coder encodeString:_url forCKey:"url"]; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + return [self initWithUrl:[aDecoder decodeObjectForKey:@"url"]]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_url forKey:@"url"]; +} + +- (BOOL)isEqual:(id)object { + return [object isKindOfClass:[TGBotReplyMarkupButtonActionUrl class]] && TGStringCompare(_url, ((TGBotReplyMarkupButtonActionUrl *)object)->_url); +} + +@end + +@implementation TGBotReplyMarkupButtonActionCallback + +- (instancetype)initWithData:(NSData *)data { + self = [super init]; + if (self != nil) { + _data = data; + } + return self; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder { + return [self initWithData:[coder decodeDataCorCKey:"data"]]; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder { + [coder encodeData:_data forCKey:"data"]; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + return [self initWithData:[aDecoder decodeObjectForKey:@"data"]]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_data forKey:@"data"]; +} + +- (BOOL)isEqual:(id)object { + return [object isKindOfClass:[TGBotReplyMarkupButtonActionCallback class]] && TGObjectCompare(_data, ((TGBotReplyMarkupButtonActionCallback *)object)->_data); +} + +@end + +@implementation TGBotReplyMarkupButtonActionRequestPhone + +- (instancetype)init { + return [super init]; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)__unused coder { + return [self init]; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)__unused coder { +} + +- (instancetype)initWithCoder:(NSCoder *)__unused aDecoder { + return [self init]; +} + +- (void)encodeWithCoder:(NSCoder *)__unused aCoder { +} + +- (BOOL)isEqual:(id)object { + return [object isKindOfClass:[TGBotReplyMarkupButtonActionRequestPhone class]]; +} + +@end + +@implementation TGBotReplyMarkupButtonActionRequestLocation + +- (instancetype)init { + return [super init]; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)__unused coder { + return [self init]; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)__unused coder { +} + +- (instancetype)initWithCoder:(NSCoder *)__unused aDecoder { + return [self init]; +} + +- (void)encodeWithCoder:(NSCoder *)__unused aCoder { +} + +- (BOOL)isEqual:(id)object { + return [object isKindOfClass:[TGBotReplyMarkupButtonActionRequestLocation class]]; +} + +@end + +@implementation TGBotReplyMarkupButtonActionSwitchInline + +- (instancetype)initWithQuery:(NSString *)query samePeer:(bool)samePeer { + self = [super init]; + if (self != nil) { + _query = query; + _samePeer = samePeer; + } + return self; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder { + return [self initWithQuery:[coder decodeStringForCKey:"query"] samePeer:[coder decodeInt32ForCKey:"samePeer"]]; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder { + [coder encodeString:_query forCKey:"query"]; + [coder encodeInt32:_samePeer ? 1 : 0 forCKey:"samePeer"]; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + return [self initWithQuery:[aDecoder decodeObjectForKey:@"query"] samePeer:[aDecoder decodeBoolForKey:@"samePeer"]]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_query forKey:@"query"]; + [aCoder encodeBool:_samePeer forKey:@"samePeer"]; +} + +- (BOOL)isEqual:(id)object { + return [object isKindOfClass:[TGBotReplyMarkupButtonActionSwitchInline class]] && TGStringCompare(_query, ((TGBotReplyMarkupButtonActionSwitchInline *)object)->_query) && _samePeer == ((TGBotReplyMarkupButtonActionSwitchInline *)object)->_samePeer; +} + +@end + +@implementation TGBotReplyMarkupButtonActionGame + +- (instancetype)initWithText:(NSString *)text { + self = [super init]; + if (self != nil) { + _text = text; + } + return self; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder { + return [self initWithText:[coder decodeStringForCKey:"text"]]; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder { + [coder encodeString:_text forCKey:"text"]; +} + + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + return [self initWithText:[aDecoder decodeObjectForKey:@"text"]]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_text forKey:@"text"]; +} + +- (BOOL)isEqual:(id)object { + return [object isKindOfClass:[TGBotReplyMarkupButtonActionGame class]] && TGStringCompare(_text, ((TGBotReplyMarkupButtonActionGame *)object)->_text); +} + +@end + +@implementation TGBotReplyMarkupButtonActionPurchase + +- (instancetype)initWithText:(NSString *)text { + self = [super init]; + if (self != nil) { + _text = text; + } + return self; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder { + return [self initWithText:[coder decodeStringForCKey:"text"]]; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder { + [coder encodeString:_text forCKey:"text"]; +} + + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + return [self initWithText:[aDecoder decodeObjectForKey:@"text"]]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_text forKey:@"text"]; +} + +- (BOOL)isEqual:(id)object { + return [object isKindOfClass:[TGBotReplyMarkupButtonActionPurchase class]] && TGStringCompare(_text, ((TGBotReplyMarkupButtonActionPurchase *)object)->_text); +} + +@end + +@implementation TGBotReplyMarkupButton + +- (instancetype)initWithText:(NSString *)text action:(id)action +{ + self = [super init]; + if (self != nil) + { + _text = text; + _action = action; + } + return self; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + return [self initWithText:[coder decodeStringForCKey:"text"] action:(id)[coder decodeObjectForKey:@"action"]]; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + [coder encodeString:_text forCKey:"text"]; + [coder encodeObject:_action forCKey:"action"]; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + return [self initWithText:[aDecoder decodeObjectForKey:@"text"] action:[aDecoder decodeObjectForKey:@"action"]]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_text forKey:@"text"]; + [aCoder encodeObject:_action forKey:@"action"]; +} + +- (BOOL)isEqual:(id)object +{ + return [object isKindOfClass:[TGBotReplyMarkupButton class]] && [((TGBotReplyMarkupButton *)object)->_text isEqualToString:_text] && TGObjectCompare(_action, ((TGBotReplyMarkupButton *)object)->_action); +} + +@end diff --git a/LegacyComponents/TGBotReplyMarkupRow.h b/LegacyComponents/TGBotReplyMarkupRow.h new file mode 100644 index 0000000000..b33c40a8dc --- /dev/null +++ b/LegacyComponents/TGBotReplyMarkupRow.h @@ -0,0 +1,13 @@ +#import + +#import + +#import + +@interface TGBotReplyMarkupRow : NSObject + +@property (nonatomic, strong, readonly) NSArray *buttons; + +- (instancetype)initWithButtons:(NSArray *)buttons; + +@end diff --git a/LegacyComponents/TGBotReplyMarkupRow.m b/LegacyComponents/TGBotReplyMarkupRow.m new file mode 100644 index 0000000000..edd4296131 --- /dev/null +++ b/LegacyComponents/TGBotReplyMarkupRow.m @@ -0,0 +1,40 @@ +#import "TGBotReplyMarkupRow.h" + +#import "PSKeyValueCoder.h" + +@implementation TGBotReplyMarkupRow + +- (instancetype)initWithButtons:(NSArray *)buttons +{ + self = [super init]; + if (self != nil) + { + _buttons = buttons; + } + return self; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + return [self initWithButtons:[coder decodeArrayForCKey:"buttons"]]; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + [coder encodeArray:_buttons forCKey:"buttons"]; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + return [self initWithButtons:[aDecoder decodeObjectForKey:@"buttons"]]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_buttons forKey:@"buttons"]; +} + +- (BOOL)isEqual:(id)object +{ + return [object isKindOfClass:[TGBotReplyMarkupRow class]] && [((TGBotReplyMarkupRow *)object)->_buttons isEqual:_buttons]; +} + +@end diff --git a/LegacyComponents/TGChannelAdminRights.h b/LegacyComponents/TGChannelAdminRights.h new file mode 100644 index 0000000000..6acdf02185 --- /dev/null +++ b/LegacyComponents/TGChannelAdminRights.h @@ -0,0 +1,27 @@ +#import + +#import + +@class TLChannelAdminRights; + +@interface TGChannelAdminRights : NSObject + +@property (nonatomic, readonly) bool canChangeInfo; +@property (nonatomic, readonly) bool canPostMessages; +@property (nonatomic, readonly) bool canEditMessages; +@property (nonatomic, readonly) bool canDeleteMessages; +@property (nonatomic, readonly) bool canBanUsers; +@property (nonatomic, readonly) bool canInviteUsers; +@property (nonatomic, readonly) bool canChangeInviteLink; +@property (nonatomic, readonly) bool canPinMessages; +@property (nonatomic, readonly) bool canAddAdmins; + +- (instancetype)initWithCanChangeInfo:(bool)canChangeInfo canPostMessages:(bool)canPostMessages canEditMessages:(bool)canEditMessages canDeleteMessages:(bool)canDeleteMessages canBanUsers:(bool)canBanUsers canInviteUsers:(bool)canInviteUsers canChangeInviteLink:(bool)canChangeInviteLink canPinMessages:(bool)canPinMessages canAddAdmins:(bool)canAddAdmins; + +- (instancetype)initWithFlags:(int32_t)flags; + +- (int32_t)tlFlags; +- (bool)hasAnyRights; +- (int32_t)numberOfRights; + +@end diff --git a/LegacyComponents/TGChannelAdminRights.m b/LegacyComponents/TGChannelAdminRights.m new file mode 100644 index 0000000000..a547f8a812 --- /dev/null +++ b/LegacyComponents/TGChannelAdminRights.m @@ -0,0 +1,95 @@ +#import "TGChannelAdminRights.h" + +#import "PSKeyValueCoder.h" + +@implementation TGChannelAdminRights + +- (instancetype)initWithCanChangeInfo:(bool)canChangeInfo canPostMessages:(bool)canPostMessages canEditMessages:(bool)canEditMessages canDeleteMessages:(bool)canDeleteMessages canBanUsers:(bool)canBanUsers canInviteUsers:(bool)canInviteUsers canChangeInviteLink:(bool)canChangeInviteLink canPinMessages:(bool)canPinMessages canAddAdmins:(bool)canAddAdmins { + self = [super init]; + if (self != nil) { + _canChangeInfo = canChangeInfo; + _canPostMessages = canPostMessages; + _canEditMessages = canEditMessages; + _canDeleteMessages = canDeleteMessages; + _canBanUsers = canBanUsers; + _canInviteUsers = canInviteUsers; + _canChangeInviteLink = canChangeInviteLink; + _canPinMessages = canPinMessages; + _canAddAdmins = canAddAdmins; + } + return self; +} + +- (instancetype)initWithFlags:(int32_t)flags { + return [self initWithCanChangeInfo:flags & (1 << 0) canPostMessages:flags & (1 << 1) canEditMessages:flags & (1 << 2) canDeleteMessages:flags & (1 << 3) canBanUsers:flags & (1 << 4) canInviteUsers:flags & (1 << 5) canChangeInviteLink:flags & (1 << 6) canPinMessages:flags & (1 << 7) canAddAdmins:flags & (1 << 9)]; +} + +- (int32_t)tlFlags { + int32_t flags = 0; + if (_canChangeInfo) { + flags |= (1 << 0); + } + if (_canPostMessages) { + flags |= (1 << 1); + } + if (_canEditMessages) { + flags |= (1 << 2); + } + if (_canDeleteMessages) { + flags |= (1 << 3); + } + if (_canBanUsers) { + flags |= (1 << 4); + } + if (_canInviteUsers) { + flags |= (1 << 5); + } + if (_canChangeInviteLink) { + flags |= (1 << 6); + } + if (_canPinMessages) { + flags |= (1 << 7); + } + if (_canAddAdmins) { + flags |= (1 << 9); + } + return flags; +} + +- (bool)hasAnyRights { + return [self tlFlags] != 0; +} + +- (int32_t)numberOfRights { + int32_t flags = [self tlFlags]; + int32_t count = 0; + for (int i = 0; i < 31; i++) { + if (flags == 0) { + break; + } + if ((flags & 1) != 0) { + count++; + } + flags = flags >> 1; + } + return count; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder { + int32_t flags = [coder decodeInt32ForCKey:"f"]; + return [self initWithFlags:flags]; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder { + [coder encodeInt32:[self tlFlags] forCKey:"f"]; +} + +- (BOOL)isEqual:(id)object { + if (![object isKindOfClass:[TGChannelAdminRights class]]) { + return false; + } + TGChannelAdminRights *other = object; + return [self tlFlags] == [other tlFlags]; +} + +@end diff --git a/LegacyComponents/TGChannelBannedRights.h b/LegacyComponents/TGChannelBannedRights.h new file mode 100644 index 0000000000..6553084643 --- /dev/null +++ b/LegacyComponents/TGChannelBannedRights.h @@ -0,0 +1,25 @@ +#import + +#import + +@interface TGChannelBannedRights : NSObject + +@property (nonatomic, readonly) bool banReadMessages; +@property (nonatomic, readonly) bool banSendMessages; +@property (nonatomic, readonly) bool banSendMedia; +@property (nonatomic, readonly) bool banSendStickers; +@property (nonatomic, readonly) bool banSendGifs; +@property (nonatomic, readonly) bool banSendGames; +@property (nonatomic, readonly) bool banSendInline; +@property (nonatomic, readonly) bool banEmbedLinks; +@property (nonatomic, readonly) int32_t timeout; + +- (instancetype)initWithBanReadMessages:(bool)banReadMessages banSendMessages:(bool)banSendMessages banSendMedia:(bool)banSendMedia banSendStickers:(bool)banSendStickers banSendGifs:(bool)banSendGifs banSendGames:(bool)banSendGames banSendInline:(bool)banSendInline banEmbedLinks:(bool)banEmbedLinks timeout:(int32_t)timeout; + +- (instancetype)initWithFlags:(int32_t)flags timeout:(int32_t)timeout; + +- (int32_t)tlFlags; + +- (int32_t)numberOfRestrictions; + +@end diff --git a/LegacyComponents/TGChannelBannedRights.m b/LegacyComponents/TGChannelBannedRights.m new file mode 100644 index 0000000000..038019ed64 --- /dev/null +++ b/LegacyComponents/TGChannelBannedRights.m @@ -0,0 +1,90 @@ +#import "TGChannelBannedRights.h" + +#import "PSKeyValueCoder.h" + +@implementation TGChannelBannedRights + +- (instancetype)initWithBanReadMessages:(bool)banReadMessages banSendMessages:(bool)banSendMessages banSendMedia:(bool)banSendMedia banSendStickers:(bool)banSendStickers banSendGifs:(bool)banSendGifs banSendGames:(bool)banSendGames banSendInline:(bool)banSendInline banEmbedLinks:(bool)banEmbedLinks timeout:(int32_t)timeout { + self = [super init]; + if (self != nil) { + _banReadMessages = banReadMessages; + _banSendMessages = banSendMessages; + _banSendMedia = banSendMedia; + _banSendStickers = banSendStickers; + _banSendGifs = banSendGifs; + _banSendGames = banSendGames; + _banSendInline = banSendInline; + _banEmbedLinks = banEmbedLinks; + _timeout = timeout; + } + return self; +} + +- (int32_t)tlFlags { + int32_t flags = 0; + if (_banReadMessages) { + flags |= (1 << 0); + } + if (_banSendMessages) { + flags |= (1 << 1); + } + if (_banSendMedia) { + flags |= (1 << 2); + } + if (_banSendStickers) { + flags |= (1 << 3); + } + if (_banSendGifs) { + flags |= (1 << 4); + } + if (_banSendGames) { + flags |= (1 << 5); + } + if (_banSendInline) { + flags |= (1 << 6); + } + if (_banEmbedLinks) { + flags |= (1 << 7); + } + return flags; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder { + int32_t flags = [coder decodeInt32ForCKey:"f"]; + int32_t until_date = [coder decodeInt32ForCKey:"t"]; + return [self initWithFlags:flags timeout:until_date]; +} + +- (instancetype)initWithFlags:(int32_t)flags timeout:(int32_t)timeout { + return [self initWithBanReadMessages:flags & (1 << 0) banSendMessages:flags & (1 << 1) banSendMedia:flags & (1 << 2) banSendStickers:flags & (1 << 3) banSendGifs:flags & (1 << 4) banSendGames:flags & (1 << 5) banSendInline:flags & (1 << 6) banEmbedLinks:flags & (1 << 7) timeout:timeout]; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder { + [coder encodeInt32:[self tlFlags] forCKey:"f"]; + [coder encodeInt32:_timeout forCKey:"t"]; +} + +- (BOOL)isEqual:(id)object { + if ([object isKindOfClass:[TGChannelBannedRights class]] && [((TGChannelBannedRights *)object) tlFlags] == [self tlFlags] && ((TGChannelBannedRights *)object)->_timeout == _timeout) { + return true; + } else { + return false; + } +} + +- (int32_t)numberOfRestrictions { + int32_t flags = [self tlFlags]; + int32_t count = 0; + for (int i = 0; i < 31; i++) { + if (flags == 0) { + break; + } + if ((flags & 1) != 0) { + count++; + } + flags = flags >> 1; + } + return count; +} + +@end diff --git a/LegacyComponents/TGContactMediaAttachment.h b/LegacyComponents/TGContactMediaAttachment.h new file mode 100644 index 0000000000..b6ea98e527 --- /dev/null +++ b/LegacyComponents/TGContactMediaAttachment.h @@ -0,0 +1,20 @@ +/* + * This is the source code of Telegram for iOS v. 1.1 + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Peter Iakovlev, 2013. + */ + +#import + +#define TGContactMediaAttachmentType ((int)0xB90A5663) + +@interface TGContactMediaAttachment : TGMediaAttachment + +@property (nonatomic) int uid; +@property (nonatomic, strong) NSString *firstName; +@property (nonatomic, strong) NSString *lastName; +@property (nonatomic, strong) NSString *phoneNumber; + +@end diff --git a/LegacyComponents/TGContactMediaAttachment.m b/LegacyComponents/TGContactMediaAttachment.m new file mode 100644 index 0000000000..4337b951b6 --- /dev/null +++ b/LegacyComponents/TGContactMediaAttachment.m @@ -0,0 +1,98 @@ +#import "TGContactMediaAttachment.h" + +@implementation TGContactMediaAttachment + +@synthesize uid = _uid; +@synthesize firstName = _firstName; +@synthesize lastName = _lastName; +@synthesize phoneNumber = _phoneNumber; + +- (id)init +{ + self = [super init]; + if (self != nil) + { + self.type = TGContactMediaAttachmentType; + } + return self; +} + +- (void)serialize:(NSMutableData *)data +{ + int dataLengthPtr = (int)data.length; + int zero = 0; + [data appendBytes:&zero length:4]; + + [data appendBytes:&_uid length:4]; + + NSData *firstNameData = [_firstName dataUsingEncoding:NSUTF8StringEncoding]; + int length = (int)firstNameData.length; + [data appendBytes:&length length:4]; + [data appendData:firstNameData]; + + NSData *lastNameData = [_lastName dataUsingEncoding:NSUTF8StringEncoding]; + length = (int)lastNameData.length; + [data appendBytes:&length length:4]; + [data appendData:lastNameData]; + + NSData *phoneData = [_phoneNumber dataUsingEncoding:NSUTF8StringEncoding]; + length = (int)phoneData.length; + [data appendBytes:&length length:4]; + [data appendData:phoneData]; + + int dataLength = (int)data.length - dataLengthPtr - 4; + [data replaceBytesInRange:NSMakeRange(dataLengthPtr, 4) withBytes:&dataLength]; +} + +- (TGMediaAttachment *)parseMediaAttachment:(NSInputStream *)is +{ + int dataLength = 0; + [is read:(uint8_t *)&dataLength maxLength:4]; + + TGContactMediaAttachment *contactAttachment = [[TGContactMediaAttachment alloc] init]; + + int uid = 0; + [is read:(uint8_t *)&uid maxLength:4]; + contactAttachment.uid = uid; + + int length = 0; + [is read:(uint8_t *)&length maxLength:4]; + uint8_t *firstNameBytes = malloc(length); + [is read:firstNameBytes maxLength:length]; + contactAttachment.firstName = [[NSString alloc] initWithBytesNoCopy:firstNameBytes length:length encoding:NSUTF8StringEncoding freeWhenDone:true]; + + length = 0; + [is read:(uint8_t *)&length maxLength:4]; + uint8_t *lastNameBytes = malloc(length); + [is read:lastNameBytes maxLength:length]; + contactAttachment.lastName = [[NSString alloc] initWithBytesNoCopy:lastNameBytes length:length encoding:NSUTF8StringEncoding freeWhenDone:true]; + + length = 0; + [is read:(uint8_t *)&length maxLength:4]; + uint8_t *phoneBytes = malloc(length); + [is read:phoneBytes maxLength:length]; + contactAttachment.phoneNumber = [[NSString alloc] initWithBytesNoCopy:phoneBytes length:length encoding:NSUTF8StringEncoding freeWhenDone:true]; + + return contactAttachment; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + self.type = TGContactMediaAttachmentType; + _uid = [aDecoder decodeInt32ForKey:@"uid"]; + _firstName = [aDecoder decodeObjectForKey:@"firstName"]; + _lastName = [aDecoder decodeObjectForKey:@"lastName"]; + _phoneNumber = [aDecoder decodeObjectForKey:@"phoneNumber"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeInt32:_uid forKey:@"uid"]; + [aCoder encodeObject:_firstName forKey:@"firstName"]; + [aCoder encodeObject:_lastName forKey:@"lastName"]; + [aCoder encodeObject:_phoneNumber forKey:@"phoneNumber"]; +} + +@end diff --git a/LegacyComponents/TGConversation.h b/LegacyComponents/TGConversation.h new file mode 100644 index 0000000000..1a0b0a4b94 --- /dev/null +++ b/LegacyComponents/TGConversation.h @@ -0,0 +1,296 @@ +/* + * This is the source code of Telegram for iOS v. 1.1 + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Peter Iakovlev, 2013. + */ + +#import + +#import +#import +#import +#import +#import +#import + +#define TGConversationKindPersistentChannel 0 +#define TGConversationKindTemporaryChannel 1 + +#define TGChannelDisplayVariantImportant 0 +#define TGChannelDisplayVariantAll 1 + +#define TGConversationPinnedDateBase 1600000000 + +typedef enum { + TGConversationFlagPostAsChannel = (1 << 1), + TGConversationFlagKicked = (1 << 2), + TGConversationFlagVerified = (1 << 3), + TGConversationFlagHasAdmins = (1 << 4), + TGConversationFlagIsAdmin = (1 << 5), + TGConversationFlagIsCreator = (1 << 6), + TGConversationFlagIsChannelGroup = (1 << 7), + TGConversationFlagIsDeactivated = (1 << 8), + TGConversationFlagHasExplicitContent = (1 << 9), + TGConversationFlagEverybodyCanAddMembers = (1 << 10), + TGConversationFlagSignaturesEnabled = (1 << 11), + TGConversationFlagPinnedMessageHidden = (1 << 12), + TGConversationFlagIsMin = (1 << 13), + TGConversationFlagCanNotSetUsername = (1 << 14) +} TGConversationFlags; + +typedef struct { + uint8_t key[9]; +} TGConversationSortKey; + +static inline int TGConversationSortKeyCompare(TGConversationSortKey lhs, TGConversationSortKey rhs) { + return memcmp(lhs.key, rhs.key, 9); +} + +static inline TGConversationSortKey TGConversationSortKeyDecode(PSKeyValueCoder *coder, const char *name) { + TGConversationSortKey key; + [coder decodeBytesForCKey:name value:key.key length:9]; + return key; +} + +static inline void TGConversationSortKeyEncode(PSKeyValueCoder *coder, const char *name, TGConversationSortKey key) { + [coder encodeBytes:key.key length:9 forCKey:name]; +} + +static inline TGConversationSortKey TGConversationSortKeyMake(uint8_t kind, int32_t timestamp, int32_t mid) { + TGConversationSortKey key; + key.key[0] = kind; + + timestamp = NSSwapInt(timestamp); + memcpy(key.key + 1, ×tamp, 4); + + mid = NSSwapInt(mid); + memcpy(key.key + 1 + 4, &mid, 4); + + return key; +} + +static inline TGConversationSortKey TGConversationSortKeyLowerBound(uint8_t kind) { + return TGConversationSortKeyMake(kind, 0, 0); +} + +static inline TGConversationSortKey TGConversationSortKeyUpperBound(uint8_t kind) { + return TGConversationSortKeyMake(kind, INT32_MAX, INT32_MAX); +} + +static inline uint8_t TGConversationSortKeyKind(TGConversationSortKey key) { + return key.key[0]; +} + +static inline TGConversationSortKey TGConversationSortKeyUpdateKind(TGConversationSortKey key, uint8_t kind) { + TGConversationSortKey updatedKey; + memcpy(updatedKey.key, key.key, 8); + updatedKey.key[0] = kind; + return updatedKey; +} + +static inline int32_t TGConversationSortKeyTimestamp(TGConversationSortKey key) { + int32_t timestamp = 0; + memcpy(×tamp, key.key + 1, 4); + return NSSwapInt(timestamp); +} + +static inline int32_t TGConversationSortKeyMid(TGConversationSortKey key) { + int32_t mid = 0; + memcpy(&mid, key.key + 1 + 4, 4); + return NSSwapInt(mid); +} + +static inline NSData *TGConversationSortKeyData(TGConversationSortKey key) { + return [NSData dataWithBytes:key.key length:9]; +} + +static inline TGConversationSortKey TGConversationSortKeyFromData(NSData *data) { + TGConversationSortKey key; + memcpy(key.key, data.bytes, 9); + return key; +} + +typedef enum { + TGChannelRoleMember, + TGChannelRoleCreator, + TGChannelRoleModerator, + TGChannelRolePublisher +} TGChannelRole; + +@interface TGConversationParticipantsData : NSObject +{ + NSData *_serializedData; +} + +@property (nonatomic, strong) NSArray *chatParticipantUids; +@property (nonatomic, strong) NSDictionary *chatInvitedBy; +@property (nonatomic, strong) NSDictionary *chatInvitedDates; +@property (nonatomic, strong) NSSet *chatAdminUids; + +@property (nonatomic, strong) NSArray *chatParticipantSecretChatPeerIds; +@property (nonatomic, strong) NSArray *chatParticipantChatPeerIds; + +@property (nonatomic) int chatAdminId; + +@property (nonatomic) int version; + +@property (nonatomic, strong) NSString *exportedChatInviteString; + ++ (TGConversationParticipantsData *)deserializeData:(NSData *)data; +- (NSData *)serializedData; + +- (void)addParticipantWithId:(int32_t)uid invitedBy:(int32_t)invitedBy date:(int32_t)date; +- (void)removeParticipantWithId:(int32_t)uid; + +- (void)addSecretChatPeerWithId:(int64_t)peerId; +- (void)removeSecretChatPeerWithId:(int64_t)peerId; +- (void)addChatPeerWithId:(int64_t)peerId; +- (void)removeChatPeerWithId:(int64_t)peerId; + +@end + +@interface TGEncryptedConversationData : NSObject + +@property (nonatomic) int64_t encryptedConversationId; +@property (nonatomic) int64_t accessHash; +@property (nonatomic) int64_t keyFingerprint; +@property (nonatomic) int32_t handshakeState; +@property (nonatomic) int64_t currentRekeyExchangeId; +@property (nonatomic) bool currentRekeyIsInitiatedByLocalClient; +@property (nonatomic) NSData *currentRekeyNumber; +@property (nonatomic) NSData *currentRekeyKey; +@property (nonatomic) int64_t currentRekeyKeyId; + +@end + +@interface TGConversation : NSObject + +@property (nonatomic) int64_t conversationId; +@property (nonatomic) int64_t accessHash; + +@property (nonatomic) int32_t displayVariant; +@property (nonatomic) uint8_t kind; + +@property (nonatomic, readonly) TGConversationSortKey databaseSortKey; +@property (nonatomic) TGConversationSortKey variantSortKey; +@property (nonatomic) TGConversationSortKey importantSortKey; +@property (nonatomic) TGConversationSortKey unimportantSortKey; +@property (nonatomic) int32_t pts; + +@property (nonatomic) int32_t maxReadMessageId; +@property (nonatomic) int32_t maxOutgoingReadMessageId; +@property (nonatomic) int32_t maxKnownMessageId; +@property (nonatomic) int32_t maxLocallyReadMessageId; + +@property (nonatomic) int32_t maxReadDate; +@property (nonatomic) int32_t maxOutgoingReadDate; + +@property (nonatomic, strong) NSString *about; +@property (nonatomic, strong) NSString *username; + +@property (nonatomic) id additionalProperties; + +@property (nonatomic) bool outgoing; +@property (nonatomic) bool unread; +@property (nonatomic) bool deliveryError; +@property (nonatomic) TGMessageDeliveryState deliveryState; +@property (nonatomic) int32_t messageDate; +@property (nonatomic) int32_t minMessageDate; +@property (nonatomic) int32_t pinnedDate; +@property (nonatomic) int fromUid; +@property (nonatomic, strong) NSString *text; +@property (nonatomic, strong) NSArray *media; +@property (nonatomic, strong) NSData *mediaData; + +@property (nonatomic) int unreadCount; +@property (nonatomic) int serviceUnreadCount; + +@property (nonatomic, strong) NSString *chatTitle; +@property (nonatomic, strong) NSString *chatPhotoSmall; +@property (nonatomic, strong) NSString *chatPhotoMedium; +@property (nonatomic, strong) NSString *chatPhotoBig; + +@property (nonatomic) int chatParticipantCount; + +@property (nonatomic) bool leftChat; +@property (nonatomic) bool kickedFromChat; + +@property (nonatomic) TGChannelRole channelRole; + +@property (nonatomic) int chatCreationDate; +@property (nonatomic) int chatVersion; +@property (nonatomic) bool chatIsAdmin; +@property (nonatomic) bool channelIsReadOnly; +@property (nonatomic) bool isVerified; +@property (nonatomic) bool hasExplicitContent; +@property (nonatomic, strong) NSString *restrictionReason; + +@property (nonatomic, strong) TGConversationParticipantsData *chatParticipants; + +@property (nonatomic, strong) NSDictionary *dialogListData; + +@property (nonatomic) bool isChat; +@property (nonatomic) bool isDeleted; +@property (nonatomic) bool isBroadcast; +@property (nonatomic) bool isChannel; + +@property (nonatomic) bool postAsChannel; +@property (nonatomic) bool hasAdmins; +@property (nonatomic) bool isAdmin; +@property (nonatomic) bool isCreator; +@property (nonatomic) bool isChannelGroup; +@property (nonatomic) bool everybodyCanAddMembers; +@property (nonatomic) bool signaturesEnabled; + +@property (nonatomic) bool isDeactivated; +@property (nonatomic) bool isMigrated; +@property (nonatomic) int32_t migratedToChannelId; +@property (nonatomic) int64_t migratedToChannelAccessHash; + +@property (nonatomic) int32_t pinnedMessageId; +@property (nonatomic) bool pinnedMessageHidden; + +@property (nonatomic) int64_t flags; + +@property (nonatomic) bool isMin; +@property (nonatomic) bool canNotSetUsername; + +@property (nonatomic, strong) TGEncryptedConversationData *encryptedData; + +@property (nonatomic, strong, readonly) TGDatabaseMessageDraft *draft; + +@property (nonatomic, readonly) int32_t date; +@property (nonatomic, readonly) int32_t unpinnedDate; + +@property (nonatomic, strong) TGChannelAdminRights *channelAdminRights; +@property (nonatomic, strong) TGChannelBannedRights *channelBannedRights; + +- (id)initWithConversationId:(int64_t)conversationId unreadCount:(int)unreadCount serviceUnreadCount:(int)serviceUnreadCount; + +- (void)mergeMessage:(TGMessage *)message; +- (void)mergeEmptyMessage; + +- (BOOL)isEqualToConversation:(TGConversation *)other; +- (BOOL)isEqualToConversationIgnoringMessage:(TGConversation *)other; + +- (NSData *)serializeChatPhoto; +- (void)deserializeChatPhoto:(NSData *)data; + +- (bool)isEncrypted; + +- (void)mergeConversation:(TGConversation *)conversation; +- (void)mergeChannel:(TGConversation *)channel; +- (void)mergeDraft:(TGDatabaseMessageDraft *)draft; + +- (bool)currentUserCanSendMessages; + ++ (NSString *)chatTitleForDecoder:(PSKeyValueCoder *)coder; + +- (bool)isMessageUnread:(TGMessage *)message; +- (bool)isMessageUnread:(int32_t)messageId date:(int32_t)messageDate outgoing:(bool)outgoing; + +- (bool)pinnedToTop; + +@end diff --git a/LegacyComponents/TGConversation.m b/LegacyComponents/TGConversation.m new file mode 100644 index 0000000000..ccfc9b6a77 --- /dev/null +++ b/LegacyComponents/TGConversation.m @@ -0,0 +1,1266 @@ +#import "TGConversation.h" + +#import "LegacyComponentsInternal.h" + +#import "TGMessage.h" + +#import "PSKeyValueCoder.h" + +#import "TGPeerIdAdapter.h" + +@implementation TGEncryptedConversationData + +- (BOOL)isEqualToEncryptedData:(TGEncryptedConversationData *)other +{ + if (_encryptedConversationId != other->_encryptedConversationId || _accessHash != other->_accessHash || _keyFingerprint != other->_keyFingerprint || _handshakeState != other->_handshakeState || _currentRekeyExchangeId != other->_currentRekeyExchangeId) + return false; + + return true; +} + +- (id)copyWithZone:(NSZone *)__unused zone +{ + TGEncryptedConversationData *data = [[TGEncryptedConversationData alloc] init]; + data->_encryptedConversationId = _encryptedConversationId; + data->_accessHash = _accessHash; + data->_keyFingerprint = _keyFingerprint; + data->_handshakeState = _handshakeState; + data->_currentRekeyExchangeId = _currentRekeyExchangeId; + data->_currentRekeyIsInitiatedByLocalClient = _currentRekeyIsInitiatedByLocalClient; + data->_currentRekeyNumber = _currentRekeyNumber; + data->_currentRekeyKey = _currentRekeyKey; + data->_currentRekeyKeyId = _currentRekeyKeyId; + + return data; +} + +- (void)serialize:(NSMutableData *)data +{ + uint8_t version = 3; + [data appendBytes:&version length:1]; + [data appendBytes:&_encryptedConversationId length:8]; + [data appendBytes:&_accessHash length:8]; + [data appendBytes:&_keyFingerprint length:8]; + [data appendBytes:&_handshakeState length:4]; + [data appendBytes:&_currentRekeyExchangeId length:8]; + uint8_t currentRekeyIsInitiatedByLocalClient = _currentRekeyIsInitiatedByLocalClient ? 1 : 0; + [data appendBytes:¤tRekeyIsInitiatedByLocalClient length:1]; + int32_t currentRekeyNumberLength = (int32_t)_currentRekeyNumber.length; + [data appendBytes:¤tRekeyNumberLength length:4]; + if (_currentRekeyNumber != nil) + [data appendData:_currentRekeyNumber]; + int32_t currentRekeyKeyLength = (int32_t)_currentRekeyKey.length; + [data appendBytes:¤tRekeyKeyLength length:4]; + if (_currentRekeyKey != nil) + [data appendData:_currentRekeyKey]; + [data appendBytes:&_currentRekeyKeyId length:8]; +} + ++ (TGEncryptedConversationData *)deserialize:(NSData *)data ptr:(int *)ptr +{ + uint8_t version = 0; + [data getBytes:&version range:NSMakeRange(*ptr, 1)]; + (*ptr) += 1; + + if (version != 1 && version != 2 && version != 3) + { + TGLog(@"***** Invalid encrypted data version"); + return nil; + } + + TGEncryptedConversationData *encryptedData = [TGEncryptedConversationData new]; + + [data getBytes:&encryptedData->_encryptedConversationId range:NSMakeRange(*ptr, 8)]; + (*ptr) += 8; + + [data getBytes:&encryptedData->_accessHash range:NSMakeRange(*ptr, 8)]; + (*ptr) += 8; + + [data getBytes:&encryptedData->_keyFingerprint range:NSMakeRange(*ptr, 8)]; + (*ptr) += 8; + + if (version >= 2) + { + [data getBytes:&encryptedData->_handshakeState range:NSMakeRange(*ptr, 4)]; + *ptr += 4; + } + + if (version >= 3) + { + [data getBytes:&encryptedData->_currentRekeyExchangeId range:NSMakeRange(*ptr, 8)]; + *ptr += 8; + + uint8_t currentRekeyIsInitiatedByLocalClient = 0; + [data getBytes:¤tRekeyIsInitiatedByLocalClient range:NSMakeRange(*ptr, 1)]; + encryptedData->_currentRekeyIsInitiatedByLocalClient = currentRekeyIsInitiatedByLocalClient; + *ptr += 1; + + int32_t currentRekeyNumberLength = 0; + [data getBytes:¤tRekeyNumberLength range:NSMakeRange(*ptr, 4)]; + *ptr += 4; + + if (currentRekeyNumberLength != 0) + { + encryptedData->_currentRekeyNumber = [data subdataWithRange:NSMakeRange(*ptr, currentRekeyNumberLength)]; + *ptr += currentRekeyNumberLength; + } + + int32_t currentRekeyKeyLength = 0; + [data getBytes:¤tRekeyKeyLength range:NSMakeRange(*ptr, 4)]; + *ptr += 4; + + if (currentRekeyKeyLength != 0) + { + encryptedData->_currentRekeyKey = [data subdataWithRange:NSMakeRange(*ptr, currentRekeyKeyLength)]; + *ptr += currentRekeyKeyLength; + } + + [data getBytes:&encryptedData->_currentRekeyKeyId range:NSMakeRange(*ptr, 8)]; + *ptr += 8; + } + + return encryptedData; +} + +@end + +@implementation TGConversationParticipantsData + +- (id)init +{ + self = [super init]; + if (self != nil) + { + _serializedData = nil; + } + return self; +} + +- (id)copyWithZone:(NSZone *)__unused zone +{ + TGConversationParticipantsData *participantsData = [[TGConversationParticipantsData alloc] init]; + + participantsData.chatAdminId = _chatAdminId; + participantsData.chatInvitedBy = _chatInvitedBy; + participantsData.chatInvitedDates = _chatInvitedDates; + participantsData.chatParticipantUids = _chatParticipantUids; + participantsData.chatParticipantSecretChatPeerIds = _chatParticipantSecretChatPeerIds; + participantsData.chatParticipantChatPeerIds = _chatParticipantChatPeerIds; + participantsData.chatAdminUids = _chatAdminUids; + participantsData.version = _version; + participantsData.exportedChatInviteString = _exportedChatInviteString; + + return participantsData; +} + +- (void)addParticipantWithId:(int32_t)uid invitedBy:(int32_t)invitedBy date:(int32_t)date +{ + NSMutableArray *chatParticipantUids = [[NSMutableArray alloc] initWithArray:_chatParticipantUids]; + if (![chatParticipantUids containsObject:@(uid)]) + { + [chatParticipantUids addObject:@(uid)]; + _chatParticipantUids = chatParticipantUids; + + NSMutableDictionary *chatInvitedBy = [[NSMutableDictionary alloc] initWithDictionary:_chatInvitedBy]; + chatInvitedBy[@(uid)] = @(invitedBy); + _chatInvitedBy = chatInvitedBy; + + NSMutableDictionary *chatInvitedDates = [[NSMutableDictionary alloc] initWithDictionary:_chatInvitedDates]; + chatInvitedDates[@(uid)] = @(date); + _chatInvitedDates = chatInvitedDates; + } +} + +- (void)removeParticipantWithId:(int32_t)uid +{ + NSMutableArray *chatParticipantUids = [[NSMutableArray alloc] initWithArray:_chatParticipantUids]; + [chatParticipantUids removeObject:@(uid)]; + _chatParticipantUids = chatParticipantUids; + + NSMutableDictionary *chatInvitedBy = [[NSMutableDictionary alloc] initWithDictionary:_chatInvitedBy]; + [chatInvitedBy removeObjectForKey:@(uid)]; + _chatInvitedBy = chatInvitedBy; + + NSMutableDictionary *chatInvitedDates = [[NSMutableDictionary alloc] initWithDictionary:_chatInvitedDates]; + [chatInvitedDates removeObjectForKey:@(uid)]; + _chatInvitedDates = chatInvitedDates; + + NSMutableSet *chatAdminUids = [[NSMutableSet alloc] initWithSet:_chatAdminUids]; + [chatAdminUids removeObject:@(uid)]; + _chatAdminUids = chatAdminUids; +} + +- (void)addSecretChatPeerWithId:(int64_t)peerId +{ + NSMutableArray *chatParticipantSecretChatPeerIds = [[NSMutableArray alloc] initWithArray:_chatParticipantSecretChatPeerIds]; + if (![chatParticipantSecretChatPeerIds containsObject:@(peerId)]) + { + [chatParticipantSecretChatPeerIds addObject:@(peerId)]; + _chatParticipantSecretChatPeerIds = chatParticipantSecretChatPeerIds; + } +} + +- (void)removeSecretChatPeerWithId:(int64_t)peerId +{ + NSMutableArray *chatParticipantSecretChatPeerIds = [[NSMutableArray alloc] initWithArray:_chatParticipantSecretChatPeerIds]; + [chatParticipantSecretChatPeerIds removeObject:@(peerId)]; + _chatParticipantSecretChatPeerIds = chatParticipantSecretChatPeerIds; +} + +- (void)addChatPeerWithId:(int64_t)peerId +{ + NSMutableArray *chatParticipantChatPeerIds = [[NSMutableArray alloc] initWithArray:_chatParticipantChatPeerIds]; + if (![chatParticipantChatPeerIds containsObject:@(peerId)]) + { + [chatParticipantChatPeerIds addObject:@(peerId)]; + _chatParticipantChatPeerIds = chatParticipantChatPeerIds; + } +} + +- (void)removeChatPeerWithId:(int64_t)peerId +{ + NSMutableArray *chatParticipantChatPeerIds = [[NSMutableArray alloc] initWithArray:_chatParticipantChatPeerIds]; + [chatParticipantChatPeerIds removeObject:@(peerId)]; + _chatParticipantChatPeerIds = chatParticipantChatPeerIds; +} + ++ (TGConversationParticipantsData *)deserializeData:(NSData *)data +{ + TGConversationParticipantsData *participantsData = [[TGConversationParticipantsData alloc] init]; + + int length = (int)data.length; + int ptr = 0; + if (ptr + 12 > length) + { + return nil; + } + + int version = 0; + [data getBytes:&version range:NSMakeRange(ptr, 4)]; + ptr += 4; + + int32_t formatVersion = 0; + if (version == (int)0xabcdef12) + { + [data getBytes:&formatVersion range:NSMakeRange(ptr, 4)]; + ptr += 4; + + [data getBytes:&version range:NSMakeRange(ptr, 4)]; + ptr += 4; + } + + int adminId = 0; + [data getBytes:&adminId range:NSMakeRange(ptr, 4)]; + ptr += 4; + + int count = 0; + [data getBytes:&count range:NSMakeRange(ptr, 4)]; + ptr += 4; + + NSMutableArray *uids = [[NSMutableArray alloc] init]; + NSMutableDictionary *invitedBy = [[NSMutableDictionary alloc] init]; + NSMutableDictionary *invitedDates = [[NSMutableDictionary alloc] init]; + + for (int i = 0; i < count; i++) + { + if (ptr + 4 > length) + { + TGLog(@"***** Invalid participants data"); + return nil; + } + + int uid = 0; + [data getBytes:&uid range:NSMakeRange(ptr, 4)]; + ptr += 4; + + if (ptr + 4 > length) + { + TGLog(@"***** Invalid participants data"); + return nil; + } + int inviter = 0; + [data getBytes:&inviter range:NSMakeRange(ptr, 4)]; + ptr += 4; + + if (ptr + 4 > length) + { + TGLog(@"***** Invalid participants data"); + return nil; + } + int date = 0; + [data getBytes:&date range:NSMakeRange(ptr, 4)]; + ptr += 4; + + NSNumber *nUid = [[NSNumber alloc] initWithInt:uid]; + + [uids addObject:nUid]; + [invitedBy setObject:[[NSNumber alloc] initWithInt:inviter] forKey:nUid]; + [invitedDates setObject:[[NSNumber alloc] initWithInt:date] forKey:nUid]; + } + + NSMutableArray *chatParticipantSecretChatPeerIds = [[NSMutableArray alloc] init]; + + if (formatVersion >= 1) + { + int count = 0; + [data getBytes:&count range:NSMakeRange(ptr, 4)]; + ptr += 4; + + for (int i = 0; i < count; i++) + { + if (ptr + 8 > length) + { + TGLog(@"***** Invalid participants data"); + return nil; + } + + int64_t peerId = 0; + [data getBytes:&peerId range:NSMakeRange(ptr, 8)]; + ptr += 8; + + [chatParticipantSecretChatPeerIds addObject:@(peerId)]; + } + } + + NSMutableArray *chatParticipantChatPeerIds = [[NSMutableArray alloc] init]; + + if (formatVersion >= 2) + { + int count = 0; + [data getBytes:&count range:NSMakeRange(ptr, 4)]; + ptr += 4; + + for (int i = 0; i < count; i++) + { + if (ptr + 8 > length) + { + TGLog(@"***** Invalid participants data"); + return nil; + } + + int64_t peerId = 0; + [data getBytes:&peerId range:NSMakeRange(ptr, 8)]; + ptr += 8; + + [chatParticipantChatPeerIds addObject:@(peerId)]; + } + } + + if (formatVersion >= 3) + { + int32_t length = 0; + [data getBytes:&length range:NSMakeRange(ptr, 4)]; + ptr += 4; + + NSData *linkData = [data subdataWithRange:NSMakeRange(ptr, length)]; + ptr += length; + + participantsData.exportedChatInviteString = [[NSString alloc] initWithData:linkData encoding:NSUTF8StringEncoding]; + } + + if (formatVersion >= 4) { + int32_t length = 0; + [data getBytes:&length range:NSMakeRange(ptr, 4)]; + ptr += 4; + + NSMutableSet *chatAdminUids = [[NSMutableSet alloc] init]; + for (int32_t i = 0; i < length; i++) { + int32_t item = 0; + [data getBytes:&item range:NSMakeRange(ptr, 4)]; + ptr += 4; + [chatAdminUids addObject:@(item)]; + } + + participantsData.chatAdminUids = chatAdminUids; + } + + participantsData.version = version; + participantsData.chatAdminId = adminId; + participantsData.chatParticipantUids = uids; + participantsData.chatInvitedBy = invitedBy; + participantsData.chatInvitedDates = invitedDates; + participantsData.chatParticipantSecretChatPeerIds = chatParticipantSecretChatPeerIds; + participantsData.chatParticipantChatPeerIds = chatParticipantChatPeerIds; + + return participantsData; +} + +- (NSData *)serializedData +{ + if (_serializedData == nil) + { + NSMutableData *data = [[NSMutableData alloc] init]; + + int32_t magic = 0xabcdef12; + [data appendBytes:&magic length:4]; + + int32_t formatVersion = 4; + [data appendBytes:&formatVersion length:4]; + + [data appendBytes:&_version length:4]; + [data appendBytes:&_chatAdminId length:4]; + + int count = (int)_chatParticipantUids.count; + [data appendBytes:&count length:4]; + for (NSNumber *nUid in _chatParticipantUids) + { + int uid = [nUid intValue]; + [data appendBytes:&uid length:4]; + + int invitedBy = [[_chatInvitedBy objectForKey:nUid] intValue]; + [data appendBytes:&invitedBy length:4]; + + int invitedDate = [[_chatInvitedDates objectForKey:nUid] intValue]; + [data appendBytes:&invitedDate length:4]; + } + + int32_t chatParticipantSecretChatPeerIdsCount = (int32_t)_chatParticipantSecretChatPeerIds.count; + [data appendBytes:&chatParticipantSecretChatPeerIdsCount length:4]; + + for (NSNumber *nPeerId in _chatParticipantSecretChatPeerIds) + { + int64_t peerId = [nPeerId longLongValue]; + [data appendBytes:&peerId length:8]; + } + + int32_t chatParticipantChatPeerIdsCount = (int32_t)_chatParticipantChatPeerIds.count; + [data appendBytes:&chatParticipantChatPeerIdsCount length:4]; + + for (NSNumber *nPeerId in _chatParticipantChatPeerIds) + { + int64_t peerId = [nPeerId longLongValue]; + [data appendBytes:&peerId length:8]; + } + + int32_t linkLength = (int32_t)_exportedChatInviteString.length; + [data appendBytes:&linkLength length:4]; + if (linkLength != 0) + [data appendData:[_exportedChatInviteString dataUsingEncoding:NSUTF8StringEncoding]]; + + int32_t chatAdminUidsCount = (int32_t)_chatAdminUids.count; + [data appendBytes:&chatAdminUidsCount length:4]; + for (NSNumber *nUid in _chatAdminUids) { + int32_t uid = [nUid intValue]; + [data appendBytes:&uid length:4]; + } + + _serializedData = data; + } + + return _serializedData; +} + +@end + +#pragma mark - + +@implementation TGConversation + +- (id)initWithConversationId:(int64_t)conversationId unreadCount:(int)unreadCount serviceUnreadCount:(int)serviceUnreadCount +{ + self = [super init]; + if (self != nil) + { + _conversationId = conversationId; + _unreadCount = unreadCount; + _serviceUnreadCount = serviceUnreadCount; + } + return self; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + self = [super init]; + if (self != nil) { + _conversationId = [coder decodeInt64ForCKey:"i"]; + _accessHash = [coder decodeInt64ForCKey:"ah"]; + _displayVariant = [coder decodeInt32ForCKey:"dv"]; + _kind = (uint8_t)[coder decodeInt32ForCKey:"kind"]; + _pts = [coder decodeInt32ForCKey:"pts"]; + _variantSortKey = TGConversationSortKeyDecode(coder, "vsort"); + _importantSortKey = TGConversationSortKeyDecode(coder, "isort"); + _unimportantSortKey = TGConversationSortKeyDecode(coder, "usort"); + _maxReadMessageId = [coder decodeInt32ForCKey:"mread"]; + _maxOutgoingReadMessageId = [coder decodeInt32ForCKey:"moutread"]; + _maxKnownMessageId = [coder decodeInt32ForCKey:"mknown"]; + _maxLocallyReadMessageId = [coder decodeInt32ForCKey:"mlr"]; + _maxReadDate = [coder decodeInt32ForCKey:"mrd"]; + _maxOutgoingReadDate = [coder decodeInt32ForCKey:"mrod"]; + _about = [coder decodeStringForCKey:"about"]; + _username = [coder decodeStringForCKey:"username"]; + _outgoing = [coder decodeInt32ForCKey:"out"]; + _unread = [coder decodeInt32ForCKey:"unr"]; + _deliveryError = [coder decodeInt32ForCKey:"der"]; + _deliveryState = [coder decodeInt32ForCKey:"ds"]; + _messageDate = [coder decodeInt32ForCKey:"date"]; + _fromUid = [coder decodeInt32ForCKey:"from"]; + _text = [coder decodeStringForCKey:"text"]; + _media = [TGMessage parseMediaAttachments:[coder decodeDataCorCKey:"media"]]; + _unreadCount = [coder decodeInt32ForCKey:"ucount"]; + _serviceUnreadCount = [coder decodeInt32ForCKey:"sucount"]; + _chatTitle = [coder decodeStringForCKey:"ct"]; + _chatPhotoSmall = [coder decodeStringForCKey:"cp.s"]; + _chatPhotoMedium = [coder decodeStringForCKey:"cp.m"]; + _chatPhotoBig = [coder decodeStringForCKey:"cp.l"]; + _chatParticipants = nil; + _chatParticipantCount = 0; + _chatVersion = [coder decodeInt32ForCKey:"ver"]; + _chatIsAdmin = [coder decodeInt32ForCKey:"adm"]; + _channelRole = [coder decodeInt32ForCKey:"role"]; + _channelIsReadOnly = [coder decodeInt32ForCKey:"ro"]; + _flags = [coder decodeInt64ForCKey:"flags"]; + _leftChat = [coder decodeInt32ForCKey:"lef"]; + _kickedFromChat = [coder decodeInt32ForCKey:"kk"]; + _isChat = false; + _isChannel = true; + _isDeleted = false; + _encryptedData = nil; + _isBroadcast = false; + _migratedToChannelId = [coder decodeInt32ForCKey:"mtci"]; + _migratedToChannelAccessHash = [coder decodeInt64ForCKey:"mtch"]; + _restrictionReason = [coder decodeStringForCKey:"rr"]; + _pinnedMessageId = [coder decodeInt32ForCKey:"pmi"]; + _chatCreationDate = [coder decodeInt32ForCKey:"ccd"]; + _pinnedDate = [coder decodeInt32ForCKey:"pdt"]; + _channelAdminRights = [coder decodeObjectForCKey:"car"]; + _channelBannedRights = [coder decodeObjectForCKey:"cbr"]; + } + return self; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder { + [coder encodeInt64:_conversationId forCKey:"i"]; + [coder encodeInt64:_accessHash forCKey:"ah"]; + [coder encodeInt32:_displayVariant forCKey:"dv"]; + [coder encodeInt32:_kind forCKey:"kind"]; + [coder encodeInt32:_pts forCKey:"pts"]; + TGConversationSortKeyEncode(coder, "vsort", _variantSortKey); + TGConversationSortKeyEncode(coder, "isort", _importantSortKey); + TGConversationSortKeyEncode(coder, "usort", _unimportantSortKey); + [coder encodeInt32:_maxReadMessageId forCKey:"mread"]; + [coder encodeInt32:_maxOutgoingReadMessageId forCKey:"moutread"]; + [coder encodeInt32:_maxKnownMessageId forCKey:"mknown"]; + [coder encodeInt32:_maxLocallyReadMessageId forCKey:"mlr"]; + [coder encodeInt32:_maxReadDate forCKey:"mrd"]; + [coder encodeInt32:_maxOutgoingReadDate forCKey:"mrod"]; + [coder encodeString:_about forCKey:"about"]; + [coder encodeString:_username forCKey:"username"]; + [coder encodeInt32:_outgoing ? 1 : 0 forCKey:"out"]; + [coder encodeInt32:_unread ? 1 : 0 forCKey:"unr"]; + [coder encodeInt32:_deliveryError ? 1 : 0 forCKey:"der"]; + [coder encodeInt32:_deliveryState forCKey:"ds"]; + [coder encodeInt32:_messageDate forCKey:"date"]; + [coder encodeInt32:_fromUid forCKey:"from"]; + [coder encodeString:_text forCKey:"text"]; + [coder encodeData:[TGMessage serializeMediaAttachments:true attachments:_media] forCKey:"media"]; + [coder encodeInt32:_unreadCount forCKey:"ucount"]; + [coder encodeInt32:_serviceUnreadCount forCKey:"sucount"]; + [coder encodeString:_chatTitle forCKey:"ct"]; + [coder encodeString:_chatPhotoSmall forCKey:"cp.s"]; + [coder encodeString:_chatPhotoMedium forCKey:"cp.m"]; + [coder encodeString:_chatPhotoBig forCKey:"cp.l"]; + [coder encodeInt32:_chatVersion forCKey:"ver"]; + [coder encodeInt32:_chatIsAdmin ? 1 : 0 forCKey:"adm"]; + [coder encodeInt32:_channelRole forCKey:"role"]; + [coder encodeInt32:_channelIsReadOnly ? 1 : 0 forCKey:"ro"]; + [coder encodeInt64:_flags forCKey:"flags"]; + [coder encodeInt32:_leftChat forCKey:"lef"]; + [coder encodeInt32:_kickedFromChat forCKey:"kk"]; + [coder encodeInt32:_migratedToChannelId forCKey:"mtci"]; + [coder encodeInt64:_migratedToChannelAccessHash forCKey:"mtch"]; + [coder encodeString:_restrictionReason forCKey:"rr"]; + [coder encodeInt32:_pinnedMessageId forCKey:"pmi"]; + [coder encodeInt32:_chatCreationDate forCKey:"ccd"]; + [coder encodeObject:_channelAdminRights forCKey:"car"]; + [coder encodeObject:_channelBannedRights forCKey:"cbr"]; +} + +- (id)copyWithZone:(NSZone *)__unused zone +{ + TGConversation *conversation = [[TGConversation alloc] init]; + + conversation.conversationId = _conversationId; + conversation.accessHash = _accessHash; + conversation.displayVariant = _displayVariant; + conversation->_kind = _kind; + conversation.pts = _pts; + conversation.variantSortKey = _variantSortKey; + conversation.importantSortKey = _importantSortKey; + conversation.unimportantSortKey = _unimportantSortKey; + conversation.maxReadMessageId = _maxReadMessageId; + conversation.maxOutgoingReadMessageId = _maxOutgoingReadMessageId; + conversation.maxKnownMessageId = _maxKnownMessageId; + conversation.maxLocallyReadMessageId = _maxLocallyReadMessageId; + conversation.maxReadDate = _maxReadDate; + conversation.maxOutgoingReadDate = _maxOutgoingReadDate; + conversation.about = _about; + conversation.username = _username; + conversation.outgoing = _outgoing; + conversation.unread = _unread; + conversation.deliveryError = _deliveryError; + conversation.deliveryState = _deliveryState; + conversation.messageDate = _messageDate; + conversation.minMessageDate = _minMessageDate; + conversation.fromUid = _fromUid; + conversation.text = _text; + conversation.media = _media; + conversation.unreadCount = _unreadCount; + conversation.serviceUnreadCount = _serviceUnreadCount; + conversation.chatTitle = _chatTitle; + conversation.chatPhotoSmall = _chatPhotoSmall; + conversation.chatPhotoMedium = _chatPhotoMedium; + conversation.chatPhotoBig = _chatPhotoBig; + conversation.chatParticipants = [_chatParticipants copy]; + conversation.chatParticipantCount = _chatParticipantCount; + conversation.chatVersion = _chatVersion; + conversation.chatIsAdmin = _chatIsAdmin; + conversation.channelRole = _channelRole; + conversation.leftChat = _leftChat; + conversation.kickedFromChat = _kickedFromChat; + conversation.dialogListData = _dialogListData; + conversation.isChat = _isChat; + conversation.isDeleted = _isDeleted; + conversation.restrictionReason = _restrictionReason; + conversation->_chatCreationDate = _chatCreationDate; + + conversation.encryptedData = _encryptedData == nil ? nil : [_encryptedData copy]; + + conversation.isBroadcast = _isBroadcast; + conversation.isChannel = _isChannel; + conversation.channelIsReadOnly = _channelIsReadOnly; + conversation.flags = _flags; + conversation.migratedToChannelId = _migratedToChannelId; + conversation.migratedToChannelAccessHash = _migratedToChannelAccessHash; + conversation.pinnedMessageId = _pinnedMessageId; + + conversation->_draft = _draft; + conversation.pinnedDate = _pinnedDate; + + conversation->_channelAdminRights = _channelAdminRights; + conversation->_channelBannedRights = _channelBannedRights; + + return conversation; +} + +- (void)setKind:(uint8_t)kind { + if (_kind != kind || kind != TGConversationSortKeyKind(_variantSortKey)) { + _kind = kind; + + _variantSortKey = TGConversationSortKeyUpdateKind(_variantSortKey, kind); + _importantSortKey = TGConversationSortKeyUpdateKind(_importantSortKey, kind); + _unimportantSortKey = TGConversationSortKeyUpdateKind(_unimportantSortKey, kind); + } +} + +- (void)setVariantSortKey:(TGConversationSortKey)variantSortKey { + _variantSortKey = variantSortKey; + + _messageDate = TGConversationSortKeyTimestamp(variantSortKey); +} + +- (void)mergeMessage:(TGMessage *)message +{ + _outgoing = message.outgoing; + _messageDate = (int)message.date; + _fromUid = (int)message.fromUid; + _text = message.text; + _media = message.mediaAttachments; + _unread = [self isMessageUnread:message]; + _deliveryError = message.deliveryState == TGMessageDeliveryStateFailed; + _deliveryState = message.deliveryState; +} + +- (void)mergeEmptyMessage { + _outgoing = false; + _fromUid = 0; + _text = nil; + _media = nil; + _unread = false; + _deliveryError = false; + _deliveryState = TGMessageDeliveryStateDelivered; +} + +- (NSData *)mediaData +{ + if (_mediaData != nil) + return _mediaData; + + _mediaData = [TGMessage serializeMediaAttachments:false attachments:_media]; + return _mediaData; +} + +- (BOOL)isEqual:(id)object { + return [object isKindOfClass:[TGConversation class]] && [((TGConversation *)object) isEqualToConversation:self]; +} + +- (BOOL)isEqualToConversation:(TGConversation *)other +{ + if (_conversationId != other.conversationId || _outgoing != other.outgoing || _messageDate != other.messageDate || _fromUid != other.fromUid || ![_text isEqualToString:other.text] || _unreadCount != other.unreadCount || _serviceUnreadCount != other.serviceUnreadCount || _unread != other.unread || _isChat != other.isChat || _deliveryError != other.deliveryError || _deliveryState != other.deliveryState) + return false; + + if (_media.count != other.media.count) + return false; + if (_media != nil && ![self.mediaData isEqualToData:other.mediaData]) + return false; + + if (_isChat) + { + if (![_chatTitle isEqualToString:other.chatTitle] || _chatVersion != other.chatVersion || _leftChat != other.leftChat || _kickedFromChat != other.kickedFromChat || + (((_chatParticipants != nil) != (other.chatParticipants != nil)) || (_chatParticipants != nil && ![_chatParticipants.serializedData isEqualToData:other.chatParticipants.serializedData])) + ) + return false; + if ((_chatPhotoSmall != nil) != (other.chatPhotoSmall != nil) || (_chatPhotoSmall != nil && ![_chatPhotoSmall isEqualToString:other.chatPhotoSmall])) + return false; + if ((_chatPhotoMedium != nil) != (other.chatPhotoMedium != nil) || (_chatPhotoMedium != nil && ![_chatPhotoMedium isEqualToString:other.chatPhotoMedium])) + return false; + if ((_chatPhotoBig != nil) != (other.chatPhotoBig != nil) || (_chatPhotoBig != nil && ![_chatPhotoBig isEqualToString:other.chatPhotoBig])) + return false; + } + + if (_encryptedData != nil || other->_encryptedData != nil) + { + if ((_encryptedData != nil) != (other->_encryptedData != nil) || (_encryptedData != nil && ![_encryptedData isEqualToEncryptedData:other->_encryptedData])) + return false; + } + + if (_flags != other->_flags) { + return false; + } + + if (_pinnedMessageId != other->_pinnedMessageId) { + return false; + } + + if (!TGStringCompare(_restrictionReason, other->_restrictionReason)) { + return false; + } + + if (_maxReadDate != other->_maxReadDate) { + return false; + } + + if (_maxReadMessageId != other->_maxReadMessageId) { + return false; + } + + if (_maxOutgoingReadDate != other->_maxOutgoingReadDate) { + return false; + } + + if (_maxOutgoingReadMessageId != other->_maxOutgoingReadMessageId) { + return false; + } + + if (_maxKnownMessageId != other->_maxKnownMessageId) { + return false; + } + + if (!TGObjectCompare(_draft, other->_draft)) { + return false; + } + + if (_pinnedDate != other->_pinnedDate) { + return false; + } + + if (!TGObjectCompare(_channelAdminRights, other->_channelAdminRights)) { + return false; + } + + if (!TGObjectCompare(_channelBannedRights, other->_channelBannedRights)) { + return false; + } + + return true; +} + +- (BOOL)isEqualToConversationIgnoringMessage:(TGConversation *)other +{ + if (_conversationId != other.conversationId || _isChat != other.isChat) + return false; + + if (_isChat) + { + if (![_chatTitle isEqualToString:other.chatTitle] || _chatVersion != other.chatVersion || _leftChat != other.leftChat || _kickedFromChat != other.kickedFromChat || + (((_chatParticipants != nil) != (other.chatParticipants != nil)) || (_chatParticipants != nil && ![_chatParticipants.serializedData isEqualToData:other.chatParticipants.serializedData])) + ) + return false; + if ((_chatPhotoSmall != nil) != (other.chatPhotoSmall != nil) || (_chatPhotoSmall != nil && ![_chatPhotoSmall isEqualToString:other.chatPhotoSmall])) + return false; + if ((_chatPhotoMedium != nil) != (other.chatPhotoMedium != nil) || (_chatPhotoMedium != nil && ![_chatPhotoMedium isEqualToString:other.chatPhotoMedium])) + return false; + if ((_chatPhotoBig != nil) != (other.chatPhotoBig != nil) || (_chatPhotoBig != nil && ![_chatPhotoBig isEqualToString:other.chatPhotoBig])) + return false; + } + + if (_flags != other->_flags) { + return false; + } + + if (!TGStringCompare(_restrictionReason, other->_restrictionReason)) { + return false; + } + + if (_maxReadDate != other->_maxReadDate) { + return false; + } + + if (_maxReadMessageId != other->_maxReadMessageId) { + return false; + } + + if (_maxOutgoingReadDate != other->_maxOutgoingReadDate) { + return false; + } + + if (_maxOutgoingReadMessageId != other->_maxOutgoingReadMessageId) { + return false; + } + + if (_maxKnownMessageId != other->_maxKnownMessageId) { + return false; + } + + if (!TGObjectCompare(_channelAdminRights, other->_channelAdminRights)) { + return false; + } + + if (!TGObjectCompare(_channelBannedRights, other->_channelBannedRights)) { + return false; + } + + return true; +} + +- (NSData *)serializeChatPhoto +{ + NSMutableData *data = [[NSMutableData alloc] init]; + + int32_t magic = 0x7acde441; + [data appendBytes:&magic length:4]; + int32_t version = 7; + [data appendBytes:&version length:4]; + + for (int i = 0; i < 3; i++) + { + NSString *value = nil; + if (i == 0) + value = _chatPhotoSmall; + else if (i == 1) + value = _chatPhotoMedium; + else if (i == 2) + value = _chatPhotoBig; + + NSData *valueData = [value dataUsingEncoding:NSUTF8StringEncoding]; + int length = (int)valueData.length; + [data appendBytes:&length length:4]; + if (valueData != nil) + [data appendData:valueData]; + } + + int8_t hasEncryptedData = _encryptedData != nil ? 1 : 0; + [data appendBytes:&hasEncryptedData length:1]; + if (_encryptedData != nil) + [_encryptedData serialize:data]; + + [data appendBytes:&_flags length:4]; + + [data appendBytes:&_migratedToChannelId length:4]; + [data appendBytes:&_migratedToChannelAccessHash length:8]; + + [data appendBytes:&_maxReadMessageId length:4]; + [data appendBytes:&_maxOutgoingReadMessageId length:4]; + [data appendBytes:&_maxKnownMessageId length:4]; + [data appendBytes:&_maxLocallyReadMessageId length:4]; + + [data appendBytes:&_maxReadDate length:4]; + [data appendBytes:&_maxOutgoingReadDate length:4]; + + [data appendBytes:&_messageDate length:4]; + [data appendBytes:&_minMessageDate length:4]; + + return data; +} + +- (void)deserializeChatPhoto:(NSData *)data +{ + int ptr = 0; + + int32_t version = 1; + if (data.length >= 4) { + int32_t magic = 0; + [data getBytes:&magic length:4]; + if (magic == 0x7acde441) { + ptr += 4; + + [data getBytes:&version range:NSMakeRange(ptr, 4)]; + ptr += 4; + } + } + + for (int i = 0; i < 3; i++) + { + int length = 0; + [data getBytes:&length range:NSMakeRange(ptr, 4)]; + ptr += 4; + + uint8_t *valueBytes = malloc(length); + [data getBytes:valueBytes range:NSMakeRange(ptr, length)]; + ptr += length; + NSString *value = [[NSString alloc] initWithBytesNoCopy:valueBytes length:length encoding:NSUTF8StringEncoding freeWhenDone:true]; + + if (i == 0) + _chatPhotoSmall = value; + else if (i == 1) + _chatPhotoMedium = value; + else if (i == 2) + _chatPhotoBig = value; + } + + if (version == 1) { + if (ptr + 4 <= (int)data.length) { + _encryptedData = [TGEncryptedConversationData deserialize:data ptr:&ptr]; + } + } else { + if (version >= 2) { + int8_t hasEncryptedData = 0; + [data getBytes:&hasEncryptedData range:NSMakeRange(ptr, 1)]; + ptr += 1; + + if (hasEncryptedData) { + _encryptedData = [TGEncryptedConversationData deserialize:data ptr:&ptr]; + } + + [data getBytes:&_flags range:NSMakeRange(ptr, 4)]; + ptr += 4; + + if (version >= 4) { + [data getBytes:&_migratedToChannelId range:NSMakeRange(ptr, 4)]; + ptr += 4; + [data getBytes:&_migratedToChannelAccessHash range:NSMakeRange(ptr, 8)]; + ptr += 8; + + if (version >= 5) { + [data getBytes:&_maxReadMessageId range:NSMakeRange(ptr, 4)]; + ptr += 4; + + [data getBytes:&_maxOutgoingReadMessageId range:NSMakeRange(ptr, 4)]; + ptr += 4; + + [data getBytes:&_maxKnownMessageId range:NSMakeRange(ptr, 4)]; + ptr += 4; + + [data getBytes:&_maxLocallyReadMessageId range:NSMakeRange(ptr, 4)]; + ptr += 4; + + [data getBytes:&_maxReadDate range:NSMakeRange(ptr, 4)]; + ptr += 4; + + [data getBytes:&_maxOutgoingReadDate range:NSMakeRange(ptr, 4)]; + ptr += 4; + + if (version >= 6) { + [data getBytes:&_messageDate range:NSMakeRange(ptr, 4)]; + ptr += 4; + + if (version >= 7) { + [data getBytes:&_minMessageDate range:NSMakeRange(ptr, 4)]; + ptr += 4; + } + } + } + } + } + } +} + +- (bool)isEncrypted +{ + return _encryptedData != nil; +} + +- (void)mergeConversation:(TGConversation *)conversation { + self.accessHash = conversation.accessHash; + self.about = conversation.about; + self.username = conversation.username; + self.chatTitle = conversation.chatTitle; + self.chatPhotoSmall = conversation.chatPhotoSmall; + self.chatPhotoMedium = conversation.chatPhotoMedium; + self.chatPhotoBig = conversation.chatPhotoBig; + self.chatParticipantCount = conversation.chatParticipantCount; + self.leftChat = conversation.leftChat; + self.kickedFromChat = conversation.kickedFromChat; + self.chatVersion = conversation.chatVersion; + self.chatIsAdmin = conversation.chatIsAdmin; + self.hasAdmins = conversation.hasAdmins; + self.isAdmin = conversation.isAdmin; + self.isVerified = conversation.isVerified; + if (conversation.chatParticipants != nil) { + self.chatParticipants = conversation.chatParticipants; + } + self.isChat = conversation.isChat; + self.isDeactivated = conversation.isDeactivated; + self.isMigrated = conversation.isMigrated; + self.migratedToChannelId = conversation.migratedToChannelId; + self.migratedToChannelAccessHash = conversation.migratedToChannelAccessHash; + self.canNotSetUsername = conversation.canNotSetUsername; + if (conversation.encryptedData != nil) { + self.encryptedData = conversation.encryptedData; + } + self.channelAdminRights = conversation.channelAdminRights; + self.channelBannedRights = conversation.channelBannedRights; +} + +- (void)mergeChannel:(TGConversation *)channel { + _chatTitle = channel.chatTitle; + _chatVersion = channel.chatVersion; + _chatPhotoBig = channel.chatPhotoBig; + _chatPhotoMedium = channel.chatPhotoMedium; + _chatPhotoSmall = channel.chatPhotoSmall; + _username = channel.username; + if (!channel.isMin) { + _chatIsAdmin = channel.chatIsAdmin; + self.channelRole = channel.channelRole; + _leftChat = channel.leftChat; + _kickedFromChat = channel.kickedFromChat; + self.kind = channel.leftChat || channel.kickedFromChat ? TGConversationKindTemporaryChannel : TGConversationKindPersistentChannel; + _accessHash = channel.accessHash; + self.hasExplicitContent = channel.hasExplicitContent; + self.signaturesEnabled = channel.signaturesEnabled; + self.restrictionReason = channel.restrictionReason; + self.channelAdminRights = channel.channelAdminRights; + self.channelBannedRights = channel.channelBannedRights; + } + self.everybodyCanAddMembers = channel.everybodyCanAddMembers; + _channelIsReadOnly = channel.channelIsReadOnly; + self.isVerified = channel.isVerified; +} + +- (void)mergeDraft:(TGDatabaseMessageDraft *)draft { + _draft = draft; + + if (_draft.date > TGConversationSortKeyTimestamp(_variantSortKey)) { + _variantSortKey = TGConversationSortKeyMake(TGConversationSortKeyKind(_variantSortKey), _draft.date, TGConversationSortKeyMid(_variantSortKey)); + } +} + +- (void)setPinnedDate:(int32_t)pinnedDate { + _pinnedDate = pinnedDate; + if (pinnedDate > TGConversationSortKeyTimestamp(_variantSortKey)) { + _variantSortKey = TGConversationSortKeyMake(TGConversationSortKeyKind(_variantSortKey), pinnedDate, TGConversationSortKeyMid(_variantSortKey)); + } +} + +- (bool)currentUserCanSendMessages { + if (self.isChannelGroup) { + return true; + } + + return (_channelRole == TGChannelRoleCreator || _channelAdminRights.canPostMessages || !_channelIsReadOnly) && !_leftChat && !_kickedFromChat; +} + ++ (NSString *)chatTitleForDecoder:(PSKeyValueCoder *)coder { + return [coder decodeStringForCKey:"ct"]; +} + +- (bool)postAsChannel { + return _flags & TGConversationFlagPostAsChannel; +} + +- (void)setPostAsChannel:(bool)postAsChannel { + if (postAsChannel) { + _flags |= TGConversationFlagPostAsChannel; + } else { + _flags &= ~TGConversationFlagPostAsChannel; + } +} + +- (bool)isVerified { + return _flags & TGConversationFlagVerified; +} + +- (void)setIsVerified:(bool)isVerified { + if (isVerified) { + _flags |= TGConversationFlagVerified; + } else { + _flags &= ~TGConversationFlagVerified; + } +} + +- (bool)hasExplicitContent { + return _flags & TGConversationFlagHasExplicitContent; +} + +- (void)setHasExplicitContent:(bool)hasExplicitContent { + if (hasExplicitContent) { + _flags |= TGConversationFlagHasExplicitContent; + } else { + _flags &= ~TGConversationFlagHasExplicitContent; + } +} + +- (bool)hasAdmins { + return _flags & TGConversationFlagHasAdmins; +} + +- (void)setHasAdmins:(bool)hasAdmins { + if (hasAdmins) { + _flags |= TGConversationFlagHasAdmins; + } else { + _flags &= ~TGConversationFlagHasAdmins; + } +} + +- (bool)isAdmin { + return _flags & TGConversationFlagIsAdmin; +} + +- (void)setIsAdmin:(bool)isAdmin { + if (isAdmin) { + _flags |= TGConversationFlagIsAdmin; + } else { + _flags &= ~TGConversationFlagIsAdmin; + } +} + +- (bool)isCreator { + return _flags & TGConversationFlagIsCreator; +} + +- (void)setIsCreator:(bool)isCreator { + if (isCreator) { + _flags |= TGConversationFlagIsCreator; + } else { + _flags &= ~TGConversationFlagIsCreator; + } +} + +- (bool)isChannelGroup { + return _flags & TGConversationFlagIsChannelGroup; +} + +- (void)setIsChannelGroup:(bool)isChannelGroup { + if (isChannelGroup) { + _flags |= TGConversationFlagIsChannelGroup; + } else { + _flags &= ~TGConversationFlagIsChannelGroup; + } +} + +- (bool)everybodyCanAddMembers { + return _flags & TGConversationFlagEverybodyCanAddMembers; +} + +- (void)setEverybodyCanAddMembers:(bool)everybodyCanAddMembers { + if (everybodyCanAddMembers) { + _flags |= TGConversationFlagEverybodyCanAddMembers; + } else { + _flags &= ~TGConversationFlagEverybodyCanAddMembers; + } +} + +- (bool)isMin { + return _flags & TGConversationFlagIsMin; +} + +- (void)setIsMin:(bool)isMin { + if (isMin) { + _flags |= TGConversationFlagIsMin; + } else { + _flags &= ~TGConversationFlagIsMin; + } +} + +- (bool)canNotSetUsername { + return _flags & TGConversationFlagCanNotSetUsername; +} + +- (void)setCanNotSetUsername:(bool)canNotSetUsername { + if (canNotSetUsername) { + _flags |= TGConversationFlagCanNotSetUsername; + } else { + _flags &= ~TGConversationFlagCanNotSetUsername; + } +} + +- (bool)signaturesEnabled { + return _flags & TGConversationFlagSignaturesEnabled; +} + +- (void)setSignaturesEnabled:(bool)signaturesEnabled { + if (signaturesEnabled) { + _flags |= TGConversationFlagSignaturesEnabled; + } else { + _flags &= ~TGConversationFlagSignaturesEnabled; + } +} + +- (bool)isDeactivated { + return _flags & TGConversationFlagIsDeactivated; +} + +- (void)setIsDeactivated:(bool)isDeactivated { + if (isDeactivated) { + _flags |= TGConversationFlagIsDeactivated; + } else { + _flags &= ~TGConversationFlagIsDeactivated; + } +} + +- (bool)pinnedMessageHidden { + return _flags & TGConversationFlagPinnedMessageHidden; +} + +- (void)setPinnedMessageHidden:(bool)pinnedMessageHidden { + if (pinnedMessageHidden) { + _flags |= TGConversationFlagPinnedMessageHidden; + } else { + _flags &= ~TGConversationFlagPinnedMessageHidden; + } +} + +- (bool)isMessageUnread:(TGMessage *)message { + return [self isMessageUnread:message.mid date:(int32_t)message.date outgoing:message.outgoing]; +} + +- (bool)isMessageUnread:(int32_t)messageId date:(int32_t)messageDate outgoing:(bool)outgoing { + if (TGPeerIdIsSecretChat(_conversationId)) { + if (outgoing) { + return ((int32_t)messageDate) > _maxOutgoingReadDate; + } else { + return ((int32_t)messageDate) > _maxReadDate; + } + } else { + if (TGPeerIdIsChannel(_conversationId) && _kind != TGConversationKindPersistentChannel) { + return false; + } + + if (outgoing) { + if (messageId < TGMessageLocalMidBaseline) { + return messageId > _maxOutgoingReadMessageId; + } else { + return true; + } + } else { + if (messageId < TGMessageLocalMidBaseline) { + return messageId > _maxReadMessageId; + } else { + return false; + } + } + } +} + +- (int32_t)date { + return MAX(_pinnedDate, MAX(_minMessageDate, MAX(_draft.date, _messageDate))); +} + +- (int32_t)unpinnedDate { + return MAX(_minMessageDate, MAX(_draft.date, _messageDate)); +} + +- (bool)pinnedToTop { + return _pinnedDate >= TGConversationPinnedDateBase; +} + +@end diff --git a/LegacyComponents/TGDatabaseMessageDraft.h b/LegacyComponents/TGDatabaseMessageDraft.h new file mode 100644 index 0000000000..25d96d709a --- /dev/null +++ b/LegacyComponents/TGDatabaseMessageDraft.h @@ -0,0 +1,20 @@ +#import + +#import +#import + +@interface TGDatabaseMessageDraft : NSObject + +@property (nonatomic, strong, readonly) NSString *text; +@property (nonatomic, strong, readonly) NSArray *entities; +@property (nonatomic, readonly) bool disableLinkPreview; +@property (nonatomic, readonly) int32_t replyToMessageId; +@property (nonatomic, readonly) int32_t date; + +- (instancetype)initWithText:(NSString *)text entities:(NSArray *)entities disableLinkPreview:(bool)disableLinkPreview replyToMessageId:(int32_t)replyToMessageId date:(int32_t)date; + +- (bool)isEmpty; + +- (TGDatabaseMessageDraft *)updateDate:(int32_t)date; + +@end diff --git a/LegacyComponents/TGDatabaseMessageDraft.m b/LegacyComponents/TGDatabaseMessageDraft.m new file mode 100644 index 0000000000..2ad2d56a31 --- /dev/null +++ b/LegacyComponents/TGDatabaseMessageDraft.m @@ -0,0 +1,46 @@ +#import "TGDatabaseMessageDraft.h" + +#import "LegacyComponentsInternal.h" + +#import "PSKeyValueEncoder.h" +#import "PSKeyValueDecoder.h" + +@implementation TGDatabaseMessageDraft + +- (instancetype)initWithText:(NSString *)text entities:(NSArray *)entities disableLinkPreview:(bool)disableLinkPreview replyToMessageId:(int32_t)replyToMessageId date:(int32_t)date { + self = [super init]; + if (self != nil) { + _text = text; + _entities = entities; + _disableLinkPreview = disableLinkPreview; + _replyToMessageId = replyToMessageId; + _date = date; + } + return self; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder { + return [self initWithText:[coder decodeStringForCKey:"text"] entities:[coder decodeArrayForCKey:"entities"] disableLinkPreview:[coder decodeInt32ForCKey:"disableLinkPreview"] replyToMessageId:[coder decodeInt32ForCKey:"replyToMessageId"] date:[coder decodeInt32ForCKey:"date"]]; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder { + [coder encodeString:_text forCKey:"text"]; + [coder encodeArray:_entities forCKey:"entities"]; + [coder encodeInt32:_disableLinkPreview ? 1 : 0 forCKey:"disableLinkPreview"]; + [coder encodeInt32:_replyToMessageId forCKey:"replyToMessageId"]; + [coder encodeInt32:_date forCKey:"date"]; +} + +- (bool)isEqual:(id)object { + return [object isKindOfClass:[TGDatabaseMessageDraft class]] && TGStringCompare(((TGDatabaseMessageDraft *)object)->_text, _text) && TGObjectCompare(((TGDatabaseMessageDraft *)object)->_entities, _entities) && ((TGDatabaseMessageDraft *)object)->_disableLinkPreview == _disableLinkPreview && ((TGDatabaseMessageDraft *)object)->_replyToMessageId == _replyToMessageId && ((TGDatabaseMessageDraft *)object)->_date == _date; +} + +- (bool)isEmpty { + return _text.length == 0 && _replyToMessageId == 0; +} + +- (TGDatabaseMessageDraft *)updateDate:(int32_t)date { + return [[TGDatabaseMessageDraft alloc] initWithText:_text entities:_entities disableLinkPreview:_disableLinkPreview replyToMessageId:_replyToMessageId date:date]; +} + +@end diff --git a/LegacyComponents/TGDocumentAttributeAnimated.h b/LegacyComponents/TGDocumentAttributeAnimated.h new file mode 100644 index 0000000000..c89a75a1ab --- /dev/null +++ b/LegacyComponents/TGDocumentAttributeAnimated.h @@ -0,0 +1,5 @@ +#import + +@interface TGDocumentAttributeAnimated : NSObject + +@end diff --git a/LegacyComponents/TGDocumentAttributeAnimated.m b/LegacyComponents/TGDocumentAttributeAnimated.m new file mode 100644 index 0000000000..3ec92c9639 --- /dev/null +++ b/LegacyComponents/TGDocumentAttributeAnimated.m @@ -0,0 +1,28 @@ +#import "TGDocumentAttributeAnimated.h" + +@implementation TGDocumentAttributeAnimated + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)__unused coder +{ + return [self init]; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)__unused coder +{ +} + +- (instancetype)initWithCoder:(NSCoder *)__unused aDecoder +{ + return [self init]; +} + +- (void)encodeWithCoder:(NSCoder *)__unused aCoder +{ +} + +- (BOOL)isEqual:(id)object +{ + return [object isEqual:[TGDocumentAttributeAnimated class]]; +} + +@end diff --git a/LegacyComponents/TGDocumentAttributeAudio.h b/LegacyComponents/TGDocumentAttributeAudio.h new file mode 100644 index 0000000000..eb1f94fadd --- /dev/null +++ b/LegacyComponents/TGDocumentAttributeAudio.h @@ -0,0 +1,17 @@ +#import + +#import + +#import + +@interface TGDocumentAttributeAudio : NSObject + +@property (nonatomic, readonly) bool isVoice; +@property (nonatomic, strong, readonly) NSString *title; +@property (nonatomic, strong, readonly) NSString *performer; +@property (nonatomic, readonly) int32_t duration; +@property (nonatomic, strong, readonly) TGAudioWaveform *waveform; + +- (instancetype)initWithIsVoice:(bool)isVoice title:(NSString *)title performer:(NSString *)performer duration:(int32_t)duration waveform:(TGAudioWaveform *)waveform; + +@end diff --git a/LegacyComponents/TGDocumentAttributeAudio.m b/LegacyComponents/TGDocumentAttributeAudio.m new file mode 100644 index 0000000000..988899977a --- /dev/null +++ b/LegacyComponents/TGDocumentAttributeAudio.m @@ -0,0 +1,56 @@ +#import "TGDocumentAttributeAudio.h" + +#import "LegacyComponentsInternal.h" + +#import "PSKeyValueCoder.h" + +@implementation TGDocumentAttributeAudio + +- (instancetype)initWithIsVoice:(bool)isVoice title:(NSString *)title performer:(NSString *)performer duration:(int32_t)duration waveform:(TGAudioWaveform *)waveform +{ + self = [super init]; + if (self != nil) + { + _isVoice = isVoice; + _title = title; + _performer = performer; + _duration = duration; + _waveform = waveform; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + return [self initWithIsVoice:[aDecoder decodeBoolForKey:@"isVoice"] title:[aDecoder decodeObjectForKey:@"title"] performer:[aDecoder decodeObjectForKey:@"performer"] duration:[aDecoder decodeInt32ForKey:@"duration"] waveform:[aDecoder decodeObjectForKey:@"waveform"]]; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + return [self initWithIsVoice:[coder decodeInt32ForCKey:"isVoice"] title:[coder decodeStringForCKey:"title"] performer:[coder decodeStringForCKey:"performer"] duration:[coder decodeInt32ForCKey:"duration"] waveform:(TGAudioWaveform *)[coder decodeObjectForCKey:"waveform"]]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeBool:_isVoice forKey:@"isVoice"]; + [aCoder encodeObject:_title forKey:@"title"]; + [aCoder encodeObject:_performer forKey:@"performer"]; + [aCoder encodeInt32:_duration forKey:@"duration"]; + [aCoder encodeObject:_waveform forKey:@"waveform"]; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + [coder encodeInt32:_isVoice ? 1 : 0 forCKey:"isVoice"]; + [coder encodeString:_title forCKey:"title"]; + [coder encodeString:_performer forCKey:"performer"]; + [coder encodeInt32:_duration forCKey:"duration"]; + [coder encodeObject:_waveform forCKey:"waveform"]; +} + +- (BOOL)isEqual:(id)object +{ + return [object isKindOfClass:[TGDocumentAttributeAudio class]] && TGStringCompare(((TGDocumentAttributeAudio *)object)->_title, _title) && TGStringCompare(((TGDocumentAttributeAudio *)object)->_performer, _performer) && ((TGDocumentAttributeAudio *)object)->_duration == _duration && _isVoice == ((TGDocumentAttributeAudio *)object)->_isVoice; +} + +@end diff --git a/LegacyComponents/TGDocumentAttributeFilename.h b/LegacyComponents/TGDocumentAttributeFilename.h new file mode 100644 index 0000000000..d2635839af --- /dev/null +++ b/LegacyComponents/TGDocumentAttributeFilename.h @@ -0,0 +1,9 @@ +#import + +@interface TGDocumentAttributeFilename : NSObject + +@property (nonatomic, strong, readonly) NSString *filename; + +- (instancetype)initWithFilename:(NSString *)filename; + +@end diff --git a/LegacyComponents/TGDocumentAttributeFilename.m b/LegacyComponents/TGDocumentAttributeFilename.m new file mode 100644 index 0000000000..3cad9315e6 --- /dev/null +++ b/LegacyComponents/TGDocumentAttributeFilename.m @@ -0,0 +1,45 @@ +#import "TGDocumentAttributeFilename.h" + +#import "LegacyComponentsInternal.h" + +#import "PSKeyValueCoder.h" + +@implementation TGDocumentAttributeFilename + +- (instancetype)initWithFilename:(NSString *)filename +{ + self = [super init]; + if (self != nil) + { + _filename = filename; + } + return self; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + return [self initWithFilename:[coder decodeStringForCKey:"f"]]; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + [coder encodeString:_filename forCKey:"f"]; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + return [self initWithFilename:[aDecoder decodeObjectForKey:@"filename"]]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + if (_filename != nil) + [aCoder encodeObject:_filename forKey:@"filename"]; +} + +- (BOOL)isEqual:(id)object +{ + return [object isKindOfClass:[TGDocumentAttributeFilename class]] && TGObjectCompare(_filename, ((TGDocumentAttributeFilename *)object)->_filename); +} + +@end diff --git a/LegacyComponents/TGDocumentAttributeImageSize.h b/LegacyComponents/TGDocumentAttributeImageSize.h new file mode 100644 index 0000000000..f8156c70b3 --- /dev/null +++ b/LegacyComponents/TGDocumentAttributeImageSize.h @@ -0,0 +1,11 @@ +#import + +#import + +@interface TGDocumentAttributeImageSize : NSObject + +@property (nonatomic, readonly) CGSize size; + +- (instancetype)initWithSize:(CGSize)size; + +@end diff --git a/LegacyComponents/TGDocumentAttributeImageSize.m b/LegacyComponents/TGDocumentAttributeImageSize.m new file mode 100644 index 0000000000..2d018d271f --- /dev/null +++ b/LegacyComponents/TGDocumentAttributeImageSize.m @@ -0,0 +1,43 @@ +#import "TGDocumentAttributeImageSize.h" + +#import "PSKeyValueCoder.h" + +@implementation TGDocumentAttributeImageSize + +- (instancetype)initWithSize:(CGSize)size +{ + self = [super init]; + if (self != nil) + { + _size = size; + } + return self; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + return [self initWithSize:CGSizeMake([coder decodeInt32ForCKey:"w"], [coder decodeInt32ForCKey:"h"])]; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + [coder encodeInt32:(int32_t)_size.width forCKey:"w"]; + [coder encodeInt32:(int32_t)_size.height forCKey:"h"]; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + return [self initWithSize:[aDecoder decodeCGSizeForKey:@"size"]]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeCGSize:_size forKey:@"size"]; +} + +- (BOOL)isEqual:(id)object +{ + return [object isKindOfClass:[TGDocumentAttributeImageSize class]] && CGSizeEqualToSize(_size, ((TGDocumentAttributeImageSize *)object)->_size); +} + +@end diff --git a/LegacyComponents/TGDocumentAttributeSticker.h b/LegacyComponents/TGDocumentAttributeSticker.h new file mode 100644 index 0000000000..73ffb3a7e4 --- /dev/null +++ b/LegacyComponents/TGDocumentAttributeSticker.h @@ -0,0 +1,25 @@ +#import + +#import + +#import + +@interface TGStickerMaskDescription : NSObject + +@property (nonatomic, readonly) int32_t n; +@property (nonatomic, readonly) CGPoint point; +@property (nonatomic, readonly) CGFloat zoom; + +- (instancetype)initWithN:(int32_t)n point:(CGPoint)point zoom:(CGFloat)zoom; + +@end + +@interface TGDocumentAttributeSticker : NSObject + +@property (nonatomic, strong, readonly) NSString *alt; +@property (nonatomic, strong, readonly) id packReference; +@property (nonatomic, strong, readonly) TGStickerMaskDescription *mask; + +- (instancetype)initWithAlt:(NSString *)alt packReference:(id)packReference mask:(TGStickerMaskDescription *)mask; + +@end diff --git a/LegacyComponents/TGDocumentAttributeSticker.m b/LegacyComponents/TGDocumentAttributeSticker.m new file mode 100644 index 0000000000..c75b22ca79 --- /dev/null +++ b/LegacyComponents/TGDocumentAttributeSticker.m @@ -0,0 +1,105 @@ +#import "TGDocumentAttributeSticker.h" + +#import "LegacyComponentsInternal.h" + +#import "PSKeyValueCoder.h" + +@implementation TGStickerMaskDescription + +- (instancetype)initWithN:(int32_t)n point:(CGPoint)point zoom:(CGFloat)zoom { + self = [super init]; + if (self != nil) { + _n = n; + _point = point; + _zoom = zoom; + } + return self; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder { + return [self initWithN:[coder decodeInt32ForCKey:"n"] point:CGPointMake([coder decodeDoubleForCKey:"x"], [coder decodeDoubleForCKey:"y"]) zoom:[coder decodeDoubleForCKey:"z"]]; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder { + [coder encodeInt32:_n forCKey:"n"]; + [coder encodeDouble:_point.x forCKey:"x"]; + [coder encodeDouble:_point.y forCKey:"y"]; + [coder encodeDouble:_zoom forCKey:"z"]; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + return [self initWithN:[aDecoder decodeInt32ForKey:@"n"] point:CGPointMake([aDecoder decodeDoubleForKey:@"x"], [aDecoder decodeDoubleForKey:@"y"]) zoom:[aDecoder decodeDoubleForKey:@"z"]]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeInt32:_n forKey:@"n"]; + [aCoder encodeDouble:_point.x forKey:@"x"]; + [aCoder encodeDouble:_point.y forKey:@"y"]; + [aCoder encodeDouble:_zoom forKey:@"z"]; +} + +@end + +@implementation TGDocumentAttributeSticker + +- (instancetype)initWithAlt:(NSString *)alt packReference:(id)packReference mask:(TGStickerMaskDescription *)mask +{ + self = [super init]; + if (self != nil) + { + _alt = alt; + _packReference = packReference; + _mask = mask; + } + return self; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + return [self initWithAlt:[coder decodeStringForCKey:"alt"] packReference:(id)[coder decodeObjectForCKey:"packReference"] mask:(TGStickerMaskDescription *)[coder decodeObjectForCKey:"mask"]]; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + [coder encodeString:_alt forCKey:"alt"]; + [coder encodeObject:_packReference forCKey:"packReference"]; + [coder encodeObject:_mask forCKey:"mask"]; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + return [self initWithAlt:[aDecoder decodeObjectForKey:@"alt"] packReference:[aDecoder decodeObjectForKey:@"packReference"] mask:[aDecoder decodeObjectForKey:@"mask"]]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + if (_alt != nil) + [aCoder encodeObject:_alt forKey:@"alt"]; + if (_packReference != nil) + [aCoder encodeObject:_packReference forKey:@"packReference"]; + if (_mask != nil) + [aCoder encodeObject:_mask forKey:@"mask"]; +} + +- (BOOL)isEqual:(id)object +{ + if (![object isKindOfClass:[TGDocumentAttributeSticker class]]) { + return false; + } + + if (!TGObjectCompare(_packReference, ((TGDocumentAttributeSticker *)object)->_packReference)) { + return false; + } + + if (!TGObjectCompare(_mask, ((TGDocumentAttributeSticker *)object)->_mask)) { + return false; + } + + if (!TGStringCompare(_alt, ((TGDocumentAttributeSticker *)object)->_alt)) { + return false; + } + + return true; +} + +@end diff --git a/LegacyComponents/TGDocumentAttributeVideo.h b/LegacyComponents/TGDocumentAttributeVideo.h new file mode 100644 index 0000000000..2c51dbd122 --- /dev/null +++ b/LegacyComponents/TGDocumentAttributeVideo.h @@ -0,0 +1,13 @@ +#import + +#import + +@interface TGDocumentAttributeVideo : NSObject + +@property (nonatomic, readonly) bool isRoundMessage; +@property (nonatomic, readonly) CGSize size; +@property (nonatomic, readonly) int32_t duration; + +- (instancetype)initWithRoundMessage:(bool)isRoundMessage size:(CGSize)size duration:(int32_t)duration; + +@end diff --git a/LegacyComponents/TGDocumentAttributeVideo.m b/LegacyComponents/TGDocumentAttributeVideo.m new file mode 100644 index 0000000000..fa9d9df3f5 --- /dev/null +++ b/LegacyComponents/TGDocumentAttributeVideo.m @@ -0,0 +1,42 @@ +#import "TGDocumentAttributeVideo.h" + +#import "PSKeyValueCoder.h" + +@implementation TGDocumentAttributeVideo + +- (instancetype)initWithRoundMessage:(bool)isRoundMessage size:(CGSize)size duration:(int32_t)duration { + self = [super init]; + if (self != nil) { + _isRoundMessage = isRoundMessage; + _size = size; + _duration = duration; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + return [self initWithRoundMessage:[aDecoder decodeBoolForKey:@"roundMessage"] size:[aDecoder decodeCGSizeForKey:@"size"] duration:[aDecoder decodeInt32ForKey:@"duration"]]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeBool:_isRoundMessage forKey:@"roundMessage"]; + [aCoder encodeCGSize:_size forKey:@"size"]; + [aCoder encodeInt32:_duration forKey:@"duration"]; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder { + return [self initWithRoundMessage:[coder decodeInt32ForCKey:"r"] size:CGSizeMake([coder decodeInt32ForCKey:"s.w"], [coder decodeInt32ForCKey:"s.h"]) duration:[coder decodeInt32ForCKey:"d"]]; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder { + [coder encodeInt32:(int32_t)_isRoundMessage forKey:@"r"]; + [coder encodeInt32:(int32_t)_size.width forCKey:"s.w"]; + [coder encodeInt32:(int32_t)_size.height forCKey:"s.h"]; + [coder encodeInt32:_duration forCKey:"d"]; +} + +- (BOOL)isEqual:(id)object { + return [object isKindOfClass:[TGDocumentAttributeVideo class]] && _isRoundMessage == ((TGDocumentAttributeVideo *)object)->_isRoundMessage && CGSizeEqualToSize(_size, ((TGDocumentAttributeVideo *)object)->_size) && _duration == ((TGDocumentAttributeVideo *)object)->_duration; +} + +@end diff --git a/LegacyComponents/TGDocumentMediaAttachment.h b/LegacyComponents/TGDocumentMediaAttachment.h new file mode 100644 index 0000000000..f9d7f4b6ec --- /dev/null +++ b/LegacyComponents/TGDocumentMediaAttachment.h @@ -0,0 +1,55 @@ +/* + * This is the source code of Telegram for iOS v. 1.1 + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Peter Iakovlev, 2013. + */ + +#import + +#import + +#import +#import +#import +#import +#import +#import + +#define TGDocumentMediaAttachmentType ((int)0xE6C64318) + +@interface TGDocumentMediaAttachment : TGMediaAttachment + +@property (nonatomic) int64_t localDocumentId; + +@property (nonatomic) int64_t documentId; +@property (nonatomic) int64_t accessHash; +@property (nonatomic) int datacenterId; +@property (nonatomic) int32_t userId; +@property (nonatomic) int date; +@property (nonatomic, strong) NSString *mimeType; +@property (nonatomic) int size; +@property (nonatomic) int32_t version; +@property (nonatomic, strong) TGImageInfo *thumbnailInfo; + +@property (nonatomic, strong) NSString *documentUri; + +@property (nonatomic, strong) NSArray *attributes; +@property (nonatomic, strong) NSString *caption; + +@property (nonatomic, readonly) NSArray *textCheckingResults; + +- (NSString *)safeFileName; ++ (NSString *)safeFileNameForFileName:(NSString *)fileName; +- (NSString *)fileName; + +- (bool)isAnimated; +- (bool)isSticker; +- (bool)isStickerWithPack; +- (id)stickerPackReference; +- (bool)isVoice; +- (CGSize)pictureSize; +- (bool)isRoundVideo; + +@end diff --git a/LegacyComponents/TGDocumentMediaAttachment.m b/LegacyComponents/TGDocumentMediaAttachment.m new file mode 100644 index 0000000000..d6b21a3624 --- /dev/null +++ b/LegacyComponents/TGDocumentMediaAttachment.m @@ -0,0 +1,404 @@ +#import "TGDocumentMediaAttachment.h" + +#import "LegacyComponentsInternal.h" + +#import "PSCoding.h" +#import "PSKeyValueEncoder.h" +#import "PSKeyValueDecoder.h" + +#import "TGMessage.h" + +@interface TGDocumentMediaAttachment () { + NSArray *_textCheckingResults; +} + +@end + +@implementation TGDocumentMediaAttachment + +- (id)init +{ + self = [super init]; + if (self != nil) + { + self.type = TGDocumentMediaAttachmentType; + } + return self; +} + +- (instancetype)copyWithZone:(NSZone *)__unused zone { + TGDocumentMediaAttachment *document = [[TGDocumentMediaAttachment alloc] init]; + + document->_documentId = _documentId; + document->_localDocumentId = _localDocumentId; + document->_accessHash = _accessHash; + document->_datacenterId = _datacenterId; + document->_userId = _userId; + document->_date = _date; + document->_mimeType = _mimeType; + document->_size = _size; + document->_thumbnailInfo = _thumbnailInfo; + document->_documentUri = _documentUri; + document->_attributes = _attributes; + document->_caption = _caption; + document->_textCheckingResults = _textCheckingResults; + document->_version = _version; + + return document; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + self.type = TGDocumentMediaAttachmentType; + + _documentId = [aDecoder decodeInt64ForKey:@"documentId"]; + _localDocumentId = [aDecoder decodeInt64ForKey:@"localDocumentId"]; + _accessHash = [aDecoder decodeInt64ForKey:@"accessHash"]; + _datacenterId = [aDecoder decodeInt32ForKey:@"datacenterId"]; + _userId = [aDecoder decodeInt32ForKey:@"userId"]; + _date = [aDecoder decodeInt32ForKey:@"date"]; + _mimeType = [aDecoder decodeObjectForKey:@"mimeType"]; + _size = [aDecoder decodeInt32ForKey:@"size"]; + _thumbnailInfo = [aDecoder decodeObjectForKey:@"thumbnailInfo"]; + _documentUri = [aDecoder decodeObjectForKey:@"documentUri"]; + _attributes = [aDecoder decodeObjectForKey:@"attributes"]; + _caption = [aDecoder decodeObjectForKey:@"caption"]; + _version = [aDecoder decodeInt32ForKey:@"version"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:_documentId forKey:@"documentId"]; + [aCoder encodeInt64:_localDocumentId forKey:@"localDocumentId"]; + [aCoder encodeInt64:_accessHash forKey:@"accessHash"]; + [aCoder encodeInt32:_datacenterId forKey:@"datacenterId"]; + [aCoder encodeInt32:_userId forKey:@"userId"]; + [aCoder encodeInt32:_date forKey:@"date"]; + [aCoder encodeInt32:_version forKey:@"version"]; + if (_mimeType != nil) + [aCoder encodeObject:_mimeType forKey:@"mimeType"]; + [aCoder encodeInt32:_size forKey:@"size"]; + if (_thumbnailInfo != nil) + [aCoder encodeObject:_thumbnailInfo forKey:@"thumbnailInfo"]; + if (_documentUri != nil) + [aCoder encodeObject:_documentUri forKey:@"documentUri"]; + if (_attributes != nil) + [aCoder encodeObject:_attributes forKey:@"attributes"]; + if (_caption != nil) { + [aCoder encodeObject:_caption forKey:@"caption"]; + } +} + +- (BOOL)isEqual:(id)object +{ + if (object == self) + return true; + + if (![object isKindOfClass:[TGDocumentMediaAttachment class]]) + return false; + + TGDocumentMediaAttachment *other = object; + if (_documentId == other->_documentId && _localDocumentId == other->_localDocumentId && _accessHash == other->_accessHash && _datacenterId == other->_datacenterId && _userId == other->_userId && _date == other->_date && TGObjectCompare(_mimeType, other->_mimeType) && _size == other->_size && TGObjectCompare(_thumbnailInfo, other->_thumbnailInfo) && TGObjectCompare(_documentUri, other->_documentUri) && TGObjectCompare(_attributes, other->_attributes) && TGStringCompare(_caption, other->_caption) && _version == other->_version) + return true; + return false; +} + +- (void)serialize:(NSMutableData *)data +{ + int dataLengthPtr = (int)data.length; + int zero = 0; + [data appendBytes:&zero length:4]; + + uint8_t version = 6; + [data appendBytes:&version length:sizeof(version)]; + + [data appendBytes:&_localDocumentId length:sizeof(_localDocumentId)]; + + [data appendBytes:&_documentId length:sizeof(_documentId)]; + [data appendBytes:&_accessHash length:sizeof(_accessHash)]; + [data appendBytes:&_datacenterId length:sizeof(_datacenterId)]; + [data appendBytes:&_userId length:sizeof(_userId)]; + [data appendBytes:&_date length:sizeof(_date)]; + + NSData *filenameData = [[self fileName] dataUsingEncoding:NSUTF8StringEncoding]; + int filenameLength = (int)filenameData.length; + [data appendBytes:&filenameLength length:sizeof(filenameLength)]; + [data appendData:filenameData]; + + NSData *mimeData = [_mimeType dataUsingEncoding:NSUTF8StringEncoding]; + int mimeLength = (int)mimeData.length; + [data appendBytes:&mimeLength length:sizeof(mimeLength)]; + [data appendData:mimeData]; + + [data appendBytes:&_size length:sizeof(_size)]; + + uint8_t thumbnailExists = _thumbnailInfo != nil; + [data appendBytes:&thumbnailExists length:sizeof(thumbnailExists)]; + [_thumbnailInfo serialize:data]; + + NSData *uriData = [_documentUri dataUsingEncoding:NSUTF8StringEncoding]; + int uriLength = (int)uriData.length; + [data appendBytes:&uriLength length:sizeof(uriLength)]; + if (uriData != nil) + [data appendData:uriData]; + + PSKeyValueEncoder *encoder = [[PSKeyValueEncoder alloc] init]; + [encoder encodeArray:_attributes forCKey:"attributes"]; + int32_t attributesLength = (int32_t)encoder.data.length; + [data appendBytes:&attributesLength length:4]; + [data appendData:encoder.data]; + + NSData *captionData = [_caption dataUsingEncoding:NSUTF8StringEncoding]; + int32_t captionLength = (int)captionData.length; + [data appendBytes:&captionLength length:sizeof(captionLength)]; + if (captionData != nil) { + [data appendData:captionData]; + } + + [data appendBytes:&_version length:4]; + + int dataLength = (int)(data.length - dataLengthPtr - 4); + [data replaceBytesInRange:NSMakeRange(dataLengthPtr, 4) withBytes:&dataLength]; +} + +- (TGMediaAttachment *)parseMediaAttachment:(NSInputStream *)is +{ + int dataLength = 0; + [is read:(uint8_t *)&dataLength maxLength:4]; + + uint8_t version = 0; + [is read:&version maxLength:sizeof(version)]; + if (version != 1 && version != 2 && version != 3 && version != 4 && version != 5 && version != 6) + { + TGLog(@"***** Document serialized version mismatch"); + return nil; + } + + TGDocumentMediaAttachment *documentAttachment = [[TGDocumentMediaAttachment alloc] init]; + + if (version >= 2) + [is read:(uint8_t *)&documentAttachment->_localDocumentId maxLength:sizeof(documentAttachment->_localDocumentId)]; + + [is read:(uint8_t *)&documentAttachment->_documentId maxLength:sizeof(documentAttachment->_documentId)]; + [is read:(uint8_t *)&documentAttachment->_accessHash maxLength:sizeof(documentAttachment->_accessHash)]; + [is read:(uint8_t *)&documentAttachment->_datacenterId maxLength:sizeof(documentAttachment->_datacenterId)]; + [is read:(uint8_t *)&documentAttachment->_userId maxLength:sizeof(documentAttachment->_userId)]; + [is read:(uint8_t *)&documentAttachment->_date maxLength:sizeof(documentAttachment->_date)]; + + NSString *legacyFileName = @"file"; + int filenameLength = 0; + [is read:(uint8_t *)&filenameLength maxLength:sizeof(filenameLength)]; + if (filenameLength != 0) + { + uint8_t *filenameBytes = malloc(filenameLength); + [is read:filenameBytes maxLength:filenameLength]; + legacyFileName = [[NSString alloc] initWithBytesNoCopy:filenameBytes length:filenameLength encoding:NSUTF8StringEncoding freeWhenDone:true]; + } + + int mimeLength = 0; + [is read:(uint8_t *)&mimeLength maxLength:sizeof(mimeLength)]; + if (mimeLength != 0) + { + uint8_t *mimeBytes = malloc(mimeLength); + [is read:mimeBytes maxLength:mimeLength]; + documentAttachment.mimeType = [[NSString alloc] initWithBytesNoCopy:mimeBytes length:mimeLength encoding:NSUTF8StringEncoding freeWhenDone:true]; + } + + [is read:(uint8_t *)&documentAttachment->_size maxLength:sizeof(documentAttachment->_size)]; + + uint8_t thumbnailExists = 0; + [is read:&thumbnailExists maxLength:sizeof(thumbnailExists)]; + if (thumbnailExists) + { + documentAttachment.thumbnailInfo = [TGImageInfo deserialize:is]; + } + + if (version >= 3) + { + int uriLength = 0; + [is read:(uint8_t *)&uriLength maxLength:sizeof(uriLength)]; + if (uriLength > 0) + { + uint8_t *uriBytes = malloc(uriLength); + [is read:uriBytes maxLength:uriLength]; + documentAttachment.documentUri = [[NSString alloc] initWithBytesNoCopy:uriBytes length:uriLength encoding:NSUTF8StringEncoding freeWhenDone:true]; + } + } + + if (version >= 4) + { + int32_t attributesSize = 0; + [is read:(uint8_t *)&attributesSize maxLength:4]; + if (attributesSize > 0) + { + uint8_t *attributeBytes = malloc(attributesSize); + [is read:attributeBytes maxLength:attributesSize]; + NSData *data = [[NSData alloc] initWithBytesNoCopy:attributeBytes length:attributesSize freeWhenDone:true]; + PSKeyValueDecoder *decoder = [[PSKeyValueDecoder alloc] initWithData:data]; + documentAttachment.attributes = [decoder decodeArrayForCKey:"attributes"]; + } + } + + if (version >= 5) { + int32_t captionLength = 0; + [is read:(uint8_t *)&captionLength maxLength:sizeof(captionLength)]; + if (captionLength > 0) + { + uint8_t *captionBytes = malloc(captionLength); + [is read:captionBytes maxLength:captionLength]; + documentAttachment.caption = [[NSString alloc] initWithBytesNoCopy:captionBytes length:captionLength encoding:NSUTF8StringEncoding freeWhenDone:true]; + } + } + + if (version >= 6) { + int32_t fileVersion = 0; + [is read:(uint8_t *)&fileVersion maxLength:4]; + documentAttachment.version = fileVersion; + } + + bool hasFilenameAttribute = false; + for (id attribute in documentAttachment.attributes) + { + if ([attribute isKindOfClass:[TGDocumentAttributeFilename class]]) + { + hasFilenameAttribute = true; + break; + } + } + + if (!hasFilenameAttribute) + { + NSMutableArray *array = [[NSMutableArray alloc] initWithArray:documentAttachment.attributes]; + [array addObject:[[TGDocumentAttributeFilename alloc] initWithFilename:legacyFileName]]; + documentAttachment.attributes = array; + } + + return documentAttachment; +} + +- (NSString *)fileName +{ + NSString *fileName = @"file"; + for (id attribute in _attributes) + { + if ([attribute isKindOfClass:[TGDocumentAttributeFilename class]]) + { + fileName = ((TGDocumentAttributeFilename *)attribute).filename; + break; + } + } + + return fileName; +} + +- (NSString *)safeFileName +{ + return [TGDocumentMediaAttachment safeFileNameForFileName:[self fileName]]; +} + ++ (NSString *)safeFileNameForFileName:(NSString *)fileName +{ + if (fileName.length == 0) + return @"file"; + + return [fileName stringByReplacingOccurrencesOfString:@"/" withString:@"_"]; +} + +- (bool)isAnimated { + for (id attribute in _attributes) { + if ([attribute isKindOfClass:[TGDocumentAttributeAnimated class]]) { + return true; + } + } + + return false; +} + + +- (bool)isSticker { + for (id attribute in _attributes) { + if ([attribute isKindOfClass:[TGDocumentAttributeSticker class]]) { + return true; + } + } + + return false; +} + +- (bool)isStickerWithPack { + for (id attribute in _attributes) { + if ([attribute isKindOfClass:[TGDocumentAttributeSticker class]]) { + return ((TGDocumentAttributeSticker *)attribute).packReference != nil; + } + } + + return false; +} + +- (id)stickerPackReference { + for (id attribute in _attributes) { + if ([attribute isKindOfClass:[TGDocumentAttributeSticker class]]) { + return ((TGDocumentAttributeSticker *)attribute).packReference; + } + } + return nil; +} + +- (bool)isVoice { + for (id attribute in _attributes) { + if ([attribute isKindOfClass:[TGDocumentAttributeAudio class]]) { + TGDocumentAttributeAudio *audio = attribute; + if (audio.isVoice) { + return true; + } + } + } + + return false; +} + +- (bool)isRoundVideo { + for (id attribute in _attributes) { + if ([attribute isKindOfClass:[TGDocumentAttributeVideo class]]) { + TGDocumentAttributeVideo *video = attribute; + if (video.isRoundMessage) { + return true; + } + } + } + + return false; +} + +- (CGSize)pictureSize { + for (id attribute in _attributes) { + if ([attribute isKindOfClass:[TGDocumentAttributeImageSize class]]) { + return ((TGDocumentAttributeImageSize *)attribute).size; + } else if ([attribute isKindOfClass:[TGDocumentAttributeVideo class]]) { + return ((TGDocumentAttributeVideo *)attribute).size; + } + } + + return CGSizeZero; +} + +- (NSArray *)textCheckingResults +{ + if (_caption.length < 2) + _textCheckingResults = [NSArray array]; + + if (_textCheckingResults == nil) + { + NSArray *textCheckingResults = [TGMessage textCheckingResultsForText:_caption highlightMentionsAndTags:true highlightCommands:true entities:nil]; + _textCheckingResults = textCheckingResults ?: [NSArray array]; + } + + return _textCheckingResults; +} + +@end diff --git a/LegacyComponents/TGForwardedMessageMediaAttachment.h b/LegacyComponents/TGForwardedMessageMediaAttachment.h new file mode 100644 index 0000000000..7c38552329 --- /dev/null +++ b/LegacyComponents/TGForwardedMessageMediaAttachment.h @@ -0,0 +1,27 @@ +/* + * This is the source code of Telegram for iOS v. 1.1 + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Peter Iakovlev, 2013. + */ + +#import + +#define TGForwardedMessageMediaAttachmentType ((int)0xAA1050C1) + +@interface TGForwardedMessageMediaAttachment : TGMediaAttachment + +@property (nonatomic) int64_t forwardSourcePeerId; + +@property (nonatomic) int64_t forwardPeerId; +@property (nonatomic) int forwardDate; + +@property (nonatomic) int32_t forwardAuthorUserId; +@property (nonatomic) int32_t forwardPostId; + +@property (nonatomic) NSString *forwardAuthorSignature; + +@property (nonatomic) int forwardMid; + +@end diff --git a/LegacyComponents/TGForwardedMessageMediaAttachment.m b/LegacyComponents/TGForwardedMessageMediaAttachment.m new file mode 100644 index 0000000000..bd7483f49d --- /dev/null +++ b/LegacyComponents/TGForwardedMessageMediaAttachment.m @@ -0,0 +1,133 @@ +#import "TGForwardedMessageMediaAttachment.h" + +@implementation TGForwardedMessageMediaAttachment + +- (id)init +{ + self = [super init]; + if (self != nil) + { + self.type = TGForwardedMessageMediaAttachmentType; + } + return self; +} + +- (id)copyWithZone:(NSZone *)__unused zone +{ + TGForwardedMessageMediaAttachment *attachment = [[TGForwardedMessageMediaAttachment alloc] init]; + + attachment.forwardSourcePeerId = _forwardSourcePeerId; + attachment.forwardPeerId = _forwardPeerId; + attachment.forwardDate = _forwardDate; + attachment.forwardAuthorUserId = _forwardAuthorUserId; + attachment.forwardPostId = _forwardPostId; + attachment.forwardMid = _forwardMid; + + return attachment; +} + +- (void)serialize:(NSMutableData *)data +{ + int32_t magic = 0x72413fad; + [data appendBytes:&magic length:4]; + + int dataLengthPtr = (int)data.length; + int zero = 0; + [data appendBytes:&zero length:4]; + + [data appendBytes:&_forwardPeerId length:8]; + [data appendBytes:&_forwardDate length:4]; + [data appendBytes:&_forwardMid length:4]; + + [data appendBytes:&_forwardAuthorUserId length:4]; + [data appendBytes:&_forwardPostId length:4]; + + [data appendBytes:&_forwardSourcePeerId length:8]; + + NSData *signatureData = [_forwardAuthorSignature dataUsingEncoding:NSUTF8StringEncoding]; + int32_t signatureLength = (int32_t)signatureData.length; + [data appendBytes:&signatureLength length:4]; + [data appendData:signatureData]; + + int dataLength = (int)(data.length - dataLengthPtr - 4); + [data replaceBytesInRange:NSMakeRange(dataLengthPtr, 4) withBytes:&dataLength]; +} + +- (TGMediaAttachment *)parseMediaAttachment:(NSInputStream *)is +{ + int32_t magic = 0; + [is read:(uint8_t *)&magic maxLength:4]; + + int32_t dataLength = 0; + + int32_t version = 0; + + if (magic == 0x72413faa) { + version = 2; + [is read:(uint8_t *)&dataLength maxLength:4]; + } else if (magic == 0x72413fab) { + version = 3; + [is read:(uint8_t *)&dataLength maxLength:4]; + } else if (magic == 0x72413fac) { + version = 4; + [is read:(uint8_t *)&dataLength maxLength:4]; + } else if (magic == 0x72413fad) { + version = 5; + [is read:(uint8_t *)&dataLength maxLength:4]; + } else { + dataLength = magic; + } + + TGForwardedMessageMediaAttachment *messageAttachment = [[TGForwardedMessageMediaAttachment alloc] init]; + + if (version >= 2) { + int64_t forwardPeerId = 0; + [is read:(uint8_t *)&forwardPeerId maxLength:8]; + messageAttachment.forwardPeerId = forwardPeerId; + } else { + int32_t forwardUid = 0; + [is read:(uint8_t *)&forwardUid maxLength:4]; + messageAttachment.forwardPeerId = forwardUid; + } + + int forwardDate = 0; + [is read:(uint8_t *)&forwardDate maxLength:4]; + messageAttachment.forwardDate = forwardDate; + + int forwardMid = 0; + [is read:(uint8_t *)&forwardMid maxLength:4]; + messageAttachment.forwardMid = forwardMid; + + if (version >= 3) { + int32_t forwardAuthorUserId = 0; + [is read:(uint8_t *)&forwardAuthorUserId maxLength:4]; + messageAttachment.forwardAuthorUserId = forwardAuthorUserId; + + int32_t forwardPostId = 0; + [is read:(uint8_t *)&forwardPostId maxLength:4]; + messageAttachment.forwardPostId = forwardPostId; + } + + if (version >= 4) { + int64_t forwardSourcePeerId = 0; + [is read:(uint8_t *)&forwardSourcePeerId maxLength:8]; + messageAttachment.forwardSourcePeerId = forwardSourcePeerId; + } + + if (version >= 5) { + int32_t signatureLength = 0; + [is read:(uint8_t *)&signatureLength maxLength:4]; + uint8_t *signatureBytes = malloc(signatureLength); + [is read:signatureBytes maxLength:signatureLength]; + NSString *signature = [[NSString alloc] initWithBytesNoCopy:signatureBytes length:signatureLength encoding:NSUTF8StringEncoding freeWhenDone:true]; + if (signatureLength != 0) { + messageAttachment.forwardAuthorSignature = signature; + } else { + messageAttachment.forwardAuthorSignature = nil; + } + } + + return messageAttachment; +} + +@end diff --git a/LegacyComponents/TGGameMediaAttachment.h b/LegacyComponents/TGGameMediaAttachment.h new file mode 100644 index 0000000000..fc28d95d5b --- /dev/null +++ b/LegacyComponents/TGGameMediaAttachment.h @@ -0,0 +1,23 @@ +#import + +@class TGImageMediaAttachment; +@class TGDocumentMediaAttachment; +@class TGWebPageMediaAttachment; + +#define TGGameAttachmentType ((int)0x57af081e) + +@interface TGGameMediaAttachment : TGMediaAttachment + +@property (nonatomic, readonly) int64_t gameId; +@property (nonatomic, readonly) int64_t accessHash; +@property (nonatomic, strong, readonly) NSString *shortName; +@property (nonatomic, strong, readonly) NSString *title; +@property (nonatomic, strong, readonly) NSString *gameDescription; +@property (nonatomic, strong, readonly) TGImageMediaAttachment *photo; +@property (nonatomic, strong, readonly) TGDocumentMediaAttachment *document; + +- (instancetype)initWithGameId:(int64_t)gameId accessHash:(int64_t)accessHash shortName:(NSString *)shortName title:(NSString *)title gameDescription:(NSString *)gameDescription photo:(TGImageMediaAttachment *)photo document:(TGDocumentMediaAttachment *)document; + +- (TGWebPageMediaAttachment *)webPageWithText:(NSString *)text entities:(NSArray *)entities; + +@end diff --git a/LegacyComponents/TGGameMediaAttachment.m b/LegacyComponents/TGGameMediaAttachment.m new file mode 100644 index 0000000000..008b2799e5 --- /dev/null +++ b/LegacyComponents/TGGameMediaAttachment.m @@ -0,0 +1,72 @@ +#import "TGGameMediaAttachment.h" + +#import "LegacyComponentsInternal.h" + +#import "NSInputStream+TL.h" + +#import "TGWebPageMediaAttachment.h" + +@implementation TGGameMediaAttachment + +- (instancetype)initWithGameId:(int64_t)gameId accessHash:(int64_t)accessHash shortName:(NSString *)shortName title:(NSString *)title gameDescription:(NSString *)gameDescription photo:(TGImageMediaAttachment *)photo document:(TGDocumentMediaAttachment *)document { + self = [super init]; + if (self != nil) { + self.type = TGGameAttachmentType; + + _gameId = gameId; + _accessHash = accessHash; + _shortName = shortName; + _title = title; + _gameDescription = gameDescription; + _photo = photo; + _document = document; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + return [self initWithGameId:[aDecoder decodeInt64ForKey:@"gameId"] accessHash:[aDecoder decodeInt64ForKey:@"accessHash"] shortName:[aDecoder decodeObjectForKey:@"shortName"] title:[aDecoder decodeObjectForKey:@"title"] gameDescription:[aDecoder decodeObjectForKey:@"gameDescription"] photo:[aDecoder decodeObjectForKey:@"photo"] document:[aDecoder decodeObjectForKey:@"document"]]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeInt64:_gameId forKey:@"gameId"]; + [aCoder encodeInt64:_accessHash forKey:@"accessHash"]; + [aCoder encodeObject:_shortName forKey:@"shortName"]; + [aCoder encodeObject:_title forKey:@"title"]; + [aCoder encodeObject:_gameDescription forKey:@"gameDescription"]; + [aCoder encodeObject:_photo forKey:@"photo"]; + [aCoder encodeObject:_document forKey:@"document"]; +} + +- (void)serialize:(NSMutableData *)data +{ + NSData *serializedData = [NSKeyedArchiver archivedDataWithRootObject:self]; + int32_t length = (int32_t)serializedData.length; + [data appendBytes:&length length:4]; + [data appendData:serializedData]; +} + +- (TGMediaAttachment *)parseMediaAttachment:(NSInputStream *)is +{ + int32_t length = [is readInt32]; + NSData *data = [is readData:length]; + return [NSKeyedUnarchiver unarchiveObjectWithData:data]; +} + +- (TGWebPageMediaAttachment *)webPageWithText:(NSString *)text entities:(NSArray *)entities { + TGWebPageMediaAttachment *webPage = [[TGWebPageMediaAttachment alloc] init]; + webPage.siteName = self.title; + webPage.pageDescription = (text.length == 0 || [text isEqualToString:@" "]) ? self.gameDescription : text; + + webPage.photo = self.photo; + webPage.document = self.document; + webPage.pageType = @"game"; + webPage.pageDescriptionEntities = (text.length == 0 || [text isEqualToString:@" "]) ? nil : entities; + return webPage; +} + +- (BOOL)isEqual:(id)object { + return [object isKindOfClass:[TGGameMediaAttachment class]] && ((TGGameMediaAttachment *)object)->_gameId == _gameId && ((TGGameMediaAttachment *)object)->_accessHash == _accessHash && TGStringCompare(((TGGameMediaAttachment *)object)->_title, _title) && TGStringCompare(((TGGameMediaAttachment *)object)->_gameDescription, _gameDescription) && TGStringCompare(((TGGameMediaAttachment *)object)->_shortName, _shortName); +} + +@end diff --git a/LegacyComponents/TGImageInfo.h b/LegacyComponents/TGImageInfo.h new file mode 100644 index 0000000000..7e9a706090 --- /dev/null +++ b/LegacyComponents/TGImageInfo.h @@ -0,0 +1,34 @@ +/* + * This is the source code of Telegram for iOS v. 1.1 + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Peter Iakovlev, 2013. + */ + +#import + +@interface TGImageInfo : NSObject + +- (void)addImageWithSize:(CGSize)size url:(NSString *)url; +- (void)addImageWithSize:(CGSize)size url:(NSString *)url fileSize:(int)fileSize; + +- (NSString *)closestImageUrlWithWidth:(int)width resultingSize:(CGSize *)resultingSize; +- (NSString *)closestImageUrlWithHeight:(int)height resultingSize:(CGSize *)resultingSize; +- (NSString *)closestImageUrlWithSize:(CGSize)size resultingSize:(CGSize *)resultingSize; +- (NSString *)closestImageUrlWithSize:(CGSize)size resultingSize:(CGSize *)resultingSize pickLargest:(bool)pickLargest; +- (NSString *)closestImageUrlWithSize:(CGSize)size resultingSize:(CGSize *)resultingSize resultingFileSize:(int *)resultingFileSize; +- (NSString *)closestImageUrlWithSize:(CGSize)size resultingSize:(CGSize *)resultingSize resultingFileSize:(int *)resultingFileSize pickLargest:(bool)pickLargest; +- (NSString *)imageUrlWithExactSize:(CGSize)size; +- (NSString *)imageUrlForLargestSize:(CGSize *)actualSize; +- (NSString *)imageUrlForSizeLargerThanSize:(CGSize)size actualSize:(CGSize *)actualSize; + +- (bool)containsSizeWithUrl:(NSString *)url; + +- (NSDictionary *)allSizes; +- (bool)empty; + +- (void)serialize:(NSMutableData *)data; ++ (TGImageInfo *)deserialize:(NSInputStream *)is; + +@end diff --git a/LegacyComponents/TGImageInfo.mm b/LegacyComponents/TGImageInfo.mm new file mode 100644 index 0000000000..fafa851bfc --- /dev/null +++ b/LegacyComponents/TGImageInfo.mm @@ -0,0 +1,358 @@ +#import "TGImageInfo.h" + +#import + +struct TGImageSizeRecord +{ + CGSize size; + NSString *url; + int fileSize; + + TGImageSizeRecord(CGSize size_, NSString *url_, int fileSize_) : + size(size_), fileSize(fileSize_) + { + url = url_; + } + + TGImageSizeRecord(const TGImageSizeRecord &other) + { + url = other.url; + size = other.size; + fileSize = other.fileSize; + } + + TGImageSizeRecord & operator= (const TGImageSizeRecord &other) + { + if (this != &other) + { + url = other.url; + size = other.size; + fileSize = other.fileSize; + } + + return *this; + } + + ~TGImageSizeRecord() + { + url = nil; + } +}; + +@interface TGImageInfo () +{ + std::vector sizes; +} + +@end + +@implementation TGImageInfo + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + for (NSDictionary *sizeDict in [aDecoder decodeObjectForKey:@"sizes"]) + { + [self addImageWithSize:CGSizeMake([sizeDict[@"width"] floatValue], [sizeDict[@"height"] floatValue]) url:sizeDict[@"url"] fileSize:[sizeDict[@"fileSize"] intValue]]; + } + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + NSMutableArray *array = [[NSMutableArray alloc] init]; + for (auto it : sizes) + { + [array addObject:@{@"width": @(it.size.width), @"height": @(it.size.height), @"url": it.url == nil ? @"" : it.url, @"fileSize": @(it.fileSize)}]; + } + [aCoder encodeObject:array forKey:@"sizes"]; +} + +- (BOOL)isEqual:(id)object +{ + if (![object isKindOfClass:[TGImageInfo class]]) + return false; + + TGImageInfo *other = (TGImageInfo *)object; + + if (sizes.size() != other->sizes.size()) + return false; + + for (int i = 0; i < (int)sizes.size(); i++) + { + if (!CGSizeEqualToSize(sizes[i].size, other->sizes[i].size)) + return false; + + if (![sizes[i].url isEqualToString:other->sizes[i].url]) + return false; + } + + return true; +} + +- (void)addImageWithSize:(CGSize)size url:(NSString *)url +{ + sizes.push_back(TGImageSizeRecord(size, url, 0)); +} + +- (void)addImageWithSize:(CGSize)size url:(NSString *)url fileSize:(int)fileSize +{ + sizes.push_back(TGImageSizeRecord(size, url, fileSize)); +} + +- (NSString *)closestImageUrlWithWidth:(int)width resultingSize:(CGSize *)resultingSize +{ + CGSize closestSize = CGSizeZero; + NSString *closestUrl = nil; + for (std::vector::iterator it = sizes.begin(); it != sizes.end(); it++) + { + if (closestUrl == nil) + { + closestUrl = it->url; + closestSize = it->size; + } + else + { + if (ABS(width - it->size.width) < ABS(width - closestSize.width)) + { + closestUrl = it->url; + closestSize = it->size; + } + } + } + + if (resultingSize != NULL) + *resultingSize = closestSize; + + return closestUrl; +} + +- (NSString *)closestImageUrlWithHeight:(int)height resultingSize:(CGSize *)resultingSize +{ + CGSize closestSize = CGSizeZero; + NSString *closestUrl = nil; + for (std::vector::iterator it = sizes.begin(); it != sizes.end(); it++) + { + if (closestUrl == nil) + { + closestUrl = it->url; + closestSize = it->size; + } + else + { + if (ABS(height - it->size.height) < ABS(height - closestSize.height)) + { + closestUrl = it->url; + closestSize = it->size; + } + } + } + + if (resultingSize != NULL) + *resultingSize = closestSize; + + return closestUrl; +} + +- (NSString *)closestImageUrlWithSize:(CGSize)size resultingSize:(CGSize *)resultingSize +{ + return [self closestImageUrlWithSize:size resultingSize:resultingSize pickLargest:false]; +} + +- (NSString *)closestImageUrlWithSize:(CGSize)size resultingSize:(CGSize *)resultingSize resultingFileSize:(int *)resultingFileSize +{ + return [self closestImageUrlWithSize:size resultingSize:resultingSize resultingFileSize:resultingFileSize pickLargest:false]; +} + +- (NSString *)closestImageUrlWithSize:(CGSize)size resultingSize:(CGSize *)resultingSize pickLargest:(bool)pickLargest +{ + return [self closestImageUrlWithSize:size resultingSize:resultingSize resultingFileSize:NULL pickLargest:pickLargest]; +} + +- (NSString *)closestImageUrlWithSize:(CGSize)size resultingSize:(CGSize *)resultingSize resultingFileSize:(int *)resultingFileSize pickLargest:(bool)pickLargest +{ + CGSize closestSize = CGSizeZero; + int closestFileSize = 0; + CGFloat closestDeltaSquared = FLT_MAX; + NSString *closestUrl = nil; + for (std::vector::iterator it = sizes.begin(); it != sizes.end(); it++) + { + CGFloat deltaWidth = ABS(size.width - it->size.width); + CGFloat deltaHeight = ABS(size.height - it->size.height); + + CGFloat currentDeltaSquared = deltaWidth * deltaWidth + deltaHeight * deltaHeight; + + if (closestUrl == nil || currentDeltaSquared < closestDeltaSquared || (pickLargest && ((currentDeltaSquared <= closestDeltaSquared + FLT_EPSILON) || ((closestSize.width < size.width || closestSize.height < size.height) && (it->size.width > closestSize.width && it->size.height > closestSize.height))))) + { + closestUrl = it->url; + closestSize = it->size; + closestFileSize = it->fileSize; + closestDeltaSquared = deltaWidth * deltaWidth + deltaHeight * deltaHeight; + } + } + + if (resultingSize != NULL) + *resultingSize = closestSize; + + if (resultingFileSize != NULL) + *resultingFileSize = closestFileSize; + + return closestUrl; +} + +- (NSString *)imageUrlWithExactSize:(CGSize)size +{ + for (std::vector::iterator it = sizes.begin(); it != sizes.end(); it++) + { + CGFloat deltaWidth = ABS(size.width - it->size.width); + CGFloat deltaHeight = ABS(size.height - it->size.height); + + if (deltaWidth < 1 + FLT_EPSILON && deltaHeight < 1 + FLT_EPSILON) + { + return it->url; + } + } + + return nil; +} + +- (NSString *)imageUrlForLargestSize:(CGSize *)actualSize +{ + NSString *largestUrl = nil; + CGSize largestSize = CGSizeZero; + + for (auto it = sizes.begin(); it != sizes.end(); it++) + { + if (it->size.width > largestSize.width) + { + largestUrl = it->url; + largestSize = it->size; + } + } + + if (actualSize != NULL) + *actualSize = largestSize; + + return largestUrl; +} + +- (NSString *)imageUrlForSizeLargerThanSize:(CGSize)size actualSize:(CGSize *)actualSize +{ + NSString *largestUrl = nil; + CGSize largestSize = CGSizeZero; + + for (auto it = sizes.begin(); it != sizes.end(); it++) + { + if (it->size.width > size.width && (largestUrl == nil || it->size.width < largestSize.width)) + { + largestUrl = it->url; + largestSize = it->size; + break; + } + } + + if (largestUrl == nil) + largestUrl = [self closestImageUrlWithSize:size resultingSize:actualSize pickLargest:true]; + else if (actualSize) + *actualSize = largestSize; + + return largestUrl; +} + +- (bool)containsSizeWithUrl:(NSString *)url +{ + if (url == nil) + return false; + + for (std::vector::iterator it = sizes.begin(); it != sizes.end(); it++) + { + if ([url isEqualToString:it->url]) + return true; + } + + return false; +} + +- (NSDictionary *)allSizes +{ + NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; + + for (std::vector::iterator it = sizes.begin(); it != sizes.end(); it++) + { + [dict setObject:[NSValue valueWithCGSize:it->size] forKey:it->url]; + } + + return dict; +} + +- (bool)empty +{ + return sizes.empty(); +} + +- (void)serialize:(NSMutableData *)data +{ + int writtenCount = (int)sizes.size(); + writtenCount |= (1 << 31); + [data appendBytes:&writtenCount length:4]; + + uint16_t version = 1; + [data appendBytes:&version length:2]; + + for (std::vector::iterator it = sizes.begin(); it != sizes.end(); it++) + { + NSData *urlData = [it->url dataUsingEncoding:NSUTF8StringEncoding]; + int length = (int)urlData.length; + [data appendBytes:&length length:4]; + [data appendData:urlData]; + + float width = (float)it->size.width; + float height = (float)it->size.height; + [data appendBytes:&width length:4]; + [data appendBytes:&height length:4]; + [data appendBytes:&it->fileSize length:4]; + } +} + ++ (TGImageInfo *)deserialize:(NSInputStream *)is +{ + TGImageInfo *info = [[TGImageInfo alloc] init]; + + uint16_t version = 0; + + int count = 0; + [is read:(uint8_t *)&count maxLength:4]; + + if (count & (1 << 31)) + { + count &= ~(1 << 31); + [is read:(uint8_t *)&version maxLength:2]; + } + + for (int i = 0; i < count; i++) + { + int length = 0; + [is read:(uint8_t *)&length maxLength:4]; + uint8_t *urlBytes = (uint8_t *)malloc(length); + [is read:urlBytes maxLength:length]; + NSString *url = [[NSString alloc] initWithBytesNoCopy:urlBytes length:length encoding:NSUTF8StringEncoding freeWhenDone:true]; + + float sizeWidth = 0.0f; + float sizeHeight = 0.0f; + [is read:(uint8_t *)&sizeWidth maxLength:4]; + [is read:(uint8_t *)&sizeHeight maxLength:4]; + + int fileSize = 0; + if (version >= 1) + [is read:(uint8_t *)&fileSize maxLength:4]; + + [info addImageWithSize:CGSizeMake(sizeWidth, sizeHeight) url:url fileSize:fileSize]; + } + + return info; +} + +@end diff --git a/LegacyComponents/TGImageMediaAttachment.h b/LegacyComponents/TGImageMediaAttachment.h new file mode 100644 index 0000000000..61f09d06bc --- /dev/null +++ b/LegacyComponents/TGImageMediaAttachment.h @@ -0,0 +1,35 @@ +/* + * This is the source code of Telegram for iOS v. 1.1 + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Peter Iakovlev, 2013. + */ + +#import + +#import + +#define TGImageMediaAttachmentType 0x269BD8A8 + +@interface TGImageMediaAttachment : TGMediaAttachment + +@property (nonatomic) int64_t imageId; +@property (nonatomic, readonly) int64_t localImageId; +@property (nonatomic) int64_t accessHash; +@property (nonatomic) int date; +@property (nonatomic) bool hasLocation; +@property (nonatomic) double locationLatitude; +@property (nonatomic) double locationLongitude; +@property (nonatomic, strong) TGImageInfo *imageInfo; +@property (nonatomic) NSString *caption; +@property (nonatomic) bool hasStickers; +@property (nonatomic, strong) NSArray *embeddedStickerDocuments; + +@property (nonatomic, readonly) NSArray *textCheckingResults; + ++ (int64_t)localImageIdForImageInfo:(TGImageInfo *)imageInfo; + +- (CGSize)dimensions; + +@end diff --git a/LegacyComponents/TGImageMediaAttachment.m b/LegacyComponents/TGImageMediaAttachment.m new file mode 100644 index 0000000000..2c4a2cf2e3 --- /dev/null +++ b/LegacyComponents/TGImageMediaAttachment.m @@ -0,0 +1,267 @@ +#import "TGImageMediaAttachment.h" + +#import "TGMessage.h" +#import "TGStringUtils.h" + +@interface TGImageMediaAttachment () +{ + NSArray *_textCheckingResults; +} +@end + +@implementation TGImageMediaAttachment + +- (id)init +{ + self = [super init]; + if (self != nil) + { + self.type = TGImageMediaAttachmentType; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + self.type = TGImageMediaAttachmentType; + _imageId = [aDecoder decodeInt64ForKey:@"imageId"]; + _accessHash = [aDecoder decodeInt64ForKey:@"accessHash"]; + _date = [aDecoder decodeInt32ForKey:@"date"]; + _hasLocation = false; + _locationLatitude = 0; + _locationLongitude = 0; + _imageInfo = [aDecoder decodeObjectForKey:@"imageInfo"]; + _caption = [aDecoder decodeObjectForKey:@"caption"]; + _hasStickers = [aDecoder decodeBoolForKey:@"hasStickers"]; + _embeddedStickerDocuments = [aDecoder decodeObjectForKey:@"embeddedStickerDocuments"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:_imageId forKey:@"imageId"]; + [aCoder encodeInt64:_accessHash forKey:@"accessHash"]; + [aCoder encodeInt32:_date forKey:@"date"]; + [aCoder encodeObject:_imageInfo forKey:@"imageInfo"]; + [aCoder encodeObject:_caption forKey:@"caption"]; + [aCoder encodeBool:_hasStickers forKey:@"hasStickers"]; + [aCoder encodeObject:_embeddedStickerDocuments forKey:@"embeddedStickerDocuments"]; +} + +- (id)copyWithZone:(NSZone *)__unused zone +{ + TGImageMediaAttachment *imageAttachment = [[TGImageMediaAttachment alloc] init]; + + imageAttachment.imageId = _imageId; + imageAttachment.accessHash = _accessHash; + imageAttachment.date = _date; + imageAttachment.hasLocation = _hasLocation; + imageAttachment.locationLatitude = _locationLatitude; + imageAttachment.locationLongitude = _locationLongitude; + imageAttachment.imageInfo = _imageInfo; + imageAttachment.caption = _caption; + imageAttachment.hasStickers = _hasStickers; + imageAttachment.embeddedStickerDocuments = _embeddedStickerDocuments; + + return imageAttachment; +} + +- (BOOL)isEqual:(id)object { + if (![object isKindOfClass:[TGImageMediaAttachment class]]) { + return false; + } + TGImageMediaAttachment *other = object; + if (_imageId != other->_imageId || _accessHash != other->_accessHash || _date != other->_date || _hasLocation != other->_hasLocation) { + return false; + } + if (![_imageInfo isEqual:other->_imageInfo]) { + return false; + } + return true; +} + +- (int64_t)localImageId +{ + return [TGImageMediaAttachment localImageIdForImageInfo:self.imageInfo]; +} + ++ (int64_t)localImageIdForImageInfo:(TGImageInfo *)imageInfo { + NSString *legacyCacheUrl = [imageInfo imageUrlForLargestSize:NULL]; + int64_t localImageId = 0; + if (legacyCacheUrl.length != 0) + localImageId = murMurHash32(legacyCacheUrl); + + return localImageId; +} + +- (void)serialize:(NSMutableData *)data +{ + int32_t modernTag = 0x7abacaf1; + [data appendBytes:&modernTag length:4]; + + uint8_t version = 3; + [data appendBytes:&version length:1]; + + int dataLengthPtr = (int)data.length; + int zero = 0; + [data appendBytes:&zero length:4]; + + [data appendBytes:&_imageId length:8]; + + [data appendBytes:(uint8_t *)&_date length:4]; + + uint8_t hasLocation = _hasLocation ? 1 : 0; + [data appendBytes:&hasLocation length:1]; + + if (_hasLocation) + { + [data appendBytes:(uint8_t *)&_locationLatitude length:8]; + [data appendBytes:(uint8_t *)&_locationLongitude length:8]; + } + + uint8_t hasImageInfo = _imageInfo != nil ? 1 : 0; + [data appendBytes:&hasImageInfo length:1]; + if (hasImageInfo != 0) + { + [_imageInfo serialize:data]; + } + + [data appendBytes:&_accessHash length:8]; + + NSData *captionData = [_caption dataUsingEncoding:NSUTF8StringEncoding]; + int32_t captionLength = (int32_t)captionData.length; + [data appendBytes:&captionLength length:4]; + if (captionLength != 0) + [data appendData:captionData]; + + int8_t hasStickers = _hasStickers ? 1 : 0; + [data appendBytes:&hasStickers length:1]; + + if (_embeddedStickerDocuments.count == 0) { + int32_t zero = 0; + [data appendBytes:&zero length:4]; + } else { + NSData *stickerData = [NSKeyedArchiver archivedDataWithRootObject:_embeddedStickerDocuments]; + int32_t length = (int32_t)stickerData.length; + [data appendBytes:&length length:4]; + [data appendData:stickerData]; + } + + int dataLength = (int)(data.length - dataLengthPtr - 4); + [data replaceBytesInRange:NSMakeRange(dataLengthPtr, 4) withBytes:&dataLength]; +} + +- (TGMediaAttachment *)parseMediaAttachment:(NSInputStream *)is +{ + int32_t dataLength = 0; + [is read:(uint8_t *)&dataLength maxLength:4]; + + uint8_t version = 1; + if (dataLength == 0x7abacaf1) + { + [is read:(uint8_t *)&version maxLength:1]; + [is read:(uint8_t *)&dataLength maxLength:4]; + } + + TGImageMediaAttachment *imageAttachment = [[TGImageMediaAttachment alloc] init]; + + int64_t imageId = 0; + [is read:(uint8_t *)&imageId maxLength:8]; + dataLength -= 8; + + imageAttachment.imageId = imageId; + + int date = 0; + [is read:(uint8_t *)&date maxLength:4]; + dataLength -= 4; + + imageAttachment.date = date; + + uint8_t hasLocation = 0; + [is read:&hasLocation maxLength:1]; + dataLength -= 1; + + imageAttachment.hasLocation = hasLocation != 0; + + if (hasLocation != 0) + { + double value = 0; + [is read:(uint8_t *)&value maxLength:8]; + imageAttachment.locationLatitude = value; + [is read:(uint8_t *)&value maxLength:8]; + imageAttachment.locationLongitude = value; + + dataLength -= 16; + } + + uint8_t hasImageInfo = 0; + [is read:&hasImageInfo maxLength:1]; + dataLength -= 1; + + if (hasImageInfo != 0) + { + TGImageInfo *imageInfo = [TGImageInfo deserialize:is]; + if (imageInfo != nil) + imageAttachment.imageInfo = imageInfo; + } + + int64_t accessHash = 0; + [is read:(uint8_t *)&accessHash maxLength:8]; + imageAttachment.accessHash = accessHash; + + if (version >= 2) + { + int32_t captionLength = 0; + [is read:(uint8_t *)&captionLength maxLength:4]; + if (captionLength != 0) + { + uint8_t *captionBytes = malloc(captionLength); + [is read:captionBytes maxLength:captionLength]; + imageAttachment.caption = [[NSString alloc] initWithBytes:captionBytes length:captionLength encoding:NSUTF8StringEncoding]; + free(captionBytes); + } + } + + if (version >= 3) { + int8_t hasStickers = 0; + [is read:(uint8_t *)&hasStickers maxLength:1]; + imageAttachment.hasStickers = hasStickers != 0; + + int32_t stickerDataLength = 0; + [is read:(uint8_t *)&stickerDataLength maxLength:4]; + if (stickerDataLength != 0) { + uint8_t *stickerBytes = malloc(stickerDataLength); + [is read:stickerBytes maxLength:stickerDataLength]; + NSData *stickerData = [[NSData alloc] initWithBytesNoCopy:stickerBytes length:stickerDataLength freeWhenDone:true]; + imageAttachment.embeddedStickerDocuments = [NSKeyedUnarchiver unarchiveObjectWithData:stickerData]; + } + } + + return imageAttachment; +} + +- (NSArray *)textCheckingResults +{ + if (_caption.length < 2) + _textCheckingResults = [NSArray array]; + + if (_textCheckingResults == nil) + { + NSArray *textCheckingResults = [TGMessage textCheckingResultsForText:_caption highlightMentionsAndTags:true highlightCommands:true entities:nil]; + _textCheckingResults = textCheckingResults ?: [NSArray array]; + } + + return _textCheckingResults; +} + +- (CGSize)dimensions { + CGSize size = CGSizeZero; + [_imageInfo imageUrlForLargestSize:&size]; + return size; +} + +@end diff --git a/LegacyComponents/TGInstantPage.h b/LegacyComponents/TGInstantPage.h new file mode 100644 index 0000000000..1d8e208145 --- /dev/null +++ b/LegacyComponents/TGInstantPage.h @@ -0,0 +1,298 @@ +#import +#import + +@class TGImageMediaAttachment; +@class TGVideoMediaAttachment; +@class TGDocumentMediaAttachment; +@class TGConversation; + +@interface TGRichText : NSObject + +@end + +@interface TGRichTextPlain : TGRichText + +@property (nonatomic, strong, readonly) NSString *text; + +- (instancetype)initWithText:(NSString *)text; + +@end + +@interface TGRichTextBold : TGRichText + +@property (nonatomic, strong, readonly) TGRichText *text; + +- (instancetype)initWithText:(TGRichText *)text; + +@end + +@interface TGRichTextItalic : TGRichText + +@property (nonatomic, strong, readonly) TGRichText *text; + +- (instancetype)initWithText:(TGRichText *)text; + +@end + +@interface TGRichTextUnderline : TGRichText + +@property (nonatomic, strong, readonly) TGRichText *text; + +- (instancetype)initWithText:(TGRichText *)text; + +@end + +@interface TGRichTextStrikethrough : TGRichText + +@property (nonatomic, strong, readonly) TGRichText *text; + +- (instancetype)initWithText:(TGRichText *)text; + +@end + +@interface TGRichTextFixed : TGRichText + +@property (nonatomic, strong, readonly) TGRichText *text; + +- (instancetype)initWithText:(TGRichText *)text; + +@end + +@interface TGRichTextUrl : TGRichText + +@property (nonatomic, strong, readonly) TGRichText *text; +@property (nonatomic, strong, readonly) NSString *url; +@property (nonatomic, readonly) int64_t webpageId; + +- (instancetype)initWithText:(TGRichText *)text url:(NSString *)url webpageId:(int64_t)webpageId; + +@end + +@interface TGRichTextEmail: TGRichText + +@property (nonatomic, strong, readonly) TGRichText *text; +@property (nonatomic, strong) NSString *email; + +- (instancetype)initWithText:(TGRichText *)text email:(NSString *)email; + +@end + +@interface TGRichTextCollection : TGRichText + +@property (nonatomic, strong, readonly) NSArray *texts; + +- (instancetype)initWithTexts:(NSArray *)texts; + +@end + +@interface TGInstantPageBlock : NSObject + +@end + +@interface TGInstantPageBlockCover : TGInstantPageBlock + +@property (nonatomic, strong, readonly) TGInstantPageBlock *block; + +- (instancetype)initWithBlock:(TGInstantPageBlock *)block; + +@end + +@interface TGInstantPageBlockChannel : TGInstantPageBlock + +@property (nonatomic, strong, readonly) TGConversation *channel; + +- (instancetype)initWithChannel:(TGConversation *)channel; + +@end + +@interface TGInstantPageBlockTitle : TGInstantPageBlock + +@property (nonatomic, strong, readonly) TGRichText *text; + +- (instancetype)initWithText:(TGRichText *)text; + +@end + +@interface TGInstantPageBlockSubtitle : TGInstantPageBlock + +@property (nonatomic, strong, readonly) TGRichText *text; + +- (instancetype)initWithText:(TGRichText *)text; + +@end + +@interface TGInstantPageBlockAuthorAndDate : TGInstantPageBlock + +@property (nonatomic, strong, readonly) TGRichText *author; +@property (nonatomic, readonly) int32_t date; + +- (instancetype)initWithAuthor:(TGRichText *)author date:(int32_t)date; + +@end + +@interface TGInstantPageBlockHeader : TGInstantPageBlock + +@property (nonatomic, strong, readonly) TGRichText *text; + +- (instancetype)initWithText:(TGRichText *)text; + +@end + +@interface TGInstantPageBlockSubheader : TGInstantPageBlock + +@property (nonatomic, strong, readonly) TGRichText *text; + +- (instancetype)initWithText:(TGRichText *)text; + +@end + +@interface TGInstantPageBlockParagraph : TGInstantPageBlock + +@property (nonatomic, strong, readonly) TGRichText *text; + +- (instancetype)initWithText:(TGRichText *)text; + +@end + +@interface TGInstantPageBlockPreFormatted : TGInstantPageBlock + +@property (nonatomic, strong, readonly) TGRichText *text; +@property (nonatomic, strong, readonly) NSString *language; + +- (instancetype)initWithText:(TGRichText *)text language:(NSString *)language; + +@end + +@interface TGInstantPageBlockFooter : TGInstantPageBlock + +@property (nonatomic, strong, readonly) TGRichText *text; + +- (instancetype)initWithText:(TGRichText *)text; + +@end + +@interface TGInstantPageBlockDivider : TGInstantPageBlock + +@end + +@interface TGInstantPageBlockList : TGInstantPageBlock + +@property (nonatomic, readonly) bool ordered; +@property (nonatomic, strong, readonly) NSArray *items; + +- (instancetype)initWithOrdered:(bool)ordered items:(NSArray *)items; + +@end + +@interface TGInstantPageBlockBlockQuote : TGInstantPageBlock + +@property (nonatomic, strong, readonly) TGRichText *text; +@property (nonatomic, strong, readonly) TGRichText *caption; + +- (instancetype)initWithText:(TGRichText *)text caption:(TGRichText *)caption; + +@end + +@interface TGInstantPageBlockPullQuote : TGInstantPageBlock + +@property (nonatomic, strong, readonly) TGRichText *text; +@property (nonatomic, strong, readonly) TGRichText *caption; + +- (instancetype)initWithText:(TGRichText *)text caption:(TGRichText *)caption; + +@end + +@interface TGInstantPageBlockPhoto : TGInstantPageBlock + +@property (nonatomic, readonly) int64_t photoId; +@property (nonatomic, strong, readonly) TGRichText *caption; + +- (instancetype)initWithPhotoId:(int64_t)photoId caption:(TGRichText *)caption; + +@end + +@interface TGInstantPageBlockVideo : TGInstantPageBlock + +@property (nonatomic, readonly) int64_t videoId; +@property (nonatomic, strong, readonly) TGRichText *caption; +@property (nonatomic, readonly) bool autoplay; +@property (nonatomic, readonly) bool loop; + +- (instancetype)initWithVideoId:(int64_t)videoId caption:(TGRichText *)caption autoplay:(bool)autoplay loop:(bool)loop; + +@end + +@interface TGInstantPageBlockEmbed : TGInstantPageBlock + +@property (nonatomic, strong, readonly) NSString *url; +@property (nonatomic, strong, readonly) NSString *html; +@property (nonatomic, readonly) int64_t posterPhotoId; +@property (nonatomic, strong, readonly) TGRichText *caption; +@property (nonatomic, readonly) CGSize size; +@property (nonatomic, readonly) bool fillWidth; +@property (nonatomic, readonly) bool enableScrolling; + +- (instancetype)initWithUrl:(NSString *)url html:(NSString *)html posterPhotoId:(int64_t)posterPhotoId caption:(TGRichText *)caption size:(CGSize)size fillWidth:(bool)fillWidth enableScrolling:(bool)enableScrolling; + +@end + +@interface TGInstantPageBlockSlideshow : TGInstantPageBlock + +@property (nonatomic, strong, readonly) NSArray *items; +@property (nonatomic, strong, readonly) TGRichText *caption; + +- (instancetype)initWithItems:(NSArray *)items caption:(TGRichText *)caption; + +@end + +@interface TGInstantPageBlockCollage : TGInstantPageBlock + +@property (nonatomic, strong, readonly) NSArray *items; +@property (nonatomic, strong, readonly) TGRichText *caption; + +- (instancetype)initWithItems:(NSArray *)items caption:(TGRichText *)caption; + +@end + +@interface TGInstantPageBlockAnchor : TGInstantPageBlock + +@property (nonatomic, strong, readonly) NSString *name; + +- (instancetype)initWithName:(NSString *)name; + +@end + +@interface TGInstantPageBlockEmbedPost : TGInstantPageBlock + +@property (nonatomic, strong, readonly) NSString *author; +@property (nonatomic, readonly) int32_t date; +@property (nonatomic, strong, readonly) TGRichText *caption; +@property (nonatomic, strong, readonly) NSString *url; +@property (nonatomic, readonly) int64_t webpageId; +@property (nonatomic, strong, readonly) NSArray *blocks; +@property (nonatomic, readonly) int64_t authorPhotoId; + +- (instancetype)initWithAuthor:(NSString *)author date:(int32_t)date caption:(TGRichText *)caption url:(NSString *)url webpageId:(int64_t)webpageId blocks:(NSArray *)blocks authorPhotoId:(int64_t)authorPhotoId; + +@end + +@interface TGInstantPageBlockAudio : TGInstantPageBlock + +@property (nonatomic, readonly) int64_t audioId; +@property (nonatomic, strong, readonly) TGRichText *caption; + +- (instancetype)initWithAudioId:(int64_t)audioId caption:(TGRichText *)caption; + +@end + +@interface TGInstantPage : NSObject + +@property (nonatomic, readonly) bool isPartial; +@property (nonatomic, strong, readonly) NSArray *blocks; +@property (nonatomic, strong, readonly) NSDictionary *images; +@property (nonatomic, strong, readonly) NSDictionary *videos; +@property (nonatomic, strong, readonly) NSDictionary *documents; + +- (instancetype)initWithIsPartial:(bool)isPartial blocks:(NSArray *)blocks images:(NSDictionary *)images videos:(NSDictionary *)videos documents:(NSDictionary *)documents; + +@end diff --git a/LegacyComponents/TGInstantPage.m b/LegacyComponents/TGInstantPage.m new file mode 100644 index 0000000000..234f0587c1 --- /dev/null +++ b/LegacyComponents/TGInstantPage.m @@ -0,0 +1,909 @@ +#import "TGInstantPage.h" + +#import "TGConversation.h" +#import "PSKeyValueEncoder.h" +#import "PSKeyValueDecoder.h" + +@implementation TGRichText + +- (instancetype)initWithCoder:(NSCoder *)__unused aDecoder { + self = [super init]; + if (self != nil) { + + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)__unused aCoder { +} + +@end + +@implementation TGRichTextPlain + +- (instancetype)initWithText:(NSString *)text { + self = [super init]; + if (self != nil) { + _text = text; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _text = [aDecoder decodeObjectForKey:@"text"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_text forKey:@"text"]; +} + +@end + +@implementation TGRichTextBold + +- (instancetype)initWithText:(TGRichText *)text { + self = [super init]; + if (self != nil) { + _text = text; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _text = [aDecoder decodeObjectForKey:@"text"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_text forKey:@"text"]; +} + +@end + +@implementation TGRichTextItalic + +- (instancetype)initWithText:(TGRichText *)text { + self = [super init]; + if (self != nil) { + _text = text; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _text = [aDecoder decodeObjectForKey:@"text"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_text forKey:@"text"]; +} + +@end + +@implementation TGRichTextUnderline + +- (instancetype)initWithText:(TGRichText *)text { + self = [super init]; + if (self != nil) { + _text = text; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _text = [aDecoder decodeObjectForKey:@"text"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_text forKey:@"text"]; +} + +@end + +@implementation TGRichTextStrikethrough + +- (instancetype)initWithText:(TGRichText *)text { + self = [super init]; + if (self != nil) { + _text = text; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _text = [aDecoder decodeObjectForKey:@"text"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_text forKey:@"text"]; +} + +@end + +@implementation TGRichTextFixed + +- (instancetype)initWithText:(TGRichText *)text { + self = [super init]; + if (self != nil) { + _text = text; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _text = [aDecoder decodeObjectForKey:@"text"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_text forKey:@"text"]; +} + +@end + +@implementation TGRichTextUrl + +- (instancetype)initWithText:(TGRichText *)text url:(NSString *)url webpageId:(int64_t)webpageId { + self = [super init]; + if (self != nil) { + _text = text; + _url = url; + _webpageId = webpageId; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _text = [aDecoder decodeObjectForKey:@"text"]; + _url = [aDecoder decodeObjectForKey:@"url"]; + _webpageId = [aDecoder decodeInt64ForKey:@"wpid"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_text forKey:@"text"]; + [aCoder encodeObject:_url forKey:@"url"]; + [aCoder encodeInt64:_webpageId forKey:@"wpid"]; +} + +@end + +@implementation TGRichTextEmail: TGRichText + +- (instancetype)initWithText:(TGRichText *)text email:(NSString *)email { + self = [super init]; + if (self != nil) { + _text = text; + _email = email; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _text = [aDecoder decodeObjectForKey:@"text"]; + _email = [aDecoder decodeObjectForKey:@"email"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_text forKey:@"text"]; + [aCoder encodeObject:_email forKey:@"email"]; +} + +@end + +@implementation TGRichTextCollection + +- (instancetype)initWithTexts:(NSArray *)texts { + self = [super init]; + if (self != nil) { + _texts = texts; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _texts = [aDecoder decodeObjectForKey:@"texts"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_texts forKey:@"texts"]; +} + +@end + +@implementation TGInstantPageBlock + +- (instancetype)initWithCoder:(NSCoder *)__unused aDecoder { + self = [super init]; + if (self != nil) { + + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)__unused aCoder { + +} + +@end + +@implementation TGInstantPageBlockCover + +- (instancetype)initWithBlock:(TGInstantPageBlock *)block { + self = [super init]; + if (self != nil) { + _block = block; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _block = [aDecoder decodeObjectForKey:@"block"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_block forKey:@"block"]; +} + + +@end + +@implementation TGInstantPageBlockChannel + +- (instancetype)initWithChannel:(TGConversation *)channel { + self = [super init]; + if (self != nil) { + _channel = channel; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + NSData *data = [aDecoder decodeObjectForKey:@"channelData"]; + PSKeyValueDecoder *decoder = [[PSKeyValueDecoder alloc] initWithData:data]; + _channel = (TGConversation *)[decoder decodeObjectForKey:@"channel"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + PSKeyValueEncoder *encoder = [[PSKeyValueEncoder alloc] init]; + [encoder encodeObject:_channel forKey:@"channel"]; + [aCoder encodeObject:[encoder data] forKey:@"channelData"]; +} + +@end + +@implementation TGInstantPageBlockTitle + +- (instancetype)initWithText:(TGRichText *)text { + self = [super init]; + if (self != nil) { + _text = text; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _text = [aDecoder decodeObjectForKey:@"text"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_text forKey:@"text"]; +} + +@end + +@implementation TGInstantPageBlockSubtitle + +- (instancetype)initWithText:(TGRichText *)text { + self = [super init]; + if (self != nil) { + _text = text; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _text = [aDecoder decodeObjectForKey:@"text"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_text forKey:@"text"]; +} + +@end + +@implementation TGInstantPageBlockAuthorAndDate + +- (instancetype)initWithAuthor:(TGRichText *)author date:(int32_t)date { + self = [super init]; + if (self != nil) { + _author = author; + _date = date; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + TGRichText *richAuthor = [aDecoder decodeObjectForKey:@"rauthor"]; + if (richAuthor != nil) { + _author = richAuthor; + } else { + NSString *author = [aDecoder decodeObjectForKey:@"author"]; + _author = [[TGRichTextPlain alloc] initWithText:author]; + } + _date = [aDecoder decodeInt32ForKey:@"date"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_author forKey:@"rauthor"]; + [aCoder encodeInt32:_date forKey:@"date"]; +} + +@end + +@implementation TGInstantPageBlockHeader + +- (instancetype)initWithText:(TGRichText *)text { + self = [super init]; + if (self != nil) { + _text = text; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _text = [aDecoder decodeObjectForKey:@"text"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_text forKey:@"text"]; +} + +@end + +@implementation TGInstantPageBlockSubheader + +- (instancetype)initWithText:(TGRichText *)text { + self = [super init]; + if (self != nil) { + _text = text; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _text = [aDecoder decodeObjectForKey:@"text"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_text forKey:@"text"]; +} + +@end + +@implementation TGInstantPageBlockParagraph + +- (instancetype)initWithText:(TGRichText *)text { + self = [super init]; + if (self != nil) { + _text = text; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _text = [aDecoder decodeObjectForKey:@"text"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_text forKey:@"text"]; +} + +@end + +@implementation TGInstantPageBlockPreFormatted + +- (instancetype)initWithText:(TGRichText *)text language:(NSString *)language { + self = [super init]; + if (self != nil) { + _text = text; + _language = language; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _text = [aDecoder decodeObjectForKey:@"text"]; + _language = [aDecoder decodeObjectForKey:@"lang"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_text forKey:@"text"]; + [aCoder encodeObject:_language forKey:@"lang"]; +} + +@end + +@implementation TGInstantPageBlockFooter + +- (instancetype)initWithText:(TGRichText *)text { + self = [super init]; + if (self != nil) { + _text = text; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _text = [aDecoder decodeObjectForKey:@"text"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_text forKey:@"text"]; +} + +@end + +@implementation TGInstantPageBlockDivider + +- (instancetype)initWithCoder:(NSCoder *)__unused aDecoder { + self = [super init]; + if (self != nil) { + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)__unused aCoder { +} + +@end + +@implementation TGInstantPageBlockList + +- (instancetype)initWithOrdered:(bool)ordered items:(NSArray *)items { + self = [super init]; + if (self != nil) { + _ordered = ordered; + _items = items; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _ordered = [aDecoder decodeBoolForKey:@"ord"]; + _items = [aDecoder decodeObjectForKey:@"items"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeBool:_ordered forKey:@"ord"]; + [aCoder encodeObject:_items forKey:@"items"]; +} + +@end + +@implementation TGInstantPageBlockBlockQuote + +- (instancetype)initWithText:(TGRichText *)text caption:(TGRichText *)caption { + self = [super init]; + if (self != nil) { + _text = text; + _caption = caption; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _text = [aDecoder decodeObjectForKey:@"text"]; + _caption = [aDecoder decodeObjectForKey:@"caption"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_text forKey:@"text"]; + [aCoder encodeObject:_caption forKey:@"caption"]; +} + +@end + +@implementation TGInstantPageBlockPullQuote + +- (instancetype)initWithText:(TGRichText *)text caption:(TGRichText *)caption { + self = [super init]; + if (self != nil) { + _text = text; + _caption = caption; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _text = [aDecoder decodeObjectForKey:@"text"]; + _caption = [aDecoder decodeObjectForKey:@"caption"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_text forKey:@"text"]; + [aCoder encodeObject:_caption forKey:@"caption"]; +} + +@end + +@implementation TGInstantPageBlockPhoto + +- (instancetype)initWithPhotoId:(int64_t)photoId caption:(TGRichText *)caption { + self = [super init]; + if (self != nil) { + _photoId = photoId; + _caption = caption; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _photoId = [aDecoder decodeInt64ForKey:@"pid"]; + _caption = [aDecoder decodeObjectForKey:@"caption"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeInt64:_photoId forKey:@"pid"]; + [aCoder encodeObject:_caption forKey:@"caption"]; +} + +@end + +@implementation TGInstantPageBlockVideo + +- (instancetype)initWithVideoId:(int64_t)videoId caption:(TGRichText *)caption autoplay:(bool)autoplay loop:(bool)loop { + self = [super init]; + if (self != nil) { + _videoId = videoId; + _caption = caption; + _autoplay = autoplay; + _loop = loop; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _videoId = [aDecoder decodeInt64ForKey:@"vid"]; + _caption = [aDecoder decodeObjectForKey:@"caption"]; + _autoplay = [aDecoder decodeBoolForKey:@"autoplay"]; + _loop = [aDecoder decodeBoolForKey:@"loop"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeInt64:_videoId forKey:@"vid"]; + [aCoder encodeObject:_caption forKey:@"caption"]; + [aCoder encodeBool:_autoplay forKey:@"autoplay"]; + [aCoder encodeBool:_loop forKey:@"loop"]; +} + +@end + +@implementation TGInstantPageBlockEmbed + +- (instancetype)initWithUrl:(NSString *)url html:(NSString *)html posterPhotoId:(int64_t)posterPhotoId caption:(TGRichText *)caption size:(CGSize)size fillWidth:(bool)fillWidth enableScrolling:(bool)enableScrolling { + self = [super init]; + if (self != nil) { + _url = url; + _html = html; + _posterPhotoId = posterPhotoId; + _caption = caption; + _size = size; + _fillWidth = fillWidth; + _enableScrolling = enableScrolling; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _url = [aDecoder decodeObjectForKey:@"url"]; + _html = [aDecoder decodeObjectForKey:@"html"]; + _posterPhotoId = [aDecoder decodeInt64ForKey:@"posterPhotoId"]; + _caption = [aDecoder decodeObjectForKey:@"caption"]; + _size = [aDecoder decodeCGSizeForKey:@"size"]; + _fillWidth = [aDecoder decodeBoolForKey:@"fillWidth"]; + _enableScrolling = [aDecoder decodeBoolForKey:@"enableScrolling"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_url forKey:@"url"]; + [aCoder encodeObject:_html forKey:@"html"]; + [aCoder encodeInt64:_posterPhotoId forKey:@"posterPhotoId"]; + [aCoder encodeObject:_caption forKey:@"caption"]; + [aCoder encodeCGSize:_size forKey:@"size"]; + [aCoder encodeBool:_fillWidth forKey:@"fillWidth"]; + [aCoder encodeBool:_enableScrolling forKey:@"enableScrolling"]; +} + +@end + +@implementation TGInstantPageBlockSlideshow + +- (instancetype)initWithItems:(NSArray *)items caption:(TGRichText *)caption { + self = [super init]; + if (self != nil) { + _items = items; + _caption = caption; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _items = [aDecoder decodeObjectForKey:@"items"]; + _caption = [aDecoder decodeObjectForKey:@"caption"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_items forKey:@"items"]; + [aCoder encodeObject:_caption forKey:@"caption"]; +} + +@end + +@implementation TGInstantPageBlockCollage + +- (instancetype)initWithItems:(NSArray *)items caption:(TGRichText *)caption { + self = [super init]; + if (self != nil) { + _items = items; + _caption = caption; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _items = [aDecoder decodeObjectForKey:@"items"]; + _caption = [aDecoder decodeObjectForKey:@"caption"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_items forKey:@"items"]; + [aCoder encodeObject:_caption forKey:@"caption"]; +} + +@end + +@implementation TGInstantPageBlockAnchor + +- (instancetype)initWithName:(NSString *)name { + self = [super init]; + if (self != nil) { + _name = name; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self != nil) { + _name = [aDecoder decodeObjectForKey:@"name"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_name forKey:@"name"]; +} + +@end + +@implementation TGInstantPageBlockEmbedPost + +- (instancetype)initWithAuthor:(NSString *)author date:(int32_t)date caption:(TGRichText *)caption url:(NSString *)url webpageId:(int64_t)webpageId blocks:(NSArray *)blocks authorPhotoId:(int64_t)authorPhotoId { + self = [super init]; + if (self != nil) { + _author = author; + _date = date; + _caption = caption; + _url = url; + _webpageId = webpageId; + _blocks = blocks; + _authorPhotoId = authorPhotoId; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _author = [aDecoder decodeObjectForKey:@"author"]; + _date = [aDecoder decodeInt32ForKey:@"date"]; + _caption = [aDecoder decodeObjectForKey:@"caption"]; + _url = [aDecoder decodeObjectForKey:@"url"]; + _webpageId = [aDecoder decodeInt64ForKey:@"webpageId"]; + _blocks = [aDecoder decodeObjectForKey:@"blocks"]; + _authorPhotoId = [aDecoder decodeInt64ForKey:@"authorPhotoId"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_author forKey:@"author"]; + [aCoder encodeInt32:_date forKey:@"date"]; + [aCoder encodeObject:_caption forKey:@"caption"]; + [aCoder encodeObject:_url forKey:@"url"]; + [aCoder encodeInt64:_webpageId forKey:@"webpageId"]; + [aCoder encodeObject:_blocks forKey:@"blocks"]; + [aCoder encodeInt64:_authorPhotoId forKey:@"authorPhotoId"]; +} + +@end + +@implementation TGInstantPageBlockAudio + +- (instancetype)initWithAudioId:(int64_t)audioId caption:(TGRichText *)caption { + self = [super init]; + if (self != nil) { + _audioId = audioId; + _caption = caption; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _audioId = [aDecoder decodeInt64ForKey:@"audioId"]; + _caption = [aDecoder decodeObjectForKey:@"caption"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeInt64:_audioId forKey:@"audioId"]; + [aCoder encodeObject:_caption forKey:@"caption"]; +} + +@end + +@implementation TGInstantPage + +- (instancetype)initWithIsPartial:(bool)isPartial blocks:(NSArray *)blocks images:(NSDictionary *)images videos:(NSDictionary *)videos documents:(NSDictionary *)documents { + self = [super init]; + if (self != nil) { + _isPartial = isPartial; + _blocks = blocks; + _images = images; + _videos = videos; + _documents = documents; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _isPartial = [aDecoder decodeBoolForKey:@"partial"]; + _blocks = [aDecoder decodeObjectForKey:@"blocks"]; + _images = [aDecoder decodeObjectForKey:@"images"]; + _videos = [aDecoder decodeObjectForKey:@"videos"]; + _documents = [aDecoder decodeObjectForKey:@"documents"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeBool:_isPartial forKey:@"partial"]; + [aCoder encodeObject:_blocks forKey:@"blocks"]; + [aCoder encodeObject:_images forKey:@"images"]; + [aCoder encodeObject:_videos forKey:@"videos"]; + [aCoder encodeObject:_documents forKey:@"documents"]; +} + +- (BOOL)isEqual:(id)object { + if (![object isKindOfClass:[TGInstantPage class]]) { + return false; + } + TGInstantPage *other = object; + if (_blocks.count != other->_blocks.count) { + return false; + } + return true; +} + +@end diff --git a/LegacyComponents/TGInvoiceMediaAttachment.h b/LegacyComponents/TGInvoiceMediaAttachment.h new file mode 100644 index 0000000000..c7729a6567 --- /dev/null +++ b/LegacyComponents/TGInvoiceMediaAttachment.h @@ -0,0 +1,25 @@ +#import "TGMediaAttachment.h" + +#import + +#define TGInvoiceMediaAttachmentType ((int)0x17af081f) + +@class TGWebPageMediaAttachment; + +@interface TGInvoiceMediaAttachment : TGMediaAttachment + +@property (nonatomic, readonly) NSString *title; +@property (nonatomic, readonly) NSString *text; +@property (nonatomic, readonly) TGWebDocument *photo; +@property (nonatomic, strong, readonly) NSString *currency; +@property (nonatomic, readonly) int64_t totalAmount; +@property (nonatomic, readonly) int32_t receiptMessageId; +@property (nonatomic, strong, readonly) NSString *invoiceStartParam; +@property (nonatomic, readonly) bool shippingAddressRequested; +@property (nonatomic, readonly) bool isTest; + +- (instancetype)initWithTitle:(NSString *)title text:(NSString *)text photo:(TGWebDocument *)photo currency:(NSString *)currency totalAmount:(int64_t)totalAmount receiptMessageId:(int32_t)receiptMessageId invoiceStartParam:(NSString *)invoiceStartParam shippingAddressRequested:(bool)shippingAddressRequested isTest:(bool)isTest; + +- (TGWebPageMediaAttachment *)webpage; + +@end diff --git a/LegacyComponents/TGInvoiceMediaAttachment.m b/LegacyComponents/TGInvoiceMediaAttachment.m new file mode 100644 index 0000000000..42ed06eca8 --- /dev/null +++ b/LegacyComponents/TGInvoiceMediaAttachment.m @@ -0,0 +1,121 @@ +#import "TGInvoiceMediaAttachment.h" + +#import "LegacyComponentsInternal.h" + +#import "NSInputStream+TL.h" + +#import "TGWebPageMediaAttachment.h" + +@implementation TGInvoiceMediaAttachment + +- (instancetype)initWithTitle:(NSString *)title text:(NSString *)text photo:(TGWebDocument *)photo currency:(NSString *)currency totalAmount:(int64_t)totalAmount receiptMessageId:(int32_t)receiptMessageId invoiceStartParam:(NSString *)invoiceStartParam shippingAddressRequested:(bool)shippingAddressRequested isTest:(bool)isTest { + self = [super init]; + if (self != nil) { + self.type = TGInvoiceMediaAttachmentType; + + _title = title; + _text = text; + _photo = photo; + _currency = currency; + _totalAmount = totalAmount; + _receiptMessageId = receiptMessageId; + _invoiceStartParam = invoiceStartParam; + _shippingAddressRequested = shippingAddressRequested; + _isTest = isTest; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + return [self initWithTitle:[aDecoder decodeObjectForKey:@"title"] text:[aDecoder decodeObjectForKey:@"text"] photo:[aDecoder decodeObjectForKey:@"photo"] currency:[aDecoder decodeObjectForKey:@"currency"] totalAmount:[aDecoder decodeInt64ForKey:@"totalAmount"] receiptMessageId:[aDecoder decodeInt32ForKey:@"receiptMessageId"] invoiceStartParam:[aDecoder decodeObjectForKey:@"invoiceStartParam"] shippingAddressRequested:[aDecoder decodeBoolForKey:@"shippingAddressRequested"] isTest:[aDecoder decodeBoolForKey:@"isTest"]]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_title forKey:@"title"]; + [aCoder encodeObject:_text forKey:@"text"]; + [aCoder encodeObject:_photo forKey:@"photo"]; + [aCoder encodeObject:_currency forKey:@"currency"]; + [aCoder encodeInt64:_totalAmount forKey:@"totalAmount"]; + [aCoder encodeInt32:_receiptMessageId forKey:@"receiptMessageId"]; + [aCoder encodeObject:_invoiceStartParam forKey:@"invoiceStartParam"]; + [aCoder encodeBool:_shippingAddressRequested forKey:@"shippingAddressRequested"]; + [aCoder encodeBool:_isTest forKey:@"isTest"]; +} + +- (BOOL)isEqual:(id)object { + if (![object isKindOfClass:[TGInvoiceMediaAttachment class]]) { + return false; + } + TGInvoiceMediaAttachment *other = (TGInvoiceMediaAttachment *)object; + if (!TGStringCompare(_title, other->_title)) { + return false; + } + if (!TGStringCompare(_text, other->_text)) { + return false; + } + if (!TGObjectCompare(_photo, other->_photo)) { + return false; + } + if (!TGStringCompare(_currency, other->_currency)) { + return false; + } + if (_totalAmount != other->_totalAmount) { + return false; + } + if (_receiptMessageId != other->_receiptMessageId) { + return false; + } + if (!TGStringCompare(_invoiceStartParam, other->_invoiceStartParam)) { + return false; + } + if (_shippingAddressRequested != other->_shippingAddressRequested) { + return false; + } + if (_isTest != other->_isTest) { + return false; + } + return true; +} + +- (void)serialize:(NSMutableData *)data +{ + NSData *serializedData = [NSKeyedArchiver archivedDataWithRootObject:self]; + int32_t length = (int32_t)serializedData.length; + [data appendBytes:&length length:4]; + [data appendData:serializedData]; +} + +- (TGMediaAttachment *)parseMediaAttachment:(NSInputStream *)is +{ + int32_t length = [is readInt32]; + NSData *data = [is readData:length]; + return [NSKeyedUnarchiver unarchiveObjectWithData:data]; +} + + +- (TGWebPageMediaAttachment *)webpage { + TGWebPageMediaAttachment *webPage = [[TGWebPageMediaAttachment alloc] init]; + webPage.siteName = self.title; + webPage.pageDescription = _text; + + if (_photo != nil) { + TGImageInfo *imageInfo = [[TGImageInfo alloc] init]; + for (id attribute in _photo.attributes) { + if ([attribute isKindOfClass:[TGDocumentAttributeImageSize class]]) { + CGSize imageSize = ((TGDocumentAttributeImageSize *)attribute).size; + if (imageSize.width < 1.0f || imageSize.height < 1.0f) { + imageSize = CGSizeMake(480.0f, 480.0f); + } + [imageInfo addImageWithSize:imageSize url:[[_photo reference] toString]]; + TGImageMediaAttachment *imageMedia = [[TGImageMediaAttachment alloc] init]; + imageMedia.imageInfo = imageInfo; + webPage.photo = imageMedia; + break; + } + } + } + webPage.pageType = @"invoice"; + return webPage; +} + +@end diff --git a/LegacyComponents/TGLocalMessageMetaMediaAttachment.h b/LegacyComponents/TGLocalMessageMetaMediaAttachment.h new file mode 100644 index 0000000000..3f1b689bb1 --- /dev/null +++ b/LegacyComponents/TGLocalMessageMetaMediaAttachment.h @@ -0,0 +1,19 @@ +/* + * This is the source code of Telegram for iOS v. 1.1 + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Peter Iakovlev, 2013. + */ + +#import + +#define TGLocalMessageMetaMediaAttachmentType 0x944DE6B6 + +@interface TGLocalMessageMetaMediaAttachment : TGMediaAttachment + +@property (nonatomic, strong) NSMutableArray *imageInfoList; +@property (nonatomic, strong) NSMutableDictionary *imageUrlToDataFile; +@property (nonatomic) int localMediaId; + +@end diff --git a/LegacyComponents/TGLocalMessageMetaMediaAttachment.m b/LegacyComponents/TGLocalMessageMetaMediaAttachment.m new file mode 100644 index 0000000000..ed1024160a --- /dev/null +++ b/LegacyComponents/TGLocalMessageMetaMediaAttachment.m @@ -0,0 +1,101 @@ +#import "TGLocalMessageMetaMediaAttachment.h" + +#import "TGImageInfo.h" + +@implementation TGLocalMessageMetaMediaAttachment + +@synthesize imageInfoList = _imageInfoList; +@synthesize imageUrlToDataFile = _imageUrlToDataFile; +@synthesize localMediaId = _localMediaId; + +- (id)init +{ + self = [super init]; + if (self != nil) + { + self.type = TGLocalMessageMetaMediaAttachmentType; + + _imageInfoList = [[NSMutableArray alloc] init]; + _imageUrlToDataFile = [[NSMutableDictionary alloc] init]; + } + return self; +} + +- (void)serialize:(NSMutableData *)data +{ + int dataLengthPtr = (int)data.length; + int zero = 0; + [data appendBytes:&zero length:4]; + + int count = (int)_imageInfoList.count; + [data appendBytes:&count length:4]; + for (int i = 0; i < count; i++) + { + [(TGImageInfo *)[_imageInfoList objectAtIndex:i] serialize:data]; + } + + count = (int)_imageUrlToDataFile.count; + [data appendBytes:&count length:4]; + [_imageUrlToDataFile enumerateKeysAndObjectsUsingBlock:^(NSString *imageUrl, NSString *filePath, __unused BOOL *stop) + { + NSData *byteData = [imageUrl dataUsingEncoding:NSUTF8StringEncoding]; + int length = (int)byteData.length; + [data appendBytes:&length length:4]; + [data appendData:byteData]; + + byteData = [filePath dataUsingEncoding:NSUTF8StringEncoding]; + length = (int)byteData.length; + [data appendBytes:&length length:4]; + [data appendData:byteData]; + }]; + + [data appendBytes:&_localMediaId length:4]; + + int dataLength = (int)(data.length - dataLengthPtr - 4); + [data replaceBytesInRange:NSMakeRange(dataLengthPtr, 4) withBytes:&dataLength]; +} + +- (TGMediaAttachment *)parseMediaAttachment:(NSInputStream *)is +{ + int dataLength = 0; + [is read:(uint8_t *)&dataLength maxLength:4]; + + TGLocalMessageMetaMediaAttachment *attachment = [[TGLocalMessageMetaMediaAttachment alloc] init]; + + int count = 0; + [is read:(uint8_t *)&count maxLength:4]; + for (int i = 0; i < count; i++) + { + TGImageInfo *imageInfo = [TGImageInfo deserialize:is]; + if (imageInfo != nil) + [attachment.imageInfoList addObject:imageInfo]; + } + + count = 0; + [is read:(uint8_t *)&count maxLength:4]; + for (int i = 0; i < count; i++) + { + int length = 0; + [is read:(uint8_t *)&length maxLength:4]; + uint8_t *bytes = malloc(length); + [is read:bytes maxLength:length]; + NSString *imageUrl = [[NSString alloc] initWithBytesNoCopy:bytes length:length encoding:NSUTF8StringEncoding freeWhenDone:true]; + + length = 0; + [is read:(uint8_t *)&length maxLength:4]; + bytes = malloc(length); + [is read:bytes maxLength:length]; + NSString *filePath = [[NSString alloc] initWithBytesNoCopy:bytes length:length encoding:NSUTF8StringEncoding freeWhenDone:true]; + + if (imageUrl != nil && filePath != nil) + [attachment.imageUrlToDataFile setObject:filePath forKey:imageUrl]; + } + + int mediaId = 0; + [is read:(uint8_t *)&mediaId maxLength:4]; + attachment.localMediaId = mediaId; + + return attachment; +} + +@end diff --git a/LegacyComponents/TGLocalization.h b/LegacyComponents/TGLocalization.h new file mode 100644 index 0000000000..3974b2647e --- /dev/null +++ b/LegacyComponents/TGLocalization.h @@ -0,0 +1,20 @@ +#import + +@interface TGLocalization : NSObject + +@property (nonatomic, readonly) int32_t version; +@property (nonatomic, strong, readonly) NSString *code; +@property (nonatomic, readonly) int languageCodeHash; +@property (nonatomic, readonly) bool isActive; + +- (instancetype)initWithVersion:(int32_t)version code:(NSString *)code dict:(NSDictionary *)dict isActive:(bool)isActive; + +- (TGLocalization *)mergedWith:(NSDictionary *)other version:(int32_t)version; + +- (TGLocalization *)withUpdatedIsActive:(bool)isActive; + +- (NSString *)get:(NSString *)key; +- (NSString *)getPluralized:(NSString *)key count:(int32_t)count; +- (bool)contains:(NSString *)key; + +@end diff --git a/LegacyComponents/TGLocalization.m b/LegacyComponents/TGLocalization.m new file mode 100644 index 0000000000..2856feb01a --- /dev/null +++ b/LegacyComponents/TGLocalization.m @@ -0,0 +1,124 @@ +#import "TGLocalization.h" + +#import "TGPluralization.h" + +static NSDictionary *fallbackDict() { + static NSDictionary *dict = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSBundle *bundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:@"en" ofType:@"lproj"]]; + NSString *path = [bundle pathForResource:@"Localizable" ofType:@"strings"]; + dict = [NSDictionary dictionaryWithContentsOfFile:path]; + }); + return dict; +} + +static NSString *fallbackString(NSString *key) { + NSString *value = fallbackDict()[key]; + if (value == nil) { + return key; + } else { + return value; + } +} + +@interface TGLocalization () { + NSDictionary *_dict; +} + +@end + +@implementation TGLocalization + +- (instancetype)initWithVersion:(int32_t)version code:(NSString *)code dict:(NSDictionary *)dict isActive:(bool)isActive { + self = [super init]; + if (self != nil) { + _version = version; + _code = code; + _dict = dict; + _isActive = isActive; + + NSString *rawCode = code; + NSRange range = [code rangeOfString:@"_"]; + if (range.location != NSNotFound) { + rawCode = [code substringToIndex:range.location]; + } + rawCode = [rawCode lowercaseString]; + unsigned int lc = 0; + const char *s = rawCode.UTF8String; + for (; *s; s++) { lc = (lc << 8) + *s; } + _languageCodeHash = lc; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + return [self initWithVersion:[aDecoder decodeInt32ForKey:@"version"] code:[aDecoder decodeObjectForKey:@"code"] dict:[aDecoder decodeObjectForKey:@"dict"] isActive:[aDecoder decodeBoolForKey:@"isActive"]]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeInt32:_version forKey:@"version"]; + [aCoder encodeObject:_code forKey:@"code"]; + [aCoder encodeObject:_dict forKey:@"dict"]; + [aCoder encodeBool:_isActive forKey:@"isActive"]; +} + +- (TGLocalization *)mergedWith:(NSDictionary *)other version:(int32_t)version { + NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithDictionary:_dict]; + [dict addEntriesFromDictionary:other]; + + return [[TGLocalization alloc] initWithVersion:version code:_code dict:dict isActive:_isActive]; +} + +- (TGLocalization *)withUpdatedIsActive:(bool)isActive { + return [[TGLocalization alloc] initWithVersion:_version code:_code dict:_dict isActive:isActive]; +} + +- (NSString *)get:(NSString *)key { + if (key == nil) { + return nil; + } + NSString *value = _dict[key]; + + if (value != nil && value.length != 0) { + return value; + } else { + return fallbackString(key); + } +} + +- (NSString *)getPluralized:(NSString *)key count:(int32_t)count { + NSString *suffix = nil; + switch (TGPluralForm(_languageCodeHash, count)) { + case TGPluralFormZero: + suffix = @"_0"; + break; + case TGPluralFormOne: + suffix = @"_1"; + break; + case TGPluralFormTwo: + suffix = @"_2"; + break; + case TGPluralFormFew: + suffix = @"_3_10"; + break; + case TGPluralFormMany: + suffix = @"_many"; + break; + case TGPluralFormOther: + suffix = @"_any"; + break; + } + NSString *finalKey = [key stringByAppendingString:suffix]; + if (_dict[finalKey] == nil) { + finalKey = [key stringByAppendingString:@"_any"]; + } + + return [[NSString alloc] initWithFormat:[self get:finalKey], [NSString stringWithFormat:@"%d", count]]; +} + +- (bool)contains:(NSString *)key { + return _dict[key] != nil; +} + +@end diff --git a/LegacyComponents/TGLocationMediaAttachment.h b/LegacyComponents/TGLocationMediaAttachment.h new file mode 100644 index 0000000000..6dde72adf2 --- /dev/null +++ b/LegacyComponents/TGLocationMediaAttachment.h @@ -0,0 +1,31 @@ +/* + * This is the source code of Telegram for iOS v. 1.1 + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Peter Iakovlev, 2013. + */ + +#import + +#define TGLocationMediaAttachmentType 0x0C9ED06E + +@interface TGVenueAttachment : NSObject + +@property (nonatomic, strong, readonly) NSString *title; +@property (nonatomic, strong, readonly) NSString *address; +@property (nonatomic, strong, readonly) NSString *provider; +@property (nonatomic, strong, readonly) NSString *venueId; + +- (instancetype)initWithTitle:(NSString *)title address:(NSString *)address provider:(NSString *)provider venueId:(NSString *)venueId; + +@end + +@interface TGLocationMediaAttachment : TGMediaAttachment + +@property (nonatomic) double latitude; +@property (nonatomic) double longitude; + +@property (nonatomic, strong) TGVenueAttachment *venue; + +@end diff --git a/LegacyComponents/TGLocationMediaAttachment.m b/LegacyComponents/TGLocationMediaAttachment.m new file mode 100644 index 0000000000..3270a0d11d --- /dev/null +++ b/LegacyComponents/TGLocationMediaAttachment.m @@ -0,0 +1,117 @@ +#import "TGLocationMediaAttachment.h" + +@implementation TGVenueAttachment + +- (instancetype)initWithTitle:(NSString *)title address:(NSString *)address provider:(NSString *)provider venueId:(NSString *)venueId +{ + self = [super init]; + if (self != nil) + { + _title = title; + _address = address; + _provider = provider; + _venueId = venueId; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + return [self initWithTitle:[aDecoder decodeObjectForKey:@"title"] address:[aDecoder decodeObjectForKey:@"address"] provider:[aDecoder decodeObjectForKey:@"provider"] venueId:[aDecoder decodeObjectForKey:@"venueId"]]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + if (_title != nil) + [aCoder encodeObject:_title forKey:@"title"]; + if (_address != nil) + [aCoder encodeObject:_address forKey:@"address"]; + if (_provider != nil) + [aCoder encodeObject:_provider forKey:@"provider"]; + if (_venueId != nil) + [aCoder encodeObject:_venueId forKey:@"venueId"]; +} + +@end + +@implementation TGLocationMediaAttachment + +- (id)init +{ + self = [super init]; + if (self != nil) + { + self.type = TGLocationMediaAttachmentType; + } + return self; +} + +- (void)serialize:(NSMutableData *)data +{ + int dataLengthPtr = (int)data.length; + int zero = 0; + [data appendBytes:&zero length:4]; + + [data appendBytes:&_latitude length:8]; + [data appendBytes:&_longitude length:8]; + + NSData *venueData = nil; + if (_venue != nil) + venueData = [NSKeyedArchiver archivedDataWithRootObject:_venue]; + int32_t venueDataLength = (int32_t)venueData.length; + [data appendBytes:&venueDataLength length:4]; + [data appendData:venueData]; + + int dataLength = (int)(data.length - dataLengthPtr - 4); + [data replaceBytesInRange:NSMakeRange(dataLengthPtr, 4) withBytes:&dataLength]; +} + +- (TGMediaAttachment *)parseMediaAttachment:(NSInputStream *)is +{ + int dataLength = 0; + [is read:(uint8_t *)&dataLength maxLength:4]; + + TGLocationMediaAttachment *locationAttachment = [[TGLocationMediaAttachment alloc] init]; + + double tmp = 0; + [is read:(uint8_t *)&tmp maxLength:8]; + locationAttachment.latitude = tmp; + + tmp = 0; + [is read:(uint8_t *)&tmp maxLength:8]; + locationAttachment.longitude = tmp; + + if (dataLength >= 8 + 8 + 4) + { + int32_t venueDataLength = 0; + [is read:(uint8_t *)&venueDataLength maxLength:4]; + if (venueDataLength > 0) + { + uint8_t *venueBytes = malloc(venueDataLength); + [is read:venueBytes maxLength:venueDataLength]; + NSData *venueData = [[NSData alloc] initWithBytesNoCopy:venueBytes length:venueDataLength freeWhenDone:true]; + locationAttachment.venue = [NSKeyedUnarchiver unarchiveObjectWithData:venueData]; + } + } + + return locationAttachment; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + self.type = TGLocationMediaAttachmentType; + _latitude = [aDecoder decodeDoubleForKey:@"latitude"]; + _longitude = [aDecoder decodeDoubleForKey:@"longitude"]; + _venue = [aDecoder decodeObjectForKey:@"venue"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeDouble:_latitude forKey:@"latitude"]; + [aCoder encodeDouble:_longitude forKey:@"longitude"]; + [aCoder encodeObject:_venue forKey:@"venue"]; +} + +@end diff --git a/LegacyComponents/TGMediaAttachment.h b/LegacyComponents/TGMediaAttachment.h new file mode 100644 index 0000000000..67c9b69767 --- /dev/null +++ b/LegacyComponents/TGMediaAttachment.h @@ -0,0 +1,26 @@ +/* + * This is the source code of Telegram for iOS v. 1.1 + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Peter Iakovlev, 2013. + */ + +#import + +@interface TGMediaAttachment : NSObject + +@property (nonatomic) int type; +@property (nonatomic) bool isMeta; + +- (void)serialize:(NSMutableData *)data; + +@end + +@protocol TGMediaAttachmentParser + +@required + +- (TGMediaAttachment *)parseMediaAttachment:(NSInputStream *)is; + +@end diff --git a/LegacyComponents/TGMediaAttachment.m b/LegacyComponents/TGMediaAttachment.m new file mode 100644 index 0000000000..63f3634906 --- /dev/null +++ b/LegacyComponents/TGMediaAttachment.m @@ -0,0 +1,15 @@ +#import "TGMediaAttachment.h" + +#import "LegacyComponentsInternal.h" + +@implementation TGMediaAttachment + +@synthesize type = _type; +@synthesize isMeta = _isMeta; + +- (void)serialize:(NSMutableData *)__unused data +{ + TGLog(@"***** TGMediaAttachment: default implementation not provided"); +} + +@end diff --git a/LegacyComponents/TGMessage.h b/LegacyComponents/TGMessage.h new file mode 100644 index 0000000000..601249587f --- /dev/null +++ b/LegacyComponents/TGMessage.h @@ -0,0 +1,285 @@ +/* + * This is the source code of Telegram for iOS v. 1.1 + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Peter Iakovlev, 2013. + */ + +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import + +#import + +#import + +#import +#import + +typedef enum { + TGMessageDeliveryStateDelivered = 0, + TGMessageDeliveryStatePending = 1, + TGMessageDeliveryStateFailed = 2 +} TGMessageDeliveryState; + +#define TGMessageLocalMidBaseline 800000000 + +typedef struct { + uint8_t key[8 + 1 + 4 + 4]; +} TGMessageSortKey; + +typedef struct { + uint8_t key[8 + 4 + 4 + 1]; +} TGMessageTransparentSortKey; + +#define TGMessageSpaceUnimportant 0 +#define TGMessageSpaceImportant 1 +#define TGMessageSpaceUnimportantGroup 126 +#define TGMessageSpaceHole 127 + +static inline TGMessageSortKey TGMessageSortKeyMake(int64_t peerId, uint8_t space, int32_t timestamp, int32_t mid) { + TGMessageSortKey key; + memcpy(key.key, &peerId, 8); + key.key[8] = space; + timestamp = NSSwapInt(timestamp); + memcpy(key.key + 8 + 1, ×tamp, 4); + mid = NSSwapInt(mid); + memcpy(key.key + 8 + 1 + 4, &mid, 4); + return key; +} + +static inline TGMessageTransparentSortKey TGMessageTransparentSortKeyMake(int64_t peerId, int32_t timestamp, int32_t mid, uint8_t space) { + TGMessageTransparentSortKey key; + memcpy(key.key, &peerId, 8); + timestamp = NSSwapInt(timestamp); + memcpy(key.key + 8, ×tamp, 4); + mid = NSSwapInt(mid); + memcpy(key.key + 8 + 4, &mid, 4); + key.key[8 + 4 + 4] = space; + return key; +} + +static inline TGMessageTransparentSortKey TGMessageTransparentSortKeyLowerBound(int64_t peerId) { + return TGMessageTransparentSortKeyMake(peerId, 0, 0, 0); +} + +static inline TGMessageTransparentSortKey TGMessageTransparentSortKeyUpperBound(int64_t peerId) { + return TGMessageTransparentSortKeyMake(peerId, INT32_MAX, INT32_MAX, 127); +} + +static inline TGMessageSortKey TGMessageSortKeyLowerBound(int64_t peerId, uint8_t space) { + return TGMessageSortKeyMake(peerId, space, 0, 0); +} + +static inline TGMessageSortKey TGMessageSortKeyUpperBound(int64_t peerId, uint8_t space) { + return TGMessageSortKeyMake(peerId, space, INT32_MAX, INT32_MAX); +} + +static inline TGMessageSortKey TGMessageSortKeyFromData(NSData *data) { + TGMessageSortKey key; + memcpy(key.key, data.bytes, 8 + 1 + 4 + 4); + return key; +} + +static inline NSData *TGMessageSortKeyData(TGMessageSortKey key) { + return [NSData dataWithBytes:key.key length: 8 + 1 + 4 + 4]; +} + +static inline NSData *TGMessageTransparentSortKeyData(TGMessageTransparentSortKey key) { + return [NSData dataWithBytes:key.key length: 8 + 4 + 4 + 1]; +} + +static inline TGMessageTransparentSortKey TGMessageTransparentSortKeyFromData(NSData *data) { + TGMessageTransparentSortKey key; + memcpy(key.key, data.bytes, 8 + 4 + 4 + 1); + return key; +} + +static inline int TGMessageSortKeyCompare(TGMessageSortKey lhs, TGMessageSortKey rhs) { + return memcmp(lhs.key, rhs.key, 8 + 1 + 4 + 4); +} + +static inline int TGMessageTransparentSortKeyCompare(TGMessageTransparentSortKey lhs, TGMessageTransparentSortKey rhs) { + return memcmp(lhs.key, rhs.key, 8 + 4 + 4 + 1); +} + +static inline int64_t TGMessageSortKeyPeerId(TGMessageSortKey key) { + int64_t peerId = 0; + memcpy(&peerId, key.key, 8); + return peerId; +} + +static inline int64_t TGMessageTransparentSortKeyPeerId(TGMessageTransparentSortKey key) { + int64_t peerId = 0; + memcpy(&peerId, key.key, 8); + return peerId; +} + +static inline uint8_t TGMessageSortKeySpace(TGMessageSortKey key) { + return key.key[8]; +} + +static inline uint8_t TGMessageTransparentSortKeySpace(TGMessageTransparentSortKey key) { + return key.key[8 + 4 + 4]; +} + +static inline int32_t TGMessageSortKeyTimestamp(TGMessageSortKey key) { + int32_t timestamp = 0; + memcpy(×tamp, key.key + 8 + 1, 4); + return NSSwapInt(timestamp); +} + +static inline int32_t TGMessageTransparentSortKeyTimestamp(TGMessageTransparentSortKey key) { + int32_t timestamp = 0; + memcpy(×tamp, key.key + 8, 4); + return NSSwapInt(timestamp); +} + +static inline int32_t TGMessageSortKeyMid(TGMessageSortKey key) { + int32_t mid = 0; + memcpy(&mid, key.key + 8 + 1 + 4, 4); + return NSSwapInt(mid); +} + +static inline int32_t TGMessageTransparentSortKeyMid(TGMessageTransparentSortKey key) { + int32_t mid = 0; + memcpy(&mid, key.key + 8 + 4, 4); + return NSSwapInt(mid); +} + +static inline NSData *TGTaggedMessageSortKeyData(int32_t tag, TGMessageSortKey key) { + uint8_t data[4 + 8 + 1 + 4 + 4]; + memcpy(data, &tag, 4); + memcpy(data + 4, key.key, 8 + 1 + 4 + 4); + return [[NSData alloc] initWithBytes:data length:4 + 8 + 1 + 4 + 4]; +} + +static inline TGMessageSortKey TGTaggedMessageSortKeyExtract(NSData *data, int32_t *outTag) { + if (outTag != NULL) { + [data getBytes:(void *)outTag range:NSMakeRange(0, 4)]; + } + + TGMessageSortKey sortKey; + [data getBytes:sortKey.key range:NSMakeRange(4, 17)]; + return sortKey; +} + +@interface TGMessage : NSObject + +@property (nonatomic) int mid; + +@property (nonatomic) TGMessageSortKey sortKey; +@property (nonatomic, readonly) TGMessageTransparentSortKey transparentSortKey; + +@property (nonatomic) int32_t pts; + +//@property (nonatomic, readonly) bool unread; +@property (nonatomic) bool hintUnread; +@property (nonatomic) bool outgoing; +@property (nonatomic) TGMessageDeliveryState deliveryState; +@property (nonatomic) int64_t fromUid; +@property (nonatomic) int64_t toUid; +@property (nonatomic) int64_t cid; +@property (nonatomic, copy) NSString *text; +@property (nonatomic) NSTimeInterval date; +@property (nonatomic, strong) NSArray *mediaAttachments; + +@property (nonatomic) int32_t realDate; +@property (nonatomic) int64_t randomId; + +@property (nonatomic, readonly) int64_t forwardPeerId; + +@property (nonatomic) bool containsMention; + +@property (nonatomic, strong) TGActionMediaAttachment *actionInfo; + +@property (nonatomic, strong) NSArray *textCheckingResults; + +@property (nonatomic) int32_t messageLifetime; +@property (nonatomic) int64_t flags; +@property (nonatomic) int32_t seqIn; +@property (nonatomic) int32_t seqOut; + +@property (nonatomic) bool isBroadcast; +@property (nonatomic) NSUInteger layer; + +@property (nonatomic, strong) TGBotReplyMarkup *replyMarkup; +@property (nonatomic) bool hideReplyMarkup; +@property (nonatomic) bool forceReply; + +@property (nonatomic) bool isSilent; +@property (nonatomic) bool isEdited; + +@property (nonatomic, strong) TGMessageViewCountContentProperty *viewCount; + +@property (nonatomic, strong) NSArray *entities; +@property (nonatomic, strong, readonly) NSString *authorSignature; +@property (nonatomic, strong, readonly) NSString *forwardAuthorSignature; + +@property (nonatomic, strong) NSDictionary *contentProperties; + +@property (nonatomic, strong) TGMessageHole *hole; +@property (nonatomic, strong) TGMessageGroup *group; + +- (NSArray *)effectiveTextAndEntities; + +- (bool)local; + ++ (void)registerMediaAttachmentParser:(int)type parser:(id)parser; ++ (NSArray *)textCheckingResultsForText:(NSString *)text highlightMentionsAndTags:(bool)highlightMentionsAndTags highlightCommands:(bool)highlightCommands entities:(NSArray *)entities; ++ (NSArray *)entitiesForMarkedUpText:(NSString *)text resultingText:(__autoreleasing NSString **)resultingText; + +- (NSData *)serializeMediaAttachments:(bool)includeMeta; ++ (NSData *)serializeMediaAttachments:(bool)includeMeta attachments:(NSArray *)attachments; ++ (NSData *)serializeAttachment:(TGMediaAttachment *)attachment; ++ (NSArray *)parseMediaAttachments:(NSData *)data; ++ (NSUInteger)layerFromFlags:(int64_t)flags; + +- (NSData *)serializeContentProperties; ++ (NSData *)serializeContentProperties:(NSDictionary *)contentProperties; ++ (NSDictionary *)parseContentProperties:(NSData *)data; + +- (void)removeReplyAndMarkup; + +- (void)filterOutExpiredMedia; +- (bool)hasExpiredMedia; + +@end + +@interface TGMediaId : NSObject + +@property (nonatomic, readonly) uint8_t type; +@property (nonatomic, readonly) int64_t itemId; + +- (id)initWithType:(uint8_t)type itemId:(int64_t)itemId; + +@end + +@interface NSTextCheckingResult (TGMessage) + +- (bool)isTelegramHiddenLink; + +@end + diff --git a/LegacyComponents/TGMessage.mm b/LegacyComponents/TGMessage.mm new file mode 100644 index 0000000000..17b053ca6c --- /dev/null +++ b/LegacyComponents/TGMessage.mm @@ -0,0 +1,1179 @@ +#import "TGMessage.h" + +#import "LegacyComponentsInternal.h" + +#import "PSKeyValueCoder.h" +#import "PSKeyValueEncoder.h" +#import "PSKeyValueDecoder.h" +#import + +#import "TGTextCheckingResult.h" +#import "TGPeerIdAdapter.h" + +#include + +static void *NSTextCheckingResultTelegramHiddenLinkKey = &NSTextCheckingResultTelegramHiddenLinkKey; + +@implementation NSTextCheckingResult (TGMessage) + +- (void)setIsTelegramHiddenLink:(bool)value { + objc_setAssociatedObject(self, NSTextCheckingResultTelegramHiddenLinkKey, @(value), OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (bool)isTelegramHiddenLink { + return [objc_getAssociatedObject(self, NSTextCheckingResultTelegramHiddenLinkKey) boolValue]; +} + +@end + +static std::unordered_map > mediaAttachmentParsers; + +typedef enum { + TGMessageFlagBroadcast = 1, + TGMessageFlagLayerMask = 2 | 4 | 8 | 16 | 32, + TGMessageFlagContainsMention = 64, + TGMessageFlagForceReply = (1 << 7), + TGMessageFlagLayerMaskExtended = 0xff << 8, + TGMessageFlagSilent = (1 << 16), + TGMessageFlagEdited = (1 << 17) +} TGMessageFlags; + + +@interface TGMessage () { + bool _unread; +} + +@property (nonatomic) bool hasNoCheckingResults; + +@end + +@implementation TGMessage + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + TGMessage *object = [[TGMessage alloc] init]; + + object->_mid = [coder decodeInt32ForCKey:"i"]; + + [coder decodeBytesForCKey:"sk" value:object->_sortKey.key length:8 + 1 + 4 + 4]; + + object->_pts = [coder decodeInt32ForCKey:"pts"]; + + object->_unread = [coder decodeInt32ForCKey:"unr"] != 0; + object->_outgoing = [coder decodeInt32ForCKey:"out"] != 0; + object->_deliveryState = (TGMessageDeliveryState)[coder decodeInt32ForCKey:"ds"]; + object->_fromUid = [coder decodeInt64ForCKey:"fi"]; + object->_toUid = [coder decodeInt64ForCKey:"ti"]; + object->_cid = [coder decodeInt64ForCKey:"ci"]; + + object->_text = [coder decodeStringForCKey:"t"]; + object->_date = [coder decodeInt32ForCKey:"d"]; + object.mediaAttachments = [TGMessage parseMediaAttachments:[coder decodeDataCorCKey:"md"]]; + + object->_realDate = [coder decodeInt32ForCKey:"rd"]; + object->_randomId = [coder decodeInt64ForCKey:"ri"]; + + object->_messageLifetime = [coder decodeInt32ForCKey:"lt"]; + object->_flags = [coder decodeInt64ForCKey:"f"]; + object->_seqIn = [coder decodeInt32ForCKey:"sqi"]; + object->_seqOut = [coder decodeInt32ForCKey:"sqo"]; + + object->_contentProperties = [TGMessage parseContentProperties:[coder decodeDataCorCKey:"cpr"]]; + + return object; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + [coder encodeInt32:_mid forCKey:"i"]; + + [coder encodeBytes:_sortKey.key length:8 + 1 + 4 + 4 forCKey:"sk"]; + + [coder encodeInt32:_pts forCKey:"pts"]; + + [coder encodeInt32:_unread ? 1 : 0 forCKey:"unr"]; + [coder encodeInt32:_outgoing ? 1 : 0 forCKey:"out"]; + [coder encodeInt32:_deliveryState forCKey:"ds"]; + [coder encodeInt64:_fromUid forCKey:"fi"]; + [coder encodeInt64:_toUid forCKey:"ti"]; + [coder encodeInt64:_cid forCKey:"ci"]; + + [coder encodeString:_text forCKey:"t"]; + [coder encodeInt32:(int32_t)_date forCKey:"d"]; + [coder encodeData:[self serializeMediaAttachments:true] forCKey:"md"]; + + [coder encodeInt32:(int32_t)_realDate forCKey:"rd"]; + [coder encodeInt64:_randomId forCKey:"ri"]; + + [coder encodeInt32:_messageLifetime forCKey:"lt"]; + [coder encodeInt64:_flags forCKey:"f"]; + + [coder encodeInt32:_seqIn forCKey:"sqi"]; + [coder encodeInt32:_seqOut forCKey:"sqo"]; + + [coder encodeData:[self serializeContentProperties] forCKey:"cpr"]; +} + +- (id)copyWithZone:(NSZone *)__unused zone +{ + TGMessage *copyMessage = [[TGMessage alloc] init]; + + copyMessage->_hintUnread = _hintUnread; + + copyMessage->_mid = _mid; + copyMessage->_sortKey = _sortKey; + copyMessage->_pts = _pts; + copyMessage->_unread = _unread; + copyMessage->_outgoing = _outgoing; + copyMessage->_deliveryState = _deliveryState; + copyMessage->_fromUid = _fromUid; + copyMessage->_toUid = _toUid; + copyMessage->_cid = _cid; + + copyMessage->_text = _text; + copyMessage->_date = _date; + copyMessage->_mediaAttachments = [[NSArray alloc] initWithArray:_mediaAttachments]; + + copyMessage->_realDate = _realDate; + copyMessage->_randomId = _randomId; + + copyMessage->_actionInfo = _actionInfo; + + copyMessage->_textCheckingResults = _textCheckingResults; + + copyMessage->_messageLifetime = _messageLifetime; + copyMessage->_flags = _flags; + + copyMessage->_seqIn = _seqIn; + copyMessage->_seqOut = _seqOut; + + copyMessage->_contentProperties = [[NSDictionary alloc] initWithDictionary:_contentProperties]; + + copyMessage->_hideReplyMarkup = _hideReplyMarkup; + + copyMessage->_hole = _hole; + copyMessage->_group = _group; + + return copyMessage; +} + +- (TGMessageTransparentSortKey)transparentSortKey +{ + return TGMessageTransparentSortKeyMake(TGMessageSortKeyPeerId(_sortKey), TGMessageSortKeyTimestamp(_sortKey), TGMessageSortKeyMid(_sortKey), TGMessageSortKeySpace(_sortKey)); +} + +- (void)setIsBroadcast:(bool)isBroadcast +{ + if (isBroadcast) + _flags |= TGMessageFlagBroadcast; + else + _flags &= ~TGMessageFlagBroadcast; +} + +- (bool)isBroadcast +{ + return _flags & TGMessageFlagBroadcast; +} + +- (void)setForceReply:(bool)forceReply +{ + if (forceReply) + _flags |= TGMessageFlagForceReply; + else + _flags &= TGMessageFlagForceReply; +} + +- (bool)forceReply +{ + return _flags & TGMessageFlagForceReply; +} + +- (void)setIsSilent:(bool)isSilent { + if (isSilent) { + _flags |= TGMessageFlagSilent; + } else { + _flags &= ~TGMessageFlagSilent; + } +} + +- (bool)isSilent { + return _flags & TGMessageFlagSilent; +} + +- (bool)isEdited { + return _flags & TGMessageFlagEdited; +} + +- (void)setIsEdited:(bool)isEdited { + if (isEdited) { + _flags |= TGMessageFlagEdited; + } else { + _flags &= ~TGMessageFlagEdited; + } +} + +- (void)setLayer:(NSUInteger)layer +{ + int32_t layerLow = (int32_t)MIN((int32_t)layer, 31); + int32_t layerHigh = (int32_t)((int32_t)layer - layerLow); + _flags = (_flags & ~TGMessageFlagLayerMask) | ((layerLow & (1 | 2 | 4 | 8 | 16)) << 1); + _flags = (_flags & ~TGMessageFlagLayerMaskExtended) | ((layerHigh & 0xff) << 8); +} + +- (NSUInteger)layer +{ + if (!TGPeerIdIsSecretChat(self.cid)) { + return 70; + } + NSUInteger value = [TGMessage layerFromFlags:_flags]; + if (value < 1) + value = 1; + return value; +} + +- (void)setContainsMention:(bool)containsMention +{ + if (containsMention) + _flags |= TGMessageFlagContainsMention; + else + _flags &= (~TGMessageFlagContainsMention); +} + +- (bool)containsMention +{ + return _flags & TGMessageFlagContainsMention; +} + ++ (NSUInteger)layerFromFlags:(int64_t)flags +{ + int32_t layerLow = (int32_t)((flags & TGMessageFlagLayerMask) >> 1); + int32_t layerHigh = (int32_t)((flags & TGMessageFlagLayerMaskExtended) >> 8); + int32_t value = layerLow + layerHigh; + if (value < 1) + value = 1; + return value; +} + +- (int64_t)forwardPeerId +{ + for (TGMediaAttachment *attachment in _mediaAttachments) + { + if (attachment.type == TGForwardedMessageMediaAttachmentType) + { + TGForwardedMessageMediaAttachment *forwardedMessageAttachment = (TGForwardedMessageMediaAttachment *)attachment; + return forwardedMessageAttachment.forwardPeerId; + } + } + + return 0; +} + +- (TGMessageViewCountContentProperty *)viewCount { + return _contentProperties[@"viewCount"]; +} + +- (void)setViewCount:(TGMessageViewCountContentProperty *)viewCount { + NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithDictionary:_contentProperties]; + if (viewCount != nil) { + dict[@"viewCount"] = viewCount; + } else { + [dict removeObjectForKey:@"viewCount"]; + } + _contentProperties = dict; +} + +- (void)setText:(NSString *)text +{ + _text = text; + + _textCheckingResults = nil; + _hasNoCheckingResults = false; +} + +- (NSArray *)effectiveTextAndEntities { + NSArray *entities = nil; + for (id media in self.mediaAttachments) { + if ([media isKindOfClass:[TGImageMediaAttachment class]]) { + return @[((TGImageMediaAttachment *)media).caption ?: @"", @[]]; + } else if ([media isKindOfClass:[TGVideoMediaAttachment class]]) { + return @[((TGImageMediaAttachment *)media).caption ?: @"", @[]]; + } else if ([media isKindOfClass:[TGDocumentMediaAttachment class]]) { + return @[((TGImageMediaAttachment *)media).caption ?: @"", @[]]; + } else if ([media isKindOfClass:[TGMessageEntitiesAttachment class]]) { + entities = ((TGMessageEntitiesAttachment *)media).entities; + } + } + return @[_text ?: @"", entities ?: @[]]; +} + +- (bool)local +{ + return _mid >= TGMessageLocalMidBaseline; +} + ++ (NSArray *)textCheckingResultsForText:(NSString *)text highlightMentionsAndTags:(bool)highlightMentionsAndTags highlightCommands:(bool)highlightCommands entities:(NSArray *)entities +{ + if (entities != nil) { + NSMutableArray *textCheckingResults = [[NSMutableArray alloc] init]; + + for (TGMessageEntity *entity in entities) { + if (entity.range.location + entity.range.length > text.length) { + continue; + } + + if ([entity isKindOfClass:[TGMessageEntityBold class]]) { + [textCheckingResults addObject:[[TGTextCheckingResult alloc] initWithRange:entity.range type:TGTextCheckingResultTypeBold contents:@""]]; + } else if ([entity isKindOfClass:[TGMessageEntityBotCommand class]]) { + if (entity.range.length > 1) { + [textCheckingResults addObject:[[TGTextCheckingResult alloc] initWithRange:entity.range type:TGTextCheckingResultTypeCommand contents:[text substringWithRange:NSMakeRange(entity.range.location, entity.range.length)]]]; + } + } else if ([entity isKindOfClass:[TGMessageEntityCode class]]) { + [textCheckingResults addObject:[[TGTextCheckingResult alloc] initWithRange:entity.range type:TGTextCheckingResultTypeCode contents:@""]]; + } else if ([entity isKindOfClass:[TGMessageEntityEmail class]]) { + NSString *email = [text substringWithRange:entity.range]; + [textCheckingResults addObject:[NSTextCheckingResult linkCheckingResultWithRange:entity.range URL:[NSURL URLWithString:[@"mailto:" stringByAppendingString:email]]]]; + } else if ([entity isKindOfClass:[TGMessageEntityHashtag class]]) { + if (entity.range.length > 1) { + [textCheckingResults addObject:[[TGTextCheckingResult alloc] initWithRange:entity.range type:TGTextCheckingResultTypeHashtag contents:[text substringWithRange:NSMakeRange(entity.range.location + 1, entity.range.length - 1)]]]; + } + } else if ([entity isKindOfClass:[TGMessageEntityItalic class]]) { + [textCheckingResults addObject:[[TGTextCheckingResult alloc] initWithRange:entity.range type:TGTextCheckingResultTypeItalic contents:@""]]; + } else if ([entity isKindOfClass:[TGMessageEntityMention class]]) { + if (entity.range.length > 1) { + [textCheckingResults addObject:[[TGTextCheckingResult alloc] initWithRange:entity.range type:TGTextCheckingResultTypeMention contents:[text substringWithRange:NSMakeRange(entity.range.location + 1, entity.range.length - 1)]]]; + } + } else if ([entity isKindOfClass:[TGMessageEntityMentionName class]]) { + [textCheckingResults addObject:[[TGTextCheckingResult alloc] initWithRange:entity.range type:TGTextCheckingResultTypeLink contents:[NSString stringWithFormat:@"tg-user://%d", ((TGMessageEntityMentionName *)entity).userId]]]; + } else if ([entity isKindOfClass:[TGMessageEntityPre class]]) { + [textCheckingResults addObject:[[TGTextCheckingResult alloc] initWithRange:entity.range type:TGTextCheckingResultTypeCode contents:@""]]; + } else if ([entity isKindOfClass:[TGMessageEntityTextUrl class]]) { + NSTextCheckingResult *result = [NSTextCheckingResult linkCheckingResultWithRange:entity.range URL:[NSURL URLWithString:((TGMessageEntityTextUrl *)entity).url]]; + [result setIsTelegramHiddenLink:true]; + [textCheckingResults addObject:result]; + } else if ([entity isKindOfClass:[TGMessageEntityUrl class]]) { + NSString *link = [text substringWithRange:entity.range]; + NSURL *url = [NSURL URLWithString:link]; + if (url == nil) { + url = [NSURL URLWithString:[link stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; + } + [textCheckingResults addObject:[NSTextCheckingResult linkCheckingResultWithRange:entity.range URL:url]]; + } + } + + SEL sel = @selector(characterAtIndex:); + int length = (int)text.length; + unichar (*characterAtIndexImp)(id, SEL, NSUInteger) = (unichar (*)(id, SEL, NSUInteger))[text methodForSelector:sel]; + + int digitCount = 0; + for (int i = 0; i < length; i++) + { + unichar c = characterAtIndexImp(text, sel, i); + if (c >= '0' && c <= '9') { + digitCount++; + if (digitCount == 2) { + break; + } + } else { + digitCount = 0; + } + } + + if (digitCount >= 2) { + NSError *error = nil; + static NSDataDetector *dataDetector = nil; + if (dataDetector == nil) + dataDetector = [NSDataDetector dataDetectorWithTypes:(int)(NSTextCheckingTypePhoneNumber) error:&error]; + [dataDetector enumerateMatchesInString:text options:0 range:NSMakeRange(0, text.length) usingBlock:^(NSTextCheckingResult *match, __unused NSMatchingFlags flags, __unused BOOL *stop) + { + NSTextCheckingType type = [match resultType]; + if (type == NSTextCheckingTypePhoneNumber) + { + [textCheckingResults addObject:match]; + } + }]; + } + + return textCheckingResults; + } + + bool containsSomething = false; + + int length = (int)text.length; + + int digitsInRow = 0; + int schemeSequence = 0; + int dotSequence = 0; + + unichar lastChar = 0; + + SEL sel = @selector(characterAtIndex:); + unichar (*characterAtIndexImp)(id, SEL, NSUInteger) = (unichar (*)(id, SEL, NSUInteger))[text methodForSelector:sel]; + + for (int i = 0; i < length; i++) + { + unichar c = characterAtIndexImp(text, sel, i); + + if (highlightMentionsAndTags && (c == '@' || c == '#')) + { + containsSomething = true; + break; + } + + if (c >= '0' && c <= '9') + { + digitsInRow++; + if (digitsInRow >= 3) + { + containsSomething = true; + break; + } + + schemeSequence = 0; + dotSequence = 0; + } + else if (!(c != ' ' && digitsInRow > 0)) + digitsInRow = 0; + + if (c == ':') + { + if (schemeSequence == 0) + schemeSequence = 1; + else + schemeSequence = 0; + } + else if (c == '/') + { + if (highlightCommands) + { + containsSomething = true; + break; + } + + if (schemeSequence == 2) + { + containsSomething = true; + break; + } + + if (schemeSequence == 1) + schemeSequence++; + else + schemeSequence = 0; + } + else if (c == '.') + { + if (dotSequence == 0 && lastChar != ' ') + dotSequence++; + else + dotSequence = 0; + } + else if (c != ' ' && lastChar == '.' && dotSequence == 1) + { + containsSomething = true; + break; + } + else + { + dotSequence = 0; + } + + lastChar = c; + } + + if (containsSomething) + { + NSError *error = nil; + static NSDataDetector *dataDetector = nil; + if (dataDetector == nil) + dataDetector = [NSDataDetector dataDetectorWithTypes:(int)(NSTextCheckingTypeLink | NSTextCheckingTypePhoneNumber) error:&error]; + + NSMutableArray *results = [[NSMutableArray alloc] init]; + [dataDetector enumerateMatchesInString:text options:0 range:NSMakeRange(0, text.length) usingBlock:^(NSTextCheckingResult *match, __unused NSMatchingFlags flags, __unused BOOL *stop) + { + NSTextCheckingType type = [match resultType]; + if (type == NSTextCheckingTypeLink || type == NSTextCheckingTypePhoneNumber) + { + [results addObject:match]; + } + }]; + + static NSCharacterSet *characterSet = nil; + static NSCharacterSet *punctuationSet = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^ + { + characterSet = [NSCharacterSet alphanumericCharacterSet]; + punctuationSet = [NSCharacterSet punctuationCharacterSet]; + }); + + if (containsSomething && (highlightMentionsAndTags || highlightCommands)) + { + int mentionStart = -1; + int hashtagStart = -1; + int commandStart = -1; + + unichar previous = 0; + for (int i = 0; i < length; i++) + { + unichar c = characterAtIndexImp(text, sel, i); + if (highlightMentionsAndTags && commandStart == -1) + { + if (mentionStart != -1) + { + if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == 0x200C)) + { + if (i > mentionStart + 1) + { + NSRange range = NSMakeRange(mentionStart + 1, i - mentionStart - 1); + NSRange mentionRange = NSMakeRange(range.location - 1, range.length + 1); + + unichar mentionStartChar = [text characterAtIndex:mentionRange.location + 1]; + if (!(mentionRange.length <= 1 || (mentionStartChar >= '0' && mentionStartChar <= '9'))) + { + [results addObject:[[TGTextCheckingResult alloc] initWithRange:mentionRange type:TGTextCheckingResultTypeMention contents:[text substringWithRange:range]]]; + } + } + mentionStart = -1; + } + } + else if (hashtagStart != -1) + { + if (c == ' ' || (![characterSet characterIsMember:c] && c != '_' && c != 0x200C)) + { + if (i > hashtagStart + 1) + { + NSRange range = NSMakeRange(hashtagStart + 1, i - hashtagStart - 1); + NSRange hashtagRange = NSMakeRange(range.location - 1, range.length + 1); + + [results addObject:[[TGTextCheckingResult alloc] initWithRange:hashtagRange type:TGTextCheckingResultTypeHashtag contents:[text substringWithRange:range]]]; + } + hashtagStart = -1; + } + } + + if (c == '@') + { + if (previous == 0 || previous == ' ' || previous == '\n' || previous == '[' || previous == ']' || previous == '(' || previous == ')' || previous == ':') { + mentionStart = i; + } + } + else if (c == '#') + { + hashtagStart = i; + } + } + + if (highlightCommands && mentionStart == -1 && hashtagStart == -1) + { + if (commandStart != -1 && ![characterSet characterIsMember:c] && c != '@' && c != '_') + { + if (i - commandStart > 1) + { + NSRange range = NSMakeRange(commandStart, i - commandStart); + [results addObject:[[TGTextCheckingResult alloc] initWithRange:range type:TGTextCheckingResultTypeCommand contents:[text substringWithRange:range]]]; + } + + commandStart = -1; + } + else if (c == '/' && (previous == 0 || previous == ' ' || previous == '\n' || previous == '\t')) + { + commandStart = i; + } + } + previous = c; + } + + if (mentionStart != -1 && mentionStart + 1 < length - 1) + { + NSRange range = NSMakeRange(mentionStart + 1, length - mentionStart - 1); + NSRange mentionRange = NSMakeRange(range.location - 1, range.length + 1); + unichar mentionStartChar = [text characterAtIndex:mentionRange.location + 1]; + if (!(mentionRange.length <= 2 || (mentionStartChar >= '0' && mentionStartChar <= '9'))) + { + [results addObject:[[TGTextCheckingResult alloc] initWithRange:mentionRange type:TGTextCheckingResultTypeMention contents:[text substringWithRange:range]]]; + } + } + + if (hashtagStart != -1 && hashtagStart + 1 < length - 1) + { + NSRange range = NSMakeRange(hashtagStart + 1, length - hashtagStart - 1); + NSRange hashtagRange = NSMakeRange(range.location - 1, range.length + 1); + [results addObject:[[TGTextCheckingResult alloc] initWithRange:hashtagRange type:TGTextCheckingResultTypeHashtag contents:[text substringWithRange:range]]]; + } + + if (commandStart != -1 && commandStart + 1 < length) + { + NSRange range = NSMakeRange(commandStart, length - commandStart); + [results addObject:[[TGTextCheckingResult alloc] initWithRange:range type:TGTextCheckingResultTypeCommand contents:[text substringWithRange:range]]]; + } + } + + return results; + } + + return nil; +} + ++ (NSArray *)entitiesForMarkedUpText:(NSString *)text resultingText:(__autoreleasing NSString **)resultingText { + NSMutableArray *entities = [[NSMutableArray alloc] init]; + + NSMutableString *cleanText = [[NSMutableString alloc] initWithString:text]; + +#ifdef DEBUG + while (true) + { + NSRange startRange = [cleanText rangeOfString:@"***"]; + if (startRange.location == NSNotFound) + break; + + [cleanText deleteCharactersInRange:startRange]; + + NSRange endRange = [cleanText rangeOfString:@"***"]; + if (endRange.location == NSNotFound) + break; + + [cleanText deleteCharactersInRange:endRange]; + + NSRange range = NSMakeRange(startRange.location, endRange.location - startRange.location); + [entities addObject:[[TGMessageEntityBold alloc] initWithRange:range]]; + } + + while (true) + { + NSRange startRange = [cleanText rangeOfString:@"%%%"]; + if (startRange.location == NSNotFound) + break; + + [cleanText deleteCharactersInRange:startRange]; + + NSRange endRange = [cleanText rangeOfString:@"%%%"]; + if (endRange.location == NSNotFound) + break; + + [cleanText deleteCharactersInRange:endRange]; + + NSRange range = NSMakeRange(startRange.location, endRange.location - startRange.location); + [entities addObject:[[TGMessageEntityItalic alloc] initWithRange:range]]; + } + + while (true) + { + NSRange startRange = [cleanText rangeOfString:@"```"]; + if (startRange.location == NSNotFound) + break; + + [cleanText deleteCharactersInRange:startRange]; + + NSRange endRange = [cleanText rangeOfString:@"```"]; + if (endRange.location == NSNotFound) + break; + + [cleanText deleteCharactersInRange:endRange]; + + NSRange range = NSMakeRange(startRange.location, endRange.location - startRange.location); + [entities addObject:[[TGMessageEntityPre alloc] initWithRange:range]]; + } + + while (true) + { + NSRange startRange = [cleanText rangeOfString:@"[[["]; + if (startRange.location == NSNotFound) + break; + + [cleanText deleteCharactersInRange:startRange]; + + NSRange endRange = [cleanText rangeOfString:@"]]]"]; + if (endRange.location == NSNotFound) + break; + + [cleanText deleteCharactersInRange:endRange]; + + NSRange range = NSMakeRange(startRange.location, endRange.location - startRange.location); + [entities addObject:[[TGMessageEntityTextUrl alloc] initWithRange:range url:@"http://google.com"]]; + } +#endif + + if (resultingText != NULL) { + *resultingText = cleanText; + } + + return entities.count == 0 ? nil : entities; +} + +- (NSArray *)textCheckingResults +{ + if (_textCheckingResults != nil) { + return _textCheckingResults; + } + + if (_mediaAttachments.count != 0) { + for (TGMediaAttachment *attachment in _mediaAttachments) { + if (attachment.type == TGMessageEntitiesAttachmentType) { + NSMutableArray *textCheckingResults = [[NSMutableArray alloc] init]; + + for (TGMessageEntity *entity in ((TGMessageEntitiesAttachment *)attachment).entities) { + if (entity.range.location + entity.range.length > _text.length) { + continue; + } + + if ([entity isKindOfClass:[TGMessageEntityBold class]]) { + [textCheckingResults addObject:[[TGTextCheckingResult alloc] initWithRange:entity.range type:TGTextCheckingResultTypeBold contents:@""]]; + } else if ([entity isKindOfClass:[TGMessageEntityBotCommand class]]) { + if (entity.range.length > 1) { + [textCheckingResults addObject:[[TGTextCheckingResult alloc] initWithRange:entity.range type:TGTextCheckingResultTypeCommand contents:[_text substringWithRange:NSMakeRange(entity.range.location, entity.range.length)]]]; + } + } else if ([entity isKindOfClass:[TGMessageEntityCode class]]) { + [textCheckingResults addObject:[[TGTextCheckingResult alloc] initWithRange:entity.range type:TGTextCheckingResultTypeCode contents:@""]]; + } else if ([entity isKindOfClass:[TGMessageEntityEmail class]]) { + NSString *email = [_text substringWithRange:entity.range]; + [textCheckingResults addObject:[NSTextCheckingResult linkCheckingResultWithRange:entity.range URL:[NSURL URLWithString:[@"mailto:" stringByAppendingString:email]]]]; + } else if ([entity isKindOfClass:[TGMessageEntityHashtag class]]) { + if (entity.range.length > 1) { + [textCheckingResults addObject:[[TGTextCheckingResult alloc] initWithRange:entity.range type:TGTextCheckingResultTypeHashtag contents:[_text substringWithRange:NSMakeRange(entity.range.location + 1, entity.range.length - 1)]]]; + } + } else if ([entity isKindOfClass:[TGMessageEntityItalic class]]) { + [textCheckingResults addObject:[[TGTextCheckingResult alloc] initWithRange:entity.range type:TGTextCheckingResultTypeItalic contents:@""]]; + } else if ([entity isKindOfClass:[TGMessageEntityMention class]]) { + if (entity.range.length > 1) { + [textCheckingResults addObject:[[TGTextCheckingResult alloc] initWithRange:entity.range type:TGTextCheckingResultTypeMention contents:[_text substringWithRange:NSMakeRange(entity.range.location + 1, entity.range.length - 1)]]]; + } + } else if ([entity isKindOfClass:[TGMessageEntityMentionName class]]) { + [textCheckingResults addObject:[[TGTextCheckingResult alloc] initWithRange:entity.range type:TGTextCheckingResultTypeLink contents:[NSString stringWithFormat:@"tg-user://%d", ((TGMessageEntityMentionName *)entity).userId]]]; + } else if ([entity isKindOfClass:[TGMessageEntityPre class]]) { + [textCheckingResults addObject:[[TGTextCheckingResult alloc] initWithRange:entity.range type:TGTextCheckingResultTypeCode contents:@""]]; + } else if ([entity isKindOfClass:[TGMessageEntityTextUrl class]]) { + //NSTextCheckingResult *result = [NSTextCheckingResult linkCheckingResultWithRange:entity.range URL:[NSURL URLWithString:((TGMessageEntityTextUrl *)entity).url]]; + TGTextCheckingResult *result = [[TGTextCheckingResult alloc] initWithRange:entity.range type:TGTextCheckingResultTypeLink contents:((TGMessageEntityTextUrl *)entity).url value:nil highlightAsLink:true]; + //[result setIsTelegramHiddenLink:true]; + [textCheckingResults addObject:result]; + } else if ([entity isKindOfClass:[TGMessageEntityUrl class]]) { + NSString *link = [_text substringWithRange:entity.range]; + NSURL *url = [NSURL URLWithString:link]; + if (url == nil) { + url = [NSURL URLWithString:[link stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; + } + [textCheckingResults addObject:[NSTextCheckingResult linkCheckingResultWithRange:entity.range URL:url]]; + } + } + + SEL sel = @selector(characterAtIndex:); + NSString *text = _text; + int length = (int)text.length; + unichar (*characterAtIndexImp)(id, SEL, NSUInteger) = (unichar (*)(id, SEL, NSUInteger))[text methodForSelector:sel]; + + int digitCount = 0; + for (int i = 0; i < length; i++) + { + unichar c = characterAtIndexImp(text, sel, i); + if (c >= '0' && c <= '9') { + digitCount++; + if (digitCount == 2) { + break; + } + } else { + digitCount = 0; + } + } + + if (digitCount >= 2) { + static NSDataDetector *dataDetector = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSError *error = nil; + dataDetector = [NSDataDetector dataDetectorWithTypes:(int)(NSTextCheckingTypeLink | NSTextCheckingTypePhoneNumber) error:&error]; + }); + [dataDetector enumerateMatchesInString:text options:0 range:NSMakeRange(0, text.length) usingBlock:^(NSTextCheckingResult *match, __unused NSMatchingFlags flags, __unused BOOL *stop) + { + NSTextCheckingType type = [match resultType]; + if (type == NSTextCheckingTypePhoneNumber) + { + [textCheckingResults addObject:match]; + } + }]; + } + + _textCheckingResults = textCheckingResults; + return textCheckingResults; + } + } + } + + if (_text.length < 2 || _text.length > 1024 * 20) + return nil; + + if (_textCheckingResults == nil && !_hasNoCheckingResults) + { + _textCheckingResults = [TGMessage textCheckingResultsForText:_text highlightMentionsAndTags:true highlightCommands:true entities:nil]; + _hasNoCheckingResults = _textCheckingResults == nil; + } + + return _textCheckingResults; +} + +- (void)setReplyMarkup:(TGBotReplyMarkup *)replyMarkup +{ + NSMutableArray *array = [[NSMutableArray alloc] initWithArray:_mediaAttachments]; + NSUInteger index = 0; + for (TGMediaAttachment *attachment in array) + { + if (attachment.type == TGReplyMarkupAttachmentType) + { + [array removeObjectAtIndex:index]; + break; + } + index++; + } + TGReplyMarkupAttachment *attachment = [[TGReplyMarkupAttachment alloc] init]; + attachment.replyMarkup = replyMarkup; + [array addObject:attachment]; + _mediaAttachments = array; +} + +- (TGBotReplyMarkup *)replyMarkup +{ + for (TGMediaAttachment *attachment in _mediaAttachments) + { + if (attachment.type == TGReplyMarkupAttachmentType) + { + return ((TGReplyMarkupAttachment *)attachment).replyMarkup; + } + } + + return nil; +} + +- (void)setEntities:(NSArray *)entities +{ + NSMutableArray *array = [[NSMutableArray alloc] initWithArray:_mediaAttachments]; + NSUInteger index = 0; + for (TGMediaAttachment *attachment in array) + { + if (attachment.type == TGMessageEntitiesAttachmentType) + { + [array removeObjectAtIndex:index]; + break; + } + index++; + } + TGMessageEntitiesAttachment *attachment = [[TGMessageEntitiesAttachment alloc] init]; + attachment.entities = entities; + [array addObject:attachment]; + _mediaAttachments = array; +} + +- (NSArray *)entities +{ + for (TGMediaAttachment *attachment in _mediaAttachments) + { + if (attachment.type == TGMessageEntitiesAttachmentType) + { + return ((TGMessageEntitiesAttachment *)attachment).entities; + } + } + + return nil; +} + +- (NSString *)authorSignature { + for (TGMediaAttachment *attachment in _mediaAttachments) + { + if (attachment.type == TGAuthorSignatureMediaAttachmentType) + { + return ((TGAuthorSignatureMediaAttachment *)attachment).signature; + } + } + + return nil; +} + +- (NSString *)forwardAuthorSignature { + for (TGMediaAttachment *attachment in _mediaAttachments) + { + if (attachment.type == TGForwardedMessageMediaAttachmentType) { + return ((TGForwardedMessageMediaAttachment *)attachment).forwardAuthorSignature; + } + } + + return nil; +} + ++ (void)registerMediaAttachmentParser:(int)type parser:(id)parser +{ + mediaAttachmentParsers.insert(std::pair >(type, parser)); +} + +- (NSData *)serializeMediaAttachments:(bool)includeMeta +{ + if (_mediaAttachments == nil || _mediaAttachments.count == 0) + return [NSData data]; + + NSMutableData *data = [[NSMutableData alloc] init]; + + int count = 0; + NSRange countRange = NSMakeRange(data.length, 4); + [data appendBytes:&count length:4]; + + for (TGMediaAttachment *attachment in _mediaAttachments) + { + if (!includeMeta && attachment.isMeta) + continue; + + int type = attachment.type; + [data appendBytes:&type length:4]; + + [attachment serialize:data]; + + count++; + } + + [data replaceBytesInRange:countRange withBytes:&count]; + + return data; +} + ++ (NSData *)serializeMediaAttachments:(bool)includeMeta attachments:(NSArray *)attachments +{ + if (attachments == nil || attachments.count == 0) + return [NSData data]; + + NSMutableData *data = [[NSMutableData alloc] init]; + + int count = 0; + NSRange countRange = NSMakeRange(data.length, 4); + [data appendBytes:&count length:4]; + for (TGMediaAttachment *attachment in attachments) + { + if (!includeMeta && attachment.isMeta) + continue; + + int type = attachment.type; + [data appendBytes:&type length:4]; + + [attachment serialize:data]; + + count++; + } + + [data replaceBytesInRange:countRange withBytes:&count]; + + return data; +} + ++ (NSData *)serializeAttachment:(TGMediaAttachment *)attachment +{ + if (attachment == nil) + return [NSData data]; + + NSMutableData *data = [[NSMutableData alloc] init]; + + int count = 1; + [data appendBytes:&count length:4]; + + int type = attachment.type; + [data appendBytes:&type length:4]; + + [attachment serialize:data]; + + return data; +} + +- (void)setMediaAttachments:(NSArray *)mediaAttachments +{ + for (TGMediaAttachment *attachment in mediaAttachments) + { + if (attachment.type == TGActionMediaAttachmentType) { + _actionInfo = (TGActionMediaAttachment *)attachment; + } + } + + _mediaAttachments = mediaAttachments; +} + ++ (NSArray *)parseMediaAttachments:(NSData *)data +{ + if (data == nil || data.length == 0) + return [NSArray array]; + + NSInputStream *is = [[NSInputStream alloc] initWithData:data]; + [is open]; + + int count = 0; + [is read:(uint8_t *)&count maxLength:4]; + NSMutableArray *attachments = [[NSMutableArray alloc] initWithCapacity:count]; + + for (int i = 0; i < count; i++) + { + int type = 0; + [is read:(uint8_t *)&type maxLength:4]; + + std::unordered_map >::iterator it = mediaAttachmentParsers.find(type); + if (it == mediaAttachmentParsers.end()) + { + TGLog(@"***** Unknown media attachment type %d", type); + return [NSArray array]; + } + + TGMediaAttachment *attachment = [it->second parseMediaAttachment:is]; + if (attachment != nil) + { + [attachments addObject:attachment]; + } + } + + [is close]; + + return [NSArray arrayWithArray:attachments]; +} + +- (NSData *)serializeContentProperties +{ + if (_contentProperties.count == 0) + return nil; + + PSKeyValueEncoder *encoder = [[PSKeyValueEncoder alloc] init]; + [_contentProperties enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, __unused BOOL *stop) + { + [encoder encodeObject:value forKey:key]; + }]; + + return encoder.data; +} + ++ (NSData *)serializeContentProperties:(NSDictionary *)contentProperties +{ + if (contentProperties.count == 0) + return nil; + + PSKeyValueEncoder *encoder = [[PSKeyValueEncoder alloc] init]; + [contentProperties enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, __unused BOOL *stop) + { + [encoder encodeObject:value forKey:key]; + }]; + + return encoder.data; +} + ++ (NSDictionary *)parseContentProperties:(NSData *)data +{ + if (data.length == 0) + return nil; + + PSKeyValueDecoder *decoder = [[PSKeyValueDecoder alloc] initWithData:data]; + return [decoder decodeObjectsByKeys]; +} + +- (void)removeReplyAndMarkup { + if (_mediaAttachments.count != 0) { + for (NSUInteger i = 0; i < _mediaAttachments.count; i++) { + if ([_mediaAttachments[i] isKindOfClass:[TGReplyMessageMediaAttachment class]]) { + NSMutableArray *result = [[NSMutableArray alloc] initWithArray:_mediaAttachments]; + [result removeObjectAtIndex:i]; + _mediaAttachments = result; + break; + } + } + + for (NSUInteger i = 0; i < _mediaAttachments.count; i++) { + if ([_mediaAttachments[i] isKindOfClass:[TGReplyMarkupAttachment class]]) { + NSMutableArray *result = [[NSMutableArray alloc] initWithArray:_mediaAttachments]; + [result removeObjectAtIndex:i]; + _mediaAttachments = result; + break; + } + } + } +} + +- (void)filterOutExpiredMedia { + if (self.mediaAttachments.count != 0) { + NSMutableArray *updatedMedia = [[NSMutableArray alloc] initWithArray:self.mediaAttachments]; + for (NSUInteger index = 0; index < updatedMedia.count; index++) { + if ([updatedMedia[index] isKindOfClass:[TGImageMediaAttachment class]]) { + TGImageMediaAttachment *imageMedia = updatedMedia[index]; + TGImageMediaAttachment *updatedImageMedia = [[TGImageMediaAttachment alloc] init]; + updatedImageMedia.caption = imageMedia.caption; + updatedMedia[index] = updatedImageMedia; + } else if ([updatedMedia[index] isKindOfClass:[TGVideoMediaAttachment class]]) { + TGVideoMediaAttachment *videoMedia = updatedMedia[index]; + TGVideoMediaAttachment *updatedVideoMedia = [[TGVideoMediaAttachment alloc] init]; + updatedVideoMedia.caption = videoMedia.caption; + updatedMedia[index] = updatedVideoMedia; + } else if ([updatedMedia[index] isKindOfClass:[TGDocumentMediaAttachment class]]) { + TGDocumentMediaAttachment *documentMedia = updatedMedia[index]; + TGDocumentMediaAttachment *updatedDocumentMedia = [[TGDocumentMediaAttachment alloc] init]; + updatedDocumentMedia.caption = documentMedia.caption; + updatedMedia[index] = updatedDocumentMedia; + } + } + self.mediaAttachments = updatedMedia; + } +} + +- (bool)hasExpiredMedia { + for (id media in self.mediaAttachments) { + if ([media isKindOfClass:[TGImageMediaAttachment class]]) { + TGImageMediaAttachment *imageMedia = media; + if (imageMedia.imageId == 0 && imageMedia.localImageId == 0) { + return true; + } + } else if ([media isKindOfClass:[TGVideoMediaAttachment class]]) { + TGVideoMediaAttachment *videoMedia = media; + if (videoMedia.videoId == 0 && videoMedia.localVideoId == 0) { + return true; + } + } if ([media isKindOfClass:[TGDocumentMediaAttachment class]]) { + TGDocumentMediaAttachment *documentMedia = media; + if (documentMedia.documentId == 0 && documentMedia.localDocumentId == 0) { + return true; + } + } + } + return false; +} + +@end + +@interface TGMediaId () +{ + int _cachedHash; +} + +@end + +@implementation TGMediaId + +- (id)initWithType:(uint8_t)type itemId:(int64_t)itemId +{ + self = [super init]; + if (self != nil) + { + _type = type; + _itemId = itemId; + } + return self; +} + +- (id)copyWithZone:(NSZone *)__unused zone +{ + TGMediaId *copyMediaId = [[TGMediaId alloc] initWithType:_type itemId:_itemId]; + return copyMediaId; +} + +- (NSUInteger)hash +{ + if (_cachedHash == 0) + _cachedHash = (int)(((_itemId >> 32) ^ _itemId & 0xffffffff) + (int)_type); + return _cachedHash; +} + +- (BOOL)isEqual:(id)anObject +{ + if (![anObject isKindOfClass:[TGMediaId class]]) + return false; + + TGMediaId *other = (TGMediaId *)anObject; + return other.itemId == _itemId && other.type == _type; +} + +@end + diff --git a/LegacyComponents/TGMessageEntitiesAttachment.h b/LegacyComponents/TGMessageEntitiesAttachment.h new file mode 100644 index 0000000000..448601968a --- /dev/null +++ b/LegacyComponents/TGMessageEntitiesAttachment.h @@ -0,0 +1,21 @@ +#import "TGMediaAttachment.h" + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#define TGMessageEntitiesAttachmentType ((int)0x8C2E3CCE) + +@interface TGMessageEntitiesAttachment : TGMediaAttachment + +@property (nonatomic, strong) NSArray *entities; + +@end diff --git a/LegacyComponents/TGMessageEntitiesAttachment.m b/LegacyComponents/TGMessageEntitiesAttachment.m new file mode 100644 index 0000000000..e53491ff12 --- /dev/null +++ b/LegacyComponents/TGMessageEntitiesAttachment.m @@ -0,0 +1,62 @@ +#import "TGMessageEntitiesAttachment.h" + +#import "LegacyComponentsInternal.h" + +#import "PSKeyValueEncoder.h" +#import "PSKeyValueDecoder.h" + +#import "NSInputStream+TL.h" + +@implementation TGMessageEntitiesAttachment + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + self.type = TGMessageEntitiesAttachmentType; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + self.type = TGMessageEntitiesAttachmentType; + + NSData *entitiesData = [aDecoder decodeObjectForKey:@"entitiesData"]; + _entities = [[[PSKeyValueDecoder alloc] initWithData:entitiesData] decodeArrayForCKey:"_"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + PSKeyValueEncoder *encoder = [[PSKeyValueEncoder alloc] init]; + [encoder encodeArray:_entities forCKey:"_"]; + [aCoder encodeObject:[encoder data] forKey:@"entitiesData"]; +} + +- (BOOL)isEqual:(id)object +{ + return [object isKindOfClass:[TGMessageEntitiesAttachment class]] && TGObjectCompare(((TGMessageEntitiesAttachment *)object)->_entities, _entities); +} + +- (void)serialize:(NSMutableData *)data +{ + NSData *serializedData = [NSKeyedArchiver archivedDataWithRootObject:self]; + int32_t length = (int32_t)serializedData.length; + [data appendBytes:&length length:4]; + [data appendData:serializedData]; +} + +- (TGMediaAttachment *)parseMediaAttachment:(NSInputStream *)is +{ + int32_t length = [is readInt32]; + NSData *data = [is readData:length]; + return [NSKeyedUnarchiver unarchiveObjectWithData:data]; +} + +@end diff --git a/LegacyComponents/TGMessageEntity.h b/LegacyComponents/TGMessageEntity.h new file mode 100644 index 0000000000..34574e97c3 --- /dev/null +++ b/LegacyComponents/TGMessageEntity.h @@ -0,0 +1,11 @@ +#import + +#import + +@interface TGMessageEntity : NSObject + +@property (nonatomic, readonly) NSRange range; + +- (instancetype)initWithRange:(NSRange)range; + +@end diff --git a/LegacyComponents/TGMessageEntity.m b/LegacyComponents/TGMessageEntity.m new file mode 100644 index 0000000000..c35ccb9449 --- /dev/null +++ b/LegacyComponents/TGMessageEntity.m @@ -0,0 +1,42 @@ +#import "TGMessageEntity.h" + +#import "PSKeyValueCoder.h" + +@implementation TGMessageEntity + +- (instancetype)initWithRange:(NSRange)range +{ + self = [super init]; + if (self != nil) + { + _range = range; + } + return self; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + return [self initWithRange:NSMakeRange([coder decodeInt32ForCKey:"r.s"], [coder decodeInt32ForCKey:"r.l"])]; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + [coder encodeInt32:(int32_t)_range.location forCKey:"r.s"]; + [coder encodeInt32:(int32_t)_range.length forCKey:"r.l"]; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + return [self initWithRange:NSMakeRange([aDecoder decodeInt32ForKey:@"r.s"], [aDecoder decodeInt32ForKey:@"r.l"])]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeInt32:(int32_t)_range.location forKey:@"r.s"]; + [aCoder encodeInt32:(int32_t)_range.length forKey:@"r.l"]; +} + +- (BOOL)isEqual:(id)object +{ + return [object isKindOfClass:[TGMessageEntity class]] && ((TGMessageEntity *)object)->_range.location == _range.location && ((TGMessageEntity *)object)->_range.length == _range.length; +} + +@end diff --git a/LegacyComponents/TGMessageEntityBold.h b/LegacyComponents/TGMessageEntityBold.h new file mode 100644 index 0000000000..b9ae0ff7ca --- /dev/null +++ b/LegacyComponents/TGMessageEntityBold.h @@ -0,0 +1,5 @@ +#import + +@interface TGMessageEntityBold : TGMessageEntity + +@end diff --git a/LegacyComponents/TGMessageEntityBold.m b/LegacyComponents/TGMessageEntityBold.m new file mode 100644 index 0000000000..02e32ec61a --- /dev/null +++ b/LegacyComponents/TGMessageEntityBold.m @@ -0,0 +1,10 @@ +#import "TGMessageEntityBold.h" + +@implementation TGMessageEntityBold + +- (BOOL)isEqual:(id)object +{ + return [super isEqual:object] && [object isKindOfClass:[TGMessageEntityBold class]]; +} + +@end diff --git a/LegacyComponents/TGMessageEntityBotCommand.h b/LegacyComponents/TGMessageEntityBotCommand.h new file mode 100644 index 0000000000..c7359542e1 --- /dev/null +++ b/LegacyComponents/TGMessageEntityBotCommand.h @@ -0,0 +1,5 @@ +#import + +@interface TGMessageEntityBotCommand : TGMessageEntity + +@end diff --git a/LegacyComponents/TGMessageEntityBotCommand.m b/LegacyComponents/TGMessageEntityBotCommand.m new file mode 100644 index 0000000000..c2c64411af --- /dev/null +++ b/LegacyComponents/TGMessageEntityBotCommand.m @@ -0,0 +1,10 @@ +#import "TGMessageEntityBotCommand.h" + +@implementation TGMessageEntityBotCommand + +- (BOOL)isEqual:(id)object +{ + return [super isEqual:object] && [object isKindOfClass:[TGMessageEntityBotCommand class]]; +} + +@end diff --git a/LegacyComponents/TGMessageEntityCode.h b/LegacyComponents/TGMessageEntityCode.h new file mode 100644 index 0000000000..2e4e0ba7e3 --- /dev/null +++ b/LegacyComponents/TGMessageEntityCode.h @@ -0,0 +1,5 @@ +#import + +@interface TGMessageEntityCode : TGMessageEntity + +@end diff --git a/LegacyComponents/TGMessageEntityCode.m b/LegacyComponents/TGMessageEntityCode.m new file mode 100644 index 0000000000..607a004248 --- /dev/null +++ b/LegacyComponents/TGMessageEntityCode.m @@ -0,0 +1,10 @@ +#import "TGMessageEntityCode.h" + +@implementation TGMessageEntityCode + +- (BOOL)isEqual:(id)object +{ + return [super isEqual:object] && [object isKindOfClass:[TGMessageEntityCode class]]; +} + +@end diff --git a/LegacyComponents/TGMessageEntityEmail.h b/LegacyComponents/TGMessageEntityEmail.h new file mode 100644 index 0000000000..f4b66dece0 --- /dev/null +++ b/LegacyComponents/TGMessageEntityEmail.h @@ -0,0 +1,5 @@ +#import + +@interface TGMessageEntityEmail : TGMessageEntity + +@end diff --git a/LegacyComponents/TGMessageEntityEmail.m b/LegacyComponents/TGMessageEntityEmail.m new file mode 100644 index 0000000000..ad82998a44 --- /dev/null +++ b/LegacyComponents/TGMessageEntityEmail.m @@ -0,0 +1,10 @@ +#import "TGMessageEntityEmail.h" + +@implementation TGMessageEntityEmail + +- (BOOL)isEqual:(id)object +{ + return [super isEqual:object] && [object isKindOfClass:[TGMessageEntityEmail class]]; +} + +@end diff --git a/LegacyComponents/TGMessageEntityHashtag.h b/LegacyComponents/TGMessageEntityHashtag.h new file mode 100644 index 0000000000..cc9e202e1f --- /dev/null +++ b/LegacyComponents/TGMessageEntityHashtag.h @@ -0,0 +1,5 @@ +#import + +@interface TGMessageEntityHashtag : TGMessageEntity + +@end diff --git a/LegacyComponents/TGMessageEntityHashtag.m b/LegacyComponents/TGMessageEntityHashtag.m new file mode 100644 index 0000000000..c9b45d2089 --- /dev/null +++ b/LegacyComponents/TGMessageEntityHashtag.m @@ -0,0 +1,10 @@ +#import "TGMessageEntityHashtag.h" + +@implementation TGMessageEntityHashtag + +- (BOOL)isEqual:(id)object +{ + return [super isEqual:object] && [object isKindOfClass:[TGMessageEntityHashtag class]]; +} + +@end diff --git a/LegacyComponents/TGMessageEntityItalic.h b/LegacyComponents/TGMessageEntityItalic.h new file mode 100644 index 0000000000..573688d2fa --- /dev/null +++ b/LegacyComponents/TGMessageEntityItalic.h @@ -0,0 +1,5 @@ +#import + +@interface TGMessageEntityItalic : TGMessageEntity + +@end diff --git a/LegacyComponents/TGMessageEntityItalic.m b/LegacyComponents/TGMessageEntityItalic.m new file mode 100644 index 0000000000..708789d718 --- /dev/null +++ b/LegacyComponents/TGMessageEntityItalic.m @@ -0,0 +1,10 @@ +#import "TGMessageEntityItalic.h" + +@implementation TGMessageEntityItalic + +- (BOOL)isEqual:(id)object +{ + return [super isEqual:object] && [object isKindOfClass:[TGMessageEntityItalic class]]; +} + +@end diff --git a/LegacyComponents/TGMessageEntityMention.h b/LegacyComponents/TGMessageEntityMention.h new file mode 100644 index 0000000000..5a5e92c12f --- /dev/null +++ b/LegacyComponents/TGMessageEntityMention.h @@ -0,0 +1,5 @@ +#import + +@interface TGMessageEntityMention : TGMessageEntity + +@end diff --git a/LegacyComponents/TGMessageEntityMention.m b/LegacyComponents/TGMessageEntityMention.m new file mode 100644 index 0000000000..6ebc26171b --- /dev/null +++ b/LegacyComponents/TGMessageEntityMention.m @@ -0,0 +1,10 @@ +#import "TGMessageEntityMention.h" + +@implementation TGMessageEntityMention + +- (BOOL)isEqual:(id)object +{ + return [super isEqual:object] && [object isKindOfClass:[TGMessageEntityMention class]]; +} + +@end diff --git a/LegacyComponents/TGMessageEntityMentionName.h b/LegacyComponents/TGMessageEntityMentionName.h new file mode 100644 index 0000000000..267c514b93 --- /dev/null +++ b/LegacyComponents/TGMessageEntityMentionName.h @@ -0,0 +1,9 @@ +#import + +@interface TGMessageEntityMentionName : TGMessageEntity + +@property (nonatomic, readonly) int32_t userId; + +- (instancetype)initWithRange:(NSRange)range userId:(int32_t)userId; + +@end diff --git a/LegacyComponents/TGMessageEntityMentionName.m b/LegacyComponents/TGMessageEntityMentionName.m new file mode 100644 index 0000000000..ffe5f0297b --- /dev/null +++ b/LegacyComponents/TGMessageEntityMentionName.m @@ -0,0 +1,41 @@ +#import "TGMessageEntityMentionName.h" + +#import "PSKeyValueCoder.h" + +@implementation TGMessageEntityMentionName + +- (instancetype)initWithRange:(NSRange)range userId:(int32_t)userId { + self = [super initWithRange:range]; + if (self != nil) { + _userId = userId; + } + return self; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder { + self = [super initWithKeyValueCoder:coder]; + if (self != nil) { + _userId = [coder decodeInt32ForCKey:"userId"]; + } + return self; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder { + [super encodeWithKeyValueCoder:coder]; + [coder encodeInt32:_userId forCKey:"userId"]; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self != nil) { + _userId = [aDecoder decodeInt32ForKey:@"userId"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [super encodeWithCoder:aCoder]; + [aCoder encodeInt32:_userId forKey:@"userId"]; +} + +@end diff --git a/LegacyComponents/TGMessageEntityPre.h b/LegacyComponents/TGMessageEntityPre.h new file mode 100644 index 0000000000..2873ee80fa --- /dev/null +++ b/LegacyComponents/TGMessageEntityPre.h @@ -0,0 +1,9 @@ +#import + +@interface TGMessageEntityPre : TGMessageEntity + +@property (nonatomic, strong, readonly) NSString *language; + +- (instancetype)initWithRange:(NSRange)range language:(NSString *)language; + +@end diff --git a/LegacyComponents/TGMessageEntityPre.m b/LegacyComponents/TGMessageEntityPre.m new file mode 100644 index 0000000000..10efc49cee --- /dev/null +++ b/LegacyComponents/TGMessageEntityPre.m @@ -0,0 +1,41 @@ +#import "TGMessageEntityPre.h" + +#import "PSKeyValueCoder.h" + +@implementation TGMessageEntityPre + +- (instancetype)initWithRange:(NSRange)range language:(NSString *)language { + self = [super initWithRange:range]; + if (self != nil) { + _language = language; + } + return self; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder { + self = [super initWithKeyValueCoder:coder]; + if (self != nil) { + _language = [coder decodeStringForCKey:"language"]; + } + return self; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder { + [super encodeWithKeyValueCoder:coder]; + [coder encodeString:_language forCKey:"language"]; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self != nil) { + _language = [aDecoder decodeObjectForKey:@"language"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [super encodeWithCoder:aCoder]; + [aCoder encodeObject:_language forKey:@"language"]; +} + +@end diff --git a/LegacyComponents/TGMessageEntityTextUrl.h b/LegacyComponents/TGMessageEntityTextUrl.h new file mode 100644 index 0000000000..929322ee98 --- /dev/null +++ b/LegacyComponents/TGMessageEntityTextUrl.h @@ -0,0 +1,9 @@ +#import + +@interface TGMessageEntityTextUrl : TGMessageEntity + +@property (nonatomic, strong, readonly) NSString *url; + +- (instancetype)initWithRange:(NSRange)range url:(NSString *)url; + +@end diff --git a/LegacyComponents/TGMessageEntityTextUrl.m b/LegacyComponents/TGMessageEntityTextUrl.m new file mode 100644 index 0000000000..cbd83756b5 --- /dev/null +++ b/LegacyComponents/TGMessageEntityTextUrl.m @@ -0,0 +1,46 @@ +#import "TGMessageEntityTextUrl.h" + +#import "PSKeyValueCoder.h" + +@implementation TGMessageEntityTextUrl + +- (instancetype)initWithRange:(NSRange)range url:(NSString *)url +{ + self = [super initWithRange:range]; + if (self != nil) + { + _url = url; + } + return self; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + self = [super initWithKeyValueCoder:coder]; + if (self != nil) + { + _url = [coder decodeStringForCKey:"url"]; + } + return self; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + [super encodeWithKeyValueCoder:coder]; + [coder encodeString:_url forCKey:"url"]; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super initWithCoder:aDecoder]; + if (self != nil) { + _url = [aDecoder decodeObjectForKey:@"url"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [super encodeWithCoder:aCoder]; + [aCoder encodeObject:_url forKey:@"url"]; +} + +@end diff --git a/LegacyComponents/TGMessageEntityUrl.h b/LegacyComponents/TGMessageEntityUrl.h new file mode 100644 index 0000000000..90359d71a4 --- /dev/null +++ b/LegacyComponents/TGMessageEntityUrl.h @@ -0,0 +1,7 @@ +#import + +#import + +@interface TGMessageEntityUrl : TGMessageEntity + +@end diff --git a/LegacyComponents/TGMessageEntityUrl.m b/LegacyComponents/TGMessageEntityUrl.m new file mode 100644 index 0000000000..d2fc4d3b2a --- /dev/null +++ b/LegacyComponents/TGMessageEntityUrl.m @@ -0,0 +1,10 @@ +#import "TGMessageEntityUrl.h" + +@implementation TGMessageEntityUrl + +- (BOOL)isEqual:(id)object +{ + return [super isEqual:object] && [object isKindOfClass:[TGMessageEntityUrl class]]; +} + +@end diff --git a/LegacyComponents/TGMessageGroup.h b/LegacyComponents/TGMessageGroup.h new file mode 100644 index 0000000000..2b15197c8e --- /dev/null +++ b/LegacyComponents/TGMessageGroup.h @@ -0,0 +1,13 @@ +#import + +@interface TGMessageGroup : NSObject + +@property (nonatomic, readonly) int32_t minId; +@property (nonatomic, readonly) int32_t minTimestamp; +@property (nonatomic, readonly) int32_t maxId; +@property (nonatomic, readonly) int32_t maxTimestamp; +@property (nonatomic, readonly) int32_t count; + +- (instancetype)initWithMinId:(int32_t)minId minTimestamp:(int32_t)minTimestamp maxId:(int32_t)maxId maxTimestamp:(int32_t)maxTimestamp count:(int32_t)count; + +@end diff --git a/LegacyComponents/TGMessageGroup.m b/LegacyComponents/TGMessageGroup.m new file mode 100644 index 0000000000..1156fa551e --- /dev/null +++ b/LegacyComponents/TGMessageGroup.m @@ -0,0 +1,32 @@ +#import "TGMessageGroup.h" + +@implementation TGMessageGroup + +- (instancetype)initWithMinId:(int32_t)minId minTimestamp:(int32_t)minTimestamp maxId:(int32_t)maxId maxTimestamp:(int32_t)maxTimestamp count:(int32_t)count { + self = [super init]; + if (self != nil) { + _minId = minId; + _minTimestamp = minTimestamp; + _maxId = maxId; + _maxTimestamp = maxTimestamp; + _count = count; + } + return self; +} + +- (bool)isEqual:(id)object { + return [object isKindOfClass:[TGMessageGroup class]] && ((TGMessageGroup *)object)->_minId == _minId && ((TGMessageGroup *)object)->_maxId == _maxId && ((TGMessageGroup *)object)->_maxTimestamp == _maxTimestamp && ((TGMessageGroup *)object)->_count == _count; +} + +- (NSString *)description { + return [[NSString alloc] initWithFormat:@"(%d...%d at %d)", _minId, _maxId, _maxTimestamp]; +} + +- (bool)intersects:(TGMessageGroup *)other { + if (other == nil) + return false; + + return _minId <= other.maxId && _maxId >= other.minId; +} + +@end diff --git a/LegacyComponents/TGMessageHole.h b/LegacyComponents/TGMessageHole.h new file mode 100644 index 0000000000..50832e532c --- /dev/null +++ b/LegacyComponents/TGMessageHole.h @@ -0,0 +1,16 @@ +#import + +@interface TGMessageHole : NSObject + +@property (nonatomic, readonly) int32_t minId; +@property (nonatomic, readonly) int32_t minTimestamp; +@property (nonatomic, readonly) int32_t maxId; +@property (nonatomic, readonly) int32_t maxTimestamp; + +- (instancetype)initWithMinId:(int32_t)minId minTimestamp:(int32_t)minTimestamp maxId:(int32_t)maxId maxTimestamp:(int32_t)maxTimestamp; + +- (bool)intersects:(TGMessageHole *)other; +- (bool)covers:(TGMessageHole *)other; +- (NSArray *)exclude:(TGMessageHole *)other; + +@end diff --git a/LegacyComponents/TGMessageHole.m b/LegacyComponents/TGMessageHole.m new file mode 100644 index 0000000000..1d7d36f68f --- /dev/null +++ b/LegacyComponents/TGMessageHole.m @@ -0,0 +1,51 @@ +#import "TGMessageHole.h" + +@implementation TGMessageHole + +- (instancetype)initWithMinId:(int32_t)minId minTimestamp:(int32_t)minTimestamp maxId:(int32_t)maxId maxTimestamp:(int32_t)maxTimestamp { + self = [super init]; + if (self != nil) { + _minId = minId; + _minTimestamp = minTimestamp; + _maxId = maxId; + _maxTimestamp = maxTimestamp; + } + return self; +} + +- (bool)isEqual:(id)object { + return [object isKindOfClass:[TGMessageHole class]] && ((TGMessageHole *)object)->_minId == _minId && ((TGMessageHole *)object)->_maxId == _maxId && ((TGMessageHole *)object)->_maxTimestamp == _maxTimestamp; +} + +- (NSString *)description { + return [[NSString alloc] initWithFormat:@"(%d...%d at %d)", _minId, _maxId, _maxTimestamp]; +} + +- (bool)intersects:(TGMessageHole *)other { + if (other == nil) + return false; + + return _minId <= other.maxId && _maxId >= other.minId; +} + +- (bool)covers:(TGMessageHole *)other { + return other.minId >= _minId && other.maxId <= _maxId; +} + +- (NSArray *)exclude:(TGMessageHole *)other { + NSMutableArray *result = [[NSMutableArray alloc] init]; + if (other.minId <= _minId) { + if (other.maxId < _maxId) { + [result addObject:[[TGMessageHole alloc] initWithMinId:other.maxId + 1 minTimestamp:other.maxTimestamp maxId:_maxId maxTimestamp:_maxTimestamp]]; + } + } else { + [result addObject:[[TGMessageHole alloc] initWithMinId:_minId minTimestamp:_minTimestamp maxId:other.minId - 1 maxTimestamp:other.minTimestamp]]; + + if (other.maxId < _maxId) { + [result addObject:[[TGMessageHole alloc] initWithMinId:other.maxId + 1 minTimestamp:other.maxTimestamp maxId:_maxId maxTimestamp:_maxTimestamp]]; + } + } + return result; +} + +@end diff --git a/LegacyComponents/TGMessageViewCountContentProperty.h b/LegacyComponents/TGMessageViewCountContentProperty.h new file mode 100644 index 0000000000..df759d75e4 --- /dev/null +++ b/LegacyComponents/TGMessageViewCountContentProperty.h @@ -0,0 +1,10 @@ +#import +#import + +@interface TGMessageViewCountContentProperty : NSObject + +@property (nonatomic, readonly) int32_t viewCount; + +- (instancetype)initWithViewCount:(int32_t)viewCount; + +@end diff --git a/LegacyComponents/TGMessageViewCountContentProperty.m b/LegacyComponents/TGMessageViewCountContentProperty.m new file mode 100644 index 0000000000..11f16c7d1a --- /dev/null +++ b/LegacyComponents/TGMessageViewCountContentProperty.m @@ -0,0 +1,23 @@ +#import "TGMessageViewCountContentProperty.h" + +#import "PSKeyValueCoder.h" + +@implementation TGMessageViewCountContentProperty + +- (instancetype)initWithViewCount:(int32_t)viewCount { + self = [super init]; + if (self != nil) { + _viewCount = viewCount; + } + return self; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder { + return [self initWithViewCount:[coder decodeInt32ForCKey:"vc"]]; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder { + [coder encodeInt32:_viewCount forCKey:"vc"]; +} + +@end diff --git a/LegacyComponents/TGPeerIdAdapter.h b/LegacyComponents/TGPeerIdAdapter.h new file mode 100644 index 0000000000..c5f0ac92fc --- /dev/null +++ b/LegacyComponents/TGPeerIdAdapter.h @@ -0,0 +1,52 @@ +#ifndef Telegraph_TGPeerIdAdapter_h +#define Telegraph_TGPeerIdAdapter_h + +static inline bool TGPeerIdIsGroup(int64_t peerId) { + return peerId < 0 && peerId > INT32_MIN; +} + +static inline bool TGPeerIdIsUser(int64_t peerId) { + return peerId > 0 && peerId < INT32_MAX; +} + +static inline bool TGPeerIdIsChannel(int64_t peerId) { + return peerId <= ((int64_t)INT32_MIN) * 2 && peerId > ((int64_t)INT32_MIN) * 3; +} + +static inline bool TGPeerIdIsAdminLog(int64_t peerId) { + return peerId <= ((int64_t)INT32_MIN) * 3 && peerId > ((int64_t)INT32_MIN) * 4; +} + +static inline int32_t TGChannelIdFromPeerId(int64_t peerId) { + if (TGPeerIdIsChannel(peerId)) { + return (int32_t)(((int64_t)INT32_MIN) * 2 - peerId); + } else { + return 0; + } +} + +static inline int64_t TGPeerIdFromChannelId(int32_t channelId) { + return ((int64_t)INT32_MIN) * 2 - ((int64_t)channelId); +} + +static inline int64_t TGPeerIdFromAdminLogId(int32_t channelId) { + return ((int64_t)INT32_MIN) * 3 - ((int64_t)channelId); +} + +static inline int64_t TGPeerIdFromGroupId(int32_t groupId) { + return -groupId; +} + +static inline int32_t TGGroupIdFromPeerId(int64_t peerId) { + if (TGPeerIdIsGroup(peerId)) { + return (int32_t)-peerId; + } else { + return 0; + } +} + +static inline bool TGPeerIdIsSecretChat(int64_t peerId) { + return peerId <= ((int64_t)INT32_MIN) && peerId > ((int64_t)INT32_MIN) * 2; +} + +#endif diff --git a/LegacyComponents/TGPhoneUtils.h b/LegacyComponents/TGPhoneUtils.h new file mode 100644 index 0000000000..6516569b1a --- /dev/null +++ b/LegacyComponents/TGPhoneUtils.h @@ -0,0 +1,21 @@ +/* + * This is the source code of Telegram for iOS v. 1.1 + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Peter Iakovlev, 2013. + */ + +#import + +@interface TGPhoneUtils : NSObject + ++ (NSString *)formatPhone:(NSString *)phone forceInternational:(bool)forceInternational; ++ (NSString *)formatPhoneUrl:(NSString *)phone; + ++ (NSString *)cleanPhone:(NSString *)phone; ++ (NSString *)cleanInternationalPhone:(NSString *)phone forceInternational:(bool)forceInternational; + ++ (bool)maybePhone:(NSString *)phone; + +@end diff --git a/LegacyComponents/TGPhoneUtils.m b/LegacyComponents/TGPhoneUtils.m new file mode 100644 index 0000000000..a82c71f000 --- /dev/null +++ b/LegacyComponents/TGPhoneUtils.m @@ -0,0 +1,101 @@ +#import "TGPhoneUtils.h" + +#import "RMPhoneFormat.h" + +@implementation TGPhoneUtils + ++ (NSString *)formatPhone:(NSString *)phone forceInternational:(bool)forceInternational +{ + if (phone == nil) + return @""; + + return [[RMPhoneFormat instance] format:phone implicitPlus:forceInternational]; +} + ++ (NSString *)formatPhoneUrl:(NSString *)phone +{ + if (phone == nil) + return @""; + + unichar cleanPhone[phone.length]; + int cleanPhoneLength = 0; + + int length = (int)phone.length; + for (int i = 0; i < length; i++) + { + unichar c = [phone characterAtIndex:i]; + if (!(c == ' ' || c == '(' || c == ')' || c == '-')) + cleanPhone[cleanPhoneLength++] = c; + } + + return [[NSString alloc] initWithCharacters:cleanPhone length:cleanPhoneLength]; +} + ++ (NSString *)cleanPhone:(NSString *)phone +{ + if (phone.length == 0) + return @""; + + char buf[phone.length]; + int bufPtr = 0; + + int length = (int)phone.length; + for (int i = 0; i < length; i++) + { + unichar c = [phone characterAtIndex:i]; + if (c >= '0' && c <= '9') + { + buf[bufPtr++] = (char)c; + } + } + + return [[NSString alloc] initWithBytes:buf length:bufPtr encoding:NSUTF8StringEncoding]; +} + ++ (NSString *)cleanInternationalPhone:(NSString *)phone forceInternational:(bool)forceInternational +{ + if (phone.length == 0) + return @""; + + char buf[phone.length]; + int bufPtr = 0; + + bool hadPlus = false; + int length = (int)phone.length; + for (int i = 0; i < length; i++) + { + unichar c = [phone characterAtIndex:i]; + if ((c >= '0' && c <= '9') || (c == '+' && !hadPlus)) + { + buf[bufPtr++] = (char)c; + if (c == '+') + hadPlus = true; + } + } + + NSString *result = [[NSString alloc] initWithBytes:buf length:bufPtr encoding:NSUTF8StringEncoding]; + if (forceInternational && bufPtr != 0 && buf[0] != '+') + result = [[NSString alloc] initWithFormat:@"+%@", result]; + return result; +} + ++ (bool)maybePhone:(NSString *)phone +{ + if (phone.length < 2) + return false; + + bool hasDigits = false; + for (int i = 0; i < (int)phone.length; i++) + { + unichar c = [phone characterAtIndex:i]; + if (c >= '0' && c <= '9') + hasDigits = true; + + if (!((c >= '0' && c <= '9') || c == '(' || c == ')' || c == '+' || c == '-' || c == ' ')) + return false; + } + + return hasDigits; +} + +@end diff --git a/LegacyComponents/TGPluralization.h b/LegacyComponents/TGPluralization.h new file mode 100644 index 0000000000..63bbaddee6 --- /dev/null +++ b/LegacyComponents/TGPluralization.h @@ -0,0 +1,20 @@ +#import + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + TGPluralFormZero, + TGPluralFormOne, + TGPluralFormTwo, + TGPluralFormFew, + TGPluralFormMany, + TGPluralFormOther +} TGPluralFormValue; + +TGPluralFormValue TGPluralForm(unsigned int, int n); + +#ifdef __cplusplus +} +#endif diff --git a/LegacyComponents/TGPluralization.m b/LegacyComponents/TGPluralization.m new file mode 100644 index 0000000000..4dc05aec79 --- /dev/null +++ b/LegacyComponents/TGPluralization.m @@ -0,0 +1,346 @@ +#import "TGPluralization.h" + +TGPluralFormValue TGPluralForm(unsigned int lc, int n) { + switch (lc) { + + // set1 + case 0x6c74: // lt + if (((n % 10) == 1) && (((n % 100) < 11 || (n % 100) > 19))) // n mod 10 is 1 and n mod 100 not in 11..19 + return TGPluralFormOne; + if ((((n % 10) >= 2 && (n % 10) <= 9)) && (((n % 100) < 11 || (n % 100) > 19))) // n mod 10 in 2..9 and n mod 100 not in 11..19 + return TGPluralFormFew; + break; + + // set2 + case 0x6c76: // lv + if (n == 0) // n is 0 + return TGPluralFormZero; + if (((n % 10) == 1) && ((n % 100) != 11)) // n mod 10 is 1 and n mod 100 is not 11 + return TGPluralFormOne; + break; + + // set3 + case 0x6379: // cy + if (n == 2) // n is 2 + return TGPluralFormTwo; + if (n == 3) // n is 3 + return TGPluralFormFew; + if (n == 0) // n is 0 + return TGPluralFormZero; + if (n == 1) // n is 1 + return TGPluralFormOne; + if (n == 6) // n is 6 + return TGPluralFormMany; + break; + + // set4 + case 0x6265: // be + case 0x6273: // bs + case 0x6872: // hr + case 0x7275: // ru + case 0x7368: // sh + case 0x7372: // sr + case 0x756b: // uk + if (((n % 10) == 1) && ((n % 100) != 11)) // n mod 10 is 1 and n mod 100 is not 11 + return TGPluralFormOne; + if ((((n % 10) >= 2 && (n % 10) <= 4)) && (((n % 100) < 12 || (n % 100) > 14))) // n mod 10 in 2..4 and n mod 100 not in 12..14 + return TGPluralFormFew; + if (((n % 10) == 0) || (((n % 10) >= 5 && (n % 10) <= 9)) || (((n % 100) >= 11 && (n % 100) <= 14))) // n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14 + return TGPluralFormMany; + break; + + // set5 + case 0x6b7368: // ksh + if (n == 0) // n is 0 + return TGPluralFormZero; + if (n == 1) // n is 1 + return TGPluralFormOne; + break; + + // set6 + case 0x736869: // shi + if ((n >= 2 && n <= 10)) // n in 2..10 + return TGPluralFormFew; + if ((n >= 0 && n <= 1)) // n within 0..1 + return TGPluralFormOne; + break; + + // set7 + case 0x6865: // he + if (n == 2) // n is 2 + return TGPluralFormTwo; + if (n == 1) // n is 1 + return TGPluralFormOne; + if ((n != 0) && ((n % 10) == 0)) // n is not 0 AND n mod 10 is 0 + return TGPluralFormMany; + break; + + // set8 + case 0x6373: // cs + case 0x736b: // sk + if (n == 1) // n is 1 + return TGPluralFormOne; + if ((n >= 2 && n <= 4)) // n in 2..4 + return TGPluralFormFew; + break; + + // set9 + case 0x6272: // br + if ((n != 0) && ((n % 1000000) == 0)) // n is not 0 and n mod 1000000 is 0 + return TGPluralFormMany; + if (((n % 10) == 1) && (((n % 100) != 11) && ((n % 100) != 71) && ((n % 100) != 91))) // n mod 10 is 1 and n mod 100 not in 11,71,91 + return TGPluralFormOne; + if (((n % 10) == 2) && (((n % 100) != 12) && ((n % 100) != 72) && ((n % 100) != 92))) // n mod 10 is 2 and n mod 100 not in 12,72,92 + return TGPluralFormTwo; + if ((((n % 10) >= 3 && (n % 10) <= 4) || ((n % 10) == 9)) && (((n % 100) < 10 || (n % 100) > 19) && ((n % 100) < 70 || (n % 100) > 79) && ((n % 100) < 90 || (n % 100) > 99))) // n mod 10 in 3..4,9 and n mod 100 not in 10..19,70..79,90..99 + return TGPluralFormFew; + break; + + // set10 + case 0x736c: // sl + if ((n % 100) == 2) // n mod 100 is 2 + return TGPluralFormTwo; + if ((n % 100) == 1) // n mod 100 is 1 + return TGPluralFormOne; + if (((n % 100) >= 3 && (n % 100) <= 4)) // n mod 100 in 3..4 + return TGPluralFormFew; + break; + + // set11 + case 0x6c6167: // lag + if (n == 0) // n is 0 + return TGPluralFormZero; + if (((n >= 0 && n <= 2)) && (n != 0) && (n != 2)) // n within 0..2 and n is not 0 and n is not 2 + return TGPluralFormOne; + break; + + // set12 + case 0x706c: // pl + if (n == 1) // n is 1 + return TGPluralFormOne; + if ((((n % 10) >= 2 && (n % 10) <= 4)) && (((n % 100) < 12 || (n % 100) > 14))) // n mod 10 in 2..4 and n mod 100 not in 12..14 + return TGPluralFormFew; + if (((n != 1) && (((n % 10) >= 0 && (n % 10) <= 1))) || (((n % 10) >= 5 && (n % 10) <= 9)) || (((n % 100) >= 12 && (n % 100) <= 14))) // n is not 1 and n mod 10 in 0..1 or n mod 10 in 5..9 or n mod 100 in 12..14 + return TGPluralFormMany; + break; + + // set13 + case 0x6764: // gd + if ((n == 2) || (n == 12)) // n in 2,12 + return TGPluralFormTwo; + if ((n == 1) || (n == 11)) // n in 1,11 + return TGPluralFormOne; + if ((n >= 3 && n <= 10) || (n >= 13 && n <= 19)) // n in 3..10,13..19 + return TGPluralFormFew; + break; + + // set14 + case 0x6776: // gv + if ((((n % 10) >= 1 && (n % 10) <= 2)) || ((n % 20) == 0)) // n mod 10 in 1..2 or n mod 20 is 0 + return TGPluralFormOne; + break; + + // set15 + case 0x6d6b: // mk + if (((n % 10) == 1) && (n != 11)) // n mod 10 is 1 and n is not 11 + return TGPluralFormOne; + break; + + // set16 + case 0x6d74: // mt + if (n == 1) // n is 1 + return TGPluralFormOne; + if (((n % 100) >= 11 && (n % 100) <= 19)) // n mod 100 in 11..19 + return TGPluralFormMany; + if ((n == 0) || (((n % 100) >= 2 && (n % 100) <= 10))) // n is 0 or n mod 100 in 2..10 + return TGPluralFormFew; + break; + + // set17 + case 0x6d6f: // mo + case 0x726f: // ro + if (n == 1) // n is 1 + return TGPluralFormOne; + if ((n == 0) || ((n != 1) && (((n % 100) >= 1 && (n % 100) <= 19)))) // n is 0 OR n is not 1 AND n mod 100 in 1..19 + return TGPluralFormFew; + break; + + // set18 + case 0x6761: // ga + if (n == 2) // n is 2 + return TGPluralFormTwo; + if (n == 1) // n is 1 + return TGPluralFormOne; + if ((n >= 3 && n <= 6)) // n in 3..6 + return TGPluralFormFew; + if ((n >= 7 && n <= 10)) // n in 7..10 + return TGPluralFormMany; + break; + + // set19 + case 0x6666: // ff + case 0x6672: // fr + case 0x6b6162: // kab + if (((n >= 0 && n <= 2)) && (n != 2)) // n within 0..2 and n is not 2 + return TGPluralFormOne; + break; + + // set20 + case 0x6975: // iu + case 0x6b77: // kw + case 0x7365: // se + case 0x6e6171: // naq + case 0x736d61: // sma + case 0x736d69: // smi + case 0x736d6a: // smj + case 0x736d6e: // smn + case 0x736d73: // sms + if (n == 2) // n is 2 + return TGPluralFormTwo; + if (n == 1) // n is 1 + return TGPluralFormOne; + break; + + // set21 + case 0x616b: // ak + case 0x616d: // am + case 0x6268: // bh + case 0x6869: // hi + case 0x6c6e: // ln + case 0x6d67: // mg + case 0x7469: // ti + case 0x746c: // tl + case 0x7761: // wa + case 0x66696c: // fil + case 0x677577: // guw + case 0x6e736f: // nso + if ((n >= 0 && n <= 1)) // n in 0..1 + return TGPluralFormOne; + break; + + // set22 + case 0x747a6d: // tzm + if (((n >= 0 && n <= 1)) || ((n >= 11 && n <= 99))) // n in 0..1 or n in 11..99 + return TGPluralFormOne; + break; + + // set23 + case 0x6166: // af + case 0x6267: // bg + case 0x626e: // bn + case 0x6361: // ca + case 0x6461: // da + case 0x6465: // de + case 0x6476: // dv + case 0x6565: // ee + case 0x656c: // el + case 0x656e: // en + case 0x656f: // eo + case 0x6573: // es + case 0x6574: // et + case 0x6575: // eu + case 0x6669: // fi + case 0x666f: // fo + case 0x6679: // fy + case 0x676c: // gl + case 0x6775: // gu + case 0x6861: // ha + case 0x6973: // is + case 0x6974: // it + case 0x6b6b: // kk + case 0x6b6c: // kl + case 0x6b73: // ks + case 0x6b75: // ku + case 0x6b79: // ky + case 0x6c62: // lb + case 0x6c67: // lg + case 0x6d6c: // ml + case 0x6d6e: // mn + case 0x6d72: // mr + case 0x6e62: // nb + case 0x6e64: // nd + case 0x6e65: // ne + case 0x6e6c: // nl + case 0x6e6e: // nn + case 0x6e6f: // no + case 0x6e72: // nr + case 0x6e79: // ny + case 0x6f6d: // om + case 0x6f72: // or + case 0x6f73: // os + case 0x7061: // pa + case 0x7073: // ps + case 0x7074: // pt + case 0x726d: // rm + case 0x736e: // sn + case 0x736f: // so + case 0x7371: // sq + case 0x7373: // ss + case 0x7374: // st + case 0x7376: // sv + case 0x7377: // sw + case 0x7461: // ta + case 0x7465: // te + case 0x746b: // tk + case 0x746e: // tn + case 0x7473: // ts + case 0x7572: // ur + case 0x7665: // ve + case 0x766f: // vo + case 0x7868: // xh + case 0x7a75: // zu + case 0x617361: // asa + case 0x617374: // ast + case 0x62656d: // bem + case 0x62657a: // bez + case 0x627278: // brx + case 0x636767: // cgg + case 0x636872: // chr + case 0x636b62: // ckb + case 0x667572: // fur + case 0x677377: // gsw + case 0x686177: // haw + case 0x6a676f: // jgo + case 0x6a6d63: // jmc + case 0x6b616a: // kaj + case 0x6b6367: // kcg + case 0x6b6b6a: // kkj + case 0x6b7362: // ksb + case 0x6d6173: // mas + case 0x6d676f: // mgo + case 0x6e6168: // nah + case 0x6e6e68: // nnh + case 0x6e796e: // nyn + case 0x706170: // pap + case 0x726f66: // rof + case 0x72776b: // rwk + case 0x736171: // saq + case 0x736568: // seh + case 0x737379: // ssy + case 0x737972: // syr + case 0x74656f: // teo + case 0x746967: // tig + case 0x76756e: // vun + case 0x776165: // wae + case 0x786f67: // xog + if (n == 1) // n is 1 + return TGPluralFormOne; + break; + + // set24 + case 0x6172: // ar + if (n == 2) // n is 2 + return TGPluralFormTwo; + if (n == 1) // n is 1 + return TGPluralFormOne; + if (n == 0) // n is 0 + return TGPluralFormZero; + if (((n % 100) >= 3 && (n % 100) <= 10)) // n mod 100 in 3..10 + return TGPluralFormFew; + if (((n % 100) >= 11 && (n % 100) <= 99)) // n mod 100 in 11..99 + return TGPluralFormMany; + break; + } + + return TGPluralFormOther; +} diff --git a/LegacyComponents/TGReplyMarkupAttachment.h b/LegacyComponents/TGReplyMarkupAttachment.h new file mode 100644 index 0000000000..e9834239b2 --- /dev/null +++ b/LegacyComponents/TGReplyMarkupAttachment.h @@ -0,0 +1,13 @@ +#import + +#import + +#define TGReplyMarkupAttachmentType ((int)0x5678acc1) + +@interface TGReplyMarkupAttachment : TGMediaAttachment + +@property (nonatomic, strong) TGBotReplyMarkup *replyMarkup; + +- (instancetype)initWithReplyMarkup:(TGBotReplyMarkup *)replyMarkup; + +@end diff --git a/LegacyComponents/TGReplyMarkupAttachment.m b/LegacyComponents/TGReplyMarkupAttachment.m new file mode 100644 index 0000000000..77498f46da --- /dev/null +++ b/LegacyComponents/TGReplyMarkupAttachment.m @@ -0,0 +1,72 @@ +#import "TGReplyMarkupAttachment.h" + +#import "LegacyComponentsInternal.h" + +#import "PSKeyValueEncoder.h" +#import "PSKeyValueDecoder.h" + +#import "NSInputStream+TL.h" + +@implementation TGReplyMarkupAttachment + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + self.type = TGReplyMarkupAttachmentType; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + self.type = TGReplyMarkupAttachmentType; + + NSData *replyMarkupData = [aDecoder decodeObjectForKey:@"replyMarkupData"]; + _replyMarkup = [[TGBotReplyMarkup alloc] initWithKeyValueCoder:[[PSKeyValueDecoder alloc] initWithData:replyMarkupData]]; + } + return self; +} + +- (instancetype)initWithReplyMarkup:(TGBotReplyMarkup *)replyMarkup { + self = [super init]; + if (self != nil) + { + _replyMarkup = replyMarkup; + self.type = TGReplyMarkupAttachmentType; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + PSKeyValueEncoder *encoder = [[PSKeyValueEncoder alloc] init]; + [_replyMarkup encodeWithKeyValueCoder:encoder]; + [aCoder encodeObject:[encoder data] forKey:@"replyMarkupData"]; +} + +- (BOOL)isEqual:(id)object +{ + return [object isKindOfClass:[TGReplyMarkupAttachment class]] && TGObjectCompare(((TGReplyMarkupAttachment *)object)->_replyMarkup, _replyMarkup); +} + +- (void)serialize:(NSMutableData *)data +{ + NSData *serializedData = [NSKeyedArchiver archivedDataWithRootObject:self]; + int32_t length = (int32_t)serializedData.length; + [data appendBytes:&length length:4]; + [data appendData:serializedData]; +} + +- (TGMediaAttachment *)parseMediaAttachment:(NSInputStream *)is +{ + int32_t length = [is readInt32]; + NSData *data = [is readData:length]; + return [NSKeyedUnarchiver unarchiveObjectWithData:data]; +} + +@end diff --git a/LegacyComponents/TGReplyMessageMediaAttachment.h b/LegacyComponents/TGReplyMessageMediaAttachment.h new file mode 100644 index 0000000000..3e27e49bee --- /dev/null +++ b/LegacyComponents/TGReplyMessageMediaAttachment.h @@ -0,0 +1,12 @@ +#import + +@class TGMessage; + +#define TGReplyMessageMediaAttachmentType ((int)414002169) + +@interface TGReplyMessageMediaAttachment : TGMediaAttachment + +@property (nonatomic) int32_t replyMessageId; +@property (nonatomic, strong) TGMessage *replyMessage; + +@end diff --git a/LegacyComponents/TGReplyMessageMediaAttachment.m b/LegacyComponents/TGReplyMessageMediaAttachment.m new file mode 100644 index 0000000000..dffa6772ba --- /dev/null +++ b/LegacyComponents/TGReplyMessageMediaAttachment.m @@ -0,0 +1,66 @@ +#import "TGReplyMessageMediaAttachment.h" + +#import "TGMessage.h" + +#import "PSKeyValueDecoder.h" +#import "PSKeyValueEncoder.h" + +@implementation TGReplyMessageMediaAttachment + +- (id)init +{ + self = [super init]; + if (self != nil) + { + self.type = TGReplyMessageMediaAttachmentType; + } + return self; +} + +- (id)copyWithZone:(NSZone *)__unused zone +{ + TGReplyMessageMediaAttachment *attachment = [[TGReplyMessageMediaAttachment alloc] init]; + + attachment->_replyMessageId = _replyMessageId; + attachment->_replyMessage = [_replyMessage copy]; + + return attachment; +} + +- (void)serialize:(NSMutableData *)data +{ + int dataLengthPtr = (int)data.length; + int zero = 0; + [data appendBytes:&zero length:4]; + + PSKeyValueEncoder *encoder = [[PSKeyValueEncoder alloc] init]; + [encoder encodeObject:_replyMessage forCKey:"replyMessage"]; + [encoder encodeInt32:_replyMessageId forCKey:"replyMessageId"]; + NSData *replyMessageData = [encoder data]; + [data appendData:replyMessageData]; + + int dataLength = (int)(data.length - dataLengthPtr - 4); + [data replaceBytesInRange:NSMakeRange(dataLengthPtr, 4) withBytes:&dataLength]; +} + +- (TGMediaAttachment *)parseMediaAttachment:(NSInputStream *)is +{ + int dataLength = 0; + [is read:(uint8_t *)&dataLength maxLength:4]; + + TGReplyMessageMediaAttachment *messageAttachment = [[TGReplyMessageMediaAttachment alloc] init]; + + uint8_t *replyMessageBytes = malloc(dataLength); + [is read:replyMessageBytes maxLength:dataLength]; + + NSData *replyMessageData = [NSData dataWithBytesNoCopy:replyMessageBytes length:dataLength freeWhenDone:true]; + PSKeyValueDecoder *decoder = [[PSKeyValueDecoder alloc] initWithData:replyMessageData]; + TGMessage *replyMessage = (TGMessage *)[decoder decodeObjectForCKey:"replyMessage"]; + int32_t replyMessageId = [decoder decodeInt32ForCKey:"replyMessageId"]; + messageAttachment.replyMessage = replyMessage; + messageAttachment.replyMessageId = replyMessageId; + + return messageAttachment; +} + +@end diff --git a/LegacyComponents/TGStickerPackReference.h b/LegacyComponents/TGStickerPackReference.h new file mode 100644 index 0000000000..4e2aaa3cce --- /dev/null +++ b/LegacyComponents/TGStickerPackReference.h @@ -0,0 +1,29 @@ +#import + +#import + +@protocol TGStickerPackReference + +@end + +@interface TGStickerPackBuiltinReference : NSObject + +@end + +@interface TGStickerPackIdReference : NSObject + +@property (nonatomic, readonly) int64_t packId; +@property (nonatomic, readonly) int64_t packAccessHash; +@property (nonatomic, strong, readonly) NSString *shortName; + +- (instancetype)initWithPackId:(int64_t)packId packAccessHash:(int64_t)packAccessHash shortName:(NSString *)shortName; + +@end + +@interface TGStickerPackShortnameReference : NSObject + +@property (nonatomic, strong, readonly) NSString *shortName; + +- (instancetype)initWithShortName:(NSString *)shortName; + +@end diff --git a/LegacyComponents/TGStickerPackReference.m b/LegacyComponents/TGStickerPackReference.m new file mode 100644 index 0000000000..14e41d61d3 --- /dev/null +++ b/LegacyComponents/TGStickerPackReference.m @@ -0,0 +1,145 @@ +#import "TGStickerPackReference.h" + +#import "LegacyComponentsInternal.h" + +#import "PSKeyValueCoder.h" + +@implementation TGStickerPackBuiltinReference + +- (instancetype)initWithCoder:(NSCoder *)__unused aDecoder +{ + return [self init]; +} + +- (instancetype)copyWithZone:(NSZone *)__unused zone { + return self; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)__unused coder +{ + return [self init]; +} + +- (void)encodeWithCoder:(NSCoder *)__unused aCoder +{ +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)__unused coder +{ +} + +- (BOOL)isEqual:(id)object +{ + return [object isKindOfClass:[TGStickerPackBuiltinReference class]]; +} + +- (NSUInteger)hash { + return 1; +} + +@end + +@implementation TGStickerPackIdReference + +- (instancetype)initWithPackId:(int64_t)packId packAccessHash:(int64_t)packAccessHash shortName:(NSString *)shortName +{ + self = [super init]; + if (self != nil) + { + _packId = packId; + _packAccessHash = packAccessHash; + _shortName = shortName; + } + return self; +} + +- (instancetype)copyWithZone:(NSZone *)__unused zone { + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + return [self initWithPackId:[aDecoder decodeInt64ForKey:@"packId"] packAccessHash:[aDecoder decodeInt64ForKey:@"packAccessHash"] shortName:[aDecoder decodeObjectForKey:@"shortName"]]; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + return [self initWithPackId:[coder decodeInt64ForCKey:"i"] packAccessHash:[coder decodeInt64ForCKey:"a"] shortName:[coder decodeStringForCKey:"s"]]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:_packId forKey:@"packId"]; + [aCoder encodeInt64:_packAccessHash forKey:@"packAccessHash"]; + if (_shortName != nil) + [aCoder encodeObject:_shortName forKey:@"shortName"]; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + [coder encodeInt64:_packId forCKey:"i"]; + [coder encodeInt64:_packAccessHash forCKey:"a"]; + [coder encodeString:_shortName forCKey:"s"]; +} + +- (BOOL)isEqual:(id)object +{ + return [object isKindOfClass:[TGStickerPackIdReference class]] && ((TGStickerPackIdReference *)object)->_packId == _packId && ((TGStickerPackIdReference *)object)->_packAccessHash == _packAccessHash; +} + +- (NSString *)description { + return [[NSString alloc] initWithFormat:@"(TGStickerPackIdReference packId: %lld, %lld, %@)", _packId, _packAccessHash, _shortName]; +} + +- (NSUInteger)hash { + return (NSUInteger)_packId; +} + +@end + +@implementation TGStickerPackShortnameReference + +- (instancetype)initWithShortName:(NSString *)shortName +{ + self = [super init]; + if (self != nil) + { + _shortName = shortName; + } + return self; +} + +- (instancetype)copyWithZone:(NSZone *)__unused zone { + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + return [self initWithShortName:[aDecoder decodeObjectForKey:@"shortName"]]; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + return [self initWithShortName:[coder decodeStringForCKey:"s"]]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeObject:_shortName forKey:@"shortName"]; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + [coder encodeString:_shortName forCKey:"s"]; +} + +- (BOOL)isEqual:(id)object +{ + return [object isKindOfClass:[TGStickerPackShortnameReference class]] && TGStringCompare(((TGStickerPackShortnameReference *)object)->_shortName, _shortName); +} + +- (NSUInteger)hash { + return 2; +} + +@end diff --git a/LegacyComponents/TGStringUtils.h b/LegacyComponents/TGStringUtils.h new file mode 100644 index 0000000000..2424f8c5ac --- /dev/null +++ b/LegacyComponents/TGStringUtils.h @@ -0,0 +1,83 @@ +#import +#import + +#ifdef __cplusplus +extern "C" { +#endif + +int32_t murMurHash32(NSString *string); +int32_t murMurHashBytes32(void *bytes, int length); +int32_t phoneMatchHash(NSString *phone); + +bool TGIsRTL(); +bool TGIsArabic(); +bool TGIsKorean(); +bool TGIsLocaleArabic(); + +#ifdef __cplusplus +} +#endif + +@interface TGStringUtils : NSObject + ++ (NSString *)stringByEscapingForURL:(NSString *)string; ++ (NSString *)stringByEscapingForActorURL:(NSString *)string; ++ (NSString *)stringByEncodingInBase64:(NSData *)data; ++ (NSString *)stringByUnescapingFromHTML:(NSString *)srcString; + ++ (NSString *)stringWithLocalizedNumber:(NSInteger)number; ++ (NSString *)stringWithLocalizedNumberCharacters:(NSString *)string; + ++ (NSString *)md5:(NSString *)string; ++ (NSString *)md5ForData:(NSData *)data; + ++ (NSDictionary *)argumentDictionaryInUrlString:(NSString *)string; + ++ (bool)stringContainsEmoji:(NSString *)string; ++ (bool)stringContainsEmojiOnly:(NSString *)string length:(NSUInteger *)length; + ++ (NSString *)stringForMessageTimerSeconds:(NSUInteger)seconds; ++ (NSString *)stringForShortMessageTimerSeconds:(NSUInteger)seconds; ++ (NSArray *)stringComponentsForMessageTimerSeconds:(NSUInteger)seconds; ++ (NSString *)stringForCallDurationSeconds:(NSUInteger)seconds; ++ (NSString *)stringForShortCallDurationSeconds:(NSUInteger)seconds; ++ (NSString *)stringForUserCount:(NSUInteger)userCount; ++ (NSString *)stringForFileSize:(int64_t)size; ++ (NSString *)stringForFileSize:(int64_t)size precision:(NSInteger)precision; + ++ (NSString *)integerValueFormat:(NSString *)prefix value:(NSInteger)value; ++ (NSString *)stringForMuteInterval:(int)value; ++ (NSString *)stringForRemainingMuteInterval:(int)value; + ++ (NSString *)stringForDeviceType; + ++ (NSString *)stringForCurrency:(NSString *)currency amount:(int64_t)amount; + ++ (NSString *)stringForEmojiHashOfData:(NSData *)data count:(NSInteger)count positionExtractor:(int32_t (^)(uint8_t *, int32_t, int32_t))positionExtractor; + +@end + +@interface NSString (Telegraph) + +- (int)lengthByComposedCharacterSequences; +- (int)lengthByComposedCharacterSequencesInRange:(NSRange)range; + +- (NSData *)dataByDecodingHexString; +- (NSArray *)getEmojiFromString:(BOOL)checkColor checkString:(__autoreleasing NSString **)checkString; + +- (bool)containsSingleEmoji; + +- (bool)hasNonWhitespaceCharacters; + +- (NSAttributedString *)attributedFormattedStringWithRegularFont:(UIFont *)regularFont boldFont:(UIFont *)boldFont lineSpacing:(CGFloat)lineSpacing paragraphSpacing:(CGFloat)paragraphSpacing alignment:(NSTextAlignment)alignment; + +- (NSString *)urlAnchorPart; + +@end + +@interface NSData (Telegraph) + +- (NSString *)stringByEncodingInHex; +- (NSString *)stringByEncodingInHexSeparatedByString:(NSString *)string; + +@end diff --git a/LegacyComponents/TGStringUtils.mm b/LegacyComponents/TGStringUtils.mm new file mode 100644 index 0000000000..24b0487f2c --- /dev/null +++ b/LegacyComponents/TGStringUtils.mm @@ -0,0 +1,1561 @@ +#import "TGStringUtils.h" + +#import "LegacyComponentsInternal.h" + +#import + +#import "TGLocalization.h" +#import "TGPluralization.h" + +typedef struct { + __unsafe_unretained NSString *escapeSequence; + unichar uchar; +} HTMLEscapeMap; + +// Taken from http://www.w3.org/TR/xhtml1/dtds.html#a_dtd_Special_characters +// Ordered by uchar lowest to highest for bsearching +static HTMLEscapeMap gAsciiHTMLEscapeMap[] = { + // A.2.2. Special characters + { @""", 34 }, + { @"&", 38 }, + { @"'", 39 }, + { @"<", 60 }, + { @">", 62 }, + + // A.2.1. Latin-1 characters + { @" ", 160 }, + { @"¡", 161 }, + { @"¢", 162 }, + { @"£", 163 }, + { @"¤", 164 }, + { @"¥", 165 }, + { @"¦", 166 }, + { @"§", 167 }, + { @"¨", 168 }, + { @"©", 169 }, + { @"ª", 170 }, + { @"«", 171 }, + { @"¬", 172 }, + { @"­", 173 }, + { @"®", 174 }, + { @"¯", 175 }, + { @"°", 176 }, + { @"±", 177 }, + { @"²", 178 }, + { @"³", 179 }, + { @"´", 180 }, + { @"µ", 181 }, + { @"¶", 182 }, + { @"·", 183 }, + { @"¸", 184 }, + { @"¹", 185 }, + { @"º", 186 }, + { @"»", 187 }, + { @"¼", 188 }, + { @"½", 189 }, + { @"¾", 190 }, + { @"¿", 191 }, + { @"À", 192 }, + { @"Á", 193 }, + { @"Â", 194 }, + { @"Ã", 195 }, + { @"Ä", 196 }, + { @"Å", 197 }, + { @"Æ", 198 }, + { @"Ç", 199 }, + { @"È", 200 }, + { @"É", 201 }, + { @"Ê", 202 }, + { @"Ë", 203 }, + { @"Ì", 204 }, + { @"Í", 205 }, + { @"Î", 206 }, + { @"Ï", 207 }, + { @"Ð", 208 }, + { @"Ñ", 209 }, + { @"Ò", 210 }, + { @"Ó", 211 }, + { @"Ô", 212 }, + { @"Õ", 213 }, + { @"Ö", 214 }, + { @"×", 215 }, + { @"Ø", 216 }, + { @"Ù", 217 }, + { @"Ú", 218 }, + { @"Û", 219 }, + { @"Ü", 220 }, + { @"Ý", 221 }, + { @"Þ", 222 }, + { @"ß", 223 }, + { @"à", 224 }, + { @"á", 225 }, + { @"â", 226 }, + { @"ã", 227 }, + { @"ä", 228 }, + { @"å", 229 }, + { @"æ", 230 }, + { @"ç", 231 }, + { @"è", 232 }, + { @"é", 233 }, + { @"ê", 234 }, + { @"ë", 235 }, + { @"ì", 236 }, + { @"í", 237 }, + { @"î", 238 }, + { @"ï", 239 }, + { @"ð", 240 }, + { @"ñ", 241 }, + { @"ò", 242 }, + { @"ó", 243 }, + { @"ô", 244 }, + { @"õ", 245 }, + { @"ö", 246 }, + { @"÷", 247 }, + { @"ø", 248 }, + { @"ù", 249 }, + { @"ú", 250 }, + { @"û", 251 }, + { @"ü", 252 }, + { @"ý", 253 }, + { @"þ", 254 }, + { @"ÿ", 255 }, + + // A.2.2. Special characters cont'd + { @"Œ", 338 }, + { @"œ", 339 }, + { @"Š", 352 }, + { @"š", 353 }, + { @"Ÿ", 376 }, + + // A.2.3. Symbols + { @"ƒ", 402 }, + + // A.2.2. Special characters cont'd + { @"ˆ", 710 }, + { @"˜", 732 }, + + // A.2.3. Symbols cont'd + { @"Α", 913 }, + { @"Β", 914 }, + { @"Γ", 915 }, + { @"Δ", 916 }, + { @"Ε", 917 }, + { @"Ζ", 918 }, + { @"Η", 919 }, + { @"Θ", 920 }, + { @"Ι", 921 }, + { @"Κ", 922 }, + { @"Λ", 923 }, + { @"Μ", 924 }, + { @"Ν", 925 }, + { @"Ξ", 926 }, + { @"Ο", 927 }, + { @"Π", 928 }, + { @"Ρ", 929 }, + { @"Σ", 931 }, + { @"Τ", 932 }, + { @"Υ", 933 }, + { @"Φ", 934 }, + { @"Χ", 935 }, + { @"Ψ", 936 }, + { @"Ω", 937 }, + { @"α", 945 }, + { @"β", 946 }, + { @"γ", 947 }, + { @"δ", 948 }, + { @"ε", 949 }, + { @"ζ", 950 }, + { @"η", 951 }, + { @"θ", 952 }, + { @"ι", 953 }, + { @"κ", 954 }, + { @"λ", 955 }, + { @"μ", 956 }, + { @"ν", 957 }, + { @"ξ", 958 }, + { @"ο", 959 }, + { @"π", 960 }, + { @"ρ", 961 }, + { @"ς", 962 }, + { @"σ", 963 }, + { @"τ", 964 }, + { @"υ", 965 }, + { @"φ", 966 }, + { @"χ", 967 }, + { @"ψ", 968 }, + { @"ω", 969 }, + { @"ϑ", 977 }, + { @"ϒ", 978 }, + { @"ϖ", 982 }, + + // A.2.2. Special characters cont'd + { @" ", 8194 }, + { @" ", 8195 }, + { @" ", 8201 }, + { @"‌", 8204 }, + { @"‍", 8205 }, + { @"‎", 8206 }, + { @"‏", 8207 }, + { @"–", 8211 }, + { @"—", 8212 }, + { @"‘", 8216 }, + { @"’", 8217 }, + { @"‚", 8218 }, + { @"“", 8220 }, + { @"”", 8221 }, + { @"„", 8222 }, + { @"†", 8224 }, + { @"‡", 8225 }, + // A.2.3. Symbols cont'd + { @"•", 8226 }, + { @"…", 8230 }, + + // A.2.2. Special characters cont'd + { @"‰", 8240 }, + + // A.2.3. Symbols cont'd + { @"′", 8242 }, + { @"″", 8243 }, + + // A.2.2. Special characters cont'd + { @"‹", 8249 }, + { @"›", 8250 }, + + // A.2.3. Symbols cont'd + { @"‾", 8254 }, + { @"⁄", 8260 }, + + // A.2.2. Special characters cont'd + { @"€", 8364 }, + + // A.2.3. Symbols cont'd + { @"ℑ", 8465 }, + { @"℘", 8472 }, + { @"ℜ", 8476 }, + { @"™", 8482 }, + { @"ℵ", 8501 }, + { @"←", 8592 }, + { @"↑", 8593 }, + { @"→", 8594 }, + { @"↓", 8595 }, + { @"↔", 8596 }, + { @"↵", 8629 }, + { @"⇐", 8656 }, + { @"⇑", 8657 }, + { @"⇒", 8658 }, + { @"⇓", 8659 }, + { @"⇔", 8660 }, + { @"∀", 8704 }, + { @"∂", 8706 }, + { @"∃", 8707 }, + { @"∅", 8709 }, + { @"∇", 8711 }, + { @"∈", 8712 }, + { @"∉", 8713 }, + { @"∋", 8715 }, + { @"∏", 8719 }, + { @"∑", 8721 }, + { @"−", 8722 }, + { @"∗", 8727 }, + { @"√", 8730 }, + { @"∝", 8733 }, + { @"∞", 8734 }, + { @"∠", 8736 }, + { @"∧", 8743 }, + { @"∨", 8744 }, + { @"∩", 8745 }, + { @"∪", 8746 }, + { @"∫", 8747 }, + { @"∴", 8756 }, + { @"∼", 8764 }, + { @"≅", 8773 }, + { @"≈", 8776 }, + { @"≠", 8800 }, + { @"≡", 8801 }, + { @"≤", 8804 }, + { @"≥", 8805 }, + { @"⊂", 8834 }, + { @"⊃", 8835 }, + { @"⊄", 8836 }, + { @"⊆", 8838 }, + { @"⊇", 8839 }, + { @"⊕", 8853 }, + { @"⊗", 8855 }, + { @"⊥", 8869 }, + { @"⋅", 8901 }, + { @"⌈", 8968 }, + { @"⌉", 8969 }, + { @"⌊", 8970 }, + { @"⌋", 8971 }, + { @"⟨", 9001 }, + { @"⟩", 9002 }, + { @"◊", 9674 }, + { @"♠", 9824 }, + { @"♣", 9827 }, + { @"♥", 9829 }, + { @"♦", 9830 } +}; + +@implementation TGStringUtils + ++ (void)reset +{ + +} + ++ (NSString *)stringByEscapingForURL:(NSString *)string +{ + static NSString * const kAFLegalCharactersToBeEscaped = @"?!@#$^&%*+=,.:;'\"`<>()[]{}/\\|~ "; + + NSString *unescapedString = [string stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + if (unescapedString == nil) + unescapedString = string; + + return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)unescapedString, NULL, (CFStringRef)kAFLegalCharactersToBeEscaped, CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding)); +} + ++ (NSString *)stringByEscapingForActorURL:(NSString *)string +{ + static NSString * const kAFLegalCharactersToBeEscaped = @"?!@#$^&%*+=,:;'\"`<>()[]{}/\\|~ "; + + NSString *unescapedString = [string stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + if (unescapedString == nil) + unescapedString = string; + + return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)unescapedString, NULL, (CFStringRef)kAFLegalCharactersToBeEscaped, CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding)); +} + ++ (NSString *)stringByEncodingInBase64:(NSData *)data +{ + NSUInteger length = [data length]; + NSMutableData *mutableData = [[NSMutableData alloc] initWithLength:((length + 2) / 3) * 4]; + + uint8_t *input = (uint8_t *)[data bytes]; + uint8_t *output = (uint8_t *)[mutableData mutableBytes]; + + for (NSUInteger i = 0; i < length; i += 3) + { + NSUInteger value = 0; + for (NSUInteger j = i; j < (i + 3); j++) + { + value <<= 8; + if (j < length) + { + value |= (0xFF & input[j]); + } + } + + static uint8_t const kAFBase64EncodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + NSUInteger idx = (i / 3) * 4; + output[idx + 0] = kAFBase64EncodingTable[(value >> 18) & 0x3F]; + output[idx + 1] = kAFBase64EncodingTable[(value >> 12) & 0x3F]; + output[idx + 2] = (i + 1) < length ? kAFBase64EncodingTable[(value >> 6) & 0x3F] : '='; + output[idx + 3] = (i + 2) < length ? kAFBase64EncodingTable[(value >> 0) & 0x3F] : '='; + } + + return [[NSString alloc] initWithData:mutableData encoding:NSASCIIStringEncoding]; +} + ++ (NSString *)stringByUnescapingFromHTML:(NSString *)srcString +{ + NSRange range = NSMakeRange(0, [srcString length]); + NSRange subrange = [srcString rangeOfString:@"&" options:NSBackwardsSearch range:range]; + NSRange tagSubrange = NSMakeRange(0, 0); + + if (subrange.length == 0) + { + tagSubrange = [srcString rangeOfString:@"<" options:NSBackwardsSearch range:range]; + if (tagSubrange.length == 0) + return srcString; + } + + NSMutableString *finalString = [NSMutableString stringWithString:srcString]; + if (subrange.length != 0) + { + do + { + NSRange semiColonRange = NSMakeRange(subrange.location, NSMaxRange(range) - subrange.location); + semiColonRange = [srcString rangeOfString:@";" options:0 range:semiColonRange]; + range = NSMakeRange(0, subrange.location); + // if we don't find a semicolon in the range, we don't have a sequence + if (semiColonRange.location == NSNotFound) + { + continue; + } + NSRange escapeRange = NSMakeRange(subrange.location, semiColonRange.location - subrange.location + 1); + NSString *escapeString = [srcString substringWithRange:escapeRange]; + NSUInteger length = [escapeString length]; + + // a squence must be longer than 3 (<) and less than 11 (ϑ) + if (length > 3 && length < 11) + { + if ([escapeString characterAtIndex:1] == '#') + { + unichar char2 = [escapeString characterAtIndex:2]; + if (char2 == 'x' || char2 == 'X') { + // Hex escape squences £ + NSString *hexSequence = [escapeString substringWithRange:NSMakeRange(3, length - 4)]; + NSScanner *scanner = [NSScanner scannerWithString:hexSequence]; + unsigned value; + if ([scanner scanHexInt:&value] && + value < USHRT_MAX && + value > 0 + && [scanner scanLocation] == length - 4) { + unichar uchar = (unichar)value; + NSString *charString = [NSString stringWithCharacters:&uchar length:1]; + [finalString replaceCharactersInRange:escapeRange withString:charString]; + } + + } + else + { + // Decimal Sequences { + NSString *numberSequence = [escapeString substringWithRange:NSMakeRange(2, length - 3)]; + NSScanner *scanner = [NSScanner scannerWithString:numberSequence]; + int value; + if ([scanner scanInt:&value] && + value < USHRT_MAX && + value > 0 + && [scanner scanLocation] == length - 3) + { + unichar uchar = (unichar)value; + NSString *charString = [NSString stringWithCharacters:&uchar length:1]; + [finalString replaceCharactersInRange:escapeRange withString:charString]; + } + } + } + else + { + for (unsigned i = 0; i < sizeof(gAsciiHTMLEscapeMap) / sizeof(HTMLEscapeMap); ++i) + { + if ([escapeString isEqualToString:gAsciiHTMLEscapeMap[i].escapeSequence]) + { + [finalString replaceCharactersInRange:escapeRange withString:[NSString stringWithCharacters:&gAsciiHTMLEscapeMap[i].uchar length:1]]; + break; + } + } + } + } + } while ((subrange = [srcString rangeOfString:@"&" options:NSBackwardsSearch range:range]).length != 0); + } + + [finalString replaceOccurrencesOfString:@"
" withString:@"\n" options:NSLiteralSearch range:NSMakeRange(0, finalString.length)]; + + return finalString; +} + ++ (NSString *)stringWithLocalizedNumber:(NSInteger)number +{ + return [self stringWithLocalizedNumberCharacters:[[NSString alloc] initWithFormat:@"%d", (int)number]]; +} + ++ (NSString *)stringWithLocalizedNumberCharacters:(NSString *)string +{ + NSString *resultString = string; + + if (TGIsArabic()) + { + static NSString *arabicNumbers = @"٠١٢٣٤٥٦٧٨٩"; + NSMutableString *mutableString = [[NSMutableString alloc] init]; + for (int i = 0; i < (int)string.length; i++) + { + unichar c = [string characterAtIndex:i]; + if (c >= '0' && c <= '9') + [mutableString replaceCharactersInRange:NSMakeRange(mutableString.length, 0) withString:[arabicNumbers substringWithRange:NSMakeRange(c - '0', 1)]]; + else + [mutableString replaceCharactersInRange:NSMakeRange(mutableString.length, 0) withString:[string substringWithRange:NSMakeRange(i, 1)]]; + } + resultString = mutableString; + } + + return resultString; +} + ++ (NSString *)md5:(NSString *)string +{ + /*static const char *md5PropertyKey = "MD5Key"; + NSString *result = objc_getAssociatedObject(string, md5PropertyKey); + if (result != nil) + return result;*/ + + const char *ptr = [string UTF8String]; + unsigned char md5Buffer[16]; + CC_MD5(ptr, (CC_LONG)[string lengthOfBytesUsingEncoding:NSUTF8StringEncoding], md5Buffer); + NSString *output = [[NSString alloc] initWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", md5Buffer[0], md5Buffer[1], md5Buffer[2], md5Buffer[3], md5Buffer[4], md5Buffer[5], md5Buffer[6], md5Buffer[7], md5Buffer[8], md5Buffer[9], md5Buffer[10], md5Buffer[11], md5Buffer[12], md5Buffer[13], md5Buffer[14], md5Buffer[15]]; + //objc_setAssociatedObject(string, md5PropertyKey, output, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + return output; +} + ++ (NSString *)md5ForData:(NSData *)data { + unsigned char md5Buffer[16]; + CC_MD5(data.bytes, (CC_LONG)data.length, md5Buffer); + NSString *output = [[NSString alloc] initWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", md5Buffer[0], md5Buffer[1], md5Buffer[2], md5Buffer[3], md5Buffer[4], md5Buffer[5], md5Buffer[6], md5Buffer[7], md5Buffer[8], md5Buffer[9], md5Buffer[10], md5Buffer[11], md5Buffer[12], md5Buffer[13], md5Buffer[14], md5Buffer[15]]; + return output; +} + ++ (NSDictionary *)argumentDictionaryInUrlString:(NSString *)string +{ + NSMutableDictionary *queryStringDictionary = [[NSMutableDictionary alloc] init]; + NSArray *urlComponents = [string componentsSeparatedByString:@"&"]; + + for (NSString *keyValuePair in urlComponents) + { + NSRange equalsSignRange = [keyValuePair rangeOfString:@"="]; + if (equalsSignRange.location != NSNotFound) { + NSString *key = [keyValuePair substringToIndex:equalsSignRange.location]; + NSString *value = [[[keyValuePair substringFromIndex:equalsSignRange.location + equalsSignRange.length] stringByReplacingOccurrencesOfString:@"+" withString:@" "] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + + + [queryStringDictionary setObject:value forKey:key]; + } + } + + return queryStringDictionary; +} + ++ (bool)stringContainsEmoji:(NSString *)string +{ + __block bool returnValue = NO; + [string enumerateSubstringsInRange:NSMakeRange(0, [string length]) options:NSStringEnumerationByComposedCharacterSequences usingBlock: + ^(NSString *substring, __unused NSRange substringRange, __unused NSRange enclosingRange, __unused BOOL *stop) + { + const unichar hs = [substring characterAtIndex:0]; + + if (0xd800 <= hs && hs <= 0xdbff) + { + if (substring.length > 1) + { + const unichar ls = [substring characterAtIndex:1]; + const int uc = ((hs - 0xd800) * 0x400) + (ls - 0xdc00) + 0x10000; + if (0x1d000 <= uc && uc <= 0x1f77f) + { + returnValue = YES; + } + } + } else if (substring.length > 1) + { + const unichar ls = [substring characterAtIndex:1]; + if (ls == 0x20e3) + { + returnValue = YES; + } + + } else + { + if (0x2100 <= hs && hs <= 0x27ff) + { + returnValue = YES; + } else if (0x2B05 <= hs && hs <= 0x2b07) + { + returnValue = YES; + } else if (0x2934 <= hs && hs <= 0x2935) + { + returnValue = YES; + } else if (0x3297 <= hs && hs <= 0x3299) + { + returnValue = YES; + } else if (hs == 0xa9 || hs == 0xae || hs == 0x303d || hs == 0x3030 || hs == 0x2b55 || hs == 0x2b1c || hs == 0x2b1b || hs == 0x2b50) + { + returnValue = YES; + } + } + + if (returnValue && stop != NULL) + *stop = true; + }]; + + return returnValue; +} + +static bool isEmojiCharacter(NSString *singleChar) +{ + const unichar high = [singleChar characterAtIndex:0]; + + if (0xd800 <= high && high <= 0xdbff && singleChar.length >= 2) + { + const unichar low = [singleChar characterAtIndex:1]; + const int codepoint = ((high - 0xd800) * 0x400) + (low - 0xdc00) + 0x10000; + + return (0x1d000 <= codepoint && codepoint <= 0x1f77f); + } + + return (0x2100 <= high && high <= 0x27bf); +} + ++ (bool)stringContainsEmojiOnly:(NSString *)string length:(NSUInteger *)length +{ + if (string.length == 0) + return false; + + __block bool result = true; + + __block NSUInteger count = 0; + [string enumerateSubstringsInRange:NSMakeRange(0, string.length) + options:NSStringEnumerationByComposedCharacterSequences + usingBlock: ^(NSString *substring, __unused NSRange substringRange, __unused NSRange enclosingRange, BOOL *stop) + { + if (!isEmojiCharacter(substring)) + { + result = false; + *stop = true; + } + count++; + }]; + + if (length != NULL) + *length = count; + + return result; +} + ++ (NSString *)stringForMessageTimerSeconds:(NSUInteger)seconds +{ + if (seconds < 60) + { + int number = (int)seconds; + + return [effectiveLocalization() getPluralized:@"MessageTimer.Seconds" count:(int32_t)number]; + } + else if (seconds < 60 * 60) + { + int number = (int)seconds / 60; + + return [effectiveLocalization() getPluralized:@"MessageTimer.Minutes" count:(int32_t)number]; + } + else if (seconds < 60 * 60 * 24) + { + int number = (int)seconds / (60 * 60); + + return [effectiveLocalization() getPluralized:@"MessageTimer.Hours" count:(int32_t)number]; + } + else if (seconds < 60 * 60 * 24 * 7) + { + int number = (int)seconds / (60 * 60 * 24); + + return [effectiveLocalization() getPluralized:@"MessageTimer.Days" count:(int32_t)number]; + } + else if (seconds < 60 * 60 * 24 * 7 * 4) + { + int number = (int)seconds / (60 * 60 * 24 * 7); + + return [effectiveLocalization() getPluralized:@"MessageTimer.Weeks" count:(int32_t)number]; + } + else if (seconds < 60 * 60 * 24 * 365) + { + int number = MAX(1, (int)ceilf((int)(seconds / (60 * 60 * 24 * 29)))); + + return [effectiveLocalization() getPluralized:@"MessageTimer.Months" count:(int32_t)number]; + } + else + { + int number = (int)seconds / (60 * 60 * 24 * 365); + + return [effectiveLocalization() getPluralized:@"MessageTimer.Years" count:(int32_t)number]; + } + + return @""; +} + ++ (NSString *)stringForShortMessageTimerSeconds:(NSUInteger)seconds +{ + if (seconds < 60) + { + int number = (int)seconds; + + return [effectiveLocalization() getPluralized:@"MessageTimer.ShortSeconds" count:(int32_t)number]; + } + else if (seconds < 60 * 60) + { + int number = (int)seconds / 60; + + return [effectiveLocalization() getPluralized:@"MessageTimer.ShortMinutes" count:(int32_t)number]; + } + else if (seconds < 60 * 60 * 24) + { + int number = (int)seconds / (60 * 60); + + return [effectiveLocalization() getPluralized:@"MessageTimer.ShortHours" count:(int32_t)number]; + } + else if (seconds < 60 * 60 * 24 * 7) + { + int number = (int)seconds / (60 * 60 * 24); + + return [effectiveLocalization() getPluralized:@"MessageTimer.ShortDays" count:(int32_t)number]; + } + else + { + int number = (int)seconds / (60 * 60 * 24 * 7); + + return [effectiveLocalization() getPluralized:@"MessageTimer.ShortWeeks" count:(int32_t)number]; + } + + return @""; +} + ++ (NSArray *)stringComponentsForMessageTimerSeconds:(NSUInteger)seconds +{ + NSString *first = @""; + NSString *second = @""; + + if (seconds < 60) + { + int number = (int)seconds; + + NSString *format = TGLocalized([self integerValueFormat:@"MessageTimer.Seconds_" value:number]); + + NSRange range = [format rangeOfString:@"%@"]; + if (range.location != NSNotFound) + { + first = [[NSString alloc] initWithFormat:@"%d", number]; + second = [[format substringFromIndex:range.location + range.length] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + } else { + first = format; + } + } + else if (seconds < 60 * 60) + { + int number = (int)seconds / 60; + + NSString *format = TGLocalized([self integerValueFormat:@"MessageTimer.Minutes_" value:number]); + + NSRange range = [format rangeOfString:@"%@"]; + if (range.location != NSNotFound) + { + first = [[NSString alloc] initWithFormat:@"%d", number]; + second = [[format substringFromIndex:range.location + range.length] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + } else { + first = format; + } + } + else if (seconds < 60 * 60 * 24) + { + int number = (int)seconds / (60 * 60); + + NSString *format = TGLocalized([self integerValueFormat:@"MessageTimer.Hours_" value:number]); + + NSRange range = [format rangeOfString:@"%@"]; + if (range.location != NSNotFound) + { + first = [[NSString alloc] initWithFormat:@"%d", number]; + second = [[format substringFromIndex:range.location + range.length] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + } else { + first = format; + } + } + else if (seconds < 60 * 60 * 24 * 7) + { + int number = (int)seconds / (60 * 60 * 24); + + NSString *format = TGLocalized([self integerValueFormat:@"MessageTimer.Days_" value:number]); + + NSRange range = [format rangeOfString:@"%@"]; + if (range.location != NSNotFound) + { + first = [[NSString alloc] initWithFormat:@"%d", number]; + second = [[format substringFromIndex:range.location + range.length] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + } else { + first = format; + } + } + else if (seconds < 60 * 60 * 24 * 30) + { + int number = (int)seconds / (60 * 60 * 24 * 7); + + NSString *format = TGLocalized([self integerValueFormat:@"MessageTimer.Weeks_" value:number]); + + NSRange range = [format rangeOfString:@"%@"]; + if (range.location != NSNotFound) + { + first = [[NSString alloc] initWithFormat:@"%d", number]; + second = [[format substringFromIndex:range.location + range.length] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + } else { + first = format; + } + } + else if (seconds < 60 * 60 * 24 * 365) + { + int number = (int)ceilf((seconds / (60 * 60 * 24 * 30.5f))); + + NSString *format = TGLocalized([self integerValueFormat:@"MessageTimer.Months_" value:number]); + + NSRange range = [format rangeOfString:@"%@"]; + if (range.location != NSNotFound) + { + first = [[NSString alloc] initWithFormat:@"%d", number]; + second = [[format substringFromIndex:range.location + range.length] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + } else { + first = format; + } + } + else + { + int number = (int)seconds / (60 * 60 * 24 * 365); + + NSString *format = TGLocalized([self integerValueFormat:@"MessageTimer.Years_" value:number]); + + NSRange range = [format rangeOfString:@"%@"]; + if (range.location != NSNotFound) + { + first = [[NSString alloc] initWithFormat:@"%d", number]; + second = [[format substringFromIndex:range.location + range.length] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + } else { + first = format; + } + } + + return @[first, second]; +} + ++ (NSString *)stringForCallDurationSeconds:(NSUInteger)seconds +{ + if (seconds < 60) + { + int number = (int)seconds; + + return [effectiveLocalization() getPluralized:@"Call.Seconds" count:number]; + } + else + { + int number = (int)seconds / 60; + + return [effectiveLocalization() getPluralized:@"Call.Minutes" count:number]; + } +} + ++ (NSString *)stringForShortCallDurationSeconds:(NSUInteger)seconds +{ + if (seconds < 60) + { + int number = (int)seconds; + + return [effectiveLocalization() getPluralized:@"Call.ShortSeconds" count:(int32_t)number]; + } + else + { + int number = (int)seconds / 60; + + return [effectiveLocalization() getPluralized:@"Call.ShortMinutes" count:(int32_t)number]; + } +} + ++ (NSString *)stringForUserCount:(NSUInteger)userCount +{ + NSUInteger number = userCount; + + return [effectiveLocalization() getPluralized:@"UserCount" count:(int32_t)number]; +} + ++ (NSString *)stringForFileSize:(int64_t)size +{ + NSString *format = @""; + float floatSize = size; + bool useFloat = false; + + if (floatSize < 1024) + format = TGLocalized(@"FileSize.B"); + else if (size < 1024 * 1024) + { + format = TGLocalized(@"FileSize.KB"); + floatSize = size / 1024; + } + else if (size < 1024 * 1024 * 1024) + { + format = TGLocalized(@"FileSize.MB"); + floatSize = size / (1024 * 1024); + } else { + format = TGLocalized(@"FileSize.GB"); + floatSize = size / (1024.0f * 1024.0f * 1024.0f); + useFloat = true; + } + + if (useFloat) { + return [[NSString alloc] initWithFormat:format, [[NSString alloc] initWithFormat:@"%0.1f", floatSize]]; + } else { + return [[NSString alloc] initWithFormat:format, [[NSString alloc] initWithFormat:@"%d", (int)floatSize]]; + } +} + ++ (NSString *)stringForFileSize:(int64_t)size precision:(NSInteger)precision +{ + NSString *string = @""; + if (size < 1024) + { + string = [[NSString alloc] initWithFormat:TGLocalized(@"FileSize.B"), [[NSString alloc] initWithFormat:@"%d", (int)size]];} + else if (size < 1024 * 1024) + { + string = [[NSString alloc] initWithFormat:TGLocalized(@"FileSize.KB"), [[NSString alloc] initWithFormat:@"%d", (int)(size / 1024)]]; + } + else + { + NSString *format = [NSString stringWithFormat:@"%%0.%df", (int)precision]; + string = [[NSString alloc] initWithFormat:TGLocalized(@"FileSize.MB"), [[NSString alloc] initWithFormat:format, (CGFloat)(size / 1024.0f / 1024.0f)]]; + } + + return string; +} + ++ (NSString *)integerValueFormat:(NSString *)prefix value:(NSInteger)value +{ + TGLocalization *localization = effectiveLocalization(); + NSString *form = @"any"; + switch (TGPluralForm(localization.languageCodeHash, (int)value)) { + case TGPluralFormZero: + form = @"0"; + break; + case TGPluralFormOne: + form = @"1"; + break; + case TGPluralFormTwo: + form = @"2"; + break; + case TGPluralFormFew: + form = @"3_10"; + break; + case TGPluralFormMany: + form = @"many"; + break; + case TGPluralFormOther: + form = @"any"; + break; + default: + break; + } + + NSString *result = [prefix stringByAppendingString:form]; + if ([localization contains:result]) { + return result; + } else { + return [prefix stringByAppendingString:@"any"]; + } +} + ++ (NSString *)stringForMuteInterval:(int)value +{ + value = MAX(1 * 60, value); + + if (value < 1 * 60 * 60) + { + value /= 60; + NSString *format = TGLocalized([self integerValueFormat:@"MuteFor.Minutes_" value:value]); + return [[NSString alloc] initWithFormat:format, [[NSString alloc] initWithFormat:@"%d", value]]; + } + else if (value < 24 * 60 * 60) + { + value /= 60 * 60; + NSString *format = TGLocalized([self integerValueFormat:@"MuteFor.Hours_" value:value]); + return [[NSString alloc] initWithFormat:format, [[NSString alloc] initWithFormat:@"%d", value]]; + } + else if (value < 7 * 24 * 60 * 60) + { + value /= 24 * 60 * 60; + NSString *format = TGLocalized([self integerValueFormat:@"MuteFor.Days_" value:value]); + return [[NSString alloc] initWithFormat:format, [[NSString alloc] initWithFormat:@"%d", value]]; + } + else + { + value /= 7 * 24 * 60 * 60; + NSString *format = TGLocalized([self integerValueFormat:@"MuteFor.Weeks_" value:value]); + return [[NSString alloc] initWithFormat:format, [[NSString alloc] initWithFormat:@"%d", value]]; + } + + return @""; +} + ++ (NSString *)stringForRemainingMuteInterval:(int)value +{ + value = MAX(1 * 60, value); + + if (value <= 1 * 60 * 60) + { + value = (int)roundf(value / 60.0f); + NSString *format = TGLocalized([self integerValueFormat:@"MuteExpires.Minutes_" value:value]); + return [[NSString alloc] initWithFormat:format, [[NSString alloc] initWithFormat:@"%d", value]]; + } + else if (value <= 24 * 60 * 60) + { + value = (int)roundf(value / (60.0f * 60.0f)); + NSString *format = TGLocalized([self integerValueFormat:@"MuteExpires.Hours_" value:value]); + return [[NSString alloc] initWithFormat:format, [[NSString alloc] initWithFormat:@"%d", value]]; + } + else + { + value = (int)roundf(value / (24.0f * 60.0f * 60.0f)); + NSString *format = TGLocalized([self integerValueFormat:@"MuteExpires.Days_" value:value]); + return [[NSString alloc] initWithFormat:format, [[NSString alloc] initWithFormat:@"%d", value]]; + } + + return @""; +} + ++ (NSString *)stringForDeviceType +{ + NSString *model = @"iPhone"; + NSString *rawModel = [[[UIDevice currentDevice] model] lowercaseString]; + if ([rawModel rangeOfString:@"ipod"].location != NSNotFound) + model = @"iPod"; + else if ([rawModel rangeOfString:@"ipad"].location != NSNotFound) + model = @"iPad"; + + return model; +} + ++ (NSString *)stringForCurrency:(NSString *)__unused currency amount:(int64_t)__unused amount { + return nil; +} + ++ (NSString *)stringForEmojiHashOfData:(NSData *)data count:(NSInteger)count positionExtractor:(int32_t (^)(uint8_t *, int32_t, int32_t))positionExtractor +{ + if (data.length != 32) + return @""; + + NSArray *emojis = @[ @"😉", @"😍", @"😛", @"😭", @"😱", @"😡", @"😎", @"😴", @"😵", @"😈", @"😬", @"😇", @"😏", @"👮", @"👷", @"💂", @"👶", @"👨", @"👩", @"👴", @"👵", @"😻", @"😽", @"🙀", @"👺", @"🙈", @"🙉", @"🙊", @"💀", @"👽", @"💩", @"🔥", @"💥", @"💤", @"👂", @"👀", @"👃", @"👅", @"👄", @"👍", @"👎", @"👌", @"👊", @"✌️", @"✋️", @"👐", @"👆", @"👇", @"👉", @"👈", @"🙏", @"👏", @"💪", @"🚶", @"🏃", @"💃", @"👫", @"👪", @"👬", @"👭", @"💅", @"🎩", @"👑", @"👒", @"👟", @"👞", @"👠", @"👕", @"👗", @"👖", @"👙", @"👜", @"👓", @"🎀", @"💄", @"💛", @"💙", @"💜", @"💚", @"💍", @"💎", @"🐶", @"🐺", @"🐱", @"🐭", @"🐹", @"🐰", @"🐸", @"🐯", @"🐨", @"🐻", @"🐷", @"🐮", @"🐗", @"🐴", @"🐑", @"🐘", @"🐼", @"🐧", @"🐥", @"🐔", @"🐍", @"🐢", @"🐛", @"🐝", @"🐜", @"🐞", @"🐌", @"🐙", @"🐚", @"🐟", @"🐬", @"🐋", @"🐐", @"🐊", @"🐫", @"🍀", @"🌹", @"🌻", @"🍁", @"🌾", @"🍄", @"🌵", @"🌴", @"🌳", @"🌞", @"🌚", @"🌙", @"🌎", @"🌋", @"⚡️", @"☔️", @"❄️", @"⛄️", @"🌀", @"🌈", @"🌊", @"🎓", @"🎆", @"🎃", @"👻", @"🎅", @"🎄", @"🎁", @"🎈", @"🔮", @"🎥", @"📷", @"💿", @"💻", @"☎️", @"📡", @"📺", @"📻", @"🔉", @"🔔", @"⏳", @"⏰", @"⌚️", @"🔒", @"🔑", @"🔎", @"💡", @"🔦", @"🔌", @"🔋", @"🚿", @"🚽", @"🔧", @"🔨", @"🚪", @"🚬", @"💣", @"🔫", @"🔪", @"💊", @"💉", @"💰", @"💵", @"💳", @"✉️", @"📫", @"📦", @"📅", @"📁", @"✂️", @"📌", @"📎", @"✒️", @"✏️", @"📐", @"📚", @"🔬", @"🔭", @"🎨", @"🎬", @"🎤", @"🎧", @"🎵", @"🎹", @"🎻", @"🎺", @"🎸", @"👾", @"🎮", @"🃏", @"🎲", @"🎯", @"🏈", @"🏀", @"⚽️", @"⚾️", @"🎾", @"🎱", @"🏉", @"🎳", @"🏁", @"🏇", @"🏆", @"🏊", @"🏄", @"☕️", @"🍼", @"🍺", @"🍷", @"🍴", @"🍕", @"🍔", @"🍟", @"🍗", @"🍱", @"🍚", @"🍜", @"🍡", @"🍳", @"🍞", @"🍩", @"🍦", @"🎂", @"🍰", @"🍪", @"🍫", @"🍭", @"🍯", @"🍎", @"🍏", @"🍊", @"🍋", @"🍒", @"🍇", @"🍉", @"🍓", @"🍑", @"🍌", @"🍐", @"🍍", @"🍆", @"🍅", @"🌽", @"🏡", @"🏥", @"🏦", @"⛪️", @"🏰", @"⛺️", @"🏭", @"🗻", @"🗽", @"🎠", @"🎡", @"⛲️", @"🎢", @"🚢", @"🚤", @"⚓️", @"🚀", @"✈️", @"🚁", @"🚂", @"🚋", @"🚎", @"🚌", @"🚙", @"🚗", @"🚕", @"🚛", @"🚨", @"🚔", @"🚒", @"🚑", @"🚲", @"🚠", @"🚜", @"🚦", @"⚠️", @"🚧", @"⛽️", @"🎰", @"🗿", @"🎪", @"🎭", @"🇯🇵", @"🇰🇷", @"🇩🇪", @"🇨🇳", @"🇺🇸", @"🇫🇷", @"🇪🇸", @"🇮🇹", @"🇷🇺", @"🇬🇧", @"1️⃣", @"2️⃣", @"3️⃣", @"4️⃣", @"5️⃣", @"6️⃣", @"7️⃣", @"8️⃣", @"9️⃣", @"0️⃣", @"🔟", @"❗️", @"❓", @"♥️", @"♦️", @"💯", @"🔗", @"🔱", @"🔴", @"🔵", @"🔶", @"🔷" ]; + + uint8_t bytes[32]; + [data getBytes:bytes length:32]; + + NSString *result = @""; + for (int32_t i = 0; i < count; i++) + { + int32_t position = positionExtractor(bytes, i, (int32_t)emojis.count); + NSString *emoji = emojis[position]; + result = [result stringByAppendingString:emoji]; + } + + return result; +} + +@end + +#if defined(_MSC_VER) + +#define FORCE_INLINE __forceinline + +#include + +#define ROTL32(x,y) _rotl(x,y) +#define ROTL64(x,y) _rotl64(x,y) + +#define BIG_CONSTANT(x) (x) + +// Other compilers + +#else // defined(_MSC_VER) + +#define FORCE_INLINE __attribute__((always_inline)) + +static inline uint32_t rotl32 ( uint32_t x, int8_t r ) +{ + return (x << r) | (x >> (32 - r)); +} + +#define ROTL32(x,y) rotl32(x,y) +#define ROTL64(x,y) rotl64(x,y) + +#define BIG_CONSTANT(x) (x##LLU) + +#endif // !defined(_MSC_VER) + +//----------------------------------------------------------------------------- +// Block read - if your platform needs to do endian-swapping or can only +// handle aligned reads, do the conversion here + +static FORCE_INLINE uint32_t getblock ( const uint32_t * p, int i ) +{ + return p[i]; +} + +//----------------------------------------------------------------------------- +// Finalization mix - force all bits of a hash block to avalanche + +static FORCE_INLINE uint32_t fmix ( uint32_t h ) +{ + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + + return h; +} + +//---------- + +//----------------------------------------------------------------------------- + +static void MurmurHash3_x86_32 ( const void * key, int len, + uint32_t seed, void * out ) +{ + const uint8_t * data = (const uint8_t*)key; + const int nblocks = len / 4; + + uint32_t h1 = seed; + + const uint32_t c1 = 0xcc9e2d51; + const uint32_t c2 = 0x1b873593; + + //---------- + // body + + const uint32_t * blocks = (const uint32_t *)(data + nblocks*4); + + for(int i = -nblocks; i; i++) + { + uint32_t k1 = getblock(blocks,i); + + k1 *= c1; + k1 = ROTL32(k1,15); + k1 *= c2; + + h1 ^= k1; + h1 = ROTL32(h1,13); + h1 = h1*5+0xe6546b64; + } + + //---------- + // tail + + const uint8_t * tail = (const uint8_t*)(data + nblocks*4); + + uint32_t k1 = 0; + + switch(len & 3) + { + case 3: k1 ^= tail[2] << 16; + case 2: k1 ^= tail[1] << 8; + case 1: k1 ^= tail[0]; + k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; + }; + + //---------- + // finalization + + h1 ^= len; + + h1 = fmix(h1); + + *(uint32_t*)out = h1; +} + +int32_t murMurHash32(NSString *string) +{ + const char *utf8 = string.UTF8String; + + int32_t result = 0; + MurmurHash3_x86_32((uint8_t *)utf8, (int)strlen(utf8), -137723950, &result); + + return result; +} + +int32_t murMurHashBytes32(void *bytes, int length) +{ + int32_t result = 0; + MurmurHash3_x86_32(bytes, length, -137723950, &result); + + return result; +} + +int32_t phoneMatchHash(NSString *phone) +{ + int length = (int)phone.length; + char cleanString[length]; + int cleanLength = 0; + + for (int i = 0; i < length; i++) + { + unichar c = [phone characterAtIndex:i]; + if (c >= '0' && c <= '9') + cleanString[cleanLength++] = (char)c; + } + + int32_t result = 0; + if (cleanLength > 8) + MurmurHash3_x86_32((uint8_t *)cleanString + (cleanLength - 8), 8, -137723950, &result); + else + MurmurHash3_x86_32((uint8_t *)cleanString, cleanLength, -137723950, &result); + + return result; +} + +bool TGIsRTL() +{ + static bool value = false; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^ + { + value = ([NSLocale characterDirectionForLanguage:[[NSLocale preferredLanguages] objectAtIndex:0]] == NSLocaleLanguageDirectionRightToLeft); + }); + + return value; +} + +bool TGIsArabic() +{ + static bool value = false; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^ + { + NSString *language = [[NSLocale preferredLanguages] objectAtIndex:0]; + value = [language isEqualToString:@"ar"]; + }); + return value; +} + +bool TGIsKorean() +{ + static bool value = false; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^ + { + NSString *language = [[NSLocale preferredLanguages] objectAtIndex:0]; + value = [language isEqualToString:@"ko"]; + }); + return value; +} + +bool TGIsLocaleArabic() +{ + static bool value = false; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^ + { + NSString *identifier = [[NSLocale currentLocale] localeIdentifier]; + value = [identifier isEqualToString:@"ar"] || [identifier hasPrefix:@"ar_"]; + }); + return value; +} + +@implementation NSString (Telegraph) + +- (int)lengthByComposedCharacterSequences +{ + return [self lengthByComposedCharacterSequencesInRange:NSMakeRange(0, self.length)]; +} + +- (int)lengthByComposedCharacterSequencesInRange:(NSRange)range +{ + __block NSInteger length = 0; + [self enumerateSubstringsInRange:range options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(__unused NSString *substring, __unused NSRange substringRange, __unused NSRange enclosingRange, __unused BOOL *stop) + { + if (substring.length != 0) + length++; + //TGLog(@"substringRange %@, enclosingRange %@, length %d", NSStringFromRange(substringRange), NSStringFromRange(enclosingRange), length); + }]; + //TGLog(@"length %d", length); + + return (int)length; +} + +- (BOOL)isEmoji { + const unichar high = [self characterAtIndex:0]; + + // Surrogate pair (U+1D000-1F77F) + if (0xd800 <= high && high <= 0xdbff && self.length >= 2) { + const unichar low = [self characterAtIndex:1]; + const int codepoint = ((high - 0xd800) * 0x400) + (low - 0xdc00) + 0x10000; + + return (0x1d000 <= codepoint && codepoint <= 0x1f77f); + + // Not surrogate pair (U+2100-27BF) + } else { + return (0x2100 <= high && high <= 0x27bf); + } +} + +- (bool)containsSingleEmoji +{ + bool result = false; + @try { + __autoreleasing NSString *checkString = nil; + NSString *firstEmoji = [[self getEmojiFromString:true checkString:&checkString] firstObject]; + if (firstEmoji.length != 0 && [checkString isEqualToString:self]) { + result = true; + } + } @catch(__unused NSException *e) { + + } + return result; +} + +- (bool)hasNonWhitespaceCharacters +{ + NSInteger textLength = self.length; + bool hasNonWhitespace = false; + for (int i = 0; i < textLength; i++) + { + unichar c = [self characterAtIndex:i]; + if (c != ' ' && c != '\n' && c != '\t' && c != NSAttachmentCharacter) + { + hasNonWhitespace = true; + break; + } + } + return hasNonWhitespace; +} + +- (NSAttributedString *)attributedFormattedStringWithRegularFont:(UIFont *)regularFont boldFont:(UIFont *)boldFont lineSpacing:(CGFloat)lineSpacing paragraphSpacing:(CGFloat)paragraphSpacing alignment:(NSTextAlignment)alignment +{ + NSMutableArray *boldRanges = [[NSMutableArray alloc] init]; + + NSMutableString *cleanText = [[NSMutableString alloc] initWithString:self]; + while (true) + { + NSRange startRange = [cleanText rangeOfString:@"**"]; + if (startRange.location == NSNotFound) + break; + + [cleanText deleteCharactersInRange:startRange]; + + NSRange endRange = [cleanText rangeOfString:@"**"]; + if (endRange.location == NSNotFound) + break; + + [cleanText deleteCharactersInRange:endRange]; + + [boldRanges addObject:[NSValue valueWithRange:NSMakeRange(startRange.location, endRange.location - startRange.location)]]; + } + + NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init]; + style.lineSpacing = lineSpacing; + style.lineBreakMode = NSLineBreakByWordWrapping; + style.alignment = alignment; + style.paragraphSpacing = paragraphSpacing; + + NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:cleanText attributes:@ + { + }]; + + [attributedString addAttributes:@{NSParagraphStyleAttributeName: style, NSFontAttributeName: regularFont} range:NSMakeRange(0, attributedString.length)]; + + NSDictionary *boldAttributes = @{NSFontAttributeName: boldFont}; + for (NSValue *nRange in boldRanges) + { + [attributedString addAttributes:boldAttributes range:[nRange rangeValue]]; + } + + return attributedString; +} + +- (NSString *)urlAnchorPart { + NSURL *url = [[NSURL alloc] initWithString:self]; + return [url fragment]; +} + +static unsigned char strToChar (char a, char b) +{ + char encoder[3] = {'\0','\0','\0'}; + encoder[0] = a; + encoder[1] = b; + return (char)strtol(encoder,NULL,16); +} + +- (NSData *)dataByDecodingHexString +{ + const char *bytes = [self cStringUsingEncoding:NSUTF8StringEncoding]; + NSUInteger length = strlen(bytes); + unsigned char *r = (unsigned char *)malloc(length / 2); + unsigned char *index = r; + + while ((*bytes) && (*(bytes + 1))) + { + *index = strToChar(*bytes, *(bytes +1)); + index++; + bytes+=2; + } + + return [[NSData alloc] initWithBytesNoCopy:r length:length / 2 freeWhenDone:true]; +} + +- (NSArray *)getEmojiFromString:(BOOL)checkColor checkString:(__autoreleasing NSString **)checkString { + + __block NSMutableDictionary *temp = [NSMutableDictionary dictionary]; + + [self enumerateSubstringsInRange: NSMakeRange(0, [self length]) options:NSStringEnumerationByComposedCharacterSequences usingBlock: + ^(NSString *substring, __unused NSRange substringRange, __unused NSRange enclosingRange, __unused BOOL *stop){ + + const unichar hs = [substring characterAtIndex: 0]; + + + // surrogate pair + if (0xd800 <= hs && hs <= 0xdbff) { + if (substring.length > 1) { + unichar ls = [substring characterAtIndex:1]; + int uc = ((hs - 0xd800) * 0x400) + (ls - 0xdc00) + 0x10000; + if (0x1d000 <= uc && uc <= 129316) { + + [temp setObject:substring forKey:@(uc)]; + } + } + } else if (substring.length > 1) { + const unichar ls = [substring characterAtIndex:1]; + if (ls == 0x20e3 || ls == 65039) { + [temp setObject:substring forKey:@(ls)]; + } + + } else { + // non surrogate + if (0x2100 <= hs && hs <= 0x27ff) { + [temp setObject:substring forKey:@(hs)]; + } else if (0x2B05 <= hs && hs <= 0x2b07) { + [temp setObject:substring forKey:@(hs)]; + } else if (0x2934 <= hs && hs <= 0x2935) { + [temp setObject:substring forKey:@(hs)]; + } else if (0x3297 <= hs && hs <= 0x3299) { + [temp setObject:substring forKey:@(hs)]; + } else if (hs == 0xa9 || hs == 0xae || hs == 0x303d || hs == 0x3030 || hs == 0x2b55 || hs == 0x2b1c || hs == 0x2b1b || hs == 0x2b50) { + [temp setObject:substring forKey:@(hs)]; + } + } + + // // surrogate pair + // if (0xd800 <= hs && hs <= 0xdbff) { + // const unichar ls = [substring characterAtIndex: 1]; + // const int uc = ((hs - 0xd800) * 0x400) + (ls - 0xdc00) + 0x10000; + // + // if((0x1d000 <= uc && uc <= 0x1f77f)) { + // [temp setObject:substring forKey:@(uc)]; + // } + // + // + // // non surrogate + // } else { + // if((0x2100 <= hs && hs <= 0x26ff)) { + // [temp setObject:substring forKey:@(hs)]; + // } + // + // } + }]; + + if (checkString) { + NSArray *tempValues = [temp allValues]; + if (tempValues.count > 0) { + *checkString = (NSString *)tempValues[0]; + } + } + + if(checkColor) { + NSMutableDictionary *t = [[NSMutableDictionary alloc] init]; + + [temp enumerateKeysAndObjectsUsingBlock:^(id key, id obj, __unused BOOL *stop) { + NSString *e = [self realEmoji:obj]; + [t setObject:e forKey:key]; + }]; + + return [t allValues]; + } + + + return [temp allValues]; + +} + +-(NSString *)realEmoji:(NSString *)raceEmoji { + NSString *e = raceEmoji; + + if(raceEmoji.length == 4) { + NSData *data = [raceEmoji dataUsingEncoding:NSNonLossyASCIIStringEncoding]; + NSString *e = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + + NSArray *s = [e componentsSeparatedByString:@"\\"]; + + + + if(s.count == 5) { + e = [NSString stringWithFormat:@"\\%@",[[s subarrayWithRange:NSMakeRange(1, 2)] componentsJoinedByString:@"\\"]]; + } + + + data = [e dataUsingEncoding:NSUTF8StringEncoding]; + + e = [[NSString alloc] initWithData:data encoding:NSNonLossyASCIIStringEncoding]; + + return e; + + } + + return e; +} + +-(NSString *)emojiModifier:(NSString *)emoji +{ + if(emoji.length == 4) { + NSData *data = [emoji dataUsingEncoding:NSNonLossyASCIIStringEncoding]; + NSString *e = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + + NSArray *s = [e componentsSeparatedByString:@"\\"]; + + + + if(s.count == 5) { + e = [NSString stringWithFormat:@"\\%@",[[s subarrayWithRange:NSMakeRange(3, 2)] componentsJoinedByString:@"\\"]]; + } + + + data = [e dataUsingEncoding:NSUTF8StringEncoding]; + + e = [[NSString alloc] initWithData:data encoding:NSNonLossyASCIIStringEncoding]; + + return e; + + } + + return nil; +} + +- (NSString *)emojiWithModifier:(NSString *)modifier emoji:(NSString *)emoji +{ + NSData *data = [emoji dataUsingEncoding:NSNonLossyASCIIStringEncoding]; + NSString *e = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + + e = [NSString stringWithFormat:@"%@%@",e,[[NSString alloc] initWithData:[modifier dataUsingEncoding:NSNonLossyASCIIStringEncoding] encoding:NSUTF8StringEncoding]]; + + data = [e dataUsingEncoding:NSUTF8StringEncoding]; + + e = [[NSString alloc] initWithData:data encoding:NSNonLossyASCIIStringEncoding]; + + return e; +} + +@end + +@implementation NSData (Telegraph) + +- (NSString *)stringByEncodingInHex +{ + const unsigned char *dataBuffer = (const unsigned char *)[self bytes]; + if (dataBuffer == NULL) + return [NSString string]; + + NSUInteger dataLength = [self length]; + NSMutableString *hexString = [NSMutableString stringWithCapacity:(dataLength * 2)]; + + for (int i = 0; i < (int)dataLength; ++i) + [hexString appendString:[NSString stringWithFormat:@"%02lx", (unsigned long)dataBuffer[i]]]; + + return hexString; +} + +- (NSString *)stringByEncodingInHexSeparatedByString:(NSString *)string +{ + const unsigned char *dataBuffer = (const unsigned char *)[self bytes]; + if (dataBuffer == NULL) + return [NSString string]; + + NSUInteger dataLength = [self length]; + NSMutableString *hexString = [NSMutableString stringWithCapacity:(dataLength * 2)]; + + NSString *divider = string; + for (int i = 0; i < (int)dataLength; ++i) { + if (i == (int)dataLength - 1) + divider = @""; + else if (i == (int)dataLength / 2 - 1) + divider = [divider stringByAppendingString:divider]; + else + divider = string; + + [hexString appendString:[NSString stringWithFormat:@"%02lx%@", (unsigned long)dataBuffer[i], divider]]; + } + return hexString; +} + +@end diff --git a/LegacyComponents/TGTextCheckingResult.h b/LegacyComponents/TGTextCheckingResult.h new file mode 100644 index 0000000000..a6fe7faad8 --- /dev/null +++ b/LegacyComponents/TGTextCheckingResult.h @@ -0,0 +1,26 @@ +#import + +typedef enum { + TGTextCheckingResultTypeMention, + TGTextCheckingResultTypeHashtag, + TGTextCheckingResultTypeCommand, + TGTextCheckingResultTypeBold, + TGTextCheckingResultTypeUltraBold, + TGTextCheckingResultTypeItalic, + TGTextCheckingResultTypeCode, + TGTextCheckingResultTypeLink, + TGTextCheckingResultTypeColor +} TGTextCheckingResultType; + +@interface TGTextCheckingResult : NSObject + +@property (nonatomic, readonly) NSRange range; +@property (nonatomic, readonly) TGTextCheckingResultType type; +@property (nonatomic, strong, readonly) NSString *contents; +@property (nonatomic, strong, readonly) id value; +@property (nonatomic, readonly) bool highlightAsLink; + +- (instancetype)initWithRange:(NSRange)range type:(TGTextCheckingResultType)type contents:(NSString *)contents; +- (instancetype)initWithRange:(NSRange)range type:(TGTextCheckingResultType)type contents:(NSString *)contents value:(id)value highlightAsLink:(bool)highlightAsLink; + +@end diff --git a/LegacyComponents/TGTextCheckingResult.m b/LegacyComponents/TGTextCheckingResult.m new file mode 100644 index 0000000000..d69e158320 --- /dev/null +++ b/LegacyComponents/TGTextCheckingResult.m @@ -0,0 +1,22 @@ +#import "TGTextCheckingResult.h" + +@implementation TGTextCheckingResult + +- (instancetype)initWithRange:(NSRange)range type:(TGTextCheckingResultType)type contents:(NSString *)contents { + return [self initWithRange:range type:type contents:contents value:nil highlightAsLink:false]; +} + +- (instancetype)initWithRange:(NSRange)range type:(TGTextCheckingResultType)type contents:(NSString *)contents value:(id)value highlightAsLink:(bool)highlightAsLink +{ + if (self != nil) + { + _range = range; + _type = type; + _contents = contents; + _value = value; + _highlightAsLink = highlightAsLink; + } + return self; +} + +@end diff --git a/LegacyComponents/TGUnsupportedMediaAttachment.h b/LegacyComponents/TGUnsupportedMediaAttachment.h new file mode 100644 index 0000000000..f0854dbc9b --- /dev/null +++ b/LegacyComponents/TGUnsupportedMediaAttachment.h @@ -0,0 +1,17 @@ +/* + * This is the source code of Telegram for iOS v. 1.1 + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Peter Iakovlev, 2013. + */ + +#import + +#define TGUnsupportedMediaAttachmentType ((int)0x3837BEF7) + +@interface TGUnsupportedMediaAttachment : TGMediaAttachment + +@property (nonatomic, strong) NSData *data; + +@end diff --git a/LegacyComponents/TGUnsupportedMediaAttachment.m b/LegacyComponents/TGUnsupportedMediaAttachment.m new file mode 100644 index 0000000000..fcb6cad9d0 --- /dev/null +++ b/LegacyComponents/TGUnsupportedMediaAttachment.m @@ -0,0 +1,44 @@ +#import "TGUnsupportedMediaAttachment.h" + +@implementation TGUnsupportedMediaAttachment + +- (id)init +{ + self = [super init]; + if (self != nil) + { + self.type = TGUnsupportedMediaAttachmentType; + } + return self; +} + +- (void)serialize:(NSMutableData *)data +{ + uint8_t version = 0; + [data appendBytes:&version length:1]; + + int32_t length = (int32_t)_data.length; + [data appendBytes:&length length:4]; + [data appendData:_data]; +} + +- (TGMediaAttachment *)parseMediaAttachment:(NSInputStream *)is +{ + uint8_t version = 0; + [is read:&version maxLength:1]; + + int32_t dataLength = 0; + [is read:(uint8_t *)&dataLength maxLength:4]; + + uint8_t *dataBytes = malloc(dataLength); + [is read:dataBytes maxLength:dataLength]; + + TGUnsupportedMediaAttachment *unsupportedMediaAttachment = [[TGUnsupportedMediaAttachment alloc] init]; + + unsupportedMediaAttachment.data = [[NSData alloc] initWithBytesNoCopy:dataBytes length:dataLength freeWhenDone:true]; + + return unsupportedMediaAttachment; +} + + +@end diff --git a/LegacyComponents/TGUser.h b/LegacyComponents/TGUser.h new file mode 100644 index 0000000000..0f54cb5e23 --- /dev/null +++ b/LegacyComponents/TGUser.h @@ -0,0 +1,127 @@ +#import + +#import +#import + +typedef enum { + TGUserSexUnknown = 0, + TGUserSexMale = 1, + TGUserSexFemale = 2 +} TGUserSex; + +typedef enum { + TGUserPresenceValueLately = -2, + TGUserPresenceValueWithinAWeek = -3, + TGUserPresenceValueWithinAMonth = -4, + TGUserPresenceValueALongTimeAgo = -5 +} TGUserPresenceValue; + +typedef struct { + bool online; + int lastSeen; + int temporaryLastSeen; +} TGUserPresence; + +typedef enum { + TGUserLinkKnown = 1, + TGUserLinkForeignRequested = 2, + TGUserLinkForeignMutual = 4, + TGUserLinkMyRequested = 8, + TGUserLinkMyContact = 16, + TGUserLinkForeignHasPhone = 32 +} TGUserLink; + +typedef enum { + TGUserFieldUid = 1, + TGUserFieldPhoneNumber = 2, + TGUserFieldPhoneNumberHash = 4, + TGUserFieldFirstName = 8, + TGUserFieldLastName = 16, + TGUserFieldPhonebookFirstName = 32, + TGUserFieldPhonebookLastName = 64, + TGUserFieldSex = 128, + TGUserFieldPhotoUrlSmall = 256, + TGUserFieldPhotoUrlMedium = 512, + TGUserFieldPhotoUrlBig = 1024, + TGUserFieldPresenceLastSeen = 2048, + TGUserFieldPresenceOnline = 4096, + TGUserFieldUsername = 8192, + TGUserFieldOther = 8192 * 2 +} TGUserFields; + +typedef enum { + TGUserKindGeneric = 0, + TGUserKindBot = 1, + TGUserKindSmartBot = 2 +} TGUserKind; + +typedef enum { + TGBotKindGeneric = 0, + TGBotKindPrivate = 1 +} TGBotKind; + +@class TGNotificationPrivacyAccountSetting; + +#define TGUserFieldsAllButPresenceMask (TGUserFieldUid | TGUserFieldPhoneNumber | TGUserFieldPhoneNumberHash | TGUserFieldFirstName| TGUserFieldLastName | TGUserFieldPhonebookFirstName | TGUserFieldPhonebookLastName | TGUserFieldSex | TGUserFieldPhotoUrlSmall | TGUserFieldPhotoUrlMedium | TGUserFieldPhotoUrlBig) + +@interface TGUser : NSObject + +@property (nonatomic) int uid; +@property (nonatomic, strong) NSString *phoneNumber; +@property (nonatomic) int64_t phoneNumberHash; +@property (nonatomic, strong) NSString *firstName; +@property (nonatomic, strong) NSString *lastName; +@property (nonatomic, strong) NSString *userName; +@property (nonatomic, strong) NSString *phonebookFirstName; +@property (nonatomic, strong) NSString *phonebookLastName; +@property (nonatomic) TGUserSex sex; +@property (nonatomic) NSString *photoUrlSmall; +@property (nonatomic) NSString *photoUrlMedium; +@property (nonatomic) NSString *photoUrlBig; + +@property (nonatomic) TGUserPresence presence; + +@property (nonatomic) int contactId; + +@property (nonatomic) int32_t kind; +@property (nonatomic) int32_t botKind; +@property (nonatomic) int32_t botInfoVersion; + +@property (nonatomic) int32_t flags; + +@property (nonatomic) bool isVerified; +@property (nonatomic) bool hasExplicitContent; +@property (nonatomic, strong) NSString *restrictionReason; +@property (nonatomic, strong) NSString *contextBotPlaceholder; +@property (nonatomic) bool isContextBot; + +@property (nonatomic, strong) NSDictionary *customProperties; + +@property (nonatomic) bool minimalRepresentation; + +@property (nonatomic, strong) NSString *about; + +@property (nonatomic) bool botInlineGeo; + +@property (nonatomic, readonly) bool isBot; + +- (id)copyWithZone:(NSZone *)zone; + +- (bool)hasAnyName; + +- (NSString *)realFirstName; +- (NSString *)realLastName; + +- (NSString *)displayName; +- (NSString *)displayRealName; +- (NSString *)displayFirstName; +- (NSString *)compactName; + +- (NSString *)formattedPhoneNumber; + +- (bool)isEqualToUser:(TGUser *)anotherUser; +- (int)differenceFromUser:(TGUser *)anotherUser; + ++ (TGUserPresence)approximatePresenceFromPresence:(TGUserPresence)presence currentTime:(NSTimeInterval)currentTime; + +@end diff --git a/LegacyComponents/TGUser.m b/LegacyComponents/TGUser.m new file mode 100644 index 0000000000..71882b156a --- /dev/null +++ b/LegacyComponents/TGUser.m @@ -0,0 +1,415 @@ +#import "TGUser.h" + +#import "LegacyComponentsInternal.h" + +#import "TGStringUtils.h" +#import "TGPhoneUtils.h" + +#import "NSObject+TGLock.h" + +#import "PSKeyValueCoder.h" + +#import "TGConversation.h" + +typedef enum { + TGUserFlagVerified = (1 << 0), + TGUserFlagHasExplicitContent = (1 << 1), + TGUserFlagIsContextBot = (1 << 2), + TGUserFlagMinimalRepresentation = (1 << 3), + TGUserFlagBotInlineGeo = (1 << 4) +} TGUserFlags; + +@interface TGUser () +{ + bool _contactIdInitialized; + bool _formattedPhoneInitialized; + + TG_SYNCHRONIZED_DEFINE(_cachedValues); +} + +@property (nonatomic, strong) NSString *cachedFormattedNumber; + +@end + +@implementation TGUser + +- (instancetype)init { + self = [super init]; + if (self != nil) { + TG_SYNCHRONIZED_INIT(_cachedValues); + } + return self; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + self = [super init]; + if (self != nil) + { + TG_SYNCHRONIZED_INIT(_cachedValues); + _kind = [coder decodeInt32ForCKey:"k"]; + if (_kind == TGUserKindBot || _kind == TGUserKindSmartBot) + { + _botInfoVersion = [coder decodeInt32ForCKey:"biv"]; + _botKind = [coder decodeInt32ForCKey:"bk"]; + } + _flags = [coder decodeInt32ForCKey:"f"]; + _restrictionReason = [coder decodeStringForCKey:"rr"]; + if ([self isContextBot]) { + _contextBotPlaceholder = [coder decodeStringForCKey:"cbp"]; + } + _about = [coder decodeStringForCKey:"a"]; + } + return self; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder +{ + [coder encodeInt32:_kind forCKey:"k"]; + if (_kind == TGUserKindBot || _kind == TGUserKindSmartBot) + { + [coder encodeInt32:_botInfoVersion forCKey:"biv"]; + [coder encodeInt32:_botKind forCKey:"bk"]; + } + [coder encodeInt32:_flags forCKey:"f"]; + [coder encodeString:_restrictionReason forCKey:"rr"]; + if ([self isContextBot]) { + [coder encodeString:_contextBotPlaceholder forCKey:"cbp"]; + } + [coder encodeString:_about forCKey:"a"]; +} + +- (id)copyWithZone:(NSZone *)__unused zone +{ + TGUser *user = [[TGUser alloc] init]; + + user.uid = _uid; + user.phoneNumber = _phoneNumber; + user.phoneNumberHash = _phoneNumberHash; + user.firstName = _firstName; + user.lastName = _lastName; + user.userName = _userName; + user.phonebookFirstName = _phonebookFirstName; + user.phonebookLastName = _phonebookLastName; + user.sex = _sex; + user.photoUrlSmall = _photoUrlSmall; + user.photoUrlMedium = _photoUrlMedium; + user.photoUrlBig = _photoUrlBig; + user.presence = _presence; + user.customProperties = _customProperties; + user.contactId = _contactId; + user->_contactIdInitialized = _contactIdInitialized; + user->_formattedPhoneInitialized = _formattedPhoneInitialized; + user.cachedFormattedNumber = _cachedFormattedNumber; + user->_kind = _kind; + user->_botInfoVersion = _botInfoVersion; + user->_botKind = _botKind; + user->_flags = _flags; + user->_restrictionReason = _restrictionReason; + user->_contextBotPlaceholder = _contextBotPlaceholder; + + return user; +} + +- (bool)hasAnyName +{ + return _firstName.length != 0 || _lastName.length != 0 || _phonebookFirstName.length != 0 || _phonebookLastName.length != 0; +} + +- (bool)isBot { + return _kind == TGUserKindBot || _kind == TGUserKindSmartBot; +} + +- (NSString *)firstName +{ + return (_phonebookFirstName.length != 0 || _phonebookLastName.length != 0) ? _phonebookFirstName : ((_firstName.length != 0 || _lastName.length != 0) ? _firstName : (_phoneNumber.length == 0 ? TGLocalized(@"User.DeletedAccount") : [self formattedPhoneNumber])); +} + +- (NSString *)lastName +{ + return (_phonebookFirstName.length != 0 || _phonebookLastName.length != 0) ? _phonebookLastName : ((_firstName.length != 0 || _lastName.length != 0) ? _lastName : nil); +} + +- (NSString *)realFirstName +{ + return _firstName; +} + +- (NSString *)realLastName +{ + return _lastName; +} + +- (NSString *)displayName +{ + NSString *firstName = self.firstName; + NSString *lastName = self.lastName; + + if (firstName != nil && firstName.length != 0 && lastName != nil && lastName.length != 0) + { + if (TGIsKorean()) + return [[NSString alloc] initWithFormat:@"%@ %@", lastName, firstName]; + else + return [[NSString alloc] initWithFormat:@"%@ %@", firstName, lastName]; + } + else if (firstName != nil && firstName.length != 0) + return firstName; + else if (lastName != nil && lastName.length != 0) + return lastName; + + return @""; +} + +- (NSString *)displayRealName +{ + NSString *firstName = self.realFirstName; + NSString *lastName = self.realLastName; + + if (firstName != nil && firstName.length != 0 && lastName != nil && lastName.length != 0) + return [[NSString alloc] initWithFormat:@"%@ %@", firstName, lastName]; + else if (firstName != nil && firstName.length != 0) + return firstName; + else if (lastName != nil && lastName.length != 0) + return lastName; + + return @""; +} + +- (NSString *)displayFirstName +{ + NSString *firstName = self.firstName; + if (firstName.length != 0) + return firstName; + + return self.lastName; +} + +- (NSString *)compactName +{ + NSString *firstName = self.firstName; + NSString *lastName = self.lastName; + + if (firstName != nil && firstName.length != 0 && lastName != nil && lastName.length != 0) + return [[NSString alloc] initWithFormat:@"%@.%@", [firstName substringToIndex:1], lastName]; + else if (firstName != nil && firstName.length != 0) + return firstName; + else if (lastName != nil && lastName.length != 0) + return lastName; + + return @""; +} + +- (void)setPhoneNumber:(NSString *)phoneNumber +{ + TG_SYNCHRONIZED_BEGIN(_cachedValues); + _phoneNumber = phoneNumber; + _contactIdInitialized = false; + _formattedPhoneInitialized = false; + TG_SYNCHRONIZED_END(_cachedValues); +} + +- (int)contactId +{ + if (!_contactIdInitialized) + { + int contactId = 0; + if (_phoneNumber != nil && _phoneNumber.length != 0) + contactId = phoneMatchHash(_phoneNumber); + + TG_SYNCHRONIZED_BEGIN(_cachedValues); + _contactId = contactId; + _contactIdInitialized = true; + TG_SYNCHRONIZED_END(_cachedValues); + } + + return _contactId; +} + +- (NSString *)formattedPhoneNumber +{ + if (_formattedPhoneInitialized) + return _cachedFormattedNumber; + else + { + NSString *cachedFormattedNumber = nil; + if (_phoneNumber.length != 0) + cachedFormattedNumber = [TGPhoneUtils formatPhone:_phoneNumber forceInternational:true]; + + TG_SYNCHRONIZED_BEGIN(_cachedValues); + _cachedFormattedNumber = cachedFormattedNumber; + _formattedPhoneInitialized = true; + TG_SYNCHRONIZED_END(_cachedValues); + + return cachedFormattedNumber; + } +} + +- (BOOL)isEqual:(id)object +{ + return [object isKindOfClass:[TGUser class]] && [self isEqualToUser:object]; +} + +- (bool)isEqualToUser:(TGUser *)anotherUser +{ + if (anotherUser.uid == _uid && + ((anotherUser.realFirstName == nil && _firstName == nil) || [anotherUser.realFirstName isEqualToString:_firstName]) && + ((anotherUser.realLastName == nil && _lastName == nil) || [anotherUser.realLastName isEqualToString:_lastName]) && + anotherUser.sex == _sex && + ((anotherUser.phonebookFirstName == nil && _phonebookFirstName == nil) || [anotherUser.phonebookFirstName isEqualToString:_phonebookFirstName]) && + ((anotherUser.phonebookLastName == nil && _phonebookLastName == nil) || [anotherUser.phonebookLastName isEqualToString:_phonebookLastName]) && + ((anotherUser.phoneNumber == nil && _phoneNumber == nil) || [anotherUser.phoneNumber isEqualToString:_phoneNumber]) && + anotherUser.phoneNumberHash == _phoneNumberHash && + ((anotherUser.photoUrlSmall == nil && _photoUrlSmall == nil) || [anotherUser.photoUrlSmall isEqualToString:_photoUrlSmall]) && + ((anotherUser.photoUrlMedium == nil && _photoUrlMedium == nil) || [anotherUser.photoUrlMedium isEqualToString:_photoUrlMedium]) && + ((anotherUser.photoUrlBig == nil && _photoUrlBig == nil) || [anotherUser.photoUrlBig isEqualToString:_photoUrlBig]) && + anotherUser.presence.online == _presence.online && anotherUser.presence.lastSeen == _presence.lastSeen && TGStringCompare(_userName, anotherUser.userName) && anotherUser.kind == _kind && anotherUser.botKind == _botKind && + TGStringCompare(_restrictionReason, anotherUser.restrictionReason)) + { + return true; + } + return false; +} + +- (int)differenceFromUser:(TGUser *)anotherUser +{ + int difference = 0; + + if (_uid != anotherUser.uid) + difference |= TGUserFieldUid; + + if ((_phoneNumber == nil) != (anotherUser.phoneNumber == nil) || (_phoneNumber != nil && ![_phoneNumber isEqualToString:anotherUser.phoneNumber])) + difference |= TGUserFieldPhoneNumber; + + if (_phoneNumberHash != anotherUser.phoneNumberHash) + difference |= TGUserFieldPhoneNumberHash; + + if ((_firstName == nil) != (anotherUser.realFirstName == nil) || (_firstName != nil && ![_firstName isEqualToString:anotherUser.realFirstName])) + difference |= TGUserFieldFirstName; + + if ((_lastName == nil) != (anotherUser.realLastName == nil) || (_lastName != nil && ![_lastName isEqualToString:anotherUser.realLastName])) + difference |= TGUserFieldLastName; + + if (!TGStringCompare(_userName, anotherUser.userName)) + difference |= TGUserFieldUsername; + + if ((_phonebookFirstName == nil) != (anotherUser.phonebookFirstName == nil) || (_phonebookFirstName != nil && ![_phonebookFirstName isEqualToString:anotherUser.phonebookFirstName])) + difference |= TGUserFieldPhonebookFirstName; + + if ((_phonebookLastName == nil) != (anotherUser.phonebookLastName == nil) || (_phonebookLastName != nil && ![_phonebookLastName isEqualToString:anotherUser.phonebookLastName])) + difference |= TGUserFieldPhonebookLastName; + + if (_sex != anotherUser.sex) + difference |= TGUserFieldSex; + + if ((_photoUrlSmall == nil) != (anotherUser.photoUrlSmall == nil) || (_photoUrlSmall != nil && ![_photoUrlSmall isEqualToString:anotherUser.photoUrlSmall])) + difference |= TGUserFieldPhotoUrlSmall; + + if ((_photoUrlMedium == nil) != (anotherUser.photoUrlMedium == nil) || (_photoUrlMedium != nil && ![_photoUrlMedium isEqualToString:anotherUser.photoUrlMedium])) + difference |= TGUserFieldPhotoUrlMedium; + + if ((_photoUrlBig == nil) != (anotherUser.photoUrlBig == nil) || (_photoUrlBig != nil && ![_photoUrlBig isEqualToString:anotherUser.photoUrlBig])) + difference |= TGUserFieldPhotoUrlBig; + + if (_presence.lastSeen != anotherUser.presence.lastSeen) + difference |= TGUserFieldPresenceLastSeen; + + if (_presence.online != anotherUser.presence.online) + difference |= TGUserFieldPresenceOnline; + + if (anotherUser.kind != _kind) + difference |= TGUserFieldOther; + + if (anotherUser.botKind != _botKind) + difference |= TGUserFieldOther; + + if (anotherUser.flags != _flags) { + difference |= TGUserFieldOther; + } + + if (!TGStringCompare(anotherUser.restrictionReason, _restrictionReason)) { + difference |= TGUserFieldOther; + } + + if (!TGStringCompare(anotherUser.contextBotPlaceholder, _contextBotPlaceholder)) { + difference |= TGUserFieldOther; + } + + return difference; +} + ++ (TGUserPresence)approximatePresenceFromPresence:(TGUserPresence)presence currentTime:(NSTimeInterval)currentTime +{ + if (presence.lastSeen <= 0) + return presence; + + if (presence.lastSeen >= (int)(currentTime - 60 * 60 * 24 * 4)) + return (TGUserPresence){.online = false, .lastSeen = TGUserPresenceValueLately, .temporaryLastSeen = 0}; + else if (presence.lastSeen >= (int)(currentTime - 60 * 60 * 24 * 4)) + return (TGUserPresence){.online = false, .lastSeen = TGUserPresenceValueWithinAWeek, .temporaryLastSeen = 0}; + else if (presence.lastSeen >= (int)(currentTime - 60 * 60 * 24 * 31)) + return (TGUserPresence){.online = false, .lastSeen = TGUserPresenceValueWithinAMonth, .temporaryLastSeen = 0}; + + return (TGUserPresence){.online = false, .lastSeen = TGUserPresenceValueALongTimeAgo, .temporaryLastSeen = 0}; +} + +- (bool)isVerified { + return _flags & TGUserFlagVerified; +} + +- (void)setIsVerified:(bool)isVerified { + if (isVerified) { + _flags |= TGUserFlagVerified; + } else { + _flags &= ~TGUserFlagVerified; + } +} + +- (bool)isContextBot { + return _flags & TGUserFlagIsContextBot; +} + +- (void)setIsContextBot:(bool)isContextBot { + if (isContextBot) { + _flags |= TGUserFlagIsContextBot; + } else { + _flags &= ~TGUserFlagIsContextBot; + } +} + +- (bool)hasExplicitContent { + return _flags & TGConversationFlagHasExplicitContent; +} + +- (void)setHasExplicitContent:(bool)hasExplicitContent { + if (hasExplicitContent) { + _flags |= TGConversationFlagHasExplicitContent; + } else { + _flags &= ~TGConversationFlagHasExplicitContent; + } +} + +- (bool)minimalRepresentation { + return _flags & TGUserFlagMinimalRepresentation; +} + +- (void)setMinimalRepresentation:(bool)minimalRepresentation { + if (minimalRepresentation) { + _flags |= TGUserFlagMinimalRepresentation; + } else { + _flags &= ~TGUserFlagMinimalRepresentation; + } +} + +- (bool)botInlineGeo { + return _flags & TGUserFlagBotInlineGeo; +} + +- (void)setBotInlineGeo:(bool)botInlineGeo { + if (botInlineGeo) { + _flags |= TGUserFlagBotInlineGeo; + } else { + _flags &= ~TGUserFlagBotInlineGeo; + } +} + +@end diff --git a/LegacyComponents/TGViaUserAttachment.h b/LegacyComponents/TGViaUserAttachment.h new file mode 100644 index 0000000000..eb91465190 --- /dev/null +++ b/LegacyComponents/TGViaUserAttachment.h @@ -0,0 +1,12 @@ +#import + +#define TGViaUserAttachmentType ((int)0xA3F4C8F5) + +@interface TGViaUserAttachment : TGMediaAttachment + +@property (nonatomic, readonly) int32_t userId; +@property (nonatomic, readonly) NSString *username; + +- (instancetype)initWithUserId:(int32_t)userId username:(NSString *)username; + +@end diff --git a/LegacyComponents/TGViaUserAttachment.m b/LegacyComponents/TGViaUserAttachment.m new file mode 100644 index 0000000000..f0d940bd8b --- /dev/null +++ b/LegacyComponents/TGViaUserAttachment.m @@ -0,0 +1,44 @@ +#import "TGViaUserAttachment.h" + +#import "NSInputStream+TL.h" + +@implementation TGViaUserAttachment + +- (instancetype)initWithUserId:(int32_t)userId username:(NSString *)username { + self = [super init]; + if (self != nil) { + self.type = TGViaUserAttachmentType; + + _userId = userId; + _username = username; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + return [self initWithUserId:[aDecoder decodeInt32ForKey:@"userId"] username:[aDecoder decodeObjectForKey:@"username"]]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeInt32:_userId forKey:@"userId"]; + if (_username != nil) { + [aCoder encodeObject:_username forKey:@"username"]; + } +} + +- (void)serialize:(NSMutableData *)data +{ + NSData *serializedData = [NSKeyedArchiver archivedDataWithRootObject:self]; + int32_t length = (int32_t)serializedData.length; + [data appendBytes:&length length:4]; + [data appendData:serializedData]; +} + +- (TGMediaAttachment *)parseMediaAttachment:(NSInputStream *)is +{ + int32_t length = [is readInt32]; + NSData *data = [is readData:length]; + return [NSKeyedUnarchiver unarchiveObjectWithData:data]; +} + +@end diff --git a/LegacyComponents/TGVideoInfo.h b/LegacyComponents/TGVideoInfo.h new file mode 100644 index 0000000000..f4f02acd3c --- /dev/null +++ b/LegacyComponents/TGVideoInfo.h @@ -0,0 +1,19 @@ +/* + * This is the source code of Telegram for iOS v. 1.1 + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Peter Iakovlev, 2013. + */ + +#import + +@interface TGVideoInfo : NSObject + +- (void)addVideoWithQuality:(int)quality url:(NSString *)url size:(int)size; +- (NSString *)urlWithQuality:(int)quality actualQuality:(int *)actualQuality actualSize:(int *)actualSize; + +- (void)serialize:(NSMutableData *)data; ++ (TGVideoInfo *)deserialize:(NSInputStream *)is; + +@end diff --git a/LegacyComponents/TGVideoInfo.mm b/LegacyComponents/TGVideoInfo.mm new file mode 100644 index 0000000000..6db3e21c64 --- /dev/null +++ b/LegacyComponents/TGVideoInfo.mm @@ -0,0 +1,176 @@ +#import "TGVideoInfo.h" + +#include + +struct TGVideoQualityRecord +{ + int quality; + int size; + NSString *url; + + TGVideoQualityRecord(int quality_, NSString *url_, int size_) : + quality(quality_), size(size_) + { + url = url_; + } + + TGVideoQualityRecord(const TGVideoQualityRecord &other) + { + url = other.url; + quality = other.quality; + size = other.size; + } + + TGVideoQualityRecord & operator= (const TGVideoQualityRecord &other) + { + if (this != &other) + { + url = other.url; + quality = other.quality; + size = other.size; + } + + return *this; + } + + ~TGVideoQualityRecord() + { + url = nil; + } +}; + +@interface TGVideoInfo () +{ + std::vector _qualitySet; +} + +@end + +@implementation TGVideoInfo + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + _qualitySet.push_back(TGVideoQualityRecord(0, [aDecoder decodeObjectForKey:@"url"], [aDecoder decodeInt32ForKey:@"size"])); + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + if (!_qualitySet.empty()) { + auto q = _qualitySet[0]; + [aCoder encodeInt32:q.size forKey:@"size"]; + [aCoder encodeObject:q.url forKey:@"url"]; + } +} + +- (BOOL)isEqual:(id)object +{ + if (![object isKindOfClass:[TGVideoInfo class]]) + return false; + + TGVideoInfo *other = object; + + if (_qualitySet.size() != other->_qualitySet.size()) + return false; + + for (size_t i = 0; i < _qualitySet.size(); i++) + { + if (_qualitySet[i].quality != other->_qualitySet[i].quality) + return false; + + if (_qualitySet[i].size != other->_qualitySet[i].size) + return false; + + if (![_qualitySet[i].url isEqualToString:other->_qualitySet[i].url]) + return false; + } + + return true; +} + +- (void)addVideoWithQuality:(int)quality url:(NSString *)url size:(int)size +{ + _qualitySet.push_back(TGVideoQualityRecord(quality, url, size)); +} + +- (NSString *)urlWithQuality:(int)quality actualQuality:(int *)actualQuality actualSize:(int *)actualSize +{ + NSString *url = nil; + int closestQuality = INT_MAX; + int closestSize = 0; + + for (std::vector::iterator it = _qualitySet.begin(); it != _qualitySet.end(); it++) + { + if (it->quality == quality) + { + if (actualQuality != NULL) + *actualQuality = quality; + if (actualSize != NULL) + *actualSize = it->size; + return it->url; + } + else if (ABS(it->quality - quality) < ABS(closestQuality - quality)) + { + closestQuality = quality; + url = it->url; + closestSize = it->size; + } + } + + if (url != nil) + { + if (actualQuality != NULL) + *actualQuality = closestQuality; + if (actualSize != NULL) + *actualSize = closestSize; + } + + return url; +} + +- (void)serialize:(NSMutableData *)data +{ + size_t count = _qualitySet.size(); + [data appendBytes:&count length:4]; + + for (std::vector::iterator it = _qualitySet.begin(); it != _qualitySet.end(); it++) + { + NSData *urlData = [it->url dataUsingEncoding:NSUTF8StringEncoding]; + int32_t length = (int32_t)urlData.length; + [data appendBytes:&length length:4]; + [data appendData:urlData]; + + [data appendBytes:&it->quality length:4]; + [data appendBytes:&it->size length:4]; + } +} + ++ (TGVideoInfo *)deserialize:(NSInputStream *)is +{ + TGVideoInfo *videoInfo = [[TGVideoInfo alloc] init]; + + int count = 0; + [is read:(uint8_t *)&count maxLength:4]; + + for (int i = 0; i < count; i++) + { + int length = 0; + [is read:(uint8_t *)&length maxLength:4]; + uint8_t *urlBytes = (uint8_t *)malloc(length); + [is read:urlBytes maxLength:length]; + NSString *url = [[NSString alloc] initWithBytesNoCopy:urlBytes length:length encoding:NSUTF8StringEncoding freeWhenDone:true]; + + int quality = 0; + [is read:(uint8_t *)&quality maxLength:4]; + + int size = 0; + [is read:(uint8_t *)&size maxLength:4]; + + [videoInfo addVideoWithQuality:quality url:url size:size]; + } + + return videoInfo; +} + +@end diff --git a/LegacyComponents/TGVideoMediaAttachment.h b/LegacyComponents/TGVideoMediaAttachment.h new file mode 100644 index 0000000000..94f2ec4c82 --- /dev/null +++ b/LegacyComponents/TGVideoMediaAttachment.h @@ -0,0 +1,38 @@ +/* + * This is the source code of Telegram for iOS v. 1.1 + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Peter Iakovlev, 2013. + */ + +#import + +#import +#import + +#define TGVideoMediaAttachmentType ((int)0x338EAA20) + +@interface TGVideoMediaAttachment : TGMediaAttachment + +@property (nonatomic) int64_t videoId; +@property (nonatomic) int64_t accessHash; + +@property (nonatomic) int64_t localVideoId; + +@property (nonatomic) int duration; +@property (nonatomic) CGSize dimensions; + +@property (nonatomic, strong) TGVideoInfo *videoInfo; +@property (nonatomic, strong) TGImageInfo *thumbnailInfo; + +@property (nonatomic) NSString *caption; +@property (nonatomic) bool hasStickers; +@property (nonatomic, strong) NSArray *embeddedStickerDocuments; + +@property (nonatomic, readonly) NSArray *textCheckingResults; + +@property (nonatomic) bool loopVideo; +@property (nonatomic) bool roundMessage; + +@end diff --git a/LegacyComponents/TGVideoMediaAttachment.m b/LegacyComponents/TGVideoMediaAttachment.m new file mode 100644 index 0000000000..f8aaaedade --- /dev/null +++ b/LegacyComponents/TGVideoMediaAttachment.m @@ -0,0 +1,285 @@ +#import "TGVideoMediaAttachment.h" + +#import "TGMessage.h" + +#import "LegacyComponentsInternal.h" + +@interface TGVideoMediaAttachment () +{ + NSArray *_textCheckingResults; +} +@end + +@implementation TGVideoMediaAttachment + +@synthesize videoId = _videoId; +@synthesize accessHash = _accessHash; + +@synthesize localVideoId = _localVideoId; + +@synthesize duration = _duration; +@synthesize dimensions = _dimensions; + +@synthesize videoInfo = _videoInfo; +@synthesize thumbnailInfo = _thumbnailInfo; + +- (id)init +{ + self = [super init]; + if (self != nil) + { + self.type = TGVideoMediaAttachmentType; + } + return self; +} + +- (id)copyWithZone:(NSZone *)__unused zone +{ + TGVideoMediaAttachment *videoAttachment = [[TGVideoMediaAttachment alloc] init]; + + videoAttachment.videoId = _videoId; + videoAttachment.accessHash = _accessHash; + videoAttachment.localVideoId = _localVideoId; + videoAttachment.duration = _duration; + videoAttachment.dimensions = _dimensions; + videoAttachment.videoInfo = _videoInfo; + videoAttachment.thumbnailInfo = _thumbnailInfo; + videoAttachment.caption = _caption; + videoAttachment.hasStickers = _hasStickers; + videoAttachment.embeddedStickerDocuments = _embeddedStickerDocuments; + videoAttachment.loopVideo = _loopVideo; + videoAttachment.roundMessage = _roundMessage; + + return videoAttachment; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + self = [super init]; + if (self != nil) { + self.type = TGVideoMediaAttachmentType; + + _videoId = [aDecoder decodeInt64ForKey:@"videoId"]; + _accessHash = [aDecoder decodeInt64ForKey:@"accessHash"]; + _localVideoId = [aDecoder decodeInt64ForKey:@"localVideoId"]; + _duration = [aDecoder decodeInt32ForKey:@"duration"]; + _dimensions = [aDecoder decodeCGSizeForKey:@"dimensions"]; + _videoInfo = [aDecoder decodeObjectForKey:@"videoInfo"]; + _thumbnailInfo = [aDecoder decodeObjectForKey:@"thumbInfo"]; + _caption = [aDecoder decodeObjectForKey:@"caption"]; + _hasStickers = [aDecoder decodeBoolForKey:@"hasStickers"]; + _embeddedStickerDocuments = [aDecoder decodeObjectForKey:@"embeddedStickerDocuments"]; + _roundMessage = [aDecoder decodeBoolForKey:@"roundMessage"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeInt64:_videoId forKey:@"videoId"]; + [aCoder encodeInt64:_accessHash forKey:@"accessHash"]; + [aCoder encodeInt64:_localVideoId forKey:@"localVideoId"]; + [aCoder encodeInt32:_duration forKey:@"duration"]; + [aCoder encodeCGSize:_dimensions forKey:@"dimensions"]; + [aCoder encodeObject:_videoInfo forKey:@"videoInfo"]; + [aCoder encodeObject:_thumbnailInfo forKey:@"thumbInfo"]; + [aCoder encodeObject:_caption forKey:@"caption"]; + [aCoder encodeBool:_hasStickers forKey:@"hasStickers"]; + if (_embeddedStickerDocuments != nil) { + [aCoder encodeObject:_embeddedStickerDocuments forKey:@"embeddedStickerDocuments"]; + } + [aCoder encodeBool:_roundMessage forKey:@"roundMessage"]; +} + +- (BOOL)isEqual:(id)object +{ + if (![object isKindOfClass:[TGVideoMediaAttachment class]]) + return false; + + TGVideoMediaAttachment *other = object; + + if (_videoId != other.videoId || _accessHash != other.accessHash || _localVideoId != other.localVideoId || _duration != other.duration || !CGSizeEqualToSize(_dimensions, other.dimensions)) + return false; + + if (!TGObjectCompare(_videoInfo, other.videoInfo)) + return false; + + if (!TGObjectCompare(_thumbnailInfo, other.thumbnailInfo)) + return false; + + if (!TGObjectCompare(_caption, other.caption)) + return false; + + if (_hasStickers != other.hasStickers) { + return false; + } + + if (_roundMessage != other.roundMessage) { + return false; + } + + return true; +} + +- (void)serialize:(NSMutableData *)data +{ + int32_t modernTag = 0x7abacaf1; + [data appendBytes:&modernTag length:4]; + + uint8_t version = 4; + [data appendBytes:&version length:1]; + + int dataLengthPtr = (int)data.length; + int zero = 0; + [data appendBytes:&zero length:4]; + + [data appendBytes:&_videoId length:8]; + [data appendBytes:&_accessHash length:8]; + + [data appendBytes:&_localVideoId length:8]; + + uint8_t hasVideoInfo = _videoInfo != nil ? 1 : 0; + [data appendBytes:&hasVideoInfo length:1]; + if (hasVideoInfo != 0) + [_videoInfo serialize:data]; + + uint8_t hasThumbnailInfo = _thumbnailInfo != nil ? 1 : 0; + [data appendBytes:&hasThumbnailInfo length:1]; + if (hasThumbnailInfo != 0) + [_thumbnailInfo serialize:data]; + + [data appendBytes:&_duration length:4]; + + int dimension = (int)_dimensions.width; + [data appendBytes:&dimension length:4]; + dimension = (int)_dimensions.height; + [data appendBytes:&dimension length:4]; + + NSData *captionData = [_caption dataUsingEncoding:NSUTF8StringEncoding]; + int32_t captionLength = (int32_t)captionData.length; + [data appendBytes:&captionLength length:4]; + if (captionLength != 0) + [data appendData:captionData]; + + int8_t hasStickers = _hasStickers ? 1 : 0; + [data appendBytes:&hasStickers length:1]; + + if (_embeddedStickerDocuments.count == 0) { + int32_t zero = 0; + [data appendBytes:&zero length:4]; + } else { + NSData *stickerData = [NSKeyedArchiver archivedDataWithRootObject:_embeddedStickerDocuments]; + int32_t length = (int32_t)stickerData.length; + [data appendBytes:&length length:4]; + [data appendData:stickerData]; + } + + int8_t roundMessage = _roundMessage ? 1 : 0; + [data appendBytes:&roundMessage length:1]; + + int dataLength = (int)(data.length - dataLengthPtr - 4); + [data replaceBytesInRange:NSMakeRange(dataLengthPtr, 4) withBytes:&dataLength]; +} + +- (TGMediaAttachment *)parseMediaAttachment:(NSInputStream *)is +{ + int32_t dataLength = 0; + [is read:(uint8_t *)&dataLength maxLength:4]; + + uint8_t version = 1; + if (dataLength == 0x7abacaf1) + { + [is read:(uint8_t *)&version maxLength:1]; + [is read:(uint8_t *)&dataLength maxLength:4]; + } + + TGVideoMediaAttachment *videoAttachment = [[TGVideoMediaAttachment alloc] init]; + + int64_t videoId = 0; + [is read:(uint8_t *)&videoId maxLength:8]; + videoAttachment.videoId = videoId; + + int64_t accessHash = 0; + [is read:(uint8_t *)&accessHash maxLength:8]; + videoAttachment.accessHash = accessHash; + + int64_t localVideoId = 0; + [is read:(uint8_t *)&localVideoId maxLength:8]; + videoAttachment.localVideoId = localVideoId; + + uint8_t hasVideoInfo = 0; + [is read:&hasVideoInfo maxLength:1]; + + if (hasVideoInfo != 0) + videoAttachment.videoInfo = [TGVideoInfo deserialize:is]; + + uint8_t hasThumbnailInfo = 0; + [is read:&hasThumbnailInfo maxLength:1]; + + if (hasThumbnailInfo != 0) + videoAttachment.thumbnailInfo = [TGImageInfo deserialize:is]; + + int duration = 0; + [is read:(uint8_t *)&duration maxLength:4]; + videoAttachment.duration = duration; + + CGSize dimensions = CGSizeZero; + int dimension = 0; + [is read:(uint8_t *)&dimension maxLength:4]; + dimensions.width = dimension; + dimension = 0; + [is read:(uint8_t *)&dimension maxLength:4]; + dimensions.height = dimension; + videoAttachment.dimensions = dimensions; + + if (version >= 2) + { + int32_t captionLength = 0; + [is read:(uint8_t *)&captionLength maxLength:4]; + if (captionLength != 0) + { + uint8_t *captionBytes = malloc(captionLength); + [is read:captionBytes maxLength:captionLength]; + videoAttachment.caption = [[NSString alloc] initWithBytesNoCopy:captionBytes length:captionLength encoding:NSUTF8StringEncoding freeWhenDone:true]; + } + } + + if (version >= 3) + { + int8_t hasStickers = 0; + [is read:(uint8_t *)&hasStickers maxLength:1]; + videoAttachment.hasStickers = hasStickers != 0; + + int32_t stickerDataLength = 0; + [is read:(uint8_t *)&stickerDataLength maxLength:4]; + if (stickerDataLength != 0) { + uint8_t *stickerBytes = malloc(stickerDataLength); + [is read:stickerBytes maxLength:stickerDataLength]; + NSData *stickerData = [[NSData alloc] initWithBytesNoCopy:stickerBytes length:stickerDataLength freeWhenDone:true]; + videoAttachment.embeddedStickerDocuments = [NSKeyedUnarchiver unarchiveObjectWithData:stickerData]; + } + } + + if (version >= 4) + { + int8_t roundMessage = 0; + [is read:(uint8_t *)&roundMessage maxLength:1]; + videoAttachment.roundMessage = roundMessage != 0; + } + + return videoAttachment; +} + +- (NSArray *)textCheckingResults +{ + if (_caption.length < 2) + _textCheckingResults = [NSArray array]; + + if (_textCheckingResults == nil) + { + NSArray *textCheckingResults = [TGMessage textCheckingResultsForText:_caption highlightMentionsAndTags:true highlightCommands:true entities:nil]; + _textCheckingResults = textCheckingResults ?: [NSArray array]; + } + + return _textCheckingResults; +} + +@end diff --git a/LegacyComponents/TGWebDocument.h b/LegacyComponents/TGWebDocument.h new file mode 100644 index 0000000000..abc12dc4f2 --- /dev/null +++ b/LegacyComponents/TGWebDocument.h @@ -0,0 +1,34 @@ +#import + +#import + +#import + +@interface TGWebDocumentReference : NSObject + +@property (nonatomic, strong, readonly) NSString *url; +@property (nonatomic, readonly) int64_t accessHash; +@property (nonatomic, readonly) int32_t size; +@property (nonatomic, readonly) int32_t datacenterId; + +- (instancetype)initWithUrl:(NSString *)url accessHash:(int64_t)accessHash size:(int32_t)size datacenterId:(int32_t)datacenterId; + +- (instancetype)initWithString:(NSString *)string; +- (NSString *)toString; + +@end + +@interface TGWebDocument : NSObject + +@property (nonatomic, strong, readonly) NSString *url; +@property (nonatomic, readonly) int64_t accessHash; +@property (nonatomic, readonly) int32_t size; +@property (nonatomic, strong, readonly) NSString *mimeType; +@property (nonatomic, strong, readonly) NSArray *attributes; +@property (nonatomic, readonly) int32_t datacenterId; + +@property (nonatomic, strong, readonly) TGWebDocumentReference *reference; + +- (instancetype)initWithUrl:(NSString *)url accessHash:(int64_t)accessHash size:(int32_t)size mimeType:(NSString *)mimeType attributes:(NSArray *)attributes datacenterId:(int32_t)datacenterId; + +@end diff --git a/LegacyComponents/TGWebDocument.m b/LegacyComponents/TGWebDocument.m new file mode 100644 index 0000000000..e5b45c0031 --- /dev/null +++ b/LegacyComponents/TGWebDocument.m @@ -0,0 +1,112 @@ +#import "TGWebDocument.h" + +#import "LegacyComponentsInternal.h" + +#import "PSKeyValueEncoder.h" +#import "PSKeyValueDecoder.h" + +@implementation TGWebDocumentReference + +- (instancetype)initWithUrl:(NSString *)url accessHash:(int64_t)accessHash size:(int32_t)size datacenterId:(int32_t)datacenterId { + self = [super init]; + if (self != nil) { + _url = url; + _accessHash = accessHash; + _size = size; + _datacenterId = datacenterId; + } + return self; +} + +- (instancetype)initWithKeyValueCoder:(PSKeyValueCoder *)coder { + return [self initWithUrl:[coder decodeStringForCKey:"u"] accessHash:[coder decodeInt64ForCKey:"a"] size:[coder decodeInt32ForCKey:"s"] datacenterId:[coder decodeInt32ForCKey:"d"]]; +} + +- (void)encodeWithKeyValueCoder:(PSKeyValueCoder *)coder { + [coder encodeString:_url forCKey:"u"]; + [coder encodeInt64:_accessHash forCKey:"a"]; + [coder encodeInt32:_size forCKey:"s"]; + [coder encodeInt32:_datacenterId forCKey:"d"]; +} + +- (instancetype)initWithString:(NSString *)string { + if ([string hasPrefix:@"webdoc"]) { + NSData *data = [[NSData alloc] initWithBase64EncodedString:[string substringFromIndex:6] options:0]; + if (data != nil) { + PSKeyValueDecoder *decoder = [[PSKeyValueDecoder alloc] initWithData:data]; + return [[TGWebDocumentReference alloc] initWithKeyValueCoder:decoder]; + } else { + return nil; + } + } else { + return nil; + } +} + +- (NSString *)toString { + PSKeyValueEncoder *encoder = [[PSKeyValueEncoder alloc] init]; + [self encodeWithKeyValueCoder:encoder]; + return [@"webdoc" stringByAppendingString:[[encoder data] base64Encoding]]; +} + +@end + +@implementation TGWebDocument + +- (instancetype)initWithUrl:(NSString *)url accessHash:(int64_t)accessHash size:(int32_t)size mimeType:(NSString *)mimeType attributes:(NSArray *)attributes datacenterId:(int32_t)datacenterId { + self = [super init]; + if (self != nil) { + _url = url; + _accessHash = accessHash; + _size = size; + _mimeType = mimeType; + _attributes = attributes; + _datacenterId = datacenterId; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + return [self initWithUrl:[aDecoder decodeObjectForKey:@"url"] accessHash:[aDecoder decodeInt64ForKey:@"accessHash"] size:[aDecoder decodeInt32ForKey:@"size"] mimeType:[aDecoder decodeObjectForKey:@"mimeType"] attributes:[aDecoder decodeObjectForKey:@"attributes"] datacenterId:[aDecoder decodeInt32ForKey:@"datacenterId"]]; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder { + [aCoder encodeObject:_url forKey:@"url"]; + [aCoder encodeInt64:_accessHash forKey:@"accessHash"]; + [aCoder encodeInt32:_size forKey:@"size"]; + [aCoder encodeObject:_mimeType forKey:@"mimeType"]; + [aCoder encodeObject:_attributes forKey:@"attributes"]; + [aCoder encodeInt32:_datacenterId forKey:@"datacenterId"]; +} + +- (BOOL)isEqual:(id)object { + if (![object isKindOfClass:[TGWebDocument class]]) { + return false; + } + TGWebDocument *other = object; + if (!TGStringCompare(_url, other->_url)) { + return false; + } + if (_accessHash != other->_accessHash) { + return false; + } + if (_size != other->_size) { + return false; + } + if (!TGStringCompare(_mimeType, other->_mimeType)) { + return false; + } + if (!TGObjectCompare(_attributes, other->_attributes)) { + return false; + } + if (_datacenterId != other->_datacenterId) { + return false; + } + return true; +} + +- (TGWebDocumentReference *)reference { + return [[TGWebDocumentReference alloc] initWithUrl:_url accessHash:_accessHash size:_size datacenterId:_datacenterId]; +} + +@end diff --git a/LegacyComponents/TGWebPageMediaAttachment.h b/LegacyComponents/TGWebPageMediaAttachment.h new file mode 100644 index 0000000000..3ab4a3e71d --- /dev/null +++ b/LegacyComponents/TGWebPageMediaAttachment.h @@ -0,0 +1,34 @@ +#import + +#import +#import +#import + +#import + +#define TGWebPageMediaAttachmentType ((int)0x584197af) + +@interface TGWebPageMediaAttachment : TGMediaAttachment + +@property (nonatomic) int64_t webPageId; +@property (nonatomic) int64_t webPageLocalId; +@property (nonatomic) int32_t pendingDate; +@property (nonatomic) int32_t webPageHash; + +@property (nonatomic, strong) NSString *url; +@property (nonatomic, strong) NSString *displayUrl; +@property (nonatomic, strong) NSString *pageType; +@property (nonatomic, strong) NSString *siteName; +@property (nonatomic, strong) NSString *title; +@property (nonatomic, strong) NSString *pageDescription; +@property (nonatomic, strong) NSArray *pageDescriptionEntities; +@property (nonatomic, strong) TGImageMediaAttachment *photo; +@property (nonatomic, strong) NSString *embedUrl; +@property (nonatomic, strong) NSString *embedType; +@property (nonatomic) CGSize embedSize; +@property (nonatomic, strong) NSNumber *duration; +@property (nonatomic, strong) NSString *author; +@property (nonatomic, strong) TGDocumentMediaAttachment *document; +@property (nonatomic, strong) TGInstantPage *instantPage; + +@end diff --git a/LegacyComponents/TGWebPageMediaAttachment.m b/LegacyComponents/TGWebPageMediaAttachment.m new file mode 100644 index 0000000000..1848dd8446 --- /dev/null +++ b/LegacyComponents/TGWebPageMediaAttachment.m @@ -0,0 +1,169 @@ +#import "TGWebPageMediaAttachment.h" + +#import "LegacyComponentsInternal.h" + +#import "NSInputStream+TL.h" + +@implementation TGWebPageMediaAttachment + +- (instancetype)init +{ + self = [super init]; + if (self != nil) + { + self.type = TGWebPageMediaAttachmentType; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super init]; + if (self != nil) + { + self.type = TGWebPageMediaAttachmentType; + + _webPageId = [aDecoder decodeInt64ForKey:@"webPageId"]; + _webPageLocalId = [aDecoder decodeInt64ForKey:@"webPageLocalId"]; + _pendingDate = [aDecoder decodeInt32ForKey:@"pendingDate"]; + _url = [aDecoder decodeObjectForKey:@"url"]; + _displayUrl = [aDecoder decodeObjectForKey:@"displayUrl"]; + _pageType = [aDecoder decodeObjectForKey:@"pageType"]; + _siteName = [aDecoder decodeObjectForKey:@"siteName"]; + _title = [aDecoder decodeObjectForKey:@"title"]; + _pageDescription = [aDecoder decodeObjectForKey:@"pageDescription"]; + _photo = [aDecoder decodeObjectForKey:@"photo"]; + _embedUrl = [aDecoder decodeObjectForKey:@"embedUrl"]; + _embedType = [aDecoder decodeObjectForKey:@"embedType"]; + _embedSize = [[aDecoder decodeObjectForKey:@"embedSize"] CGSizeValue]; + _duration = [aDecoder decodeObjectForKey:@"duration"]; + _author = [aDecoder decodeObjectForKey:@"author"]; + _document = [aDecoder decodeObjectForKey:@"document"]; + _pageDescriptionEntities = [aDecoder decodeObjectForKey:@"pageDescriptionEntities"]; + _instantPage = [aDecoder decodeObjectForKey:@"page"]; + _webPageHash = [aDecoder decodeInt32ForKey:@"phash"]; + } + return self; +} + +- (void)encodeWithCoder:(NSCoder *)aCoder +{ + [aCoder encodeInt64:_webPageId forKey:@"webPageId"]; + [aCoder encodeInt64:_webPageLocalId forKey:@"webPageLocalId"]; + [aCoder encodeInt32:_pendingDate forKey:@"pendingDate"]; + if (_url != nil) + [aCoder encodeObject:_url forKey:@"url"]; + if (_displayUrl != nil) + [aCoder encodeObject:_displayUrl forKey:@"displayUrl"]; + if (_pageType != nil) + [aCoder encodeObject:_pageType forKey:@"pageType"]; + if (_siteName != nil) + [aCoder encodeObject:_siteName forKey:@"siteName"]; + if (_title != nil) + [aCoder encodeObject:_title forKey:@"title"]; + if (_pageDescription != nil) + [aCoder encodeObject:_pageDescription forKey:@"pageDescription"]; + if (_photo != nil) + [aCoder encodeObject:_photo forKey:@"photo"]; + if (_embedUrl != nil) + [aCoder encodeObject:_embedUrl forKey:@"embedUrl"]; + if (_embedType != nil) + [aCoder encodeObject:_embedType forKey:@"embedType"]; + [aCoder encodeObject:[NSValue valueWithCGSize:_embedSize] forKey:@"embedSize"]; + if (_duration != nil) + [aCoder encodeObject:_duration forKey:@"duration"]; + if (_author != nil) + [aCoder encodeObject:_author forKey:@"author"]; + if (_document != nil) { + [aCoder encodeObject:_document forKey:@"document"]; + } + if (_pageDescriptionEntities != nil) { + [aCoder encodeObject:_pageDescriptionEntities forKey:@"pageDescriptionEntities"]; + } + if (_instantPage != nil) { + [aCoder encodeObject:_instantPage forKey:@"page"]; + } + [aCoder encodeInt32:_webPageHash forKey:@"phash"]; +} + +- (id)copyWithZone:(NSZone *)__unused zone +{ + TGWebPageMediaAttachment *attachment = [[TGWebPageMediaAttachment alloc] init]; + + attachment.webPageId = _webPageId; + attachment.webPageLocalId = _webPageLocalId; + attachment.pendingDate = _pendingDate; + attachment.url = _url; + attachment.displayUrl = _displayUrl; + attachment.pageType = _pageType; + attachment.siteName = _siteName; + attachment.title = _title; + attachment.pageDescription = _pageDescription; + attachment.photo = _photo; + attachment.embedUrl = _embedUrl; + attachment.embedType = _embedType; + attachment.embedSize = _embedSize; + attachment.duration = _duration; + attachment.author = _author; + attachment.document = _document; + attachment.pageDescriptionEntities = _pageDescriptionEntities; + attachment.instantPage = _instantPage; + attachment.webPageHash = _webPageHash; + + return attachment; +} + +- (BOOL)isEqual:(id)object +{ + if (![object isKindOfClass:[TGWebPageMediaAttachment class]]) { + return false; + } + TGWebPageMediaAttachment *other = (TGWebPageMediaAttachment *)object; + if (other.instantPage != nil && _instantPage != nil) { + if (![other.instantPage isEqual:_instantPage]) { + return false; + } + } else if ((other.instantPage != nil) != (_instantPage != nil)) { + return false; + } + + if (other->_webPageHash != _webPageHash) { + return false; + } + + return [object isKindOfClass:[TGWebPageMediaAttachment class]] && + ((TGWebPageMediaAttachment *)object)->_webPageId == _webPageId && + ((TGWebPageMediaAttachment *)object)->_webPageLocalId == _webPageLocalId && + ((TGWebPageMediaAttachment *)object)->_pendingDate == _pendingDate && + TGStringCompare(((TGWebPageMediaAttachment *)object)->_url, _url) && + TGStringCompare(((TGWebPageMediaAttachment *)object)->_displayUrl, _displayUrl) && + TGStringCompare(((TGWebPageMediaAttachment *)object)->_pageType, _pageType) && + TGStringCompare(((TGWebPageMediaAttachment *)object)->_siteName, _siteName) && + TGStringCompare(((TGWebPageMediaAttachment *)object)->_title, _title) && + TGStringCompare(((TGWebPageMediaAttachment *)object)->_pageDescription, _pageDescription) && + TGStringCompare(((TGWebPageMediaAttachment *)object)->_embedUrl, _embedUrl) && + TGStringCompare(((TGWebPageMediaAttachment *)object)->_embedType, _embedType) && + ((TGWebPageMediaAttachment *)object)->_duration == _duration && + TGStringCompare(((TGWebPageMediaAttachment *)object)->_author, _author); +} + +- (void)serialize:(NSMutableData *)data +{ + NSData *serializedData = [NSKeyedArchiver archivedDataWithRootObject:self]; + int32_t length = (int32_t)serializedData.length; + [data appendBytes:&length length:4]; + [data appendData:serializedData]; +} + +- (TGMediaAttachment *)parseMediaAttachment:(NSInputStream *)is +{ + int32_t length = [is readInt32]; + NSData *data = [is readData:length]; + @try { + return [NSKeyedUnarchiver unarchiveObjectWithData:data]; + } @catch (NSException *e) { + } + return nil; +} + +@end